webscout 7.0__py3-none-any.whl → 7.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of webscout might be problematic. Click here for more details.
- webscout/AIauto.py +191 -191
- webscout/AIbase.py +122 -122
- webscout/AIutel.py +440 -440
- webscout/Bard.py +343 -161
- webscout/DWEBS.py +489 -492
- webscout/Extra/YTToolkit/YTdownloader.py +995 -995
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +476 -479
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +103 -103
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder_utiles.py +199 -199
- webscout/Extra/autocoder/rawdog.py +5 -7
- webscout/Extra/autollama.py +230 -230
- webscout/Extra/gguf.py +3 -3
- webscout/Extra/weather.py +171 -171
- webscout/LLM.py +442 -442
- webscout/Litlogger/__init__.py +67 -681
- webscout/Litlogger/core/__init__.py +6 -0
- webscout/Litlogger/core/level.py +20 -0
- webscout/Litlogger/core/logger.py +123 -0
- webscout/Litlogger/handlers/__init__.py +12 -0
- webscout/Litlogger/handlers/console.py +50 -0
- webscout/Litlogger/handlers/file.py +143 -0
- webscout/Litlogger/handlers/network.py +174 -0
- webscout/Litlogger/styles/__init__.py +7 -0
- webscout/Litlogger/styles/colors.py +231 -0
- webscout/Litlogger/styles/formats.py +377 -0
- webscout/Litlogger/styles/text.py +87 -0
- webscout/Litlogger/utils/__init__.py +6 -0
- webscout/Litlogger/utils/detectors.py +154 -0
- webscout/Litlogger/utils/formatters.py +200 -0
- webscout/Provider/AISEARCH/DeepFind.py +250 -250
- webscout/Provider/Blackboxai.py +136 -137
- webscout/Provider/ChatGPTGratis.py +226 -0
- webscout/Provider/Cloudflare.py +91 -78
- webscout/Provider/DeepSeek.py +218 -0
- webscout/Provider/Deepinfra.py +59 -35
- webscout/Provider/Free2GPT.py +131 -124
- webscout/Provider/Gemini.py +100 -115
- webscout/Provider/Glider.py +74 -59
- webscout/Provider/Groq.py +30 -18
- webscout/Provider/Jadve.py +108 -77
- webscout/Provider/Llama3.py +117 -94
- webscout/Provider/Marcus.py +191 -137
- webscout/Provider/Netwrck.py +62 -50
- webscout/Provider/PI.py +79 -124
- webscout/Provider/PizzaGPT.py +129 -83
- webscout/Provider/QwenLM.py +311 -0
- webscout/Provider/TTI/AiForce/__init__.py +22 -22
- webscout/Provider/TTI/AiForce/async_aiforce.py +257 -257
- webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -242
- webscout/Provider/TTI/Nexra/__init__.py +22 -22
- webscout/Provider/TTI/Nexra/async_nexra.py +286 -286
- webscout/Provider/TTI/Nexra/sync_nexra.py +258 -258
- webscout/Provider/TTI/PollinationsAI/__init__.py +23 -23
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -330
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -285
- webscout/Provider/TTI/artbit/__init__.py +22 -22
- webscout/Provider/TTI/artbit/async_artbit.py +184 -184
- webscout/Provider/TTI/artbit/sync_artbit.py +176 -176
- webscout/Provider/TTI/blackbox/__init__.py +4 -4
- webscout/Provider/TTI/blackbox/async_blackbox.py +212 -212
- webscout/Provider/TTI/blackbox/sync_blackbox.py +199 -199
- webscout/Provider/TTI/deepinfra/__init__.py +4 -4
- webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -227
- webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -199
- webscout/Provider/TTI/huggingface/__init__.py +22 -22
- webscout/Provider/TTI/huggingface/async_huggingface.py +199 -199
- webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -195
- webscout/Provider/TTI/imgninza/__init__.py +4 -4
- webscout/Provider/TTI/imgninza/async_ninza.py +214 -214
- webscout/Provider/TTI/imgninza/sync_ninza.py +209 -209
- webscout/Provider/TTI/talkai/__init__.py +4 -4
- webscout/Provider/TTI/talkai/async_talkai.py +229 -229
- webscout/Provider/TTI/talkai/sync_talkai.py +207 -207
- webscout/Provider/TTS/deepgram.py +182 -182
- webscout/Provider/TTS/elevenlabs.py +136 -136
- webscout/Provider/TTS/gesserit.py +150 -150
- webscout/Provider/TTS/murfai.py +138 -138
- webscout/Provider/TTS/parler.py +133 -134
- webscout/Provider/TTS/streamElements.py +360 -360
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TTS/voicepod.py +116 -116
- webscout/Provider/TextPollinationsAI.py +74 -47
- webscout/Provider/WiseCat.py +193 -0
- webscout/Provider/__init__.py +144 -136
- webscout/Provider/cerebras.py +242 -227
- webscout/Provider/chatglm.py +204 -204
- webscout/Provider/dgaf.py +67 -39
- webscout/Provider/gaurish.py +105 -66
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +223 -0
- webscout/Provider/hermes.py +218 -218
- webscout/Provider/llama3mitril.py +179 -179
- webscout/Provider/llamatutor.py +72 -62
- webscout/Provider/llmchat.py +60 -35
- webscout/Provider/meta.py +794 -794
- webscout/Provider/multichat.py +331 -230
- webscout/Provider/typegpt.py +359 -356
- webscout/Provider/yep.py +5 -5
- webscout/__main__.py +5 -5
- webscout/cli.py +319 -319
- webscout/conversation.py +241 -242
- webscout/exceptions.py +328 -328
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +2 -3
- webscout/litprinter/__init__.py +0 -58
- webscout/scout/__init__.py +8 -8
- webscout/scout/core.py +884 -884
- webscout/scout/element.py +459 -459
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +38 -38
- webscout/swiftcli/__init__.py +811 -811
- webscout/update_checker.py +2 -12
- webscout/version.py +1 -1
- webscout/webscout_search.py +1142 -1140
- webscout/webscout_search_async.py +635 -635
- webscout/zeroart/__init__.py +54 -54
- webscout/zeroart/base.py +60 -60
- webscout/zeroart/effects.py +99 -99
- webscout/zeroart/fonts.py +816 -816
- {webscout-7.0.dist-info → webscout-7.2.dist-info}/METADATA +21 -28
- webscout-7.2.dist-info/RECORD +217 -0
- webstoken/__init__.py +30 -30
- webstoken/classifier.py +189 -189
- webstoken/keywords.py +216 -216
- webstoken/language.py +128 -128
- webstoken/ner.py +164 -164
- webstoken/normalizer.py +35 -35
- webstoken/processor.py +77 -77
- webstoken/sentiment.py +206 -206
- webstoken/stemmer.py +73 -73
- webstoken/tagger.py +60 -60
- webstoken/tokenizer.py +158 -158
- webscout/Provider/RUBIKSAI.py +0 -272
- webscout-7.0.dist-info/RECORD +0 -199
- {webscout-7.0.dist-info → webscout-7.2.dist-info}/LICENSE.md +0 -0
- {webscout-7.0.dist-info → webscout-7.2.dist-info}/WHEEL +0 -0
- {webscout-7.0.dist-info → webscout-7.2.dist-info}/entry_points.txt +0 -0
- {webscout-7.0.dist-info → webscout-7.2.dist-info}/top_level.txt +0 -0
webscout/Extra/weather.py
CHANGED
|
@@ -1,172 +1,172 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
import json
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from rich.console import Console
|
|
5
|
-
from rich.table import Table
|
|
6
|
-
from rich.panel import Panel
|
|
7
|
-
from rich.layout import Layout
|
|
8
|
-
from rich.align import Align
|
|
9
|
-
from rich import box
|
|
10
|
-
from rich.live import Live
|
|
11
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
12
|
-
from rich.style import Style
|
|
13
|
-
from rich.text import Text
|
|
14
|
-
from rich.columns import Columns
|
|
15
|
-
|
|
16
|
-
# Initialize Rich console with force terminal
|
|
17
|
-
console = Console(force_terminal=True)
|
|
18
|
-
|
|
19
|
-
def get_emoji(condition: str) -> str:
|
|
20
|
-
"""Get appropriate emoji for weather condition"""
|
|
21
|
-
conditions = {
|
|
22
|
-
'sunny': '*', 'clear': '*',
|
|
23
|
-
'partly cloudy': '~', 'cloudy': '=',
|
|
24
|
-
'rain': 'v', 'light rain': '.',
|
|
25
|
-
'heavy rain': 'V', 'thunderstorm': 'V',
|
|
26
|
-
'snow': '*', 'light snow': '*',
|
|
27
|
-
'mist': '-', 'fog': '-',
|
|
28
|
-
'overcast': '='
|
|
29
|
-
}
|
|
30
|
-
condition = condition.lower()
|
|
31
|
-
for key, symbol in conditions.items():
|
|
32
|
-
if key in condition:
|
|
33
|
-
return symbol
|
|
34
|
-
return '~'
|
|
35
|
-
|
|
36
|
-
def get_wind_arrow(degrees: int) -> str:
|
|
37
|
-
"""Convert wind degrees to arrow"""
|
|
38
|
-
arrows = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
|
|
39
|
-
index = round(degrees / 45) % 8
|
|
40
|
-
return arrows[index]
|
|
41
|
-
|
|
42
|
-
def format_temp(temp: str, scale='C') -> Text:
|
|
43
|
-
"""Format temperature with color based on value"""
|
|
44
|
-
try:
|
|
45
|
-
value = float(temp)
|
|
46
|
-
if scale == 'C':
|
|
47
|
-
if value <= 0:
|
|
48
|
-
return Text(f"{temp}°{scale}", style="bold blue")
|
|
49
|
-
elif value >= 30:
|
|
50
|
-
return Text(f"{temp}°{scale}", style="bold red")
|
|
51
|
-
else:
|
|
52
|
-
return Text(f"{temp}°{scale}", style="bold green")
|
|
53
|
-
except ValueError:
|
|
54
|
-
pass
|
|
55
|
-
return Text(f"{temp}°{scale}")
|
|
56
|
-
|
|
57
|
-
def create_current_weather_panel(data):
|
|
58
|
-
"""Create panel for current weather"""
|
|
59
|
-
current = data['current_condition'][0]
|
|
60
|
-
location = data['nearest_area'][0]
|
|
61
|
-
location_name = f"{location['areaName'][0]['value']}, {location['country'][0]['value']}"
|
|
62
|
-
|
|
63
|
-
weather_desc = current['weatherDesc'][0]['value']
|
|
64
|
-
symbol = get_emoji(weather_desc)
|
|
65
|
-
|
|
66
|
-
# Create weather info table
|
|
67
|
-
table = Table(show_header=False, box=box.ROUNDED, expand=True)
|
|
68
|
-
table.add_column("Label", style="cyan")
|
|
69
|
-
table.add_column("Value", justify="right")
|
|
70
|
-
|
|
71
|
-
table.add_row("Location", f"@ {location_name}")
|
|
72
|
-
table.add_row("Condition", f"{symbol} {weather_desc}")
|
|
73
|
-
table.add_row("Temperature",
|
|
74
|
-
f"{format_temp(current['temp_C'])} / {format_temp(current['temp_F'], 'F')}")
|
|
75
|
-
table.add_row("Feels Like",
|
|
76
|
-
f"{format_temp(current['FeelsLikeC'])} / {format_temp(current['FeelsLikeF'], 'F')}")
|
|
77
|
-
table.add_row("Humidity", f"~ {current['humidity']}%")
|
|
78
|
-
|
|
79
|
-
wind_dir = get_wind_arrow(int(current['winddirDegree']))
|
|
80
|
-
table.add_row("Wind",
|
|
81
|
-
f"> {current['windspeedKmph']} km/h {wind_dir} ({current['winddir16Point']})")
|
|
82
|
-
table.add_row("Visibility", f"O {current['visibility']} km")
|
|
83
|
-
table.add_row("Pressure", f"# {current['pressure']} mb")
|
|
84
|
-
|
|
85
|
-
return Panel(table, title="[bold]Current Weather[/]", border_style="blue")
|
|
86
|
-
|
|
87
|
-
def create_forecast_panel(data):
|
|
88
|
-
"""Create panel for weather forecast"""
|
|
89
|
-
table = Table(show_header=True, box=box.ROUNDED, expand=True)
|
|
90
|
-
table.add_column("Date", style="cyan")
|
|
91
|
-
table.add_column("Condition")
|
|
92
|
-
table.add_column("Temp (°C)")
|
|
93
|
-
table.add_column("Rain")
|
|
94
|
-
table.add_column("Wind")
|
|
95
|
-
|
|
96
|
-
for day in data['weather'][:3]:
|
|
97
|
-
date = datetime.strptime(day['date'], '%Y-%m-%d').strftime('%a, %b %d')
|
|
98
|
-
# Get mid-day conditions (noon)
|
|
99
|
-
noon = day['hourly'][4]
|
|
100
|
-
condition = noon['weatherDesc'][0]['value']
|
|
101
|
-
symbol = get_emoji(condition)
|
|
102
|
-
temp_range = f"{day['mintempC']}° - {day['maxtempC']}°"
|
|
103
|
-
rain_chance = f"v {noon['chanceofrain']}%"
|
|
104
|
-
wind = f"> {noon['windspeedKmph']} km/h"
|
|
105
|
-
|
|
106
|
-
table.add_row(
|
|
107
|
-
date,
|
|
108
|
-
f"{symbol} {condition}",
|
|
109
|
-
temp_range,
|
|
110
|
-
rain_chance,
|
|
111
|
-
wind
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
return Panel(table, title="[bold]3-Day Forecast[/]", border_style="blue")
|
|
115
|
-
|
|
116
|
-
def get(location: str):
|
|
117
|
-
"""Get weather data with progress indicator"""
|
|
118
|
-
with Progress(
|
|
119
|
-
SpinnerColumn(),
|
|
120
|
-
TextColumn("[progress.description]{task.description}"),
|
|
121
|
-
console=console,
|
|
122
|
-
) as progress:
|
|
123
|
-
progress.add_task(description="Fetching weather data...", total=None)
|
|
124
|
-
try:
|
|
125
|
-
response = requests.get(f"https://wttr.in/{location}?format=j1", timeout=10)
|
|
126
|
-
response.raise_for_status()
|
|
127
|
-
return response.json()
|
|
128
|
-
except Exception as e:
|
|
129
|
-
console.print(f"[red]Error fetching weather data: {str(e)}[/]")
|
|
130
|
-
return None
|
|
131
|
-
|
|
132
|
-
def display_weather(data):
|
|
133
|
-
"""Display weather information in a beautiful layout"""
|
|
134
|
-
if not data:
|
|
135
|
-
return
|
|
136
|
-
|
|
137
|
-
# Create layout
|
|
138
|
-
layout = Layout()
|
|
139
|
-
layout.split_column(
|
|
140
|
-
Layout(name="current", size=15),
|
|
141
|
-
Layout(name="forecast", size=10)
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
# Update layout sections
|
|
145
|
-
layout["current"].update(create_current_weather_panel(data))
|
|
146
|
-
layout["forecast"].update(create_forecast_panel(data))
|
|
147
|
-
|
|
148
|
-
# Print layout with a title
|
|
149
|
-
console.print("\n")
|
|
150
|
-
console.print(Align.center("[bold blue]Weather Report[/]"))
|
|
151
|
-
console.print("\n")
|
|
152
|
-
console.print(layout)
|
|
153
|
-
console.print("\n")
|
|
154
|
-
|
|
155
|
-
def main():
|
|
156
|
-
"""Main function to run the weather app"""
|
|
157
|
-
try:
|
|
158
|
-
console.clear()
|
|
159
|
-
console.print("\n[bold cyan]* Weather Information[/]\n")
|
|
160
|
-
location = console.input("[cyan]Enter location: [/]")
|
|
161
|
-
|
|
162
|
-
weather_data = get(location)
|
|
163
|
-
if weather_data:
|
|
164
|
-
display_weather(weather_data)
|
|
165
|
-
|
|
166
|
-
except KeyboardInterrupt:
|
|
167
|
-
console.print("\n[yellow]Operation cancelled by user[/]")
|
|
168
|
-
except Exception as e:
|
|
169
|
-
console.print(f"\n[bold red]Unexpected error:[/] {str(e)}")
|
|
170
|
-
|
|
171
|
-
if __name__ == "__main__":
|
|
1
|
+
import requests
|
|
2
|
+
import json
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.layout import Layout
|
|
8
|
+
from rich.align import Align
|
|
9
|
+
from rich import box
|
|
10
|
+
from rich.live import Live
|
|
11
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
12
|
+
from rich.style import Style
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
from rich.columns import Columns
|
|
15
|
+
|
|
16
|
+
# Initialize Rich console with force terminal
|
|
17
|
+
console = Console(force_terminal=True)
|
|
18
|
+
|
|
19
|
+
def get_emoji(condition: str) -> str:
|
|
20
|
+
"""Get appropriate emoji for weather condition"""
|
|
21
|
+
conditions = {
|
|
22
|
+
'sunny': '*', 'clear': '*',
|
|
23
|
+
'partly cloudy': '~', 'cloudy': '=',
|
|
24
|
+
'rain': 'v', 'light rain': '.',
|
|
25
|
+
'heavy rain': 'V', 'thunderstorm': 'V',
|
|
26
|
+
'snow': '*', 'light snow': '*',
|
|
27
|
+
'mist': '-', 'fog': '-',
|
|
28
|
+
'overcast': '='
|
|
29
|
+
}
|
|
30
|
+
condition = condition.lower()
|
|
31
|
+
for key, symbol in conditions.items():
|
|
32
|
+
if key in condition:
|
|
33
|
+
return symbol
|
|
34
|
+
return '~'
|
|
35
|
+
|
|
36
|
+
def get_wind_arrow(degrees: int) -> str:
|
|
37
|
+
"""Convert wind degrees to arrow"""
|
|
38
|
+
arrows = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
|
|
39
|
+
index = round(degrees / 45) % 8
|
|
40
|
+
return arrows[index]
|
|
41
|
+
|
|
42
|
+
def format_temp(temp: str, scale='C') -> Text:
|
|
43
|
+
"""Format temperature with color based on value"""
|
|
44
|
+
try:
|
|
45
|
+
value = float(temp)
|
|
46
|
+
if scale == 'C':
|
|
47
|
+
if value <= 0:
|
|
48
|
+
return Text(f"{temp}°{scale}", style="bold blue")
|
|
49
|
+
elif value >= 30:
|
|
50
|
+
return Text(f"{temp}°{scale}", style="bold red")
|
|
51
|
+
else:
|
|
52
|
+
return Text(f"{temp}°{scale}", style="bold green")
|
|
53
|
+
except ValueError:
|
|
54
|
+
pass
|
|
55
|
+
return Text(f"{temp}°{scale}")
|
|
56
|
+
|
|
57
|
+
def create_current_weather_panel(data):
|
|
58
|
+
"""Create panel for current weather"""
|
|
59
|
+
current = data['current_condition'][0]
|
|
60
|
+
location = data['nearest_area'][0]
|
|
61
|
+
location_name = f"{location['areaName'][0]['value']}, {location['country'][0]['value']}"
|
|
62
|
+
|
|
63
|
+
weather_desc = current['weatherDesc'][0]['value']
|
|
64
|
+
symbol = get_emoji(weather_desc)
|
|
65
|
+
|
|
66
|
+
# Create weather info table
|
|
67
|
+
table = Table(show_header=False, box=box.ROUNDED, expand=True)
|
|
68
|
+
table.add_column("Label", style="cyan")
|
|
69
|
+
table.add_column("Value", justify="right")
|
|
70
|
+
|
|
71
|
+
table.add_row("Location", f"@ {location_name}")
|
|
72
|
+
table.add_row("Condition", f"{symbol} {weather_desc}")
|
|
73
|
+
table.add_row("Temperature",
|
|
74
|
+
f"{format_temp(current['temp_C'])} / {format_temp(current['temp_F'], 'F')}")
|
|
75
|
+
table.add_row("Feels Like",
|
|
76
|
+
f"{format_temp(current['FeelsLikeC'])} / {format_temp(current['FeelsLikeF'], 'F')}")
|
|
77
|
+
table.add_row("Humidity", f"~ {current['humidity']}%")
|
|
78
|
+
|
|
79
|
+
wind_dir = get_wind_arrow(int(current['winddirDegree']))
|
|
80
|
+
table.add_row("Wind",
|
|
81
|
+
f"> {current['windspeedKmph']} km/h {wind_dir} ({current['winddir16Point']})")
|
|
82
|
+
table.add_row("Visibility", f"O {current['visibility']} km")
|
|
83
|
+
table.add_row("Pressure", f"# {current['pressure']} mb")
|
|
84
|
+
|
|
85
|
+
return Panel(table, title="[bold]Current Weather[/]", border_style="blue")
|
|
86
|
+
|
|
87
|
+
def create_forecast_panel(data):
|
|
88
|
+
"""Create panel for weather forecast"""
|
|
89
|
+
table = Table(show_header=True, box=box.ROUNDED, expand=True)
|
|
90
|
+
table.add_column("Date", style="cyan")
|
|
91
|
+
table.add_column("Condition")
|
|
92
|
+
table.add_column("Temp (°C)")
|
|
93
|
+
table.add_column("Rain")
|
|
94
|
+
table.add_column("Wind")
|
|
95
|
+
|
|
96
|
+
for day in data['weather'][:3]:
|
|
97
|
+
date = datetime.strptime(day['date'], '%Y-%m-%d').strftime('%a, %b %d')
|
|
98
|
+
# Get mid-day conditions (noon)
|
|
99
|
+
noon = day['hourly'][4]
|
|
100
|
+
condition = noon['weatherDesc'][0]['value']
|
|
101
|
+
symbol = get_emoji(condition)
|
|
102
|
+
temp_range = f"{day['mintempC']}° - {day['maxtempC']}°"
|
|
103
|
+
rain_chance = f"v {noon['chanceofrain']}%"
|
|
104
|
+
wind = f"> {noon['windspeedKmph']} km/h"
|
|
105
|
+
|
|
106
|
+
table.add_row(
|
|
107
|
+
date,
|
|
108
|
+
f"{symbol} {condition}",
|
|
109
|
+
temp_range,
|
|
110
|
+
rain_chance,
|
|
111
|
+
wind
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return Panel(table, title="[bold]3-Day Forecast[/]", border_style="blue")
|
|
115
|
+
|
|
116
|
+
def get(location: str):
|
|
117
|
+
"""Get weather data with progress indicator"""
|
|
118
|
+
with Progress(
|
|
119
|
+
SpinnerColumn(),
|
|
120
|
+
TextColumn("[progress.description]{task.description}"),
|
|
121
|
+
console=console,
|
|
122
|
+
) as progress:
|
|
123
|
+
progress.add_task(description="Fetching weather data...", total=None)
|
|
124
|
+
try:
|
|
125
|
+
response = requests.get(f"https://wttr.in/{location}?format=j1", timeout=10)
|
|
126
|
+
response.raise_for_status()
|
|
127
|
+
return response.json()
|
|
128
|
+
except Exception as e:
|
|
129
|
+
console.print(f"[red]Error fetching weather data: {str(e)}[/]")
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
def display_weather(data):
|
|
133
|
+
"""Display weather information in a beautiful layout"""
|
|
134
|
+
if not data:
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
# Create layout
|
|
138
|
+
layout = Layout()
|
|
139
|
+
layout.split_column(
|
|
140
|
+
Layout(name="current", size=15),
|
|
141
|
+
Layout(name="forecast", size=10)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Update layout sections
|
|
145
|
+
layout["current"].update(create_current_weather_panel(data))
|
|
146
|
+
layout["forecast"].update(create_forecast_panel(data))
|
|
147
|
+
|
|
148
|
+
# Print layout with a title
|
|
149
|
+
console.print("\n")
|
|
150
|
+
console.print(Align.center("[bold blue]Weather Report[/]"))
|
|
151
|
+
console.print("\n")
|
|
152
|
+
console.print(layout)
|
|
153
|
+
console.print("\n")
|
|
154
|
+
|
|
155
|
+
def main():
|
|
156
|
+
"""Main function to run the weather app"""
|
|
157
|
+
try:
|
|
158
|
+
console.clear()
|
|
159
|
+
console.print("\n[bold cyan]* Weather Information[/]\n")
|
|
160
|
+
location = console.input("[cyan]Enter location: [/]")
|
|
161
|
+
|
|
162
|
+
weather_data = get(location)
|
|
163
|
+
if weather_data:
|
|
164
|
+
display_weather(weather_data)
|
|
165
|
+
|
|
166
|
+
except KeyboardInterrupt:
|
|
167
|
+
console.print("\n[yellow]Operation cancelled by user[/]")
|
|
168
|
+
except Exception as e:
|
|
169
|
+
console.print(f"\n[bold red]Unexpected error:[/] {str(e)}")
|
|
170
|
+
|
|
171
|
+
if __name__ == "__main__":
|
|
172
172
|
main()
|