diff --git a/searx/engines/duckduckgo_weather.py b/searx/engines/duckduckgo_weather.py new file mode 100644 index 000000000..d0f34d60f --- /dev/null +++ b/searx/engines/duckduckgo_weather.py @@ -0,0 +1,136 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +# lint: pylint +"""DuckDuckGo Weather""" + +from json import loads +from urllib.parse import quote + +from datetime import datetime +from flask_babel import gettext + +about = { + "website": 'https://duckduckgo.com/', + "wikidata_id": 'Q12805', + "official_api_documentation": None, + "use_official_api": True, + "require_api_key": False, + "results": "JSON", +} + +categories = ["others"] + +url = "https://duckduckgo.com/js/spice/forecast/{query}/{lang}" + + +def generate_condition_table(condition): + res = "" + + res += f"{gettext('Condition')}" f"{condition['summary']}" + + res += ( + f"{gettext('Temperature')}" + f"{f_to_c(condition['temperature'])}°C / {condition['temperature']}°F" + ) + + res += ( + f"{gettext('Feels like')}{f_to_c(condition['apparentTemperature'])}°C / " + f"{condition['apparentTemperature']}°F" + ) + + res += ( + f"{gettext('Wind')}{condition['windBearing']}° — " + f"{'%.2f' % (condition['windSpeed'] * 1.6093440006147)} km/h / {condition['windSpeed']} mph" + ) + + res += f"{gettext('Visibility')}{condition['visibility']} km" + + res += f"{gettext('Humidity')}{condition['humidity'] * 100}%" + + return res + + +def generate_day_table(day): + res = "" + + res += ( + f"{gettext('Min temp.')}{f_to_c(day['temperatureLow'])}°C / " + f"{day['temperatureLow']}°F" + ) + res += ( + f"{gettext('Max temp.')}{f_to_c(day['temperatureHigh'])}°C / " + f"{day['temperatureHigh']}°F" + ) + res += f"{gettext('UV index')}{day['uvIndex']}" + res += ( + f"{gettext('Sunrise')}{datetime.fromtimestamp(day['sunriseTime']).strftime('%H:%M')}" + ) + res += ( + f"{gettext('Sunset')}{datetime.fromtimestamp(day['sunsetTime']).strftime('%H:%M')}" + ) + + return res + + +def request(query, params): + params["url"] = url.format(query=quote(query), lang=params['language'].split('-')[0]) + + return params + + +def f_to_c(temperature): + return "%.2f" % ((temperature - 32) / 1.8) + + +def response(resp): + results = [] + + if resp.text.strip() == "ddg_spice_forecast();": + return [] + + result = loads(resp.text[resp.text.find('\n') + 1 : resp.text.rfind('\n') - 2]) + + current = result["currently"] + + title = result['flags']['ddg-location'] + + infobox = f"

{gettext('Current condition')}

" + + infobox += generate_condition_table(current) + + infobox += "
" + + last_date = None + + for time in result['hourly']['data']: + current_time = datetime.fromtimestamp(time['time']) + + if last_date != current_time.date(): + if last_date is not None: + infobox += "" + + infobox += f"

{current_time.strftime('%Y-%m-%d')}

" + + infobox += "" + + for day in result['daily']['data']: + if datetime.fromtimestamp(day['time']).date() == current_time.date(): + infobox += generate_day_table(day) + + infobox += "
" + + last_date = current_time.date() + + infobox += f"" + + infobox += generate_condition_table(time) + + infobox += "
{current_time.strftime('%H:%M')}
" + + results.append( + { + "infobox": title, + "content": infobox, + } + ) + + return results diff --git a/searx/settings.yml b/searx/settings.yml index c97a86e4f..85bcec860 100644 --- a/searx/settings.yml +++ b/searx/settings.yml @@ -551,6 +551,11 @@ engines: timeout: 3.0 disabled: true + - name: duckduckgo weather + engine: duckduckgo_weather + shortcut: ddw + disabled: true + - name: apple maps engine: apple_maps shortcut: apm