andrewlee1807 commited on
Commit
f2be9d4
ยท
1 Parent(s): 2c42c41

Publish Wave app to Hugging Face

Browse files
Files changed (5) hide show
  1. .dockerignore +6 -0
  2. Dockerfile +43 -0
  3. app.py +207 -0
  4. docker-entrypoint.sh +14 -0
  5. requirements.txt +3 -0
.dockerignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ .venv/
2
+ .git/
3
+ .gitignore
4
+ .dockerignore
5
+ Dockerfile
6
+ README.md
Dockerfile ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Accept Python version during build
2
+ ARG PYTHON_VERSION="3.8"
3
+
4
+ FROM python:${PYTHON_VERSION}-slim
5
+
6
+ # Install OS dependencies
7
+ RUN apt-get update && apt-get -y upgrade \
8
+ && export DEBIAN_FRONTEND=noninteractive \
9
+ && apt-get -y install --no-install-recommends curl build-essential
10
+
11
+ # Create a non-root user
12
+ RUN useradd --create-home appuser
13
+ USER appuser
14
+ WORKDIR /home/appuser/app
15
+
16
+ # Install Wave
17
+ ARG WAVE_VERSION="0.20.0"
18
+
19
+ ENV WAVE_HOME="/home/appuser/wave"
20
+ RUN \
21
+ mkdir -p "${WAVE_HOME}" && \
22
+ curl -fsSL https://github.com/h2oai/wave/releases/download/v${WAVE_VERSION}/wave-${WAVE_VERSION}-linux-amd64.tar.gz | tar -C ${WAVE_HOME} -xzv 2>&1
23
+ ENV WAVE_PATH="${WAVE_HOME}/wave-${WAVE_VERSION}-linux-amd64"
24
+
25
+ # Create a virtual environment
26
+ ENV VIRTUAL_ENV=/home/appuser/venv
27
+ RUN python3 -m venv $VIRTUAL_ENV
28
+ ENV PATH="$VIRTUAL_ENV/bin:$PATH"
29
+
30
+ # Install Python dependencies
31
+ COPY requirements.txt .
32
+ RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
33
+
34
+ # Copy app code
35
+ COPY --chown=appuser:appuser . .
36
+
37
+ # Set permissions for the Entrypoint script
38
+ RUN chmod +x docker-entrypoint.sh
39
+
40
+ ARG PYTHON_MODULE
41
+ ENV PYTHON_MODULE="${PYTHON_MODULE}"
42
+
43
+ ENTRYPOINT [ "./docker-entrypoint.sh" ]
app.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Note: Main must be imported even though it is not used.
2
+ from h2o_wave import Q, ui, app, main, data, on
3
+ import numpy as np
4
+ import pandas as pd
5
+ import random
6
+ import time
7
+ import asyncio
8
+
9
+ NUMBER_SENSORS = 4
10
+ LIMIT_LEN_SHOWING = 20
11
+ REALTIME_UPDATE = 5 # seconds
12
+ list_data = []
13
+ line_charts_data = [] # [(sensor_id, [data]), ...]
14
+ for i in range(NUMBER_SENSORS):
15
+ line_charts_data.append((i, []))
16
+ start_year = 1991
17
+ while True:
18
+ for i in range(NUMBER_SENSORS):
19
+ random_value = random.uniform(1, 50) # Generate a random value between 1 and 50
20
+ value = (str(start_year), random_value)
21
+ line_charts_data[i][1].append(value)
22
+ start_year += 1
23
+ if start_year == 2021:
24
+ break
25
+
26
+
27
+ async def update_value(q: Q):
28
+ while True:
29
+ time.sleep(REALTIME_UPDATE)
30
+ # TODO: Update realtime values for current stats cards
31
+ # value_counts = random.randint(0, 100)
32
+ # percentages = random.random()
33
+ percentages = np.random.random(NUMBER_SENSORS)
34
+ value_counts = np.random.randint(0, 100, NUMBER_SENSORS)
35
+
36
+ show_multiple_stats_cards(q, value_counts, percentages)
37
+ await q.page.save()
38
+
39
+
40
+ async def api_get_data():
41
+ global list_data, line_charts_data, start_year
42
+ while True:
43
+ for i in range(NUMBER_SENSORS):
44
+ random_value = random.uniform(1, 50) # Generate a random value between 1 and 50
45
+ value = (str(start_year), random_value)
46
+ line_charts_data[i][1].append(value)
47
+ start_year += 1
48
+ await asyncio.sleep(REALTIME_UPDATE)
49
+
50
+
51
+ async def update_charts(q: Q):
52
+ global line_charts_data
53
+ while True:
54
+ time.sleep(REALTIME_UPDATE)
55
+ for sensor_data in line_charts_data:
56
+ sensor_id = str(sensor_data[0])
57
+ data = sensor_data[1]
58
+ limites_showing_data = LIMIT_LEN_SHOWING if len(data) > LIMIT_LEN_SHOWING else len(data)
59
+ show_line_graph(q, sensor_id, data[-limites_showing_data:])
60
+
61
+ percentages = np.random.random(NUMBER_SENSORS)
62
+ value_counts = np.random.randint(0, 100, NUMBER_SENSORS)
63
+ show_multiple_stats_cards(q, value_counts, percentages)
64
+ await q.page.save()
65
+
66
+
67
+ def on_startup():
68
+ print('App started!')
69
+
70
+
71
+ def on_shutdown():
72
+ print('App stopped!')
73
+
74
+
75
+ task_data = asyncio.create_task(api_get_data())
76
+
77
+
78
+ @app('/sensor', on_startup=on_startup, on_shutdown=on_shutdown, mode='broadcast')
79
+ async def sever(q: Q):
80
+ apply_layout(q)
81
+ show_homepage(q)
82
+ # show_histograms(q, df, index=[1, 7, 8])
83
+
84
+ # await update_value(q)
85
+ await update_charts(q)
86
+ # await q.page.save()
87
+
88
+
89
+ def show_homepage(q: Q):
90
+ q.page['header'] = ui.header_card(
91
+ box=ui.box('header', width='100%', height='86px'),
92
+ icon='Video360Generic',
93
+ icon_color='White',
94
+ title='AgroTrack Application',
95
+ subtitle='๋‹ด์–‘๋†์—…๊ธฐ์ˆ ์„ผํ„ฐ')
96
+
97
+ q.page['footer'] = ui.footer_card(
98
+ box='footer',
99
+ caption='Copyright (C) [AISeed](https://sprout-fibre-be6.notion.site/AISEED-40e62bb70c024a2c974c0a7051aca86f?pvs=4) Designed by [Blackhole](https://github.com/andrewlee1807/)')
100
+
101
+
102
+ def apply_layout(q: Q):
103
+ q.page['meta'] = ui.meta_card(box='', theme='oceanic', title="SmartFarm's Sensor Dashboard",
104
+ layouts=[
105
+ ui.layout(
106
+ breakpoint='xl',
107
+ width='1600px',
108
+ zones=[
109
+ ui.zone('header'),
110
+ ui.zone('body', direction=ui.ZoneDirection.ROW, zones=[
111
+ ui.zone('content', direction=ui.ZoneDirection.COLUMN, zones=[
112
+ ui.zone('section1'),
113
+ ui.zone('top', size='300px', direction=ui.ZoneDirection.ROW),
114
+ ui.zone('section2'),
115
+ ui.zone('bottom', direction=ui.ZoneDirection.ROW),
116
+ ui.zone('chart1', direction=ui.ZoneDirection.ROW),
117
+ ]),
118
+ ]),
119
+ ui.zone('footer')
120
+ ]),
121
+ ])
122
+
123
+
124
+ ## Making stats cards
125
+ def make_stats_card_data(q: Q, column):
126
+ value_counts = column.value_counts()
127
+ total = value_counts.sum()
128
+ return value_counts[1], total
129
+
130
+
131
+ # Randomly generate data for stats cards: percentage and value (randomly generated)
132
+
133
+
134
+ def show_stats_card(q: Q, name, value_count, percentage):
135
+ q.page['stat_' + name] = ui.tall_gauge_stat_card(
136
+ box=ui.box('top', width='12.5%'),
137
+ title='Number of houses with ' + name,
138
+ value='={{intl one}}',
139
+ aux_value='={{intl perc style="percent" minimum_fraction_digits=2 maximum_fraction_digits=2 }}',
140
+ progress=percentage,
141
+ data=dict(one=int(value_count), perc=float(percentage)),
142
+ plot_color='$red'
143
+ )
144
+
145
+
146
+ def show_multiple_stats_cards(q: Q, value_counts, percentages):
147
+ for i, j in enumerate(zip(value_counts, percentages)):
148
+ name = "feature_" + str(i)
149
+ value_count, percentage = j
150
+ show_stats_card(q, name, value_count, percentage)
151
+
152
+
153
+ # Making histograms
154
+ def make_histogram_data(values):
155
+ count, division = np.histogram(values)
156
+ return [(x, y) for x, y in zip(division.tolist(), count.tolist())]
157
+
158
+
159
+ def show_histogram(q: Q, values, variable_name, index):
160
+ q.page['feat' + str(index)] = ui.tall_series_stat_card(
161
+ box=ui.box('bottom', width='25%'),
162
+ title='Linear tall series' + variable_name,
163
+ value='=${{intl qux minimum_fraction_digits=2 maximum_fraction_digits=2}}',
164
+ aux_value='={{intl quux style="percent" minimum_fraction_digits=1 maximum_fraction_digits=1}}',
165
+ data=dict(qux=800, quux=80 / 100),
166
+ plot_type='area',
167
+ plot_category='foo',
168
+ plot_value='qux',
169
+ plot_color='$yellow',
170
+ plot_data=data('foo qux', 3, rows=[[90, 0.9], [50, 0.5], [80, 0.8]]),
171
+ plot_zero_value=0,
172
+ plot_curve='smooth',
173
+ )
174
+
175
+
176
+ def show_line_graph(q: Q, name, data_rows):
177
+ q.page['section2'] = ui.section_card(box='section2',
178
+ title='Plot of the data',
179
+ subtitle='')
180
+ position = 'bottom' if int(name) < 2 else 'chart1'
181
+ q.page[name] = ui.plot_card(
182
+ box=ui.box(position, width='50%'),
183
+ title='Sensor ' + name,
184
+ animate=True,
185
+ data=data('time value', LIMIT_LEN_SHOWING, rows=data_rows),
186
+ events=['select_marks'],
187
+ plot=ui.plot([
188
+ ui.mark(type='line',
189
+ x_scale='time',
190
+ x='=time',
191
+ y='=value',
192
+ y_min=0, interactive=True),
193
+ ui.mark(type='point',
194
+ x='=time',
195
+ # x='={{intl year type="time" month="numeric" day="numeric" hour="numeric" minute="numeric" hourCycle="h24" }}',
196
+ y='=value',
197
+ size=4,
198
+ fill_color='#FFFF28',
199
+ x_title='Time', y_title='Value',
200
+ interactive=True),
201
+ ]),
202
+ )
203
+
204
+
205
+ @on('select_marks')
206
+ async def on_marks_selected(q: Q):
207
+ print("Click")
docker-entrypoint.sh ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ export H2O_WAVE_ADDRESS="http://127.0.0.1:${PORT}"
6
+
7
+ printf '\n$ ( cd %s && ./waved -listen ":%s" & )\n\n' "${WAVE_PATH}" "${PORT}"
8
+ (cd "${WAVE_PATH}" && ./waved -listen ":${PORT}" &)
9
+
10
+ sleep 3
11
+
12
+ printf '\n$ wave run --no-reload --no-autostart %s\n\n' "$PYTHON_MODULE"
13
+
14
+ exec wave run --no-reload --no-autostart "$PYTHON_MODULE"
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ h2o-wave
2
+ numpy
3
+ pandas