Skip to content

Commit

Permalink
Align with doc on file_download control (Avaiga#943)
Browse files Browse the repository at this point in the history
* - Add code example for the file_download control
- Doc for download()

* Better (?) doc for file_download and linters.

* Comments

* Add chart rebuild property example

---------

Co-authored-by: Fabien Lelaquais <[email protected]>
  • Loading branch information
FabienLelaquais and Fabien Lelaquais authored Oct 4, 2023
1 parent bb17843 commit deea4e3
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 12 deletions.
2 changes: 1 addition & 1 deletion doc/examples/charts/advanced-shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# Python environment and run:
# python <script>
# -----------------------------------------------------------------------------------------
from taipy,gui import Gui
from taipy.gui import Gui


# Function to plot: x^3/3-x
Expand Down
2 changes: 1 addition & 1 deletion doc/examples/charts/candlestick-simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

page = """
# Candlestick - Simple
<|{stock}|chart|type=candlestick|x=Date|open=Open|close=Close|low=Low|high=High|>
"""

Expand Down
2 changes: 1 addition & 1 deletion doc/examples/charts/continuous-error-simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@

page = """
# Continuous Error - Simple
<|{data}|chart|properties={properties}|>
"""

Expand Down
2 changes: 1 addition & 1 deletion doc/examples/charts/example-rebuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@
<|{selected_type}|toggle|lov={types}|>
"""

Gui(page=page).run()
Gui(page).run()
78 changes: 78 additions & 0 deletions doc/examples/controls/file_download-dynamic-temp-file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Copyright 2023 Avaiga Private Limited
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# -----------------------------------------------------------------------------------------
# To execute this script, make sure that the taipy-gui package is installed in your
# Python environment and run:
# python <script>
# -----------------------------------------------------------------------------------------
from taipy.gui import Gui, download
from decimal import getcontext, Decimal
from tempfile import NamedTemporaryFile
import os

# Initial precision
precision = 10
# Stores the path to the temporary file
temp_path = None


def pi(precision: int) -> list[int]:
"""Compute Pi to the required precision.
Adapted from https://docs.python.org/3/library/decimal.html
"""
saved_precision = getcontext().prec # Save precision
getcontext().prec = precision
three = Decimal(3) # substitute "three=3.0" for regular floats
lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
while s != lasts:
lasts = s
n, na = n + na, na + 8
d, da = d + da, da + 32
t = (t * n) / d
s += t
digits = []
while s != 0:
integral = int(s)
digits.append(integral)
s = (s - integral) * 10
getcontext().prec = saved_precision
return digits


# Remove the temporary file
def clean_up(state):
os.remove(state.temp_path)


# Generate the digits, save them in a CSV temporary file, then trigger a download action
# for that file.
def download_pi(state):
digits = pi(state.precision)
with NamedTemporaryFile("r+t", suffix=".csv", delete=False) as temp_file:
state.temp_path = temp_file.name
temp_file.write("index,digit\n")
for i, d in enumerate(digits):
temp_file.write(f"{i},{d}\n")
download(state, content=temp_file.name, name="pi.csv", on_action=clean_up)


page = """
# File Download - Dynamic content
Precision:
<|{precision}|slider|min=2|max=10000|>
<|{None}|file_download|on_action=download_pi|label=Download Pi digits|>
"""

Gui(page).run()
69 changes: 69 additions & 0 deletions doc/examples/controls/file_download-dynamic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright 2023 Avaiga Private Limited
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# -----------------------------------------------------------------------------------------
# To execute this script, make sure that the taipy-gui package is installed in your
# Python environment and run:
# python <script>
# -----------------------------------------------------------------------------------------
from taipy.gui import Gui, download
from decimal import getcontext, Decimal
import io

# Initial precision
precision = 10


def pi(precision: int) -> list[int]:
"""Compute Pi to the required precision.
Adapted from https://docs.python.org/3/library/decimal.html
"""
saved_precision = getcontext().prec # Save precision
getcontext().prec = precision
three = Decimal(3) # substitute "three=3.0" for regular floats
lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
while s != lasts:
lasts = s
n, na = n + na, na + 8
d, da = d + da, da + 32
t = (t * n) / d
s += t
digits = []
while s != 0:
integral = int(s)
digits.append(integral)
s = (s - integral) * 10
getcontext().prec = saved_precision
return digits


# Generate the digits, save them in a CSV file content, and trigger a download action
# so the user can retrieve them
def download_pi(state):
digits = pi(state.precision)
buffer = io.StringIO()
buffer.write("index,digit\n")
for i, d in enumerate(digits):
buffer.write(f"{i},{d}\n")
download(state, content=bytes(buffer.getvalue(), "UTF-8"), name="pi.csv")


page = """
# File Download - Dynamic content
Precision:
<|{precision}|slider|min=2|max=10000|>
<|{None}|file_download|on_action=download_pi|label=Download Pi digits|>
"""

Gui(page).run()
28 changes: 26 additions & 2 deletions src/taipy/gui/gui_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,33 @@ def download(
Arguments:
state (State^): The current user state as received in any callback.
content: File path or file content.
content: File path or file content. See below.
name: File name for the content on the client browser (defaults to content name).
on_action: Callback function to call when the download ends.
on_action: Callback function (or callback name) to call when the download ends. See below.
## Notes:
- *content*: this parameter can hold several values depending on your use case:
- a string: the value must be an existing path name to the file that gets downloaded or
the URL to the resource you want to download.
- a buffer (such as a `bytes` object): if the size of the buffer is smaller than
the [*data_url_max_size*](../../gui/configuration/#p-data_url_max_size) configuration
setting, then the [`python-magic`](https://pypi.org/project/python-magic/) package is
used to determine the [MIME type](https://en.wikipedia.org/wiki/Media_type) of the
buffer content, and the download is performed using a generated "data:" URL with
the relevant type, and a base64-encoded version of the buffer content.<br/>
If the buffer is too large, its content is transferred after saving it in a temporary
server file.
- *on_action*: TODO CHECK<br/>
this callback is triggered when the transfer of the content is achieved.</br>
In this function, you can perform any clean up operation that could be required after
the download is performed.<br/>
This callback expects [] paramters:
- *state*: the `State^` instance of the caller.
- *id* (optional): a string representing the identifier of the caller. If this function is
called directly, this will always be "Gui.download". Some controls may also trigger
download actions, then *id* would reflect the identifier of those controls.
- *payload* (optional): an optional payload from the caller.
"""
if state and isinstance(state._gui, Gui):
state._gui._download(content, name, on_action)
Expand Down
12 changes: 6 additions & 6 deletions src/taipy/gui/viselements.json
Original file line number Diff line number Diff line change
Expand Up @@ -515,8 +515,8 @@
{
"name": "content",
"default_property": true,
"type": "dynamic(url|path|file|ReadableBuffer)",
"doc": "The content of the file.<br/>If a buffer is provided (string, array of bytes...), and in order to prevent the bandwidth to be consumed too much, the way the data is transferred depends on the the <i>data_url_max_size</i> parameter of the application configuration (which is set to 50kB by default):\n<ul>\n<li>If the size of the buffer is smaller than this setting, then the raw content is generated as a data URL, encoded using base64 (i.e. <code>\"data:&lt;mimetype&gt;;base64,&lt;data&gt;\"</code>).</li>\n<li>If the size of the buffer is greater than this setting, then it is transferred through a temporary file.</li>\n</ul>"
"type": "dynamic(path|file|URL|ReadableBuffer|None)",
"doc": "The content to transfer.<br/>If this is a string, a URL, or a file, then the content is read from this source.<br/>If a readable buffer is provided (such as an array of bytes...), and to prevent the bandwidth from being consumed too much, the way the data is transferred depends on the <i>data_url_max_size</i> parameter of the application configuration (which is set to 50kB by default):\n<ul>\n<li>If the buffer size is smaller than this setting, then the raw content is generated as a data URL, encoded using base64 (i.e. <code>\"data:&lt;mimetype&gt;;base64,&lt;data&gt;\"</code>).</li>\n<li>If the buffer size exceeds this setting, then it is transferred through a temporary file.</li>\n</ul>If this property is set to None, that indicates that dynamic content is generated. Please take a look at the examples below for details on dynamic generation."
},
{
"name": "label",
Expand All @@ -526,7 +526,7 @@
{
"name": "on_action",
"type": "Callback",
"doc": "The name of a function that is triggered when the download is terminated (or on user action if content is None).<br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n<li>action (optional[str]): the name of the action that provoked the change.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>args: A list of two elements: <i>args[0]</i> reflects the <i>name</i> property and <i>args[1]</i> holds the file URL.</li>\n</ul>\n</li>\n</ul>",
"doc": "The name of a function that is triggered when the download is terminated (or on user action if <i>content</i> is None).<br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n<li>action (optional[str]): the name of the action that provoked the change.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has a single key:\n<ul>\n<li>args: A list of two elements: <i>args[0]</i> reflects the <i>name</i> property and <i>args[1]</i> holds the file URL.</li>\n</ul>\n</li>\n</ul>",
"signature": [["state", "State"], ["id", "str"], ["action", "str"], ["payload", "dict"]]
},
{
Expand Down Expand Up @@ -612,8 +612,8 @@
{
"name": "content",
"default_property": true,
"type": "dynamic(url|path|file|ReadableBuffer)",
"doc": "The image source.<br/>If a buffer is provided (string, array of bytes...), and in order to prevent the bandwidth to be consumed too much, the way the image data is transferred depends on the the <i>data_url_max_size</i> parameter of the application configuration (which is set to 50kB by default):\n<ul>\n<li>If the size of the buffer is smaller than this setting, then the raw content is generated as a\n data URL, encoded using base64 (i.e. <code>\"data:&lt;mimetype&gt;;base64,&lt;data&gt;\"</code>).</li>\n<li>If the size of the buffer is greater than this setting, then it is transferred through a temporary\n file.</li>\n</ul>"
"type": "dynamic(path|URL|file|ReadableBuffer)",
"doc": "The image source.<br/>If a buffer is provided (string, array of bytes...), and in order to prevent the bandwidth to be consumed too much, the way the image data is transferred depends on the <i>data_url_max_size</i> parameter of the application configuration (which is set to 50kB by default):\n<ul>\n<li>If the size of the buffer is smaller than this setting, then the raw content is generated as a\n data URL, encoded using base64 (i.e. <code>\"data:&lt;mimetype&gt;;base64,&lt;data&gt;\"</code>).</li>\n<li>If the size of the buffer is greater than this setting, then it is transferred through a temporary\n file.</li>\n</ul>"
},
{
"name": "label",
Expand Down Expand Up @@ -1127,7 +1127,7 @@
{
"name": "page",
"type": "dynamic(str)",
"doc": "The page to show as the content of the block (page name if defined or an URL in an <i>iframe</i>).<br/>This should not be defined if <i>partial</i> is set."
"doc": "The page to show as the content of the block (page name if defined or a URL in an <i>iframe</i>).<br/>This should not be defined if <i>partial</i> is set."
},
{
"name": "height",
Expand Down

0 comments on commit deea4e3

Please sign in to comment.