Merge pull request #476 from miaowware/fix/metar
Some checks are pending
Docker Build and Deploy / Build and push docker images (push) Waiting to run
Linting / flake8 (push) Waiting to run

Fix metar + version bumps
This commit is contained in:
0x5c 2023-12-10 14:17:35 -05:00 committed by GitHub
commit abdc5ebacb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 48 additions and 54 deletions

View File

@ -7,8 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Added ### Added
- `?drapmap` command to display NOAA D Region Absorption Predictions map. - `?drapmap` command to display NOAA D Region Absorption Predictions map.
- Support for the new username format.
### Fixed ### Fixed
- Issue where `?solarweather` would not show a picture (#474). - Issue where `?solarweather` would not show a picture (#474).
- Issue where `?metar` and `?taf` failed to fetch data (#475).
## [2.9.1] - 2023-01-29 ## [2.9.1] - 2023-01-29

View File

@ -9,11 +9,9 @@ SPDX-License-Identifier: LiLiQ-Rplus-1.1
import re import re
from typing import List
import aiohttp import aiohttp
from discord import Embed
import discord.ext.commands as commands import discord.ext.commands as commands
import common as cmn import common as cmn
@ -102,7 +100,32 @@ class WeatherCog(commands.Cog):
Airports should be given as an \ Airports should be given as an \
[ICAO code](https://en.wikipedia.org/wiki/List_of_airports_by_IATA_and_ICAO_code).""" [ICAO code](https://en.wikipedia.org/wiki/List_of_airports_by_IATA_and_ICAO_code)."""
await ctx.send(embed=await self.gen_metar_taf_embed(ctx, airport, hours, False))
embed = cmn.embed_factory(ctx)
airport = airport.upper()
if not re.fullmatch(r"\w(\w|\d){2,3}", airport):
embed.title = "Invalid airport given!"
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
return
url = f"https://aviationweather.gov/api/data/metar?ids={airport}&format=raw&taf=false&hours={hours}"
async with self.session.get(url) as r:
if r.status != 200:
raise cmn.BotHTTPError(r)
metar = await r.text()
if hours > 0:
embed.title = f"METAR for {airport} for the last {hours} hour{'s' if hours > 1 else ''}"
else:
embed.title = f"Current METAR for {airport}"
embed.description = "Data from [aviationweather.gov](https://www.aviationweather.gov/)."
embed.colour = cmn.colours.good
embed.description += f"\n\n```\n{metar}\n```"
await ctx.send(embed=embed)
@commands.command(name="taf", category=cmn.Cats.WEATHER) @commands.command(name="taf", category=cmn.Cats.WEATHER)
async def taf(self, ctx: commands.Context, airport: str): async def taf(self, ctx: commands.Context, airport: str):
@ -110,57 +133,28 @@ class WeatherCog(commands.Cog):
Airports should be given as an \ Airports should be given as an \
[ICAO code](https://en.wikipedia.org/wiki/List_of_airports_by_IATA_and_ICAO_code).""" [ICAO code](https://en.wikipedia.org/wiki/List_of_airports_by_IATA_and_ICAO_code)."""
await ctx.send(embed=await self.gen_metar_taf_embed(ctx, airport, 0, True))
async def gen_metar_taf_embed(self, ctx: commands.Context, airport: str, hours: int, taf: bool) -> Embed:
embed = cmn.embed_factory(ctx) embed = cmn.embed_factory(ctx)
airport = airport.upper() airport = airport.upper()
if re.fullmatch(r"\w(\w|\d){2,3}", airport): if not re.fullmatch(r"\w(\w|\d){2,3}", airport):
metar = await self.get_metar_taf_data(airport, hours, taf)
if taf:
embed.title = f"Current TAF for {airport}"
elif hours > 0:
embed.title = f"METAR for {airport} for the last {hours} hour{'s' if hours > 1 else ''}"
else:
embed.title = f"Current METAR for {airport}"
embed.description = "Data from [aviationweather.gov](https://www.aviationweather.gov/metar/data)."
embed.colour = cmn.colours.good
data = "\n".join(metar)
embed.description += f"\n\n```\n{data}\n```"
else:
embed.title = "Invalid airport given!" embed.title = "Invalid airport given!"
embed.colour = cmn.colours.bad embed.colour = cmn.colours.bad
return embed await ctx.send(embed=embed)
return
async def get_metar_taf_data(self, airport: str, hours: int, taf: bool) -> List[str]: url = f"https://aviationweather.gov/api/data/taf?ids={airport}&format=raw&metar=true"
url = (f"https://www.aviationweather.gov/metar/data?ids={airport}&format=raw&hours={hours}"
f"&taf={'on' if taf else 'off'}&layout=off")
async with self.session.get(url) as r: async with self.session.get(url) as r:
if r.status != 200: if r.status != 200:
raise cmn.BotHTTPError(r) raise cmn.BotHTTPError(r)
page = await r.text() taf = await r.text()
# pare down to just the data embed.title = f"Current TAF for {airport}"
page = page.split("<!-- Data starts here -->")[1].split("<!-- Data ends here -->")[0].strip() embed.description = "Data from [aviationweather.gov](https://www.aviationweather.gov/)."
# split at <hr>s embed.colour = cmn.colours.good
data = re.split(r"<hr.*>", page, maxsplit=len(airport)) embed.description += f"\n\n```\n{taf}\n```"
parsed = [] await ctx.send(embed=embed)
for sec in data:
if sec.strip():
for line in sec.split("\n"):
line = line.strip()
# remove HTML stuff
line = line.replace("<code>", "").replace("</code>", "")
line = line.replace("<strong>", "").replace("</strong>", "")
line = line.replace("<br/>", "\n").replace("&nbsp;", " ")
line = line.strip("\n")
parsed.append(line)
return parsed
def setup(bot: commands.Bot): def setup(bot: commands.Bot):

View File

@ -1,5 +1,4 @@
py-cord~=2.3.2 py-cord-dev[speed]==2.5.0rc5
aiohttp[speedups]
ctyparser~=2.0 ctyparser~=2.0
gridtools~=1.0 gridtools~=1.0
callsignlookuptools[async]~=1.1 callsignlookuptools[async]~=1.1
@ -7,4 +6,4 @@ beautifulsoup4
pytz pytz
cairosvg cairosvg
httpx httpx
pydantic pydantic~=2.5

View File

@ -23,7 +23,7 @@ class ResourcesManager:
def parse_index(self, index: str): def parse_index(self, index: str):
"""Parses the index.""" """Parses the index."""
return Index.parse_raw(index) return Index.model_validate_json(index)
def sync_fetch(self, filepath: str): def sync_fetch(self, filepath: str):
"""Fetches files in sync mode.""" """Fetches files in sync mode."""

View File

@ -10,7 +10,7 @@ SPDX-License-Identifier: LiLiQ-Rplus-1.1
from collections.abc import Mapping from collections.abc import Mapping
from datetime import datetime from datetime import datetime
from pydantic import BaseModel from pydantic import BaseModel, RootModel
class File(BaseModel): class File(BaseModel):
@ -22,18 +22,17 @@ class File(BaseModel):
return repr(self) return repr(self)
class Resource(BaseModel, Mapping): class Resource(RootModel, Mapping):
# 'A Beautiful Hack' https://github.com/samuelcolvin/pydantic/issues/1802 root: dict[str, list[File]]
__root__: dict[str, list[File]]
def __getitem__(self, key: str) -> list[File]: def __getitem__(self, key: str) -> list[File]:
return self.__root__[key] return self.root[key]
def __iter__(self): def __iter__(self):
return iter(self.__root__) return iter(self.root)
def __len__(self) -> int: def __len__(self) -> int:
return len(self.__root__) return len(self.root)
# For some reason those were not the same??? # For some reason those were not the same???
def __str__(self) -> str: def __str__(self) -> str:
@ -41,7 +40,7 @@ class Resource(BaseModel, Mapping):
# Make the repr more logical (despite the technical inaccuracy) # Make the repr more logical (despite the technical inaccuracy)
def __repr_args__(self): def __repr_args__(self):
return self.__root__.items() return self.root.items()
class Index(BaseModel, Mapping): class Index(BaseModel, Mapping):