webscout 8.3.7__py3-none-any.whl → 2025.10.11__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 +250 -250
- webscout/AIbase.py +379 -379
- webscout/AIutel.py +60 -60
- webscout/Bard.py +1012 -1012
- webscout/Bing_search.py +417 -417
- webscout/DWEBS.py +529 -529
- webscout/Extra/Act.md +309 -309
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/README.md +110 -110
- webscout/Extra/GitToolkit/gitapi/__init__.py +11 -11
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -195
- webscout/Extra/GitToolkit/gitapi/user.py +96 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +61 -61
- webscout/Extra/YTToolkit/README.md +375 -375
- webscout/Extra/YTToolkit/YTdownloader.py +956 -956
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +475 -475
- webscout/Extra/YTToolkit/ytapi/README.md +44 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -118
- webscout/Extra/YTToolkit/ytapi/https.py +88 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/query.py +39 -39
- webscout/Extra/YTToolkit/ytapi/stream.py +62 -62
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +232 -232
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder.py +1105 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +332 -332
- webscout/Extra/gguf.md +429 -429
- webscout/Extra/gguf.py +1213 -1213
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +27 -27
- webscout/Extra/tempmail/async_utils.py +140 -140
- webscout/Extra/tempmail/base.py +160 -160
- webscout/Extra/tempmail/cli.py +186 -186
- webscout/Extra/tempmail/emailnator.py +84 -84
- webscout/Extra/tempmail/mail_tm.py +360 -360
- webscout/Extra/tempmail/temp_mail_io.py +291 -291
- webscout/Extra/weather.md +281 -281
- webscout/Extra/weather.py +193 -193
- webscout/Litlogger/README.md +10 -10
- webscout/Litlogger/__init__.py +15 -15
- webscout/Litlogger/formats.py +13 -13
- webscout/Litlogger/handlers.py +121 -121
- webscout/Litlogger/levels.py +13 -13
- webscout/Litlogger/logger.py +134 -134
- webscout/Provider/AISEARCH/Perplexity.py +332 -332
- webscout/Provider/AISEARCH/README.md +279 -279
- webscout/Provider/AISEARCH/__init__.py +16 -1
- webscout/Provider/AISEARCH/felo_search.py +206 -206
- webscout/Provider/AISEARCH/genspark_search.py +323 -323
- webscout/Provider/AISEARCH/hika_search.py +185 -185
- webscout/Provider/AISEARCH/iask_search.py +410 -410
- webscout/Provider/AISEARCH/monica_search.py +219 -219
- webscout/Provider/AISEARCH/scira_search.py +316 -316
- webscout/Provider/AISEARCH/stellar_search.py +177 -177
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -255
- webscout/Provider/Aitopia.py +314 -314
- webscout/Provider/Apriel.py +306 -0
- webscout/Provider/ChatGPTClone.py +236 -236
- webscout/Provider/ChatSandbox.py +343 -343
- webscout/Provider/Cloudflare.py +324 -324
- webscout/Provider/Cohere.py +208 -208
- webscout/Provider/Deepinfra.py +370 -366
- webscout/Provider/ExaAI.py +260 -260
- webscout/Provider/ExaChat.py +308 -308
- webscout/Provider/Flowith.py +221 -221
- webscout/Provider/GMI.py +293 -0
- webscout/Provider/Gemini.py +164 -164
- webscout/Provider/GeminiProxy.py +167 -167
- webscout/Provider/GithubChat.py +371 -372
- webscout/Provider/Groq.py +800 -800
- webscout/Provider/HeckAI.py +383 -383
- webscout/Provider/Jadve.py +282 -282
- webscout/Provider/K2Think.py +307 -307
- webscout/Provider/Koboldai.py +205 -205
- webscout/Provider/LambdaChat.py +423 -423
- webscout/Provider/Nemotron.py +244 -244
- webscout/Provider/Netwrck.py +248 -248
- webscout/Provider/OLLAMA.py +395 -395
- webscout/Provider/OPENAI/Cloudflare.py +393 -393
- webscout/Provider/OPENAI/FalconH1.py +451 -451
- webscout/Provider/OPENAI/FreeGemini.py +296 -296
- webscout/Provider/OPENAI/K2Think.py +431 -431
- webscout/Provider/OPENAI/NEMOTRON.py +240 -240
- webscout/Provider/OPENAI/PI.py +427 -427
- webscout/Provider/OPENAI/README.md +959 -959
- webscout/Provider/OPENAI/TogetherAI.py +345 -345
- webscout/Provider/OPENAI/TwoAI.py +465 -465
- webscout/Provider/OPENAI/__init__.py +33 -18
- webscout/Provider/OPENAI/base.py +248 -248
- webscout/Provider/OPENAI/chatglm.py +528 -0
- webscout/Provider/OPENAI/chatgpt.py +592 -592
- webscout/Provider/OPENAI/chatgptclone.py +521 -521
- webscout/Provider/OPENAI/chatsandbox.py +202 -202
- webscout/Provider/OPENAI/deepinfra.py +318 -314
- webscout/Provider/OPENAI/e2b.py +1665 -1665
- webscout/Provider/OPENAI/exaai.py +420 -420
- webscout/Provider/OPENAI/exachat.py +452 -452
- webscout/Provider/OPENAI/friendli.py +232 -232
- webscout/Provider/OPENAI/{refact.py → gmi.py} +324 -274
- webscout/Provider/OPENAI/groq.py +364 -364
- webscout/Provider/OPENAI/heckai.py +314 -314
- webscout/Provider/OPENAI/llmchatco.py +337 -337
- webscout/Provider/OPENAI/netwrck.py +355 -355
- webscout/Provider/OPENAI/oivscode.py +290 -290
- webscout/Provider/OPENAI/opkfc.py +518 -518
- webscout/Provider/OPENAI/pydantic_imports.py +1 -1
- webscout/Provider/OPENAI/scirachat.py +535 -535
- webscout/Provider/OPENAI/sonus.py +308 -308
- webscout/Provider/OPENAI/standardinput.py +442 -442
- webscout/Provider/OPENAI/textpollinations.py +340 -340
- webscout/Provider/OPENAI/toolbaz.py +419 -416
- webscout/Provider/OPENAI/typefully.py +362 -362
- webscout/Provider/OPENAI/utils.py +295 -295
- webscout/Provider/OPENAI/venice.py +436 -436
- webscout/Provider/OPENAI/wisecat.py +387 -387
- webscout/Provider/OPENAI/writecream.py +166 -166
- webscout/Provider/OPENAI/x0gpt.py +378 -378
- webscout/Provider/OPENAI/yep.py +389 -389
- webscout/Provider/OpenGPT.py +230 -230
- webscout/Provider/Openai.py +243 -243
- webscout/Provider/PI.py +405 -405
- webscout/Provider/Perplexitylabs.py +430 -430
- webscout/Provider/QwenLM.py +272 -272
- webscout/Provider/STT/__init__.py +16 -1
- webscout/Provider/Sambanova.py +257 -257
- webscout/Provider/StandardInput.py +309 -309
- webscout/Provider/TTI/README.md +82 -82
- webscout/Provider/TTI/__init__.py +33 -18
- webscout/Provider/TTI/aiarta.py +413 -413
- webscout/Provider/TTI/base.py +136 -136
- webscout/Provider/TTI/bing.py +243 -243
- webscout/Provider/TTI/gpt1image.py +149 -149
- webscout/Provider/TTI/imagen.py +196 -196
- webscout/Provider/TTI/infip.py +211 -211
- webscout/Provider/TTI/magicstudio.py +232 -232
- webscout/Provider/TTI/monochat.py +219 -219
- webscout/Provider/TTI/piclumen.py +214 -214
- webscout/Provider/TTI/pixelmuse.py +232 -232
- webscout/Provider/TTI/pollinations.py +232 -232
- webscout/Provider/TTI/together.py +288 -288
- webscout/Provider/TTI/utils.py +12 -12
- webscout/Provider/TTI/venice.py +367 -367
- webscout/Provider/TTS/README.md +192 -192
- webscout/Provider/TTS/__init__.py +33 -18
- webscout/Provider/TTS/parler.py +110 -110
- webscout/Provider/TTS/streamElements.py +333 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TeachAnything.py +237 -237
- webscout/Provider/TextPollinationsAI.py +310 -310
- webscout/Provider/TogetherAI.py +356 -356
- webscout/Provider/TwoAI.py +312 -312
- webscout/Provider/TypliAI.py +311 -311
- webscout/Provider/UNFINISHED/ChatHub.py +208 -208
- webscout/Provider/UNFINISHED/ChutesAI.py +313 -313
- webscout/Provider/UNFINISHED/GizAI.py +294 -294
- webscout/Provider/UNFINISHED/Marcus.py +198 -198
- webscout/Provider/UNFINISHED/Qodo.py +477 -477
- webscout/Provider/UNFINISHED/VercelAIGateway.py +338 -338
- webscout/Provider/UNFINISHED/XenAI.py +324 -324
- webscout/Provider/UNFINISHED/Youchat.py +330 -330
- webscout/Provider/UNFINISHED/liner.py +334 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +262 -262
- webscout/Provider/UNFINISHED/puterjs.py +634 -634
- webscout/Provider/UNFINISHED/samurai.py +223 -223
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
- webscout/Provider/Venice.py +250 -250
- webscout/Provider/VercelAI.py +256 -256
- webscout/Provider/WiseCat.py +231 -231
- webscout/Provider/WrDoChat.py +366 -366
- webscout/Provider/__init__.py +33 -18
- webscout/Provider/ai4chat.py +174 -174
- webscout/Provider/akashgpt.py +331 -331
- webscout/Provider/cerebras.py +446 -446
- webscout/Provider/chatglm.py +394 -301
- webscout/Provider/cleeai.py +211 -211
- webscout/Provider/elmo.py +282 -282
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +261 -261
- webscout/Provider/hermes.py +263 -263
- webscout/Provider/julius.py +223 -223
- webscout/Provider/learnfastai.py +309 -309
- webscout/Provider/llama3mitril.py +214 -214
- webscout/Provider/llmchat.py +243 -243
- webscout/Provider/llmchatco.py +290 -290
- webscout/Provider/meta.py +801 -801
- webscout/Provider/oivscode.py +309 -309
- webscout/Provider/scira_chat.py +383 -383
- webscout/Provider/searchchat.py +292 -292
- webscout/Provider/sonus.py +258 -258
- webscout/Provider/toolbaz.py +370 -367
- webscout/Provider/turboseek.py +273 -273
- webscout/Provider/typefully.py +207 -207
- webscout/Provider/yep.py +372 -372
- webscout/__init__.py +30 -31
- webscout/__main__.py +5 -5
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/config.py +175 -175
- webscout/auth/models.py +185 -185
- webscout/auth/routes.py +664 -664
- webscout/auth/simple_logger.py +236 -236
- webscout/cli.py +523 -523
- webscout/conversation.py +438 -438
- webscout/exceptions.py +361 -361
- webscout/litagent/Readme.md +298 -298
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +581 -581
- webscout/litagent/constants.py +59 -59
- webscout/litprinter/__init__.py +58 -58
- webscout/models.py +181 -181
- webscout/optimizers.py +419 -419
- webscout/prompt_manager.py +288 -288
- webscout/sanitize.py +1078 -1078
- webscout/scout/README.md +401 -401
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +6 -6
- webscout/scout/core/crawler.py +297 -297
- webscout/scout/core/scout.py +706 -706
- webscout/scout/core/search_result.py +95 -95
- webscout/scout/core/text_analyzer.py +62 -62
- webscout/scout/core/text_utils.py +277 -277
- webscout/scout/core/web_analyzer.py +51 -51
- webscout/scout/element.py +599 -599
- 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 +37 -37
- webscout/swiftcli/Readme.md +323 -323
- webscout/swiftcli/__init__.py +95 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +308 -308
- webscout/swiftcli/core/context.py +104 -104
- webscout/swiftcli/core/group.py +241 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +221 -221
- webscout/swiftcli/decorators/options.py +220 -220
- webscout/swiftcli/decorators/output.py +302 -302
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +135 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +59 -59
- webscout/swiftcli/utils/formatting.py +252 -252
- webscout/swiftcli/utils/parsing.py +267 -267
- webscout/update_checker.py +117 -117
- webscout/version.py +1 -1
- webscout/webscout_search.py +1183 -1183
- webscout/webscout_search_async.py +649 -649
- webscout/yep_search.py +346 -346
- webscout/zeroart/README.md +89 -89
- webscout/zeroart/__init__.py +134 -134
- webscout/zeroart/base.py +66 -66
- webscout/zeroart/effects.py +100 -100
- webscout/zeroart/fonts.py +1238 -1238
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/METADATA +937 -937
- webscout-2025.10.11.dist-info/RECORD +300 -0
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/OPENAI/Qwen3.py +0 -303
- webscout/Provider/OPENAI/qodo.py +0 -630
- webscout/Provider/OPENAI/xenai.py +0 -514
- webscout/tempid.py +0 -134
- webscout-8.3.7.dist-info/RECORD +0 -301
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/WHEEL +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/top_level.txt +0 -0
webscout/Extra/weather.py
CHANGED
|
@@ -1,194 +1,194 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Weather information module with a clean, strongly-typed API structure.
|
|
3
|
-
|
|
4
|
-
This module provides a simple client for fetching weather data
|
|
5
|
-
from the wttr.in service with proper typing and a consistent interface.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import requests
|
|
9
|
-
from datetime import datetime
|
|
10
|
-
from typing import List, Dict, Any, Optional
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class CurrentCondition:
|
|
14
|
-
"""Current weather conditions with strongly typed properties."""
|
|
15
|
-
|
|
16
|
-
def __init__(self, data: Dict[str, Any]) -> None:
|
|
17
|
-
"""Initialize with current condition data.
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
data: Current condition data dictionary from wttr.in
|
|
21
|
-
"""
|
|
22
|
-
self.temp_c: Optional[str] = data.get('temp_C')
|
|
23
|
-
self.temp_f: Optional[str] = data.get('temp_F')
|
|
24
|
-
self.feels_like_c: Optional[str] = data.get('FeelsLikeC')
|
|
25
|
-
self.feels_like_f: Optional[str] = data.get('FeelsLikeF')
|
|
26
|
-
self.weather_desc: str = data.get('weatherDesc', [{}])[0].get('value', '')
|
|
27
|
-
self.weather_code: Optional[str] = data.get('weatherCode')
|
|
28
|
-
self.humidity: Optional[str] = data.get('humidity')
|
|
29
|
-
self.visibility: Optional[str] = data.get('visibility')
|
|
30
|
-
self.pressure: Optional[str] = data.get('pressure')
|
|
31
|
-
self.wind_speed_kmph: Optional[str] = data.get('windspeedKmph')
|
|
32
|
-
self.wind_direction: Optional[str] = data.get('winddir16Point')
|
|
33
|
-
self.wind_degree: Optional[str] = data.get('winddirDegree')
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class Location:
|
|
37
|
-
"""Location information with strongly typed properties."""
|
|
38
|
-
|
|
39
|
-
def __init__(self, data: Dict[str, Any]) -> None:
|
|
40
|
-
"""Initialize with location data.
|
|
41
|
-
|
|
42
|
-
Args:
|
|
43
|
-
data: Location data dictionary from wttr.in
|
|
44
|
-
"""
|
|
45
|
-
self.name: str = data.get('areaName', [{}])[0].get('value', '')
|
|
46
|
-
self.country: str = data.get('country', [{}])[0].get('value', '')
|
|
47
|
-
self.region: str = data.get('region', [{}])[0].get('value', '')
|
|
48
|
-
self.latitude: Optional[str] = data.get('latitude')
|
|
49
|
-
self.longitude: Optional[str] = data.get('longitude')
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class HourlyForecast:
|
|
53
|
-
"""Hourly forecast information with strongly typed properties."""
|
|
54
|
-
|
|
55
|
-
def __init__(self, data: Dict[str, Any]) -> None:
|
|
56
|
-
"""Initialize with hourly forecast data.
|
|
57
|
-
|
|
58
|
-
Args:
|
|
59
|
-
data: Hourly forecast data dictionary from wttr.in
|
|
60
|
-
"""
|
|
61
|
-
self.time: Optional[str] = data.get('time')
|
|
62
|
-
self.temp_c: Optional[str] = data.get('tempC')
|
|
63
|
-
self.temp_f: Optional[str] = data.get('tempF')
|
|
64
|
-
self.weather_desc: str = data.get('weatherDesc', [{}])[0].get('value', '')
|
|
65
|
-
self.weather_code: Optional[str] = data.get('weatherCode')
|
|
66
|
-
self.wind_speed_kmph: Optional[str] = data.get('windspeedKmph')
|
|
67
|
-
self.wind_direction: Optional[str] = data.get('winddir16Point')
|
|
68
|
-
self.feels_like_c: Optional[str] = data.get('FeelsLikeC')
|
|
69
|
-
self.feels_like_f: Optional[str] = data.get('FeelsLikeF')
|
|
70
|
-
self.chance_of_rain: Optional[str] = data.get('chanceofrain')
|
|
71
|
-
self.chance_of_snow: Optional[str] = data.get('chanceofsnow')
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class DayForecast:
|
|
75
|
-
"""Daily forecast information with strongly typed properties."""
|
|
76
|
-
|
|
77
|
-
def __init__(self, data: Dict[str, Any]) -> None:
|
|
78
|
-
"""Initialize with daily forecast data.
|
|
79
|
-
|
|
80
|
-
Args:
|
|
81
|
-
data: Daily forecast data dictionary from wttr.in
|
|
82
|
-
"""
|
|
83
|
-
self.date: Optional[str] = data.get('date')
|
|
84
|
-
self.date_formatted: Optional[str] = None
|
|
85
|
-
if self.date:
|
|
86
|
-
try:
|
|
87
|
-
self.date_formatted = datetime.strptime(self.date, '%Y-%m-%d').strftime('%a, %b %d')
|
|
88
|
-
except ValueError:
|
|
89
|
-
pass
|
|
90
|
-
|
|
91
|
-
self.max_temp_c: Optional[str] = data.get('maxtempC')
|
|
92
|
-
self.max_temp_f: Optional[str] = data.get('maxtempF')
|
|
93
|
-
self.min_temp_c: Optional[str] = data.get('mintempC')
|
|
94
|
-
self.min_temp_f: Optional[str] = data.get('mintempF')
|
|
95
|
-
self.avg_temp_c: Optional[str] = data.get('avgtempC')
|
|
96
|
-
self.avg_temp_f: Optional[str] = data.get('avgtempF')
|
|
97
|
-
self.sun_hour: Optional[str] = data.get('sunHour')
|
|
98
|
-
|
|
99
|
-
# Parse astronomy data (simplified)
|
|
100
|
-
if data.get('astronomy') and len(data.get('astronomy', [])) > 0:
|
|
101
|
-
astro = data.get('astronomy', [{}])[0]
|
|
102
|
-
self.sunrise: Optional[str] = astro.get('sunrise')
|
|
103
|
-
self.sunset: Optional[str] = astro.get('sunset')
|
|
104
|
-
self.moon_phase: Optional[str] = astro.get('moon_phase')
|
|
105
|
-
else:
|
|
106
|
-
self.sunrise = self.sunset = self.moon_phase = None
|
|
107
|
-
|
|
108
|
-
# Parse hourly forecasts
|
|
109
|
-
self.hourly: List[HourlyForecast] = []
|
|
110
|
-
for hour_data in data.get('hourly', []):
|
|
111
|
-
self.hourly.append(HourlyForecast(hour_data))
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
class Weather:
|
|
115
|
-
"""Weather response object with strongly typed properties."""
|
|
116
|
-
|
|
117
|
-
def __init__(self, data: Optional[Dict[str, Any]] = None) -> None:
|
|
118
|
-
"""Initialize with weather data.
|
|
119
|
-
|
|
120
|
-
Args:
|
|
121
|
-
data: Weather data dictionary from wttr.in
|
|
122
|
-
"""
|
|
123
|
-
if not data:
|
|
124
|
-
self.current_condition = None
|
|
125
|
-
self.location = None
|
|
126
|
-
self.forecast_days = []
|
|
127
|
-
return
|
|
128
|
-
|
|
129
|
-
# Parse current condition
|
|
130
|
-
self.current_condition: Optional[CurrentCondition] = None
|
|
131
|
-
if data.get('current_condition') and len(data.get('current_condition', [])) > 0:
|
|
132
|
-
self.current_condition = CurrentCondition(data.get('current_condition', [{}])[0])
|
|
133
|
-
|
|
134
|
-
# Parse location
|
|
135
|
-
self.location: Optional[Location] = None
|
|
136
|
-
if data.get('nearest_area') and len(data.get('nearest_area', [])) > 0:
|
|
137
|
-
self.location = Location(data.get('nearest_area', [{}])[0])
|
|
138
|
-
|
|
139
|
-
# Parse forecast days
|
|
140
|
-
self.forecast_days: List[DayForecast] = []
|
|
141
|
-
for day_data in data.get('weather', []):
|
|
142
|
-
self.forecast_days.append(DayForecast(day_data))
|
|
143
|
-
|
|
144
|
-
@property
|
|
145
|
-
def today(self) -> Optional[DayForecast]:
|
|
146
|
-
"""Get today's forecast."""
|
|
147
|
-
return self.forecast_days[0] if self.forecast_days else None
|
|
148
|
-
|
|
149
|
-
@property
|
|
150
|
-
def tomorrow(self) -> Optional[DayForecast]:
|
|
151
|
-
"""Get tomorrow's forecast."""
|
|
152
|
-
return self.forecast_days[1] if len(self.forecast_days) > 1 else None
|
|
153
|
-
|
|
154
|
-
@property
|
|
155
|
-
def summary(self) -> str:
|
|
156
|
-
"""Get a simple text summary of current weather."""
|
|
157
|
-
if not self.current_condition or not self.location:
|
|
158
|
-
return "Weather data not available"
|
|
159
|
-
|
|
160
|
-
return f"{self.location.name}, {self.location.country}: {self.current_condition.weather_desc}, {self.current_condition.temp_c}°C ({self.current_condition.temp_f}°F)"
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
class WeatherClient:
|
|
164
|
-
"""Client for fetching weather information."""
|
|
165
|
-
|
|
166
|
-
def get_weather(self, location: str) -> Weather:
|
|
167
|
-
"""Get weather for the specified location.
|
|
168
|
-
|
|
169
|
-
Args:
|
|
170
|
-
location: Location to get weather for (city name, zip code, etc.)
|
|
171
|
-
|
|
172
|
-
Returns:
|
|
173
|
-
Weather object containing all weather data
|
|
174
|
-
"""
|
|
175
|
-
try:
|
|
176
|
-
response = requests.get(f"https://wttr.in/{location}?format=j1", timeout=10)
|
|
177
|
-
response.raise_for_status()
|
|
178
|
-
return Weather(response.json())
|
|
179
|
-
except Exception as e:
|
|
180
|
-
print(f"Error fetching weather data: {str(e)}")
|
|
181
|
-
return Weather()
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def get(location: str) -> Weather:
|
|
185
|
-
"""Convenience function to get weather for a location.
|
|
186
|
-
|
|
187
|
-
Args:
|
|
188
|
-
location: Location to get weather for
|
|
189
|
-
|
|
190
|
-
Returns:
|
|
191
|
-
Weather object containing all weather data
|
|
192
|
-
"""
|
|
193
|
-
client = WeatherClient()
|
|
1
|
+
"""
|
|
2
|
+
Weather information module with a clean, strongly-typed API structure.
|
|
3
|
+
|
|
4
|
+
This module provides a simple client for fetching weather data
|
|
5
|
+
from the wttr.in service with proper typing and a consistent interface.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import List, Dict, Any, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CurrentCondition:
|
|
14
|
+
"""Current weather conditions with strongly typed properties."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
17
|
+
"""Initialize with current condition data.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
data: Current condition data dictionary from wttr.in
|
|
21
|
+
"""
|
|
22
|
+
self.temp_c: Optional[str] = data.get('temp_C')
|
|
23
|
+
self.temp_f: Optional[str] = data.get('temp_F')
|
|
24
|
+
self.feels_like_c: Optional[str] = data.get('FeelsLikeC')
|
|
25
|
+
self.feels_like_f: Optional[str] = data.get('FeelsLikeF')
|
|
26
|
+
self.weather_desc: str = data.get('weatherDesc', [{}])[0].get('value', '')
|
|
27
|
+
self.weather_code: Optional[str] = data.get('weatherCode')
|
|
28
|
+
self.humidity: Optional[str] = data.get('humidity')
|
|
29
|
+
self.visibility: Optional[str] = data.get('visibility')
|
|
30
|
+
self.pressure: Optional[str] = data.get('pressure')
|
|
31
|
+
self.wind_speed_kmph: Optional[str] = data.get('windspeedKmph')
|
|
32
|
+
self.wind_direction: Optional[str] = data.get('winddir16Point')
|
|
33
|
+
self.wind_degree: Optional[str] = data.get('winddirDegree')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Location:
|
|
37
|
+
"""Location information with strongly typed properties."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
40
|
+
"""Initialize with location data.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
data: Location data dictionary from wttr.in
|
|
44
|
+
"""
|
|
45
|
+
self.name: str = data.get('areaName', [{}])[0].get('value', '')
|
|
46
|
+
self.country: str = data.get('country', [{}])[0].get('value', '')
|
|
47
|
+
self.region: str = data.get('region', [{}])[0].get('value', '')
|
|
48
|
+
self.latitude: Optional[str] = data.get('latitude')
|
|
49
|
+
self.longitude: Optional[str] = data.get('longitude')
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class HourlyForecast:
|
|
53
|
+
"""Hourly forecast information with strongly typed properties."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
56
|
+
"""Initialize with hourly forecast data.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
data: Hourly forecast data dictionary from wttr.in
|
|
60
|
+
"""
|
|
61
|
+
self.time: Optional[str] = data.get('time')
|
|
62
|
+
self.temp_c: Optional[str] = data.get('tempC')
|
|
63
|
+
self.temp_f: Optional[str] = data.get('tempF')
|
|
64
|
+
self.weather_desc: str = data.get('weatherDesc', [{}])[0].get('value', '')
|
|
65
|
+
self.weather_code: Optional[str] = data.get('weatherCode')
|
|
66
|
+
self.wind_speed_kmph: Optional[str] = data.get('windspeedKmph')
|
|
67
|
+
self.wind_direction: Optional[str] = data.get('winddir16Point')
|
|
68
|
+
self.feels_like_c: Optional[str] = data.get('FeelsLikeC')
|
|
69
|
+
self.feels_like_f: Optional[str] = data.get('FeelsLikeF')
|
|
70
|
+
self.chance_of_rain: Optional[str] = data.get('chanceofrain')
|
|
71
|
+
self.chance_of_snow: Optional[str] = data.get('chanceofsnow')
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class DayForecast:
|
|
75
|
+
"""Daily forecast information with strongly typed properties."""
|
|
76
|
+
|
|
77
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
78
|
+
"""Initialize with daily forecast data.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
data: Daily forecast data dictionary from wttr.in
|
|
82
|
+
"""
|
|
83
|
+
self.date: Optional[str] = data.get('date')
|
|
84
|
+
self.date_formatted: Optional[str] = None
|
|
85
|
+
if self.date:
|
|
86
|
+
try:
|
|
87
|
+
self.date_formatted = datetime.strptime(self.date, '%Y-%m-%d').strftime('%a, %b %d')
|
|
88
|
+
except ValueError:
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
self.max_temp_c: Optional[str] = data.get('maxtempC')
|
|
92
|
+
self.max_temp_f: Optional[str] = data.get('maxtempF')
|
|
93
|
+
self.min_temp_c: Optional[str] = data.get('mintempC')
|
|
94
|
+
self.min_temp_f: Optional[str] = data.get('mintempF')
|
|
95
|
+
self.avg_temp_c: Optional[str] = data.get('avgtempC')
|
|
96
|
+
self.avg_temp_f: Optional[str] = data.get('avgtempF')
|
|
97
|
+
self.sun_hour: Optional[str] = data.get('sunHour')
|
|
98
|
+
|
|
99
|
+
# Parse astronomy data (simplified)
|
|
100
|
+
if data.get('astronomy') and len(data.get('astronomy', [])) > 0:
|
|
101
|
+
astro = data.get('astronomy', [{}])[0]
|
|
102
|
+
self.sunrise: Optional[str] = astro.get('sunrise')
|
|
103
|
+
self.sunset: Optional[str] = astro.get('sunset')
|
|
104
|
+
self.moon_phase: Optional[str] = astro.get('moon_phase')
|
|
105
|
+
else:
|
|
106
|
+
self.sunrise = self.sunset = self.moon_phase = None
|
|
107
|
+
|
|
108
|
+
# Parse hourly forecasts
|
|
109
|
+
self.hourly: List[HourlyForecast] = []
|
|
110
|
+
for hour_data in data.get('hourly', []):
|
|
111
|
+
self.hourly.append(HourlyForecast(hour_data))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class Weather:
|
|
115
|
+
"""Weather response object with strongly typed properties."""
|
|
116
|
+
|
|
117
|
+
def __init__(self, data: Optional[Dict[str, Any]] = None) -> None:
|
|
118
|
+
"""Initialize with weather data.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
data: Weather data dictionary from wttr.in
|
|
122
|
+
"""
|
|
123
|
+
if not data:
|
|
124
|
+
self.current_condition = None
|
|
125
|
+
self.location = None
|
|
126
|
+
self.forecast_days = []
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
# Parse current condition
|
|
130
|
+
self.current_condition: Optional[CurrentCondition] = None
|
|
131
|
+
if data.get('current_condition') and len(data.get('current_condition', [])) > 0:
|
|
132
|
+
self.current_condition = CurrentCondition(data.get('current_condition', [{}])[0])
|
|
133
|
+
|
|
134
|
+
# Parse location
|
|
135
|
+
self.location: Optional[Location] = None
|
|
136
|
+
if data.get('nearest_area') and len(data.get('nearest_area', [])) > 0:
|
|
137
|
+
self.location = Location(data.get('nearest_area', [{}])[0])
|
|
138
|
+
|
|
139
|
+
# Parse forecast days
|
|
140
|
+
self.forecast_days: List[DayForecast] = []
|
|
141
|
+
for day_data in data.get('weather', []):
|
|
142
|
+
self.forecast_days.append(DayForecast(day_data))
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def today(self) -> Optional[DayForecast]:
|
|
146
|
+
"""Get today's forecast."""
|
|
147
|
+
return self.forecast_days[0] if self.forecast_days else None
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def tomorrow(self) -> Optional[DayForecast]:
|
|
151
|
+
"""Get tomorrow's forecast."""
|
|
152
|
+
return self.forecast_days[1] if len(self.forecast_days) > 1 else None
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def summary(self) -> str:
|
|
156
|
+
"""Get a simple text summary of current weather."""
|
|
157
|
+
if not self.current_condition or not self.location:
|
|
158
|
+
return "Weather data not available"
|
|
159
|
+
|
|
160
|
+
return f"{self.location.name}, {self.location.country}: {self.current_condition.weather_desc}, {self.current_condition.temp_c}°C ({self.current_condition.temp_f}°F)"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class WeatherClient:
|
|
164
|
+
"""Client for fetching weather information."""
|
|
165
|
+
|
|
166
|
+
def get_weather(self, location: str) -> Weather:
|
|
167
|
+
"""Get weather for the specified location.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
location: Location to get weather for (city name, zip code, etc.)
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Weather object containing all weather data
|
|
174
|
+
"""
|
|
175
|
+
try:
|
|
176
|
+
response = requests.get(f"https://wttr.in/{location}?format=j1", timeout=10)
|
|
177
|
+
response.raise_for_status()
|
|
178
|
+
return Weather(response.json())
|
|
179
|
+
except Exception as e:
|
|
180
|
+
print(f"Error fetching weather data: {str(e)}")
|
|
181
|
+
return Weather()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def get(location: str) -> Weather:
|
|
185
|
+
"""Convenience function to get weather for a location.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
location: Location to get weather for
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Weather object containing all weather data
|
|
192
|
+
"""
|
|
193
|
+
client = WeatherClient()
|
|
194
194
|
return client.get_weather(location)
|
webscout/Litlogger/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# LitLogger
|
|
2
|
-
|
|
3
|
-
A minimal yet flexible logging library built from scratch without external dependencies. It provides colored console output, file logging with rotation, simple network logging, and optional asynchronous support.
|
|
4
|
-
|
|
5
|
-
```python
|
|
6
|
-
from webscout.litlogger import Logger, LogLevel, FileHandler
|
|
7
|
-
|
|
8
|
-
logger = Logger(name="demo", level=LogLevel.DEBUG, handlers=[FileHandler("app.log")])
|
|
9
|
-
logger.info("hello world")
|
|
10
|
-
```
|
|
1
|
+
# LitLogger
|
|
2
|
+
|
|
3
|
+
A minimal yet flexible logging library built from scratch without external dependencies. It provides colored console output, file logging with rotation, simple network logging, and optional asynchronous support.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from webscout.litlogger import Logger, LogLevel, FileHandler
|
|
7
|
+
|
|
8
|
+
logger = Logger(name="demo", level=LogLevel.DEBUG, handlers=[FileHandler("app.log")])
|
|
9
|
+
logger.info("hello world")
|
|
10
|
+
```
|
webscout/Litlogger/__init__.py
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
"""Lightweight logger built from scratch."""
|
|
2
|
-
from .levels import LogLevel
|
|
3
|
-
from .handlers import ConsoleHandler, FileHandler, NetworkHandler, TCPHandler
|
|
4
|
-
from .logger import Logger
|
|
5
|
-
from .formats import LogFormat
|
|
6
|
-
|
|
7
|
-
__all__ = [
|
|
8
|
-
"Logger",
|
|
9
|
-
"LogLevel",
|
|
10
|
-
"ConsoleHandler",
|
|
11
|
-
"FileHandler",
|
|
12
|
-
"NetworkHandler",
|
|
13
|
-
"TCPHandler",
|
|
14
|
-
"LogFormat",
|
|
15
|
-
]
|
|
1
|
+
"""Lightweight logger built from scratch."""
|
|
2
|
+
from .levels import LogLevel
|
|
3
|
+
from .handlers import ConsoleHandler, FileHandler, NetworkHandler, TCPHandler
|
|
4
|
+
from .logger import Logger
|
|
5
|
+
from .formats import LogFormat
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"Logger",
|
|
9
|
+
"LogLevel",
|
|
10
|
+
"ConsoleHandler",
|
|
11
|
+
"FileHandler",
|
|
12
|
+
"NetworkHandler",
|
|
13
|
+
"TCPHandler",
|
|
14
|
+
"LogFormat",
|
|
15
|
+
]
|
webscout/Litlogger/formats.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
DEFAULT_FORMAT = "{time} | {level} | {name} | {message}"
|
|
2
|
-
|
|
3
|
-
SIMPLE_FORMAT = "{level}: {message}"
|
|
4
|
-
|
|
5
|
-
DETAILED_FORMAT = "{time} | {level} | {name} | {message} | Thread: {thread} | Process: {process}"
|
|
6
|
-
|
|
7
|
-
JSON_FORMAT = '{{"time": "{time}", "level": "{level}", "name": "{name}", "message": "{message}"}}'
|
|
8
|
-
|
|
9
|
-
class LogFormat:
|
|
10
|
-
DEFAULT = DEFAULT_FORMAT
|
|
11
|
-
SIMPLE = SIMPLE_FORMAT
|
|
12
|
-
DETAILED = DETAILED_FORMAT
|
|
13
|
-
JSON = JSON_FORMAT
|
|
1
|
+
DEFAULT_FORMAT = "{time} | {level} | {name} | {message}"
|
|
2
|
+
|
|
3
|
+
SIMPLE_FORMAT = "{level}: {message}"
|
|
4
|
+
|
|
5
|
+
DETAILED_FORMAT = "{time} | {level} | {name} | {message} | Thread: {thread} | Process: {process}"
|
|
6
|
+
|
|
7
|
+
JSON_FORMAT = '{{"time": "{time}", "level": "{level}", "name": "{name}", "message": "{message}"}}'
|
|
8
|
+
|
|
9
|
+
class LogFormat:
|
|
10
|
+
DEFAULT = DEFAULT_FORMAT
|
|
11
|
+
SIMPLE = SIMPLE_FORMAT
|
|
12
|
+
DETAILED = DETAILED_FORMAT
|
|
13
|
+
JSON = JSON_FORMAT
|