alonsosilva commited on
Commit
6609677
1 Parent(s): 5f23068
Files changed (6) hide show
  1. Dockerfile +30 -0
  2. LICENSE +21 -0
  3. README.md +2 -1
  4. app.py +88 -0
  5. requirements.txt +5 -0
  6. viewlistener.vue +33 -0
Dockerfile ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11
2
+
3
+ # Set up a new user named "user" with user ID 1000
4
+ RUN useradd -m -u 1000 user
5
+
6
+ # Switch to the "user" user
7
+ USER user
8
+
9
+ # Set home to the user's home directory
10
+ ENV HOME=/home/user \
11
+ PATH=/home/user/.local/bin:$PATH
12
+
13
+ # Set the working directory to the user's home directory
14
+ WORKDIR $HOME/app
15
+
16
+ # Try and run pip command after setting the user with `USER user` to avoid permission issues with Python
17
+ RUN pip install --no-cache-dir --upgrade pip
18
+
19
+ # Copy the current directory contents into the container at $HOME/app setting the owner to the user
20
+ COPY --chown=user . $HOME/app
21
+
22
+ COPY --chown=user requirements.txt .
23
+
24
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
25
+
26
+ COPY --chown=user app.py .
27
+
28
+ COPY --chown=user viewlistener.vue .
29
+
30
+ ENTRYPOINT ["solara", "run", "app.py", "--host=0.0.0.0", "--port", "7860"]
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Alonso Silva Allende
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -1,11 +1,12 @@
1
  ---
2
  title: Solara AnyWidget Vega-Altair
3
- emoji: 📉
4
  colorFrom: pink
5
  colorTo: yellow
6
  sdk: docker
7
  pinned: false
8
  license: mit
 
9
  ---
10
 
11
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: Solara AnyWidget Vega-Altair
3
+ emoji:
4
  colorFrom: pink
5
  colorTo: yellow
6
  sdk: docker
7
  pinned: false
8
  license: mit
9
+ app_port: 7860
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import solara
2
+ import anywidget
3
+ import traitlets
4
+ from vega_datasets import data
5
+ import altair as alt
6
+ import matplotlib.pyplot as plt
7
+ from matplotlib.figure import Figure
8
+ import pandas as pd
9
+ from typing import List
10
+ from typing_extensions import TypedDict
11
+
12
+ class ChartWidget(anywidget.AnyWidget):
13
+ spec = traitlets.Dict().tag(sync=True)
14
+ selection = traitlets.Dict().tag(sync=True)
15
+ _esm = """
16
+ import embed from "https://cdn.jsdelivr.net/npm/vega-embed@6/+esm";
17
+ async function render({ model, el }) {
18
+ let spec = model.get("spec");
19
+ let api = await embed(el, spec);
20
+ api.view.addSignalListener(spec.params[0].name, (_, update) => {
21
+ model.set("selection", update);
22
+ model.save_changes();
23
+ })
24
+ }
25
+ export default { render };
26
+ """
27
+
28
+ selected: solara.Reactive[TypedDict] = solara.reactive({'Horsepower': [45, 231], 'Miles_per_Gallon': [8, 47]})
29
+
30
+ @solara.component_vue("viewlistener.vue")
31
+ def ViewListener(view_data=None, on_view_data=None, children=[], style={}):
32
+ ...
33
+
34
+ @solara.component
35
+ def Plot(sub):
36
+ dpi = 100
37
+ view_data = solara.use_reactive({"width": 800, "height": 600})
38
+ width, height = view_data.value["width"], view_data.value["height"]
39
+ fig = Figure(figsize=(width / dpi, height / dpi), dpi=dpi)
40
+ ax = fig.subplots()
41
+ ax.hist(sub["Weight_in_lbs"], edgecolor="#26a269", facecolor ="#57e389")
42
+ ax.set_xlabel("Weight_in_lbs")
43
+ ax.set_ylabel("Count of Records")
44
+ with ViewListener(view_data=view_data.value, on_view_data=view_data.set, style={"width": "100%", "height": "55vh"}):
45
+ solara.FigureMatplotlib(fig)
46
+
47
+ counter = solara.reactive(0)
48
+ @solara.component
49
+ def Page():
50
+ title = "AnyWidget+Solara: Cars dataset"
51
+ with solara.Head():
52
+ solara.Title(f"{title}")
53
+ with solara.AppBar():
54
+ solara.lab.ThemeToggle(enable_auto=False)
55
+ with solara.Column(style={"padding":"30px"}):
56
+ #solara.Markdown("#Anywidget+Solara")
57
+ df = data.cars()
58
+ brush = alt.selection_interval()
59
+
60
+ points = alt.Chart(df).mark_point().encode(
61
+ x = "Horsepower",
62
+ y = "Miles_per_Gallon",
63
+ color = alt.condition(brush, "Origin", alt.value("lightgray")),
64
+ tooltip = ["Horsepower", "Miles_per_Gallon"],
65
+ ).add_params(
66
+ brush
67
+ )
68
+ bars = alt.Chart(df).mark_bar().encode(
69
+ y = "Origin",
70
+ color = "Origin",
71
+ x = "count(Origin)"
72
+ ).transform_filter(
73
+ brush
74
+ )
75
+ chart = (points & bars)
76
+ sub = df
77
+ with solara.Row():
78
+ widget = ChartWidget.element(spec=chart.to_dict(), on_selection=selected.set)
79
+ with solara.Column(style={"margin": "0"}):
80
+ for field, (lower, upper) in (selected.value).items():
81
+ sub = sub[(sub[field]>lower) & (sub[field]<upper)]
82
+ if not sub.empty:
83
+ Plot(sub)
84
+ solara.DataFrame(sub, items_per_page=10)
85
+ file_object = sub.to_csv(index=False)
86
+ with solara.FileDownload(file_object, "cars_subset.csv", mime_type="application/vnd.ms-excel"):
87
+ solara.Button("Download selection", icon_name="mdi-cloud-download-outline", color="primary")
88
+ Page()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ anywidget==0.9.9
2
+ solara==1.31.0
3
+ altair==5.3.0
4
+ vega-datasets=0.9.0
5
+ matplotlib==3.8.4
viewlistener.vue ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <template>
2
+ <div :style="style">
3
+ <jupyter-widget v-for="child in children" :key="child" :widget="child"></jupyter-widget>
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ module.exports = {
9
+ created() {
10
+ this.resizeObserver = new ResizeObserver(entries => {
11
+ this._updateViewData();
12
+ });
13
+ },
14
+ mounted() {
15
+ this.resizeObserver.observe(this.$el);
16
+ this._updateViewData();
17
+ },
18
+ destroyed() {
19
+ this.resizeObserver.unobserve(this.$el);
20
+ },
21
+ methods: {
22
+ _updateViewData() {
23
+ const view_data = {
24
+ width: this.$el.clientWidth,
25
+ height: this.$el.clientHeight,
26
+ };
27
+ this.view_data = view_data
28
+ }
29
+ },
30
+ }
31
+ </script>
32
+
33
+ <style id="viewlistener"></style>