From 94afbe08291d227c8a42b7ea2c63dca7ad7d104d Mon Sep 17 00:00:00 2001 From: Obijuan Date: Mon, 17 Jun 2024 10:48:42 +0200 Subject: [PATCH] icm install: Initial prototype --- .vscode/launch.json | 12 +++ icm/__main__.py | 14 +++- icm/commands/cmd_info.py | 147 ++---------------------------------- icm/commands/cmd_install.py | 102 +++++++++++++++++++++++++ icm/commons/__init__.py | 0 icm/commons/commons.py | 48 ++++++++++++ 6 files changed, 180 insertions(+), 143 deletions(-) create mode 100644 icm/commands/cmd_install.py create mode 100644 icm/commons/__init__.py create mode 100644 icm/commons/commons.py diff --git a/.vscode/launch.json b/.vscode/launch.json index 72faa5f..a15df60 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -30,6 +30,18 @@ "internalConsoleOptions": "neverOpen", "preLaunchTask": "Clear terminal", }, + { + "name": "ICM install", + "type": "debugpy", + "request": "launch", + "program": "icm-run.py", + "args": ["install"], + //"console": "internalConsole", + "console": "integratedTerminal", + "justMyCode": true, + "internalConsoleOptions": "neverOpen", + "preLaunchTask": "Clear terminal", + }, ] } diff --git a/icm/__main__.py b/icm/__main__.py index 38222a9..c6489fb 100644 --- a/icm/__main__.py +++ b/icm/__main__.py @@ -8,7 +8,13 @@ import click -from icm.commands import cmd_create, cmd_validate, cmd_update, cmd_info +from icm.commands import ( + cmd_create, + cmd_validate, + cmd_update, + cmd_info, + cmd_install, +) @click.group() @@ -39,3 +45,9 @@ def validate(): def info(): """Show system information""" cmd_info.main() + + +@cli.command() +def install(): + """Install collections""" + cmd_install.main() diff --git a/icm/commands/cmd_info.py b/icm/commands/cmd_info.py index 572d1b6..7f9db06 100644 --- a/icm/commands/cmd_info.py +++ b/icm/commands/cmd_info.py @@ -9,73 +9,11 @@ # -- Licence GPLv2 import platform -import shutil -import zipfile -import os -from pathlib import Path -from typing import NamedTuple - -import requests import click -from tqdm import tqdm - - -# -- Context information -class Context(NamedTuple): - """general Context information""" - - @property - def terminal_width(self) -> int: - """Get the terminal with in columns""" - return shutil.get_terminal_size().columns - - @property - def line(self) -> str: - """Return a line as long as the terminal width""" - return "─" * self.terminal_width - - -# -- Folder information -class Folders(NamedTuple): - """Icestudio related folders""" - - @property - def home(self) -> Path: - """Return the home user folder""" - return Path.home() +from icm.commons import commons - @property - def icestudio(self) -> Path: - """Return the icestudio data folder""" - return self.home / ".icestudio" - @property - def collections(self) -> Path: - """Return the icestudio collections folder""" - return self.icestudio / "collections" - - @staticmethod - def check(folder: Path) -> str: - """Return a check character depending if the folder exists - ✅ : Folder exists - ❌ : Folder does NOT exist - """ - return "✅ " if folder.exists() else "❌ " - - -# -- Information about collections -icek = {"name": "iceK", "version": "0.1.4"} - -# -- Collection file -COLLECTION_FILE = "v" - -# -- Template URL -# -- Full collection download url: PREFIX + NAME + SUFFIX + FILE -TEMPLATE_URL_PREFIX = "https://github.com/FPGAwars/" -TEMPLATE_URL_SUFFIX = "/archive/refs/tags/" - - -def print_system_info(ctx: Context) -> None: +def print_system_info(ctx: commons.Context) -> None: """Print System information""" # -- Header @@ -94,7 +32,7 @@ def print_system_info(ctx: Context) -> None: click.echo(click.style("• Version: ", fg="green") + f"{plat.version}") -def print_folders_info(ctx: Context, folders: Folders) -> None: +def print_folders_info(ctx: commons.Context, folders: commons.Folders) -> None: """Print Sytem folders info""" # -- Header @@ -127,8 +65,8 @@ def main(): """ENTRY POINT: Show system information""" # -- Get context information - ctx = Context() - folders = Folders() + ctx = commons.Context() + folders = commons.Folders() # -------------------------------- # -- Print System information @@ -139,78 +77,3 @@ def main(): # -- System folders # ---------------------------- print_folders_info(ctx, folders) - - # --- Scafold for the installation of collections - # -- Download url: - # https://github.com/FPGAwars/iceK/archive/refs/tags/v0.1.4.zip - # -- Build the collection filename - filename = f"v{icek['version']}.zip" - print(f"Colection file: {filename}") - - absolut_filename = f"{folders.collections}/{filename}" - print(f"Absolut filename: {absolut_filename}") - - url = f"{TEMPLATE_URL_PREFIX}{icek['name']}{TEMPLATE_URL_SUFFIX}{filename}" - print(f"Url: {url}") - - # -- Download the collection - # Realizar la solicitud HTTP para obtener el contenido del archivo - response = requests.get(url, stream=True, timeout=10) - - # Verificar que la solicitud se completó correctamente - if response.status_code == 200: - - # Obtener el tamaño total del archivo desde los headers - total_size = int(response.headers.get("content-length", 0)) - - # Abrir un archivo local con el nombre especificado en - # modo escritura binaria - with open(absolut_filename, "wb") as file: - - # Crear una barra de progreso con tqdm - with tqdm( - total=total_size, unit="B", unit_scale=True, desc=filename - ) as pbar: - # Iterar sobre el contenido en bloques - for chunk in response.iter_content(chunk_size=1024): - # Filtrar bloques vacíos - if chunk: - # Escribir el contenido del bloque en el archivo local - file.write(chunk) - # Actualizar la barra de progreso - pbar.update(len(chunk)) - - # shutil.copyfileobj(response.raw, file) - print(f"Archivo descargado y guardado como {filename}") - else: - print( - f"Error al descargar el archivo. " - f"Código de estado: {response.status_code}" - ) - - # -- Uncompress the collection - - # Nombre del archivo ZIP - zip_filename = absolut_filename - - # Directorio de destino para descomprimir los archivos - extract_to = folders.collections - - # Abrir el archivo ZIP y extraer su contenido con barra de progreso - with zipfile.ZipFile(zip_filename, "r") as zip_ref: - # Obtener la lista de archivos en el archivo ZIP - file_list = zip_ref.namelist() - - # Crear una barra de progreso - with tqdm( - total=len(file_list), desc="Descomprimiendo", unit="file" - ) as pbar: - # Iterar sobre cada archivo en el archivo ZIP - for file in file_list: - # Extraer cada archivo - zip_ref.extract(file, extract_to) - # Actualizar la barra de progreso - pbar.update(1) - - # -- Borrar el archivo zip - os.remove(zip_filename) diff --git a/icm/commands/cmd_install.py b/icm/commands/cmd_install.py new file mode 100644 index 0000000..0fd0d66 --- /dev/null +++ b/icm/commands/cmd_install.py @@ -0,0 +1,102 @@ +"""Install command""" + +import zipfile +import os +import requests +from tqdm import tqdm +from icm.commons import commons + +# -- Information about collections +icek = {"name": "iceK", "version": "0.1.4"} + +# -- Collection file +COLLECTION_FILE = "v" + +# -- Template URL +# -- Full collection download url: PREFIX + NAME + SUFFIX + FILE +TEMPLATE_URL_PREFIX = "https://github.com/FPGAwars/" +TEMPLATE_URL_SUFFIX = "/archive/refs/tags/" + + +def main(): + """ENTRY POINT: Install collections""" + + # -- Get context information + folders = commons.Folders() + + print("Install!!!") + + # --- Scafold for the installation of collections + # -- Download url: + # https://github.com/FPGAwars/iceK/archive/refs/tags/v0.1.4.zip + # -- Build the collection filename + filename = f"v{icek['version']}.zip" + print(f"Colection file: {filename}") + + absolut_filename = f"{folders.collections}/{filename}" + print(f"Absolut filename: {absolut_filename}") + + url = f"{TEMPLATE_URL_PREFIX}{icek['name']}{TEMPLATE_URL_SUFFIX}{filename}" + print(f"Url: {url}") + + # -- Download the collection + # Realizar la solicitud HTTP para obtener el contenido del archivo + response = requests.get(url, stream=True, timeout=10) + + # Verificar que la solicitud se completó correctamente + if response.status_code == 200: + + # Obtener el tamaño total del archivo desde los headers + total_size = int(response.headers.get("content-length", 0)) + + # Abrir un archivo local con el nombre especificado en + # modo escritura binaria + with open(absolut_filename, "wb") as file: + + # Crear una barra de progreso con tqdm + with tqdm( + total=total_size, unit="B", unit_scale=True, desc=filename + ) as pbar: + # Iterar sobre el contenido en bloques + for chunk in response.iter_content(chunk_size=1024): + # Filtrar bloques vacíos + if chunk: + # Escribir el contenido del bloque en el archivo local + file.write(chunk) + # Actualizar la barra de progreso + pbar.update(len(chunk)) + + # shutil.copyfileobj(response.raw, file) + print(f"Archivo descargado y guardado como {filename}") + else: + print( + f"Error al descargar el archivo. " + f"Código de estado: {response.status_code}" + ) + + # -- Uncompress the collection + + # Nombre del archivo ZIP + zip_filename = absolut_filename + + # Directorio de destino para descomprimir los archivos + extract_to = folders.collections + + # Abrir el archivo ZIP y extraer su contenido con barra de progreso + with zipfile.ZipFile(zip_filename, "r") as zip_ref: + # Obtener la lista de archivos en el archivo ZIP + file_list = zip_ref.namelist() + + # Crear una barra de progreso + with tqdm( + total=len(file_list), desc="Descomprimiendo", unit="file" + ) as pbar: + # Iterar sobre cada archivo en el archivo ZIP + for file in file_list: + # Extraer cada archivo + zip_ref.extract(file, extract_to) + # Actualizar la barra de progreso + pbar.update(1) + + # -- Borrar el archivo zip + os.remove(zip_filename) diff --git a/icm/commons/__init__.py b/icm/commons/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/icm/commons/commons.py b/icm/commons/commons.py new file mode 100644 index 0000000..21e4b83 --- /dev/null +++ b/icm/commons/commons.py @@ -0,0 +1,48 @@ +"""Data structures common to all the modules""" + +from typing import NamedTuple +import shutil +from pathlib import Path + + +# -- Context information +class Context(NamedTuple): + """general Context information""" + + @property + def terminal_width(self) -> int: + """Get the terminal with in columns""" + return shutil.get_terminal_size().columns + + @property + def line(self) -> str: + """Return a line as long as the terminal width""" + return "─" * self.terminal_width + + +# -- Folder information +class Folders(NamedTuple): + """Icestudio related folders""" + + @property + def home(self) -> Path: + """Return the home user folder""" + return Path.home() + + @property + def icestudio(self) -> Path: + """Return the icestudio data folder""" + return self.home / ".icestudio" + + @property + def collections(self) -> Path: + """Return the icestudio collections folder""" + return self.icestudio / "collections" + + @staticmethod + def check(folder: Path) -> str: + """Return a check character depending if the folder exists + ✅ : Folder exists + ❌ : Folder does NOT exist + """ + return "✅ " if folder.exists() else "❌ "