Interactive with shell terminal¶
Assignment¶
Build a Python application with:
-
Handle random sentence provided by user
-
Repeat n-integer times: shuffle tokens split of sentence with color change by characters and annotate with random emoji
-
Streaming output into terminal for user
Example output:
Solution¶
Techstack¶
Python language with following package:
-
standard - random: Generate pseudo-random numbers
-
rich: Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal
Flow¶
sequenceDiagram
%% component
participant client as Client
participant console as Console
participant app as Application
%% Flow
client->>console: Input the sentence and the repeat number
console->>app: Handle logic implement
loop Construct statements
app->>app: Split tokens - Annotate emoji - Random color
end
app->>console: Render output into terminal
console->>client: Yield output by live support
Step¶
Step 1: Declare the related required package
Step 2: Install dependencies in vitural environment
python -m venv venv
source venv/Script/active
python -m pip install -r requirements.txt --default-timeout 100
Step 3: Build the script
- Declare enumuration related to a random shuffle component, supported by
rich
The emoji name
# Emoji component: The list of supported emoji by rich
# Enumuration: `python -m rich.emoji`
RICH_EMOJI: list[str] = ["alien", "gem", "speedboat", "zzz", "droplet", "duck", "frog", "cloud"]
The color for shuffle
# Color shuffle: The list of color to shuffle
# Enumuration: https://rich.readthedocs.io/en/latest/appendix/colors.html#appendix-colors
RICH_COLOR: list[str] = ["red", "blue", "cyan", "gold1", "grey5", "dodger_blue2", "purple4", "deep_pink4", "tan", "violet"]
- Handle util function to render component: block text and icon
For block text with color, which follow pattern of [color]{sentence}[/color]
def render_block_text(sentence: str, color: str) -> str:
"""Render sentence with color"""
return f"[{color}]{sentence}[/{color}]"
For icon, the pattern is :{icon}:
def render_icon(name: str) -> str:
"""Render icon based on name"""
return f":{name}:"
- Construct console client by
class::Console
- Handle user input with 2 component:
a) The sentence random inputed by user
b) The number of rows repeated (number)
# Get from user
# Inspired by: https://github.com/Textualize/rich/blob/master/rich/prompt.py
input_sentence = Prompt.ask(":unicorn_face: Input sentence")
# Control
while True:
input_number: int = IntPrompt.ask(":high_voltage: Number to repeat")
if input_number > 0 and input_number <= 1000:
break
console.print(":o: Invalid number to repeat. Required positive number in range of [0, 1000]")
- Build live output with console print, inside a for loop for each sentence
Each loop, included:
-
Split text into tokens and shuffle it
-
Choose random index that will be replaced by emoji
-
Loop within tokens:
a) If satified random handler: return block text with random color
b) If it's in the random index that has emoji: return the icon related
-
Merged all the tokens into 1 sentence
-
Yield the console
# Live output
with Live(None, refresh_per_second=3): # Update 3 times a second to feel fluid
# For loop
for ind in range(0, input_number):
# Arbitrary delay
time.sleep(0.4)
# Shuffle
on_component = list(input_sentence)
random.shuffle(on_component)
# Shuffle icon
ind_emoji = random.choices(range(len(on_component)), k=min(len(on_component), 5))
# Build the component
on_log: list[str] = []
for ind, on_text in enumerate(on_component, start=1):
# Random color on text
if (int(random.random() * 10**6) + ind) % 2 == 0: # Just a random function
rnd_color = random.choice(RICH_COLOR)
on_text = render_block_text(sentence=on_text, color=rnd_color)
# Random emoji based on random index
elif ind in ind_emoji:
on_text = render_icon(name=random.choice(RICH_EMOJI))
# Append
on_log.append(on_text)
# Construct
on_log = "".join(on_log)
# Output
console.print(on_log)
Step 4: Run the script
Further reading¶
Full script¶
#!/bin/python3
# Global
import sys
import os
import random
import time
# Path Append
sys.path.append(os.path.abspath(os.curdir))
# External
from rich.prompt import Prompt, IntPrompt
from rich.console import Console
from rich.live import Live
# Emoji component: The list of supported emoji by rich
# Enumuration: `python -m rich.emoji`
RICH_EMOJI: list[str] = ["alien", "gem", "speedboat", "zzz", "droplet", "duck", "frog", "cloud"]
# Color shuffle: The list of color to shuffle
# Enumuration: https://rich.readthedocs.io/en/latest/appendix/colors.html#appendix-colors
RICH_COLOR: list[str] = ["red", "blue", "cyan", "gold1", "grey5", "dodger_blue2", "purple4", "deep_pink4", "tan", "violet"]
def render_block_text(sentence: str, color: str) -> str:
"""Render sentence with color"""
return f"[{color}]{sentence}[/{color}]"
def render_icon(name: str) -> str:
"""Render icon based on name"""
return f":{name}:"
if __name__ == "__main__":
# Handle
console = Console(emoji=True)
# Get from user
# Inspired by: https://github.com/Textualize/rich/blob/master/rich/prompt.py
input_sentence = Prompt.ask(":unicorn_face: Input sentence")
# Control
while True:
input_number: int = IntPrompt.ask(":high_voltage: Number to repeat")
if input_number > 0 and input_number <= 1000:
break
console.print(":o: Invalid number to repeat. Required positive number in range of [0, 1000]")
# Live output
with Live(None, refresh_per_second=3): # Update 3 times a second to feel fluid
# For loop
for ind in range(0, input_number):
# Arbitrary delay
time.sleep(0.4)
# Shuffle
on_component = list(input_sentence)
random.shuffle(on_component)
# Shuffle icon
ind_emoji = random.choices(range(len(on_component)), k=min(len(on_component), 5))
# Build the component
on_log: list[str] = []
for ind, on_text in enumerate(on_component, start=1):
# Random color on text
if (int(random.random() * 10**6) + ind) % 2 == 0: # Just a random function
rnd_color = random.choice(RICH_COLOR)
on_text = render_block_text(sentence=on_text, color=rnd_color)
# Random emoji based on random index
elif ind in ind_emoji:
on_text = render_icon(name=random.choice(RICH_EMOJI))
# Append
on_log.append(on_text)
# Construct
on_log = "".join(on_log)
# Output
console.print(on_log)