Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
maartenbreddels committed Nov 20, 2023
0 parents commit 70d97ad
Show file tree
Hide file tree
Showing 14 changed files with 411 additions and 0 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/ploomber-cloud.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Ploomber Cloud

on:
push:
branches:
# only deploy from the ploomber branch
- ploomber

jobs:
deploy-to-ploomber-cloud:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: '3.11'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ploomber-cloud hatch
mkdir -p ploomber/wheels
(hatch build && cp dist/*.whl ploomber/wheels)
- name: Deploy
env:
PLOOMBER_CLOUD_KEY: ${{ secrets.PLOOMBER_CLOUD_KEY }}
run: |
(cd ploomber && ploomber-cloud deploy)
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# developer specific ignores should go to ~/.gitignore like editor specific files
.DS_Store
__pycache__
**/*.pyc
dist/
build/
ploomber/wheels
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 widgetti

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@


```
pip install -e .
```

# Deploy manually

See https://docs.cloud.ploomber.io/en/latest/user-guide/cli.html for more details

```
$ pip install ploomber-cloud
$ mkdir -p ploomber/wheels
$ ploomber-cloud key YOURKEY
$ (cd ploomber && ploomber-cloud init)
(type y)
# build the wheel
$ (hatch build && cp dist/*.whl ploomber/wheels)
$ (cd ploomber && ploomber-cloud deploy)
```

# Deploy via Github Actions

[Get your Ploomber API key](https://docs.cloud.ploomber.io/en/latest/quickstart/apikey.html) and set it as `PLOOMBER_CLOUD_KEY` in GitHub (under Settings->Secrets and Variables->Actions, and click "New repository secret")


## Run only once
```
$ ploomber-cloud key YOURKEY
$ (cd ploomber && ploomber-cloud init)
(add to git and commit)
$ git add ploomber/ploomber-cloud.yaml
$ git commit -m "ci: set ploomber id"
$ git push
```


## Run to deploy a new version
```
$ git push origin master:ploomber
```
8 changes: 8 additions & 0 deletions ploomber/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM python:3.11

RUN pip install --no-cache-dir --upgrade pip
RUN mkdir wheels
COPY wheels/*.whl wheels
RUN pip install wheels/*.whl

ENTRYPOINT ["solara", "run", "solarathon.pages", "--host=0.0.0.0", "--port=80"]
4 changes: 4 additions & 0 deletions ploomber/ploomber-cloud.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"id": "raspy-term-0543",
"type": "docker"
}
22 changes: 22 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "solarathon"
description = "Template project for Solarathon 2023"
version = "0.0.1"
dependencies = [
"solara==1.23.0",
]

[project.optional-dependencies]
dev = [
"mypy",
]

[tool.hatch.build]
include = [
"**/*.css",
"**/*.py",
]
10 changes: 10 additions & 0 deletions solarathon/assets/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* Example of how to change the color of the label in a v-input */

.v-input label.v-label {
color: pink;
}

/* workaround for an issue in solara */
.solara-autorouter-content {
height: 100%;
}
Empty file.
153 changes: 153 additions & 0 deletions solarathon/components/chat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import uuid
from typing import Callable, List, Literal, Optional, Union

import solara
from solara.components.input import use_change


@solara.component
def ChatMessage(
children: Union[List[solara.Element], str],
user: bool = False,
avatar: Union[solara.Element, str, Literal[False], None] = None,
name: Optional[str] = None,
color: Optional[str] = "rgba(0,0,0,.06)",
avatar_background_color: Optional[str] = None,
border_radius: Optional[str] = None,
notch: bool = False,
):
msg_uuid = solara.use_memo(lambda: str(uuid.uuid4()), dependencies=[])
with solara.Row(
justify="end" if user else "start",
style={"flex-direction": "row-reverse" if user else "row", "padding": "5px"},
):
if avatar is not False:
with solara.v.Avatar(color=avatar_background_color if avatar_background_color is not None else color):
if avatar is None and name is not None:
initials = "".join([word[:1] for word in name.split(" ")])
solara.HTML(tag="span", unsafe_innerHTML=initials, classes=["headline"])
elif isinstance(avatar, solara.Element):
solara.display(avatar)
elif isinstance(avatar, str) and avatar.startswith("mdi-"):
solara.v.Icon(children=[avatar])
else:
solara.HTML(tag="img", attributes={"src": avatar, "width": "100%"})
with solara.Column(
classes=["chat-message-" + msg_uuid, "right" if user else "left"],
gap=0,
style="border-radius: "
+ (border_radius if border_radius is not None else "")
+ "; border-top-"
+ ("right" if user else "left")
+ "-radius: 0; padding: .5em 1.5em;",
):
if name is not None:
solara.Text(name, style="font-weight: bold;", classes=["message-name", "right" if user else "left"])
solara.display(*children)
solara.Style(
".chat-message-"
+ msg_uuid
+ "{"
+ "--color:"
+ color
+ ";"
+ """
max-width: 75%;
position: relative;
}"""
+ ".chat-message-"
+ msg_uuid
+ """.left{
border-top-left-radius: 0;
background-color:var(--color);
}"""
+ ".chat-message-"
+ msg_uuid
+ """.right{
border-top-right-radius: 0;
background-color:var(--color);
}"""
)
if notch:
solara.Style(
".chat-message-"
+ msg_uuid
+ """.right{
margin-right: 10px !important;
}
.chat-message-"""
+ msg_uuid
+ """.left{
margin-left: 10px !important;
}
.chat-message-"""
+ msg_uuid
+ """:before{
content: '';
position: absolute;
width: 0;
height: 0;
border: 6px solid;
top: 0;
}"""
+ ".chat-message-"
+ msg_uuid
+ """.left:before{
left: -12px;
border-color: var(--color) var(--color) transparent transparent;
}"""
+ ".chat-message-"
+ msg_uuid
+ """.right:before{
right: -12px;
border-color: var(--color) transparent transparent var(--color);
}
"""
)


@solara.component
def ChatBox(children: List[solara.Element] = []):
children_with_key = []
for i, child in enumerate(children):
children_with_key.append(children[i].key("chat-message-" + str(i)))
solara.Column(style={"flex-grow": "1", "flex-direction": "column-reverse", "overflow-y": "auto"}, classes=["chat-box"], children=list(reversed(children_with_key)))


@solara.component
def ChatInfo(children: List[solara.Element] = []):
with solara.Row(style={"min-height": "1em"}):
if children != []:
solara.display(*children)


@solara.component
def ChatInput(
send_callback: Optional[Callable] = None,
disabled: bool = False,
):
message, set_message = solara.use_state("") # type: ignore

with solara.Row(style={"align-items": "center"}):

def send(*ignore_args):
if message != "" and send_callback is not None:
send_callback(message)
set_message("")

message_input = solara.v.TextField(
label="Type a message...",
v_model=message,
on_v_model=set_message,
rounded=True,
filled=True,
hide_details=True,
style_="flex-grow: 1;",
disabled=disabled,
)

use_change(message_input, send, update_events=["keyup.enter"])

button = solara.v.Btn(color="primary", icon=True, children=[solara.v.Icon(children=["mdi-send"])], disabled=message == "")

use_change(button, send, update_events=["click"])
36 changes: 36 additions & 0 deletions solarathon/pages/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import solara

# Declare reactive variables at the top level. Components using these variables
# will be re-executed when their values change.
sentence = solara.reactive("Solara makes our team more productive.")
word_limit = solara.reactive(10)


# in case you want to override the default order of the tabs
route_order = ["/", "settings", "chat", "clickbutton"]

@solara.component
def Page():
with solara.Column(style={"padding-top": "30px"}):
solara.Title("Solarathon example project")
# Calculate word_count within the component to ensure re-execution when reactive variables change.
word_count = len(sentence.value.split())

solara.SliderInt("Word limit", value=word_limit, min=2, max=20)
solara.InputText(label="Your sentence", value=sentence, continuous_update=True)

# Display messages based on the current word count and word limit.
if word_count >= int(word_limit.value):
solara.Error(f"With {word_count} words, you passed the word limit of {word_limit.value}.")
elif word_count >= int(0.8 * word_limit.value):
solara.Warning(f"With {word_count} words, you are close to the word limit of {word_limit.value}.")
else:
solara.Success("Great short writing!")

solara.Markdown("*First exercise*: remove this text and write your own sentence.")


@solara.component
def Layout(children):
# this is the default layout, but you can override it here, for instance some extra padding
return solara.AppLayout(children=children, style={"padding": "20px"})
53 changes: 53 additions & 0 deletions solarathon/pages/chat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import solara
import time
from solarathon.components import chat
import typing

class Message(typing.TypedDict):
user: bool
name: str
message: str

messages = solara.reactive([])
name = solara.reactive("User")

@solara.component
def Page():
def add_message(new_message):
messages.set([
*messages.value,
{"user": True, "name": name.value, "message": new_message,},
])

def bot_response():
# only respond if the last message was from the user
if len(messages.value) == 0:
print("no messages")
return
if not messages.value[-1]["user"]:
print("I don't reply to myself")
return
time.sleep(2)
messages.set([
*messages.value,
{"user": False, "name": "Bot", "message": "Hello, " + name.value + " I cannot help you.",},
])

print(messages.value)
thread_result = solara.use_thread(bot_response, dependencies=[messages.value])
with solara.Column(style={"height": "100%"}):
# Note that we make this title component a child of the column, so that it does not interfere
# with the height 100% flow
solara.Title("Chat with a bot")
solara.InputText("username", value=name)
with chat.ChatBox():
for item in messages.value:
with chat.ChatMessage(
user=item["user"],
name=item["name"],
):
solara.Markdown(item["message"])
chat.ChatInput(send_callback=add_message)
solara.ProgressLinear(thread_result.state == solara.ResultState.RUNNING)
if thread_result.error:
solara.Error(str(thread_result.error))
Loading

0 comments on commit 70d97ad

Please sign in to comment.