altrepo: add appstream module

parent 78aa0c97
......@@ -2,5 +2,6 @@
.venv
*.db
/appstream/
__pycache__/
\ No newline at end of file
......@@ -13,7 +13,8 @@ dependencies = [
"pydantic (>=2.11.7,<3.0.0)",
"pydantic-settings (>=2.10.1,<3.0.0)",
"peewee (>=3.18.2,<4.0.0)",
"bs4 (>=0.0.2,<0.0.3)"
"bs4 (>=0.0.2,<0.0.3)",
"lxml (>=6.0.0,<7.0.0)"
]
[project.urls]
......
import aiohttp
from .api import ALTRepoAPI
from .appstream import ALTRepoAppStream
from .parser import ALTRepoParser
class ALTRepo:
......@@ -8,17 +9,21 @@ class ALTRepo:
def __init__(self):
self._session: aiohttp.ClientSession | None = None
self.api: ALTRepoAPI | None = None
self.appstream: ALTRepoAppStream | None = None
self.parser: ALTRepoParser | None = None
async def init(self):
async def init(
self,
appstream_dir: str = "appstream"
):
if self._session is None:
self._session = aiohttp.ClientSession()
self.api = ALTRepoAPI(self._session)
self.appstream = ALTRepoAppStream(self._session, appstream_dir)
self.parser = ALTRepoParser(self._session)
async def close(self):
await self._session.close()
altrepo = ALTRepo()
......
from .methods import ALTRepoAppStream
import os
import aiohttp
from lxml import etree
class AppStreamClient:
BASE_URL = (
"https://git.altlinux.org/gears/a/appstream-data-desktop.git?"
"a=blob_plain;f=xmls/altlinux.xml;hb=refs/heads/{}"
)
def __init__(self, session: aiohttp.ClientSession):
self.session = session
async def _handle_response(self, resp: aiohttp.ClientResponse):
match resp.status:
case 200 | 201:
return await resp.text(encoding="utf-8")
case _:
return None
async def get(self, branch: str) -> str | None:
url = self.BASE_URL.format(branch)
async with self.session.get(url) as resp:
return await self._handle_response(resp)
class DataInfo:
def __init__(self, client: AppStreamClient, appstream_dir: str):
self.client = client
self.dir = appstream_dir
os.makedirs(self.dir, exist_ok=True)
async def load_by_branch(self, branch: str, version: str):
text = await self.client.get(branch)
if not text:
return
old_file = self.get_file_path(branch)
if old_file:
os.remove(old_file)
filename = f"{branch}-{version}.xml"
filepath = os.path.join(self.dir, filename)
with open(filepath, "w", encoding="utf-8") as f:
f.write(text)
def get_file_path(self, branch: str) -> str | None:
for fname in os.listdir(self.dir):
if fname.startswith(f"{branch}-") and fname.endswith(".xml"):
return os.path.join(self.dir, fname)
return None
def get_current_version(self, branch: str) -> str | None:
path = self.get_file_path(branch)
if path:
fname = os.path.basename(path)
return fname[len(branch) + 1 : -4]
return None
def has_file(self, branch: str) -> bool:
return self.get_file_path(branch) is not None
class PackageInfo:
def __init__(self, client: AppStreamClient, data: DataInfo):
self.client = client
self._data = data
def id_by_pkgname(self, pkgname: str, branch: str) -> str | None:
path = self._data.get_file_path(branch)
if not path:
return None
context = etree.iterparse(path, events=("end",), recover=True)
for _, elem in context:
if elem.tag == "component":
if elem.findtext("pkgname") == pkgname:
return elem.findtext("id")
elem.clear()
while elem.getprevious() is not None:
del elem.getparent()[0]
return None
class ALTRepoAppStream:
def __init__(self, session: aiohttp.ClientSession, appstream_dir: str):
self._client = AppStreamClient(session)
self.data = DataInfo(self._client, appstream_dir)
self.package = PackageInfo(self._client, self.data)
......@@ -32,6 +32,10 @@ async def info_handler(m: Message) -> None:
f" Сопровождающих в базе: {DB.maintainer.count()}\n"
f" Пакетов переведено: {DB.package.count()}\n\n"
f"{_bold("ALTRepo Bot AppStream Data:")}\n"
f" Sisyphus: {altrepo.appstream.data.get_current_version("sisyphus")}\n"
f" P11: {altrepo.appstream.data.get_current_version("p11")}\n\n"
f"{_bold("Исходный код:")}\n"
f"{repos_formatted}\n\n"
......
......@@ -11,12 +11,14 @@ from middlewares import UserMiddleware
from services.test_api_version import test_api_version
from services.update_maintainers import update_maintainers
from services.appstream.update_appstream_data import update_appstream_data
bot = Telegrinder(tg_api)
bot.dispatch.load_from_dir("src/handlers")
bot.on.message.register_middleware(UserMiddleware)
@bot.loop_wrapper.lifespan.on_startup
async def startup():
db.create_tables([Maintainer, User, Package])
......@@ -24,31 +26,43 @@ async def startup():
await altrepo.init()
await test_api_version()
await update_maintainers()
await bot.api.set_my_commands(commands=[
BotCommand("watch", "Отслеживание по пакетам"),
BotCommand("bugs", "Отслеживание по ошибкам"),
BotCommand("ftbfs", "Ошибки пересборки"),
BotCommand("statistics", "Статистика репозитория"),
BotCommand("altrepo_info", "Информация о боте"),
BotCommand("help", "Справка")
])
await update_appstream_data()
await bot.api.set_my_commands(
commands=[
BotCommand("watch", "Отслеживание по пакетам"),
BotCommand("bugs", "Отслеживание по ошибкам"),
BotCommand("ftbfs", "Ошибки пересборки"),
BotCommand("statistics", "Статистика репозитория"),
BotCommand("altrepo_info", "Информация о боте"),
BotCommand("help", "Справка"),
]
)
@bot.loop_wrapper.lifespan.on_shutdown
async def shutdown():
logger.info("stopping ALTRepo")
await altrepo.close()
@bot.loop_wrapper.interval(days=1)
async def api_test():
await test_api_version()
@bot.loop_wrapper.interval(days=1)
async def update_maintainers_db():
await update_maintainers()
@bot.loop_wrapper.interval(days=1)
async def update_appstream():
await update_appstream_data()
@bot.on.error()
async def error_handler(err: Error[TooManyRequests], m: Message):
await m.answer(f"Слишком много запросов!")
bot.run_forever(skip_updates=True)
from telegrinder.modules import logger
from altrepo import altrepo
async def update_appstream_data():
for branch in ["sisyphus", "p11"]:
package = (
await altrepo.api.package.package_info(
"appstream-data-desktop", branch=branch, source=True
)
).packages[0]
current_version = altrepo.appstream.data.get_current_version(branch)
package_version = f"{package.version}-{package.release}"
if (
not altrepo.appstream.data.has_file(branch)
or package_version != current_version
):
await altrepo.appstream.data.load_by_branch(branch, package_version)
logger.info(f"Update AppStream data for {branch}")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment