forked from Avaiga/taipy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into bug/Avaiga#2023-trap-focus-out
- Loading branch information
Showing
21 changed files
with
578 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
## What type of PR is this? (check all applicable) | ||
|
||
- [ ] Refactor | ||
- [ ] Feature | ||
- [ ] Bug Fix | ||
- [ ] Optimization | ||
- [ ] Documentation Update | ||
|
||
## Description | ||
|
||
## Related Tickets & Documents | ||
|
||
<!-- | ||
For pull requests that relate or close an issue, please include them | ||
below. We like to follow [Github's guidance on linking issues to pull requests](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue). | ||
For example having the text: "closes #1234" would connect the current pull | ||
request to issue 1234. And when we merge the pull request, Github will | ||
automatically close the issue. | ||
--> | ||
|
||
- Related Issue # | ||
- Closes # | ||
|
||
## How to reproduce the issue | ||
|
||
_Please replace this line with instructions on how to reproduce the issue or test the feature._ | ||
|
||
## Other branches or releases that this needs to be backported | ||
_Describe which projects this change will impact and that needs to be backported._ | ||
|
||
## Checklist | ||
_We encourage you to keep the code coverage percentage at 80% and above._ | ||
|
||
- [ ] Does this solution meet the acceptance criteria of the related issue? | ||
- [ ] Is the related issue checklist completed? | ||
- [ ] Does this PR adds unit tests for the developed code? If not, why? | ||
- [ ] End-to-End tests have been added or updated? | ||
- [ ] Was the documentation updated, or a dedicated issue for documentation created? (If applicable) | ||
- [ ] Is the release notes updated? (If applicable) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
# Copyright 2021-2024 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> | ||
# ----------------------------------------------------------------------------------------- | ||
import datetime | ||
import re | ||
import time | ||
import typing as t | ||
|
||
import requests # type: ignore[import-untyped] | ||
|
||
from taipy.gui import Gui, Icon, State, get_state_id, invoke_callback, invoke_long_callback | ||
|
||
# The Wikipedia API used to generate content for a date | ||
wiki_url = "https://en.wikipedia.org/api/rest_v1/feed/onthisday/{type}/{month}/{day}" | ||
event_types = { | ||
"happen": "events", | ||
"passé": "events", | ||
"born": "births", | ||
"né": "births", | ||
"dead": "deaths", | ||
"mort": "deaths", | ||
} | ||
user_agent = "https://taipy.io/demo" | ||
|
||
# The list of messages | ||
messages: list[tuple[str, str, str]] = [] # (Message id, message, sender) | ||
|
||
# The two users of this app | ||
users = [ | ||
["wikipedia", Icon("https://www.wikipedia.org/static/apple-touch/wikipedia.png", "Wikipedia")], | ||
["taipy", Icon("https://docs.taipy.io/en/latest/assets/images/favicon.png", "Taipy")], | ||
] | ||
|
||
|
||
# Initialize the user state | ||
def on_init(state: State): | ||
# Messages are for this user only | ||
state.messages = [] | ||
|
||
|
||
# Add the image if there is one in the Wikipedia returned data | ||
def add_image_to_message(state: State, idx: int, text: str, image_url: str): | ||
msg_content: str = state.messages[idx][1] | ||
if (pos := msg_content.find(text)) > -1: | ||
msg_content = msg_content[: pos + len(text)] + f"\n\n![{text}]({image_url})" + msg_content[pos + len(text) :] | ||
set_message(state, msg_content, idx) | ||
|
||
|
||
# Invoked by update_message through a thread | ||
def update_message_with_image(gui: Gui, state_id: str, message_idx: int, text: str, image: dict): | ||
if src := image.get("source"): | ||
time.sleep(0.2) # Apply the typewriter effect | ||
invoke_callback( | ||
gui, | ||
state_id, | ||
add_image_to_message, | ||
[message_idx, text, src], | ||
) | ||
|
||
|
||
# Invoked by query_wikipedia() | ||
def update_message(state: State, json, event_type: str, for_date: str, idx: int): | ||
if isinstance(json, dict): | ||
# Initial response content | ||
set_message(state, f"{event_type} for {for_date}:\n", idx) | ||
|
||
for event in json.get(event_type, []): | ||
time.sleep(0.2) # Apply the typewriter effect | ||
# Update response text | ||
append_to_message(state, f"\n* {event.get('year', '')}: {event.get('text', '')}", idx) | ||
# Invoke update_message_with_image() in a separated thread | ||
invoke_long_callback( | ||
state=state, | ||
user_function=update_message_with_image, | ||
user_function_args=[ | ||
state.get_gui(), | ||
get_state_id(state), | ||
idx, | ||
event.get("text", ""), | ||
pages[0].get("thumbnail", {}) if (pages := event.get("pages", [])) and len(pages) else {}, | ||
], | ||
) | ||
|
||
|
||
# Set a new message or append to an existing message. | ||
# Return the message index in the list. | ||
def set_message(state: State, message: str, idx: t.Optional[int] = None): | ||
if idx is not None and idx < len(state.messages): | ||
msg = state.messages[idx] | ||
state.messages[idx] = (msg[0], message, msg[2]) | ||
else: | ||
idx = len(state.messages) | ||
state.messages.append((f"{len(state.messages)}", message, users[0][0])) | ||
state.refresh("messages") | ||
return idx | ||
|
||
|
||
# Append text to an existing message | ||
def append_to_message(state: State, message: str, idx: int): | ||
if idx < len(state.messages): | ||
msg = state.messages[idx] | ||
state.messages[idx] = (msg[0], f"{msg[1]}{message}", msg[2]) | ||
state.refresh("messages") | ||
return idx | ||
|
||
|
||
# Invoke the Wikipedia API. This is invoked by send_message() | ||
def request_wikipedia(gui: Gui, state_id: str, event_type: str, month: str, day: str): | ||
# Let the user known that a query was sent | ||
idx = invoke_callback( | ||
gui, | ||
state_id, | ||
set_message, | ||
["Fetching information from Wikipedia ..."], | ||
) | ||
request = wiki_url.format(type=event_type, month=month, day=day) | ||
req = requests.get(request, headers={"accept": "application/json; charset=utf-8;", "User-Agent": user_agent}) | ||
# Handle the response | ||
if req.status_code == 200: | ||
# Display the response | ||
invoke_callback( | ||
gui, | ||
state_id, | ||
update_message, | ||
[req.json(), event_type, f"{day}/{month}", idx], | ||
) | ||
else: | ||
# Display the error | ||
invoke_callback( | ||
gui, | ||
state_id, | ||
set_message, | ||
[f"Wikipedia API call failed: {req.status_code}", idx], | ||
) | ||
|
||
|
||
# Invoked by the 'on_action' callback of the chat control when the user presses the Send button | ||
def send_message(state: State, id: str, payload: dict): | ||
args = payload.get("args", []) | ||
|
||
# Display the request | ||
state.messages.append((f"{len(state.messages)}", args[2], args[3])) | ||
state.refresh("messages") | ||
|
||
# Analyse the request | ||
request = args[2].lower() | ||
type_event = None | ||
for word in event_types: | ||
if word in request: | ||
type_event = event_types[word] | ||
break | ||
type_event = type_event if type_event else "events" | ||
|
||
month = None | ||
day = None | ||
for m in re.finditer(r"(\d\d?)", request): | ||
if month is None: | ||
month = m.group() | ||
elif day is None: | ||
day = m.group() | ||
break | ||
if month is None: | ||
month = f"{datetime.datetime.now().month}" | ||
if day is None: | ||
day = f"{datetime.datetime.now().day}" | ||
|
||
# Process the request | ||
invoke_long_callback( | ||
state=state, | ||
user_function=request_wikipedia, | ||
user_function_args=[state.get_gui(), get_state_id(state), type_event, month, day], | ||
) | ||
|
||
|
||
page = """ | ||
<|{messages}|chat|users={users}|on_action=send_message|height=80vh|> | ||
""" | ||
|
||
if __name__ == "__main__": | ||
Gui(page).run(title="Chat - Ask Wikipedia") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
doc/gui/extension/example_library/front-end/src/GameTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import React, { useEffect, useMemo, useState } from "react"; | ||
import { | ||
createRequestDataUpdateAction, | ||
useDispatch, | ||
useDispatchRequestUpdateOnFirstRender, | ||
useModule, | ||
TaipyDynamicProps, | ||
TableValueType, | ||
RowType, | ||
RowValue, | ||
} from "taipy-gui"; | ||
|
||
interface GameTableProps extends TaipyDynamicProps { | ||
data: TableValueType; | ||
} | ||
|
||
const pageKey = "no-page-key"; | ||
|
||
const GameTable = (props: GameTableProps) => { | ||
const { data, updateVarName = "", updateVars = "", id } = props; | ||
const [value, setValue] = useState<Record<string, Array<RowValue>>>({}); | ||
const dispatch = useDispatch(); | ||
const module = useModule(); | ||
const refresh = data?.__taipy_refresh !== undefined; | ||
useDispatchRequestUpdateOnFirstRender(dispatch, id, module, updateVars); | ||
|
||
const colsOrder = useMemo(() => { | ||
return Object.keys(value); | ||
}, [value]); | ||
|
||
const rows = useMemo(() => { | ||
const rows: RowType[] = []; | ||
if (value) { | ||
Object.entries(value).forEach(([col, colValues]) => { | ||
colValues.forEach((val, idx) => { | ||
rows[idx] = rows[idx] || {}; | ||
rows[idx][col] = val; | ||
}); | ||
}); | ||
} | ||
return rows; | ||
}, [value]); | ||
|
||
useEffect(() => { | ||
if (refresh || !data || data[pageKey] === undefined) { | ||
dispatch( | ||
createRequestDataUpdateAction( | ||
updateVarName, | ||
id, | ||
module, | ||
colsOrder, | ||
pageKey, | ||
{}, | ||
true, | ||
"ExampleLibrary", | ||
), | ||
); | ||
} else { | ||
setValue(data[pageKey]); | ||
} | ||
}, [refresh, data, colsOrder, updateVarName, id, dispatch, module]); | ||
|
||
return ( | ||
<div> | ||
<table border={1} cellPadding={10} cellSpacing={0}> | ||
<tbody> | ||
{rows.map((row, index) => ( | ||
<tr key={"row" + index}> | ||
{colsOrder.map((col, cidx) => ( | ||
<td key={"val" + index + "-" + cidx}>{row[col]}</td> | ||
))} | ||
</tr> | ||
))} | ||
</tbody> | ||
</table> | ||
</div> | ||
); | ||
}; | ||
|
||
export default GameTable; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Copyright 2021-2024 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. | ||
from example_library import ExampleLibrary | ||
|
||
from taipy.gui import Gui | ||
|
||
data = [ | ||
["♖", "♘", "♗", "♕", "♔", "♗", "♘", "♖"], | ||
["♙", "♙", "♙", "♙", "♙", "♙", "♙", "♙"], | ||
["", "", "", "", "", "", "", ""], | ||
["", "", "", "", "", "", "", ""], | ||
["", "", "", "", "", "", "", ""], | ||
["", "", "", "", "", "", "", ""], | ||
["♟", "♟", "♟", "♟", "♟", "♟", "♟", "♟"], | ||
["♜", "♞", "♝", "♛", "♚", "♝", "♞", "♜"] | ||
] | ||
|
||
page = """ | ||
## Chess Game | ||
<|{data}|example.game_table|> | ||
""" | ||
|
||
if __name__ == "__main__": | ||
Gui(page, libraries=[ExampleLibrary()]).run(title="Chess Game") |
Oops, something went wrong.