commit 5dceeb5b70160728a3d456af2a922fc1f591e02d Author: David Itehua Xalamihua Date: Sun Jun 15 18:42:31 2025 -0600 repocitorio iniciado desde cero diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..da849a8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.venv +venv + +# # Ignorar todo en /static/uploads +# /static/uploads/* +# /cache/* + +# # ¡Pero no ignores el archivo .gitkeep! +# !/static/uploads/.gitkeep +# !/cache/.gitkeep + + +# Ignorar caché de Python +__pycache__/ +*.pyc \ No newline at end of file diff --git a/db_xala_dev/01_create_db.py b/db_xala_dev/01_create_db.py new file mode 100644 index 0000000..b8a7ca2 --- /dev/null +++ b/db_xala_dev/01_create_db.py @@ -0,0 +1,195 @@ +import sqlite3 +import os +import pandas as pd +from tqdm import tqdm + +# Nombre del archivo de la base de datos +db_name = "xala_dev.db" + +# Verificar si la base de datos ya existe +if os.path.exists(db_name): + os.remove(db_name) + print(f"Se elimina la base de datos pre existente.") + + +# Conectar a SQLite (si no existe, se crea automáticamente) +conn = sqlite3.connect(db_name) +cursor = conn.cursor() + +# ###################################################### +# [1/5] CREAR LA TABLA "home" +# ###################################################### +cursor.execute(''' +CREATE TABLE home ( + pk TEXT PRIMARY KEY, + label TEXT, + icon TEXT, + description TEXT, + svg_data TEXT +); +''') + +xlsx_path = lambda x: f'Files/xlsx_n_xlsx/{x}' +html_path = lambda x: f'Files/html_template/{x}' +svg_path = lambda x: f'Files/svg/{x}' + +f_home = xlsx_path("a_home.xlsx") + +df_home = pd.read_excel(f_home) +df_home = pd.DataFrame(df_home) +lst_home = [] + +for row in tqdm(range(df_home.shape[0])): + pk = df_home['pk'].iloc[row] + label = df_home['software'].iloc[row] + icon = df_home['icon'].iloc[row] + description = df_home['short_desc'].iloc[row] + with open(f"{svg_path(df_home['svg_file'].iloc[row])}", "r", encoding="utf-8") as f: + svg_data = f.read() + + lst_home.append(tuple([pk, label, icon, description, svg_data])) + +cursor.executemany('INSERT INTO home (pk, label, icon, description, svg_data) VALUES (?, ?, ?, ?, ?)', lst_home) +del df_home +print("✔️ home") + +# ###################################################### +# [2/5] CREAR LA TABLA "all_posts" +# ###################################################### +cursor.execute(''' +CREATE TABLE all_posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + pk TEXT, + date TEXT, + status TEXT, + titulo_tema TEXT, + sintesis TEXT, + html_alert TEXT, + lst_glosary TEXT, + lst_post_related TEXT +); +''') + +f_all_posts = xlsx_path("b_all_posts.xlsx") +df_all_posts = pd.read_excel(f_all_posts) +df_all_posts = pd.DataFrame(df_all_posts) +lst_all_posts = [] + +for row in tqdm(range(df_all_posts.shape[0])): + id = int(df_all_posts['index'].iloc[row]) + pk = df_all_posts['pk'].iloc[row] + date = str(df_all_posts['date'].iloc[row]) + status = df_all_posts['status'].iloc[row] + titulo_tema = df_all_posts['titulo_tema'].iloc[row] + sintesis = df_all_posts['sintesis'].iloc[row] + # file_html_template = f'{str(df_all_posts["index"].iloc[row])}.html' + # with open(f"{html_path(file_html_template)}", mode='r', encoding='utf-8') as html_temp: + # html_template = html_temp.read() + + html_alert = f'{str(df_all_posts["html_alert"].iloc[row])}' + lst_glosary = str(df_all_posts['lst_glosary'].iloc[row]) # Convertir la lista a una representación de cadena + lst_post_related = df_all_posts['lst_post_related'].iloc[row] # Convertir la lista a una representación de cadena + + lst_all_posts.append(tuple([id, pk, date, status, titulo_tema, sintesis, html_alert, lst_glosary, lst_post_related])) + +cursor.executemany(''' +INSERT INTO all_posts (id, pk, date, status, titulo_tema, sintesis, html_alert, lst_glosary, lst_post_related) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) +''', lst_all_posts) + +del df_all_posts +print("✔️ All_Posts") + +# ###################################################### +# [3/5] CREAR LA TABLA DE GLOSARIO. +# ###################################################### + +cursor.execute(''' +CREATE TABLE glosary ( + pk TEXT, + word TEXT, + desc_html TEXT +); +''') + +f_glosary = xlsx_path("d_glorary.xlsx") +df_glosary = pd.read_excel(f_glosary) +df_glosary = pd.DataFrame(df_glosary) +lst_glosary = [] + +for row in tqdm(range(df_glosary.shape[0])): + pk = df_glosary['id_key_word'].iloc[row] + word = df_glosary['word'].iloc[row] + html_template = str(df_glosary['desc_html'].iloc[row]) + + lst_glosary.append(tuple([pk, word, html_template])) + +cursor.executemany('INSERT INTO glosary (pk, word, desc_html) VALUES (?, ?, ?)', lst_glosary) +del df_glosary +print("✔️ glosary") + + +# ###################################################### +# [4/5] CREAR LA TABLA DE about_me +# ###################################################### +cursor.execute( +''' +CREATE TABLE about_me ( + about_me_html TEXT, + lst_glosary TEXT +); +''' +) + +f_about_me = xlsx_path("c_about_me.xlsx") +df_about_me = pd.read_excel(f_about_me) +df_about_me = pd.DataFrame(df_about_me) +lst_about_me = [] + +for index, row in tqdm(df_about_me.iterrows(), total=df_about_me.shape[0]): + about_me_html = str(row['about_me_html']) + lst_glosary = row['lst_glosary'] + lst_about_me.append(tuple([about_me_html,lst_glosary])) + +cursor.executemany("INSERT INTO about_me (about_me_html, lst_glosary) VALUES (?, ?);", lst_about_me) + +del df_about_me +print("✔️ about_me") + +# ###################################################### +# [5/5] CREAR TABLA DE EXTENSIONES +# ###################################################### +cursor.execute( + ''' + CREATE TABLE exts ( + pk TEXT, + ext TEXT, + name TEXT + ) + ''' +) + +f_exts = xlsx_path("e_ext.xlsx") +df_exts = pd.read_excel(f_exts) +df_exts = pd.DataFrame(df_exts) +lst_exts = [] + +for row in tqdm(range(df_exts.shape[0])): + pk = df_exts['pk'].iloc[row] + ext = df_exts['ext'].iloc[row] + name = str(df_exts['name'].iloc[row]) + + lst_exts.append(tuple([pk, ext, name])) + +cursor.executemany("INSERT INTO exts (pk, ext, name) VALUES (?, ?, ?);", lst_exts) + +del df_exts +print("✔️ ext") + +# ###################################################### +# Confirmar cambios y cerrar conexión +# ###################################################### +conn.commit() +conn.close() + +print(f"DB '{db_name}' creada exitosamente.") diff --git a/db_xala_dev/Files/svg/apache.svg b/db_xala_dev/Files/svg/apache.svg new file mode 100644 index 0000000..a9c5818 --- /dev/null +++ b/db_xala_dev/Files/svg/apache.svg @@ -0,0 +1 @@ + file_type_apache \ No newline at end of file diff --git a/db_xala_dev/Files/svg/data analytics.svg b/db_xala_dev/Files/svg/data analytics.svg new file mode 100644 index 0000000..27bac6d --- /dev/null +++ b/db_xala_dev/Files/svg/data analytics.svg @@ -0,0 +1,28 @@ + + + + + + + + + + \ No newline at end of file diff --git a/db_xala_dev/Files/svg/excel.svg b/db_xala_dev/Files/svg/excel.svg new file mode 100644 index 0000000..3f614f9 --- /dev/null +++ b/db_xala_dev/Files/svg/excel.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/db_xala_dev/Files/svg/git.svg b/db_xala_dev/Files/svg/git.svg new file mode 100644 index 0000000..ed29338 --- /dev/null +++ b/db_xala_dev/Files/svg/git.svg @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/db_xala_dev/Files/svg/haproxy.svg b/db_xala_dev/Files/svg/haproxy.svg new file mode 100644 index 0000000..c687170 --- /dev/null +++ b/db_xala_dev/Files/svg/haproxy.svg @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/db_xala_dev/Files/svg/js.svg b/db_xala_dev/Files/svg/js.svg new file mode 100644 index 0000000..f57f29f --- /dev/null +++ b/db_xala_dev/Files/svg/js.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/db_xala_dev/Files/svg/linux.svg b/db_xala_dev/Files/svg/linux.svg new file mode 100644 index 0000000..32a13c2 --- /dev/null +++ b/db_xala_dev/Files/svg/linux.svg @@ -0,0 +1,486 @@ + + Tux + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/db_xala_dev/Files/svg/notebook.svg b/db_xala_dev/Files/svg/notebook.svg new file mode 100644 index 0000000..def9b7a --- /dev/null +++ b/db_xala_dev/Files/svg/notebook.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/db_xala_dev/Files/svg/postgresql.svg b/db_xala_dev/Files/svg/postgresql.svg new file mode 100644 index 0000000..1092e1d --- /dev/null +++ b/db_xala_dev/Files/svg/postgresql.svg @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/db_xala_dev/Files/svg/powershell.svg b/db_xala_dev/Files/svg/powershell.svg new file mode 100644 index 0000000..9998025 --- /dev/null +++ b/db_xala_dev/Files/svg/powershell.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/db_xala_dev/Files/svg/python.svg b/db_xala_dev/Files/svg/python.svg new file mode 100644 index 0000000..7edba14 --- /dev/null +++ b/db_xala_dev/Files/svg/python.svg @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/db_xala_dev/Files/svg/raspberry_pi.svg b/db_xala_dev/Files/svg/raspberry_pi.svg new file mode 100644 index 0000000..969dd14 --- /dev/null +++ b/db_xala_dev/Files/svg/raspberry_pi.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/db_xala_dev/Files/svg/readme.md b/db_xala_dev/Files/svg/readme.md new file mode 100644 index 0000000..6352bb3 --- /dev/null +++ b/db_xala_dev/Files/svg/readme.md @@ -0,0 +1,2 @@ +Las imagenes las encuentro en el sitio: +[[ https://www.svgrepo.com/ | SVG ]] \ No newline at end of file diff --git a/db_xala_dev/Files/xlsx_n_xlsx/a_home.xlsx b/db_xala_dev/Files/xlsx_n_xlsx/a_home.xlsx new file mode 100644 index 0000000..b2532ec Binary files /dev/null and b/db_xala_dev/Files/xlsx_n_xlsx/a_home.xlsx differ diff --git a/db_xala_dev/Files/xlsx_n_xlsx/b_all_posts.xlsx b/db_xala_dev/Files/xlsx_n_xlsx/b_all_posts.xlsx new file mode 100644 index 0000000..5079069 Binary files /dev/null and b/db_xala_dev/Files/xlsx_n_xlsx/b_all_posts.xlsx differ diff --git a/db_xala_dev/Files/xlsx_n_xlsx/c_about_me.xlsx b/db_xala_dev/Files/xlsx_n_xlsx/c_about_me.xlsx new file mode 100644 index 0000000..8e25e16 Binary files /dev/null and b/db_xala_dev/Files/xlsx_n_xlsx/c_about_me.xlsx differ diff --git a/db_xala_dev/Files/xlsx_n_xlsx/d_glorary.xlsx b/db_xala_dev/Files/xlsx_n_xlsx/d_glorary.xlsx new file mode 100644 index 0000000..5316b9c Binary files /dev/null and b/db_xala_dev/Files/xlsx_n_xlsx/d_glorary.xlsx differ diff --git a/db_xala_dev/Files/xlsx_n_xlsx/e_ext.xlsx b/db_xala_dev/Files/xlsx_n_xlsx/e_ext.xlsx new file mode 100644 index 0000000..7729ffd Binary files /dev/null and b/db_xala_dev/Files/xlsx_n_xlsx/e_ext.xlsx differ diff --git a/db_xala_dev/db_sqlite.py b/db_xala_dev/db_sqlite.py new file mode 100644 index 0000000..7584ab2 --- /dev/null +++ b/db_xala_dev/db_sqlite.py @@ -0,0 +1,57 @@ +# sudo chmod 664 /var/www/xala.dev/db_xala_dev/xala_dev.db +# sudo chown -R www-data:www-data /var/www/xala.dev/db_xala_dev +# sudo chmod -R 775 /var/www/xala.dev/db_xala_dev +# sudo chmod 755 /var/www/xala.dev/db_xala_dev +# sudo chmod 750 /var/www/xala.dev/db_xala_dev + + +# sudo chown www-data:www-data /var/www/xala.dev/db_xala_dev/xala_dev.db +# sudo chmod 660 /var/www/xala.dev/db_xala_dev/xala_dev.db +# sudo chown www-data:www-data /var/www/xala.dev/db_xala_dev +# sudo chmod 770 /var/www/xala.dev/db_xala_dev + +import sqlite3 + +class DBSQLite: + def __init__(self, db_path): + self.db_path = db_path + + def _connect(self): + """Crea y retorna una conexión a la base de datos.""" + try: + return sqlite3.connect(self.db_path) + except sqlite3.Error as e: + print(f"Error al conectar con la base de datos: {e}") + return None + + def get_data(self, query): + """Ejecuta una consulta SELECT y retorna los resultados.""" + conn = self._connect() + if not conn: + return None + try: + with conn: + cursor = conn.cursor() + cursor.execute(query) + return cursor.fetchall() + except sqlite3.Error as e: + print(f"Error en la consulta: {e}") + return None + finally: + conn.close() + + def execute_query(self, query, params=()): + """Ejecuta una consulta INSERT, UPDATE o DELETE.""" + conn = self._connect() + if not conn: + return False + try: + with conn: + cursor = conn.cursor() + cursor.execute(query, params) + return True + except sqlite3.Error as e: + print(f"Error al ejecutar la consulta: {e}") + return False + finally: + conn.close() diff --git a/db_xala_dev/views.py b/db_xala_dev/views.py new file mode 100644 index 0000000..a8f0f56 --- /dev/null +++ b/db_xala_dev/views.py @@ -0,0 +1,12 @@ +v = { + 'home': 'home.html', + 'seccion': 'seccion/a_seccion.html', + 'tema': 'seccion/b_tema.html', + 'test': 'ayuda_template.html', + 'm.i.': { + 'me': 'more_info/about_me.html', + 'np': 'new_posts.html', + 'pjs': 'more_info/personal_projects.html', + 'cv': 'static/source_imgs/cv/CV_David_Itehua_Xalamihua.pdf' + } +} diff --git a/db_xala_dev/xala_dev.db b/db_xala_dev/xala_dev.db new file mode 100644 index 0000000..02c571e Binary files /dev/null and b/db_xala_dev/xala_dev.db differ diff --git a/log/access.log b/log/access.log new file mode 100644 index 0000000..e69de29 diff --git a/log/error.log b/log/error.log new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py new file mode 100644 index 0000000..ad1cb68 --- /dev/null +++ b/main.py @@ -0,0 +1,147 @@ + +from flask import Flask, render_template, jsonify, send_file, url_for +import os +from pprint import pprint + +# from py_funcs.db_connect import DB # esta la debes de eliminar +from db_xala_dev.db_sqlite import DBSQLite +from db_xala_dev.views import v + +# INTEGRAR EL USO DE CACHE +from flask_caching import Cache + +# from py_funcs.confs import ext + +# export n_server="Servidor No. X"; +n_server = os.getenv("n_server") + +app = Flask(__name__) + +import platform + + +db_path = None + +if 'microsoft' in platform.uname().release.lower(): + db_path = os.path.join(os.getcwd(), "db_xala_dev/xala_dev.db") +elif platform.system() == "Linux": + db_path = "/var/www/xala.dev/db_xala_dev/xala_dev.db" + +DB = DBSQLite(db_path) + +@app.route('/') +def home(): + q_dataHome = "SELECT pk, label, svg_data FROM home;" + dataHome = DB.get_data(q_dataHome) + + return render_template(v['home'], dataHome=dataHome, n_server=n_server) + +@app.route('/latest-posts') +def latest_posts(): + q_last_data = "SELECT id, pk, titulo_tema FROM ALL_POSTS ORDER BY id DESC LIMIT 7;" + last_data = DB.get_data(q_last_data) + return jsonify(last_data) + + +@app.route('/section/') +def subseccion(subseccion_name): + + q_cur_ext = f"SELECT ext FROM exts WHERE pk = '{subseccion_name}';" + cur_ext = DB.get_data(q_cur_ext)[0][0] + + q_dataHome = f"SELECT * FROM home WHERE pk = '{subseccion_name}';" + dataHome = DB.get_data(q_dataHome) + + q_db_subtemas = f"SELECT id, pk, titulo_tema, sintesis FROM all_posts WHERE pk = '{subseccion_name}';" + db_subtemas = DB.get_data(q_db_subtemas) + + return render_template(v['seccion'], dataHome=dataHome, db_subtemas=db_subtemas, cur_ext=cur_ext, subseccion_name=subseccion_name) + +@app.route('/section//tema/') +def subseccion_tema(subseccion_name, index_tema): + + # retorna la extensión para ruta actual del usuario (eje. cat file.txt) + q_cur_ext = f"SELECT ext FROM exts WHERE pk = '{subseccion_name}';" + cur_ext_base = DB.get_data(q_cur_ext)[0][0] + + # obtener solo la información necesaria de la base de datos + # me quede en esta línea, debes de borrar la columna html_template + q_data = f"SELECT date, status, titulo_tema, html_alert, lst_glosary, lst_post_related FROM all_posts WHERE pk = '{subseccion_name}' AND id = {index_tema};" + data = DB.get_data(q_data) + + # información del glosario + lst_glosary = [str(ele).strip() for ele in data[0][4].split(',')] + + # el alerta, para que tipo de sistema es aplicable la nota + alert= 'no_template' if f'{data[0][3]}' == 'nan' else f'{data[0][3]}' + + # notas relacionadas + f_lst_glosary = [DB.get_data(f"SELECT * FROM glosary WHERE pk = '{str(ele).strip()}'") for ele in lst_glosary] + + links_related = f'{data[0][5]}'.split(',') + if links_related[0] != 'None': + links_lambda = lambda x: f"SELECT pk, id, titulo_tema FROM all_posts WHERE id = {int(f'{x}'.strip())};" + f_lst_data_links = [DB.get_data(links_lambda(id))[0] for id in links_related] + else: + f_lst_data_links = [] + + cat_file = f'cat f_{subseccion_name}_{index_tema}' + + template_path=f'html_template/{index_tema}.html' + + return render_template(v['tema'], data=data, cur_ext_base=cur_ext_base, subseccion_name=subseccion_name, f_lst_glosary=f_lst_glosary, f_lst_data_links=f_lst_data_links, cat_file=cat_file, alert=alert, template_path=template_path) + + + +@app.route('/more-info/about-me') +def about_me(): + data = DB.get_data('SELECT * FROM about_me;') + lst_glosary = data[0][1].split(", ") + f_lst_glosary = [DB.get_data(f"SELECT * FROM glosary WHERE pk = '{str(ele).strip()}'") for ele in lst_glosary] + + return render_template(v['m.i.']['me'], data=data, f_lst_glosary=f_lst_glosary) + + +@app.route('/more-info/all-posts') +def all_posts(): + + q_final = ''' + SELECT DISTINCT a.pk, e.name FROM all_posts a + INNER JOIN exts e ON e.pk = a.pk ; + ''' + final = DB.get_data(q_final) + + q = ''' + SELECT AP.id, AP.pk, AP.date, AP.titulo_tema, AP.sintesis, H.icon + FROM ALL_POSTS AP + INNER JOIN home H ON AP.PK = H.PK; + ''' + db_newPosts = DB.get_data(q) + return render_template(v['m.i.']['np'], db_newPosts=db_newPosts, db_mainSections=final) + + + +@app.route('/more-info/projects') +def projects(): + return render_template(v['m.i.']['pjs']) + +@app.route('/more-info/cv') +def cv(): + filePath=v['m.i.']['cv'] + return send_file(filePath, as_attachment=True) + +# solo ayuda dix template +@app.route('/ayuda-template') +def ayuda_template(): + data_links = [("ps-wt", 2, "¿Cómo crear una variable de entorno en Windows?")] + return render_template(v['test'], data_links=data_links) + + +# # Manejo del error 404 +# @app.errorhandler(404) +# def page_not_found(error): +# return render_template('404.html'), 404 + + +if __name__ == '__main__': + app.run(debug=True, port=8088, host='0.0.0.0') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..53f4ce9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,16 @@ +blinker==1.9.0 +click==8.1.8 +et_xmlfile==2.0.0 +Flask==3.1.0 +itsdangerous==2.2.0 +Jinja2==3.1.5 +MarkupSafe==3.0.2 +numpy==2.2.3 +openpyxl==3.1.5 +pandas==2.2.3 +python-dateutil==2.9.0.post0 +pytz==2025.1 +six==1.17.0 +tqdm==4.67.1 +tzdata==2025.1 +Werkzeug==3.1.3 diff --git a/static/a.1_new_posts/btn_sear_note.js b/static/a.1_new_posts/btn_sear_note.js new file mode 100644 index 0000000..0793946 --- /dev/null +++ b/static/a.1_new_posts/btn_sear_note.js @@ -0,0 +1,33 @@ +// + +(() => { + + let search = document.getElementById("search_all_posts"); + + let whole_data = document.querySelectorAll("div.card-cont"); + + let filtered_data = Array.from(whole_data).filter(element => { + return element.style.display === 'block'; + }); + + + search.addEventListener("input", () => { + let value = search.value.toLowerCase(); + whole_data.forEach(ele => { + + // if (ele.style.display == 'block') { + + let title = ele.querySelector("span.title-card").innerText.toLowerCase(); + let desc = ele.querySelector("div.card-body-text").innerText.toLowerCase(); + + if (!title.includes(value) && !desc.includes(value)) { + ele.style.display = 'none'; + } else { + ele.style.display = 'block'; + } + // } + }); + // console.log(value) + }); + +})(); diff --git a/static/a.1_new_posts/new_posts.css b/static/a.1_new_posts/new_posts.css new file mode 100644 index 0000000..6c73c34 --- /dev/null +++ b/static/a.1_new_posts/new_posts.css @@ -0,0 +1,70 @@ + +#section_topics { + display: grid; + border: 3px solid rgb(255, 255, 255); + border-radius: 5px; + margin-top: 1em; + margin-bottom: 1em; + padding: 0.5em; + font-size: 1.25em; +} + +.all_new_posts { + display: grid; + column-gap: 0.5em; + row-gap: 1em; +} + +/* media querys */ + +@media only screen and (max-width: 767px) { + /* Reglas CSS para pantallas de celulares */ + /* red */ + #section_topics { + grid-template-columns: repeat(2, 1fr); + } + + .all_new_posts { + grid-template-columns: repeat(2, 1fr); + grid-template-rows: repeat(auto, 1fr); + } +} + +@media only screen and (min-width: 768px) and (max-width: 991px) { + /* Reglas CSS para tablets */ + /* purple */ + #section_topics { + grid-template-columns: repeat(3, 1fr); + } + + .all_new_posts { + grid-template-columns: repeat(3, 1fr); + grid-template-rows: repeat(auto, 1fr); + } +} + +@media only screen and (min-width: 992px) and (max-width: 1199px) { + /* Reglas CSS para laptops */ + /* green */ + #section_topics { + grid-template-columns: repeat(4, 1fr); + } + + .all_new_posts { + grid-template-columns: repeat(3, 1fr); + grid-template-rows: repeat(auto, 1fr); + } +} + +@media only screen and (min-width: 1200px) { + /* Reglas CSS para monitores ultra wide */ + /* yellow */ + #section_topics { + grid-template-columns: repeat(5, 1fr); + } + + .all_new_posts { + grid-template-columns: repeat(4, 1fr); + grid-template-rows: repeat(auto, 1fr); + } +} diff --git a/static/a.1_new_posts/new_posts.js b/static/a.1_new_posts/new_posts.js new file mode 100644 index 0000000..12465a7 --- /dev/null +++ b/static/a.1_new_posts/new_posts.js @@ -0,0 +1,59 @@ +// 1.- que al cargar la página todos los checkbox esten es su modo uncheck y las tarjetas esten en modo display none + +let checkBox = document.querySelectorAll("input.form-check-input"); + +function btn_checkBox(keySelector) { + + let btn = document.getElementById(`${keySelector}`); + let lst_cards = document.querySelectorAll(`div.${keySelector}`); + + btn.addEventListener("click", () => { + if (btn.checked) { + lst_cards.forEach(card => { + card.style.display = "inline"; + new_posts_localStorage(keySelector, "checked") + }); + } else { + lst_cards.forEach(card => { + card.style.display = "none"; + new_posts_localStorage(keySelector, "unchecked") + }); + } + }); +} + +for (let i = 0; i < checkBox.length; i++) { + btn_checkBox(checkBox[i].id); +} + +function new_posts_localStorage(id, status) { + if (localStorage != null) { + localStorage.setItem(id, status); + } +} + +// autoseleccionado o deseleccionado de los elementos +(() => { + if (localStorage != null) { + // deseleccionar los checkbox + for (let i = 0; i < localStorage.length; i++) { + let key = localStorage.key(i); + let value = localStorage.getItem(key); + let chkbox = document.getElementById(key); + if (value == 'unchecked') { + chkbox.checked = false; + let lst_cards = document.querySelectorAll(`div.${key}`); + lst_cards.forEach(card => { + card.style.display = "none"; + }); + } else if (value == "checked") { + chkbox.checked = true; + let lst_cards = document.querySelectorAll(`div.${key}`); + lst_cards.forEach(card => { + card.style.display = "inline"; + }); + } + } + } +})(); + diff --git a/static/a_home/home.css b/static/a_home/home.css new file mode 100644 index 0000000..894b782 --- /dev/null +++ b/static/a_home/home.css @@ -0,0 +1,72 @@ +.cards_container { + display: grid; + row-gap: 1em; + column-gap: 1em; + /* grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); */ +} + +.card-body { + text-align: center; + width: 10px; +} + + + +/* media querys */ + +@media only screen and (max-width: 767px) { + + /* red */ + svg { + height: 50px; + width: auto; + } + + .cards_container { + grid-template-columns: repeat(2, 1fr); + } +} + +@media only screen and (min-width: 768px) and (max-width: 991px) { + + /* purple */ + svg { + height: 50px; + width: auto; + } + + .cards_container { + grid-template-columns: repeat(3, 1fr); + } +} + +@media only screen and (min-width: 992px) and (max-width: 1199px) { + + /* green */ + svg { + height: 100px; + width: auto; + } + + .cards_container { + grid-template-columns: repeat(4, 1fr); + } +} + +@media only screen and (min-width: 1200px) { + + /* yellow */ + svg { + height: 150px; + width: auto; + } + + .cards_container { + grid-template-columns: repeat(5, 1fr); + } + + + + + +} \ No newline at end of file diff --git a/static/b_tema_subseccion/tema/btn-toggle.js b/static/b_tema_subseccion/tema/btn-toggle.js new file mode 100644 index 0000000..d47062e --- /dev/null +++ b/static/b_tema_subseccion/tema/btn-toggle.js @@ -0,0 +1,24 @@ +(() => { + let btnCode = document.getElementById("btn-code"); + btnCode.addEventListener("change", () => { + let lst_ele = ['p', 'img', 'ol.lst', 'ul.lst']; + if (btnCode.checked) { + lst_ele.forEach(selector => { + document.querySelectorAll(selector).forEach(element => { + element.style.display = 'none'; + }); + }); + } else { + lst_ele.forEach(selector => { + document.querySelectorAll(selector).forEach(element => { + if (element.tagName == 'IMG'){ + element.style.display = 'inline' + }else{ + element.style.display = 'block'; + } + + }); + }); + } + }); +})(); diff --git a/static/b_tema_subseccion/tema/tema.css b/static/b_tema_subseccion/tema/tema.css new file mode 100644 index 0000000..51c8c84 --- /dev/null +++ b/static/b_tema_subseccion/tema/tema.css @@ -0,0 +1,419 @@ + +/* inicio estilo imagen zoom */ +/* Estilo para el modal */ +.modal-img { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.8); /* Fondo oscuro con opacidad */ + display: none; /* Ocultarlo por defecto */ + justify-content: center; + align-items: center; + backdrop-filter: blur(10px); /* Efecto borroso */ + z-index: 1000; +} + +/* Contenido del modal */ +.modal-img-content { + position: relative; + max-width: 90%; /* Limita el tamaño de la imagen */ + max-height: 90%; +} + +/* Imagen ampliada */ +.modal-img-content img { + width: 100%; + height: auto; + border-radius: 8px; +} + +/* Clase activa para mostrar el modal */ +.modal-img.active { + display: flex; +} + +/* Cursor de la imagen zoomable */ +.zoomable img { + cursor: zoom-in; +} + +/* Botón de cierre */ +.close-btn { + position: absolute; + top: 10px; + right: 10px; + background: none; + border: none; + color: #fb8500; + font-size: 40px; + cursor: pointer; + z-index: 1001; +} + +.close-btn:hover { + color: red; +} + +/* final estilo imagen zoom */ + + +div.img{ + /* border: 2px solid red; */ + text-align: center; + & img{ + /* width: 100vw !important; */ + height: auto; + border-radius: 10px; + margin-top: 1em; + margin-bottom: 1em; + } +} + +a.link { + cursor: pointer; +} + +.lst{ + & li{ + margin: 0; + padding: 0; + } +} + + +#accordionExample{ + margin-top: 1em; +} + +.subtitle { + margin-top: 0.5em; + margin-bottom: 1em; +} + +.alert-primary { + margin-top: 0.5em; + margin-bottom: 0.5em; + display: none; +} + +/* inicio estilo sección de código */ +i.bi-copy { + font-size: 0.95em; + cursor: pointer; + /* padding-left: 0.5em; */ +} + +/* final estilo sección de código */ + +.a_noStyle { + text-decoration: none; +} + +.tema_imgs_div { + display: grid; + row-gap: 2em; +} + +.sng_img { + margin-top: 1.5em; + margin-bottom: 1.5em; + border-radius: 6px; +} + +li { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +/* inicio pre>code */ + +/* inicio otro test */ +/* + +Atom One Light by Daniel Gamage +Original One Light Syntax theme from https://github.com/atom/one-light-syntax + +base: #fafafa +mono-1: #383a42 +mono-2: #686b77 +mono-3: #a0a1a7 +hue-1: #0184bb +hue-2: #4078f2 +hue-3: #a626a4 +hue-4: #50a14f +hue-5: #e45649 +hue-5-2: #c91243 +hue-6: #986801 +hue-6-2: #c18401 + +*/ + + +pre { + margin-top: 1em; + margin-bottom: 1em; +} + + + +.code_lightMode { + &.hljs { + border: 2px solid black !important; + } + + &.hljs { + color: #383a42 !important; + background: #fafafa !important; + border-radius: 6px !important; + } + + .hljs-quote, + .hljs-comment { + color: #a0a1a7 !important; + font-style: italic !important; + } + + .hljs-doctag, + .hljs-keyword, + .hljs-formula { + color: #a626a4 !important; + } + + .hljs-section, + .hljs-name, + .hljs-selector-tag, + .hljs-deletion, + .hljs-subst { + color: #e45649 !important; + } + + .hljs-literal { + color: #0184bb !important; + } + + .hljs-string, + .hljs-regexp, + .hljs-addition, + .hljs-attribute, + .hljs-meta .hljs-string { + color: #50a14f !important; + } + + .hljs-attr, + .hljs-variable, + .hljs-template-variable, + .hljs-type, + .hljs-selector-class, + .hljs-selector-attr, + .hljs-selector-pseudo, + .hljs-number { + color: #986801 !important; + } + + .hljs-symbol, + .hljs-bullet, + .hljs-link, + .hljs-meta, + .hljs-selector-id, + .hljs-title { + color: #4078f2 !important; + } + + .hljs-built_in, + .hljs-title.class_, + .hljs-class, + .hljs-title { + color: #c18401 !important; + } + + .hljs-emphasis { + font-style: italic !important; + } + + .hljs-strong { + font-weight: bold !important; + } + + .hljs-link { + text-decoration: underline !important; + } +} + +/* inicio tema dos */ +/* + +Atom One Dark by Daniel Gamage +Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax + +base: #282c34 +mono-1: #abb2bf +mono-2: #818896 +mono-3: #5c6370 +hue-1: #56b6c2 +hue-2: #61aeee +hue-3: #c678dd +hue-4: #98c379 +hue-5: #e06c75 +hue-5-2: #be5046 +hue-6: #d19a66 +hue-6-2: #e6c07b + +*/ + +.code_darktMode { + &.hljs { + color: #abb2bf; + background: #282c34; + border: 2px solid white; + border-radius: 6px; + } + + .hljs-comment, + .hljs-quote { + color: #5c6370; + font-style: italic; + } + + .hljs-doctag, + .hljs-keyword, + .hljs-formula { + color: #c678dd; + } + + .hljs-section, + .hljs-name, + .hljs-selector-tag, + .hljs-deletion, + .hljs-subst { + color: #e06c75; + } + + .hljs-literal { + color: #56b6c2; + } + + .hljs-string, + .hljs-regexp, + .hljs-addition, + .hljs-attribute, + .hljs-meta .hljs-string { + color: #98c379; + } + + .hljs-attr, + .hljs-variable, + .hljs-template-variable, + .hljs-type, + .hljs-selector-class, + .hljs-selector-attr, + .hljs-selector-pseudo, + .hljs-number { + color: #d19a66; + } + + .hljs-symbol, + .hljs-bullet, + .hljs-link, + .hljs-meta, + .hljs-selector-id, + .hljs-title { + color: #61aeee; + } + + .hljs-built_in, + .hljs-title.class_, + .hljs-class .hljs-title { + color: #e6c07b; + } + + .hljs-emphasis { + font-style: italic; + } + + .hljs-strong { + font-weight: bold; + } + + .hljs-link { + text-decoration: underline; + } +} + +/* fin otro test */ + +/* :root { + --selection: rgba(224, 146, 58, 0.35); + --background: #242424; + --text: #000000; + --string: #67b26d; + --number: #e78c45; + --title: #9e4ee8; + --built_in: #d54e53; + --keyword: #e5b742; + --function: #e0923a; + --params: #508be5; + --comment: green; + --attribute: #e7c547; +} + +.hljs-comment, +.hljs-quote { + color: var(--comment); +} + +/* media querys */ +@media only screen and (max-width: 767px) { + /* Reglas CSS para pantallas de celulares */ + /* red */ + /* .navbar-brand { + background-color: red; + } */ + div.img{ + + & img{ + width: 75vw; + } + } +} + +@media only screen and (min-width: 768px) and (max-width: 991px) { + /* Reglas CSS para tablets */ + /* purple */ + /* .navbar-brand { + background-color: purple; + } */ + div.img{ + & img{ + width: 65vw; + } + } +} + +@media only screen and (min-width: 992px) and (max-width: 1199px) { + /* Reglas CSS para laptops */ + /* green */ + /* .navbar-brand { + background-color: green; + } */ + div.img{ + + & img{ + width: 60vw; + } + } +} + +@media only screen and (min-width: 1200px) { + /* Reglas CSS para monitores ultra wide */ + /* yellow */ + /* .navbar-brand { + background-color: yellow; + } */ + div.img{ + & img{ + width: 50vw; + } + } +} \ No newline at end of file diff --git a/static/b_tema_subseccion/tema/tema.js b/static/b_tema_subseccion/tema/tema.js new file mode 100644 index 0000000..6374f90 --- /dev/null +++ b/static/b_tema_subseccion/tema/tema.js @@ -0,0 +1,41 @@ +// https://www.freecodecamp.org/news/copy-text-to-clipboard-javascript/ +// function copy_code(id_btnCopy, id_CodeField) { +// let btn_Copy = document.getElementById(id_btnCopy); +// btn_Copy.addEventListener("click", () => { +// let fieldCode = document.getElementById(id_CodeField).innerText.trim(); +// const copyContent = async () => { +// try { +// await navigator.clipboard.writeText(fieldCode); +// btn_Copy.innerText = ' ¡Copiado!'; +// setTimeout(() => { +// btn_Copy.innerText = " Copiar código"; +// }, "5000"); +// } catch (err) { +// console.error('No se pudo copiar: ', err); +// } +// }; +// copyContent(); +// }); +// } + + +let buttons = document.querySelectorAll("pre i.bi-copy"); +buttons.forEach(btn => { + btn.addEventListener("click", async () => { + let fieldCode = btn.parentElement.querySelector("code")?.innerText.trim(); + + if (!fieldCode) { + console.error("No se encontró código para copiar."); + return; + } + try { + await navigator.clipboard.writeText(fieldCode); + btn.innerText = ' ¡Copiado!'; + setTimeout(() => { + btn.innerText = " Copiar código"; + }, 5000); + } catch (err) { + console.error('No se pudo copiar: ', err); + } + }); +}); diff --git a/static/b_tema_subseccion/tema/zoomable.js b/static/b_tema_subseccion/tema/zoomable.js new file mode 100644 index 0000000..1665b01 --- /dev/null +++ b/static/b_tema_subseccion/tema/zoomable.js @@ -0,0 +1,33 @@ +document.addEventListener('DOMContentLoaded', () => { + const zoomables = document.querySelectorAll('.zoomable img'); + const modal = document.getElementById('zoomModal'); + const zoomedImage = document.getElementById('zoomedImage'); + const closeModal = document.getElementById('closeModal'); + + // Abrir el modal al hacer clic en una imagen + zoomables.forEach(img => { + img.addEventListener('click', () => { + zoomedImage.src = img.src; // Poner la imagen seleccionada en el modal + modal.classList.add('active'); // Mostrar el modal + }); + }); + + // Cerrar el modal al hacer clic en el botón de cierre + closeModal.addEventListener('click', () => { + modal.classList.remove('active'); + }); + + // Cerrar el modal al hacer clic fuera de la imagen + modal.addEventListener('click', (e) => { + if (e.target === modal) { + modal.classList.remove('active'); + } + }); + + // Cerrar el modal con la tecla Escape + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && modal.classList.contains('active')) { + modal.classList.remove('active'); + } + }); +}); \ No newline at end of file diff --git a/static/b_tema_subseccion/tema_subseccion.css b/static/b_tema_subseccion/tema_subseccion.css new file mode 100644 index 0000000..7604577 --- /dev/null +++ b/static/b_tema_subseccion/tema_subseccion.css @@ -0,0 +1,99 @@ +#subseccion_intro { + display: grid; + margin-bottom: 1em; + margin-top: 1em; + text-align: justify; +} + + + +svg { + float: left; +} + +#icon { + grid-area: a; +} + +#resume { + grid-area: b; +} + +.accordion-header { + display: grid; +} + +.accordion-header > span { + display: grid; + place-items: center; +} + +.accordion-header > a { + text-decoration: none; + font-size: 1rem; + + align-self: center; + justify-self: center; +} + +/* media querys */ + +@media only screen and (max-width: 767px) { + /* Reglas CSS para pantallas de celulares */ + /* red */ + + #subseccion_intro { + display: inline-table; + } + + .accordion-header { + grid-template-columns: 15% 1% 84%; + grid-template-rows: 1fr; + } +} + +@media only screen and (min-width: 768px) and (max-width: 991px) { + /* Reglas CSS para tablets */ + /* purple */ + #subseccion_intro { + display: inline-table; + } + + .accordion-header { + grid-template-columns: 15% 1% 84%; + grid-template-rows: 1fr; + } +} + +@media only screen and (min-width: 992px) and (max-width: 1199px) { + /* Reglas CSS para laptops */ + /* green */ + #subseccion_intro { + display: inline-table; + } + + .accordion-header { + grid-template-columns: 15% 1% 84%; + grid-template-rows: 1fr; + } +} + +@media only screen and (min-width: 1200px) { + /* Reglas CSS para monitores ultra wide */ + /* yellow */ + + #subseccion_intro { + grid-template-columns: repeat(10, 1fr); + grid-template-rows: repeat(2, 1fr); + grid-template-areas: + "a b b b b b b b b b" + "a b b b b b b b b b"; + /* margin-top: 1em; + margin-bottom: 1em; */ + } + + .accordion-header { + grid-template-columns: 9% 1% 90%; + grid-template-rows: 1fr; + } +} diff --git a/static/c_more_info/about_me.css b/static/c_more_info/about_me.css new file mode 100644 index 0000000..7275faa --- /dev/null +++ b/static/c_more_info/about_me.css @@ -0,0 +1,53 @@ +#img_am { + /* width: 50%; + height: 350px; */ + margin: 0 auto; /* Centrar el contenedor horizontalmente */ + background-image: url("../source_imgs/about_me/img.gif"); + background-repeat: no-repeat; /* Evita que la imagen se repita */ + background-size: cover; /* Ajusta la imagen para que cubra el área del contenedor */ + background-position: center; /* Centra la imagen dentro del contenedor */ + display: block; /* Asegura que se comporte como un bloque */ +} + + +/* media querys */ +@media only screen and (max-width: 767px) { + /* Reglas CSS para pantallas de celulares */ + /* red */ + #img_am { + width: 100%; + height: 250px; + } +} + +@media only screen and (min-width: 768px) and (max-width: 991px) { + /* Reglas CSS para tablets */ + /* purple */ + #img_am { + width: 80%; + height: 450px; + } +} + +@media only screen and (min-width: 992px) and (max-width: 1199px) { + /* Reglas CSS para laptops */ + /* green */ + #img_am { + width: 50%; + height: 350px; + } +} + +@media only screen and (min-width: 1200px) { + /* Reglas CSS para monitores ultra wide */ + /* yellow */ + #img_am { + width: 45%; + height: 350px; + } + #about_me { + width: 65%; + margin-left: auto; + margin-right: auto; + } +} diff --git a/static/favicon/favicon.ico b/static/favicon/favicon.ico new file mode 100644 index 0000000..49c8919 Binary files /dev/null and b/static/favicon/favicon.ico differ diff --git a/static/javascript.js b/static/javascript.js new file mode 100644 index 0000000..38ae0e2 --- /dev/null +++ b/static/javascript.js @@ -0,0 +1,100 @@ +// Obtén el elemento +const htmlElement = document.querySelector('html'); +let btnMode = document.getElementById("mode"); +let cont_svg_icon_theme = document.getElementsByClassName('icon_theme')[0]; +let ico_home = document.getElementsByClassName("bi-house-fill")[0]; +let cursor = document.getElementById("cursor"); +let codeTag = document.getElementsByTagName("code"); +let news_banner = document.querySelector('div.ticker-wrapper-h'); + +btnMode.addEventListener("click", () => { + // light mode + // bi bi-moon-stars-fill + // bi bi-sun-fill + if (btnMode.className == 'bi bi-sun-fill') { + btnMode.setAttribute('class', 'bi bi-moon-stars-fill'); + news_banner.classList.toggle("nb_darkmode"); + htmlElement.setAttribute('data-bs-theme', 'light'); + set_theme_site("light"); + cont_svg_icon_theme.setAttribute("id", 'theme_icon_sun'); + ico_home.style.color = 'black'; + color_path("black"); + // code_lightMode + tagCodeTheme('code_lightMode'); + // dark mode + } else { + btnMode.setAttribute('class', 'bi bi-sun-fill'); + news_banner.classList.toggle("nb_lightmode"); + htmlElement.setAttribute('data-bs-theme', 'dark'); + set_theme_site("dark"); + cont_svg_icon_theme.setAttribute("id", 'theme_icon_moon'); + ico_home.style.color = 'white'; + color_path("white"); + + tagCodeTheme('code_darkMode'); + } +}); + +function set_theme_site(theme_mode) { + if (localStorage != null) { + localStorage.setItem("theme", theme_mode); + } +} + +function get_theme_site() { + if (localStorage != null) { + let mode = localStorage.getItem("theme"); + return mode; + } +} +// switchCodeTheme +function tagCodeTheme(classNameTheme) { + // code_lightMode | code_darktMode + len_elements = codeTag.length; + let c = 0; + for (; c < len_elements; c++) { + codeTag[c].setAttribute('class', classNameTheme); + } +} + +function color_path(color_elements) { + let pth = document.querySelectorAll("li.breadcrumb-item"); + pth.forEach(ele => { + if (ele.children[0].nodeName == 'A') { + ele.children[0].style.color = color_elements; + } + }); +} + +// establecer el tema guardado en la base de datos +(() => { + if (localStorage != null) { + if (localStorage.getItem('theme')) { + // establecer en el html el tema + htmlElement.setAttribute('data-bs-theme', get_theme_site()); + let current_theme = get_theme_site(); + // dark mode + if (current_theme == 'dark') { + btnMode.setAttribute('class', 'bi bi-sun-fill'); + cont_svg_icon_theme.setAttribute("id", 'theme_icon_moon'); + ico_home.style.color = 'white'; + cursor.style.backgroundColor = 'white'; + color_path("white"); + news_banner.classList.toggle("nb_darkmode"); + tagCodeTheme("code_darktMode"); + + // light mode + } else { + btnMode.setAttribute('class', 'bi bi-moon-stars-fill'); + cont_svg_icon_theme.setAttribute("id", 'theme_icon_sun'); + ico_home.style.color = 'black'; + color_path("black"); + news_banner.classList.toggle("nb_lightmode"); + tagCodeTheme("code_lightMode"); + } + } + } +})(); + + + diff --git a/static/news_banner.css b/static/news_banner.css new file mode 100644 index 0000000..22b5063 --- /dev/null +++ b/static/news_banner.css @@ -0,0 +1,103 @@ +/***************************** + * horizontal news ticker + ******************************/ + +.nb_darkmode { + background-color: #212529 ; + color: white; + border: 2px solid white; + & .heading{ + background-color: #212529; + } + +} + +.nb_lightmode{ + background-color: white; + color: black; + border: 2px solid #f8f4fa; + + & .heading{ + background-color: #FFFFFF; + } +} + +.ticker-wrapper-h { + display: flex; + position: relative; + overflow: hidden; + /* border: 2px solid #1c6547; */ + border-radius: 5px; + width: 90%; + margin-left: auto; + margin-right: auto; + margin-top: 0.5em; + /* height: 4em; */ +} + + +.heading { + /* background-color: white; */ + border-radius: 5px; + & svg { + height: 2em; + } +} + +.ticker-wrapper-h .heading { + /* background-color: #1c6547; */ + /* color: #fff; */ + padding: 5px 5px; + flex: 0 0 auto; + z-index: 1000; +} + +.news-ticker-h { + display: flex; + margin: 0; + padding: 0; + /* padding-left: 10%; */ + z-index: 999; + + animation-iteration-count: infinite; + animation-timing-function: linear; + animation-name: tic-h; + animation-duration: 40s; + + & a { + text-decoration: none; + } + + + & li a { + /* color: #212529; */ + font-weight: bold; + } + + & li { + display: flex; + width: 100%; + align-items: center; + white-space: nowrap; + padding-left: 20px; + margin-right: 5em; + } +} + +.news-ticker-h:hover { + animation-play-state: paused; +} + + +@keyframes tic-h { + 0% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + visibility: visible; + } + + 100% { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} \ No newline at end of file diff --git a/static/source_imgs/about_me/img.gif b/static/source_imgs/about_me/img.gif new file mode 100644 index 0000000..f3f4b51 Binary files /dev/null and b/static/source_imgs/about_me/img.gif differ diff --git a/static/source_imgs/apache/pst_1/apache.webp b/static/source_imgs/apache/pst_1/apache.webp new file mode 100644 index 0000000..188d13c Binary files /dev/null and b/static/source_imgs/apache/pst_1/apache.webp differ diff --git a/static/source_imgs/apache/pst_1/pst1_a.png b/static/source_imgs/apache/pst_1/pst1_a.png new file mode 100644 index 0000000..c3df457 Binary files /dev/null and b/static/source_imgs/apache/pst_1/pst1_a.png differ diff --git a/static/source_imgs/apache/pst_1/pst1_b.png b/static/source_imgs/apache/pst_1/pst1_b.png new file mode 100644 index 0000000..08cd637 Binary files /dev/null and b/static/source_imgs/apache/pst_1/pst1_b.png differ diff --git a/static/source_imgs/apache/pst_1/pst1_c.png b/static/source_imgs/apache/pst_1/pst1_c.png new file mode 100644 index 0000000..0cf63d3 Binary files /dev/null and b/static/source_imgs/apache/pst_1/pst1_c.png differ diff --git a/static/source_imgs/apache/pst_1/pst1_d.png b/static/source_imgs/apache/pst_1/pst1_d.png new file mode 100644 index 0000000..3ffe9b5 Binary files /dev/null and b/static/source_imgs/apache/pst_1/pst1_d.png differ diff --git a/static/source_imgs/cv/CV_David_Itehua_Xalamihua.pdf b/static/source_imgs/cv/CV_David_Itehua_Xalamihua.pdf new file mode 100644 index 0000000..9fcfaf2 Binary files /dev/null and b/static/source_imgs/cv/CV_David_Itehua_Xalamihua.pdf differ diff --git a/static/source_imgs/diagrams/public_n_local_network.png b/static/source_imgs/diagrams/public_n_local_network.png new file mode 100644 index 0000000..f89fad9 Binary files /dev/null and b/static/source_imgs/diagrams/public_n_local_network.png differ diff --git a/static/source_imgs/excel/pst_1/img_a.png b/static/source_imgs/excel/pst_1/img_a.png new file mode 100644 index 0000000..8690c7d Binary files /dev/null and b/static/source_imgs/excel/pst_1/img_a.png differ diff --git a/static/source_imgs/excel/pst_1/img_b.png b/static/source_imgs/excel/pst_1/img_b.png new file mode 100644 index 0000000..a4f546e Binary files /dev/null and b/static/source_imgs/excel/pst_1/img_b.png differ diff --git a/static/source_imgs/excel/pst_1/vba_index_sub_excel.webp b/static/source_imgs/excel/pst_1/vba_index_sub_excel.webp new file mode 100644 index 0000000..7022d9f Binary files /dev/null and b/static/source_imgs/excel/pst_1/vba_index_sub_excel.webp differ diff --git a/static/source_imgs/excel/pst_2/1.png b/static/source_imgs/excel/pst_2/1.png new file mode 100644 index 0000000..982ae0a Binary files /dev/null and b/static/source_imgs/excel/pst_2/1.png differ diff --git a/static/source_imgs/excel/pst_2/2.png b/static/source_imgs/excel/pst_2/2.png new file mode 100644 index 0000000..1ffd62b Binary files /dev/null and b/static/source_imgs/excel/pst_2/2.png differ diff --git a/static/source_imgs/excel/pst_2/3.png b/static/source_imgs/excel/pst_2/3.png new file mode 100644 index 0000000..491d0ee Binary files /dev/null and b/static/source_imgs/excel/pst_2/3.png differ diff --git a/static/source_imgs/excel/pst_3/1.png b/static/source_imgs/excel/pst_3/1.png new file mode 100644 index 0000000..fd1f2ac Binary files /dev/null and b/static/source_imgs/excel/pst_3/1.png differ diff --git a/static/source_imgs/excel/pst_4/1.png b/static/source_imgs/excel/pst_4/1.png new file mode 100644 index 0000000..ed43cf3 Binary files /dev/null and b/static/source_imgs/excel/pst_4/1.png differ diff --git a/static/source_imgs/excel/pst_5/1.png b/static/source_imgs/excel/pst_5/1.png new file mode 100644 index 0000000..4439fd1 Binary files /dev/null and b/static/source_imgs/excel/pst_5/1.png differ diff --git a/static/source_imgs/excel/pst_6/1.png b/static/source_imgs/excel/pst_6/1.png new file mode 100644 index 0000000..ccff01b Binary files /dev/null and b/static/source_imgs/excel/pst_6/1.png differ diff --git a/static/source_imgs/excel/pst_7/1.png b/static/source_imgs/excel/pst_7/1.png new file mode 100644 index 0000000..449a5ea Binary files /dev/null and b/static/source_imgs/excel/pst_7/1.png differ diff --git a/static/source_imgs/excel/pst_8/1.png b/static/source_imgs/excel/pst_8/1.png new file mode 100644 index 0000000..f2412b0 Binary files /dev/null and b/static/source_imgs/excel/pst_8/1.png differ diff --git a/static/source_imgs/git/pst_1/git.webp b/static/source_imgs/git/pst_1/git.webp new file mode 100644 index 0000000..e9e219a Binary files /dev/null and b/static/source_imgs/git/pst_1/git.webp differ diff --git a/static/source_imgs/git/pst_2/git.webp b/static/source_imgs/git/pst_2/git.webp new file mode 100644 index 0000000..304a7ff Binary files /dev/null and b/static/source_imgs/git/pst_2/git.webp differ diff --git a/static/source_imgs/linux/pst1/a.png b/static/source_imgs/linux/pst1/a.png new file mode 100644 index 0000000..db73a15 Binary files /dev/null and b/static/source_imgs/linux/pst1/a.png differ diff --git a/static/source_imgs/linux/pst1/b.png b/static/source_imgs/linux/pst1/b.png new file mode 100644 index 0000000..e87245e Binary files /dev/null and b/static/source_imgs/linux/pst1/b.png differ diff --git a/static/source_imgs/linux/pst_2/dns.webp b/static/source_imgs/linux/pst_2/dns.webp new file mode 100644 index 0000000..d081dc3 Binary files /dev/null and b/static/source_imgs/linux/pst_2/dns.webp differ diff --git a/static/source_imgs/more_notes/post_1/ssh_key.webp b/static/source_imgs/more_notes/post_1/ssh_key.webp new file mode 100644 index 0000000..bc5d6b2 Binary files /dev/null and b/static/source_imgs/more_notes/post_1/ssh_key.webp differ diff --git a/static/source_imgs/more_notes/pst_2/gitea_local_network.webp b/static/source_imgs/more_notes/pst_2/gitea_local_network.webp new file mode 100644 index 0000000..fe0f9dc Binary files /dev/null and b/static/source_imgs/more_notes/pst_2/gitea_local_network.webp differ diff --git a/static/source_imgs/more_notes/pst_3/logs_metabase.png b/static/source_imgs/more_notes/pst_3/logs_metabase.png new file mode 100644 index 0000000..8c391a0 Binary files /dev/null and b/static/source_imgs/more_notes/pst_3/logs_metabase.png differ diff --git a/static/source_imgs/more_notes/pst_3/metabase_active_service.png b/static/source_imgs/more_notes/pst_3/metabase_active_service.png new file mode 100644 index 0000000..cbb2d86 Binary files /dev/null and b/static/source_imgs/more_notes/pst_3/metabase_active_service.png differ diff --git a/static/source_imgs/more_notes/pst_3/metabase_version.png b/static/source_imgs/more_notes/pst_3/metabase_version.png new file mode 100644 index 0000000..951760f Binary files /dev/null and b/static/source_imgs/more_notes/pst_3/metabase_version.png differ diff --git a/static/source_imgs/postgresql/post_2/crud_psql.webp b/static/source_imgs/postgresql/post_2/crud_psql.webp new file mode 100644 index 0000000..3318bab Binary files /dev/null and b/static/source_imgs/postgresql/post_2/crud_psql.webp differ diff --git a/static/source_imgs/postgresql/post_3/psql1.webp b/static/source_imgs/postgresql/post_3/psql1.webp new file mode 100644 index 0000000..24693ac Binary files /dev/null and b/static/source_imgs/postgresql/post_3/psql1.webp differ diff --git a/static/source_imgs/postgresql/post_4/psql_conf.webp b/static/source_imgs/postgresql/post_4/psql_conf.webp new file mode 100644 index 0000000..dd1140c Binary files /dev/null and b/static/source_imgs/postgresql/post_4/psql_conf.webp differ diff --git a/static/source_imgs/postgresql/psql1/inactive.png b/static/source_imgs/postgresql/psql1/inactive.png new file mode 100644 index 0000000..6202de3 Binary files /dev/null and b/static/source_imgs/postgresql/psql1/inactive.png differ diff --git a/static/source_imgs/postgresql/psql1/psql.webp b/static/source_imgs/postgresql/psql1/psql.webp new file mode 100644 index 0000000..690ff5d Binary files /dev/null and b/static/source_imgs/postgresql/psql1/psql.webp differ diff --git a/static/source_imgs/postgresql/psql1/version_psql.png b/static/source_imgs/postgresql/psql1/version_psql.png new file mode 100644 index 0000000..7f72a23 Binary files /dev/null and b/static/source_imgs/postgresql/psql1/version_psql.png differ diff --git a/static/source_imgs/powershell/pst_1/env_var.webp b/static/source_imgs/powershell/pst_1/env_var.webp new file mode 100644 index 0000000..5d042a5 Binary files /dev/null and b/static/source_imgs/powershell/pst_1/env_var.webp differ diff --git a/static/source_imgs/powershell/pst_1/ps_1.png b/static/source_imgs/powershell/pst_1/ps_1.png new file mode 100644 index 0000000..0ee025f Binary files /dev/null and b/static/source_imgs/powershell/pst_1/ps_1.png differ diff --git a/static/source_imgs/powershell/pst_1/ps_2.png b/static/source_imgs/powershell/pst_1/ps_2.png new file mode 100644 index 0000000..f650e55 Binary files /dev/null and b/static/source_imgs/powershell/pst_1/ps_2.png differ diff --git a/static/source_imgs/powershell/pst_2/1.png b/static/source_imgs/powershell/pst_2/1.png new file mode 100644 index 0000000..bc48d78 Binary files /dev/null and b/static/source_imgs/powershell/pst_2/1.png differ diff --git a/static/source_imgs/powershell/pst_3/1.png b/static/source_imgs/powershell/pst_3/1.png new file mode 100644 index 0000000..b266e0b Binary files /dev/null and b/static/source_imgs/powershell/pst_3/1.png differ diff --git a/static/source_imgs/powershell/pst_4/1.png b/static/source_imgs/powershell/pst_4/1.png new file mode 100644 index 0000000..d6d1cc2 Binary files /dev/null and b/static/source_imgs/powershell/pst_4/1.png differ diff --git a/static/source_imgs/projects/bienestar_digital.png b/static/source_imgs/projects/bienestar_digital.png new file mode 100644 index 0000000..74da63e Binary files /dev/null and b/static/source_imgs/projects/bienestar_digital.png differ diff --git a/static/source_imgs/projects/esad_temporal_work.png b/static/source_imgs/projects/esad_temporal_work.png new file mode 100644 index 0000000..c8225cb Binary files /dev/null and b/static/source_imgs/projects/esad_temporal_work.png differ diff --git a/static/source_imgs/projects/formha.png b/static/source_imgs/projects/formha.png new file mode 100644 index 0000000..883e70d Binary files /dev/null and b/static/source_imgs/projects/formha.png differ diff --git a/static/source_imgs/projects/repo_coperasur.png b/static/source_imgs/projects/repo_coperasur.png new file mode 100644 index 0000000..8e90b24 Binary files /dev/null and b/static/source_imgs/projects/repo_coperasur.png differ diff --git a/static/source_imgs/projects/sict-csic.png b/static/source_imgs/projects/sict-csic.png new file mode 100644 index 0000000..1ddd84b Binary files /dev/null and b/static/source_imgs/projects/sict-csic.png differ diff --git a/static/source_imgs/projects/tac_consulting.png b/static/source_imgs/projects/tac_consulting.png new file mode 100644 index 0000000..89f85c5 Binary files /dev/null and b/static/source_imgs/projects/tac_consulting.png differ diff --git a/static/source_imgs/python/pst_1/py_1.png b/static/source_imgs/python/pst_1/py_1.png new file mode 100644 index 0000000..49c8687 Binary files /dev/null and b/static/source_imgs/python/pst_1/py_1.png differ diff --git a/static/source_imgs/python/py_2/code.png b/static/source_imgs/python/py_2/code.png new file mode 100644 index 0000000..3957998 Binary files /dev/null and b/static/source_imgs/python/py_2/code.png differ diff --git a/static/source_imgs/python/py_2/linux_venv_active.png b/static/source_imgs/python/py_2/linux_venv_active.png new file mode 100644 index 0000000..9ace118 Binary files /dev/null and b/static/source_imgs/python/py_2/linux_venv_active.png differ diff --git a/static/source_imgs/python/py_2/pip_freeze.png b/static/source_imgs/python/py_2/pip_freeze.png new file mode 100644 index 0000000..44bc0b0 Binary files /dev/null and b/static/source_imgs/python/py_2/pip_freeze.png differ diff --git a/static/source_imgs/python/py_2/pip_install_pandas.png b/static/source_imgs/python/py_2/pip_install_pandas.png new file mode 100644 index 0000000..cdb20ca Binary files /dev/null and b/static/source_imgs/python/py_2/pip_install_pandas.png differ diff --git a/static/source_imgs/python/py_2/venv1.png b/static/source_imgs/python/py_2/venv1.png new file mode 100644 index 0000000..043e1aa Binary files /dev/null and b/static/source_imgs/python/py_2/venv1.png differ diff --git a/static/source_imgs/python/py_2/venv2.png b/static/source_imgs/python/py_2/venv2.png new file mode 100644 index 0000000..b0498c7 Binary files /dev/null and b/static/source_imgs/python/py_2/venv2.png differ diff --git a/static/source_imgs/python/py_2/win_venv_active.png b/static/source_imgs/python/py_2/win_venv_active.png new file mode 100644 index 0000000..5b122ee Binary files /dev/null and b/static/source_imgs/python/py_2/win_venv_active.png differ diff --git a/static/styles.css b/static/styles.css new file mode 100644 index 0000000..87c2550 --- /dev/null +++ b/static/styles.css @@ -0,0 +1,176 @@ +body { + font-family: "Roboto", sans-serif; + width: 100%; +} + + + +i.bi-terminal-fill { + color: black; +} + +#breadcrumb a { + text-decoration: none; +} + +.breadcrumb-item a { + color: black; +} + +p { + text-align: justify; +} + +div.card-body { + width: 100%; + place-items: center; +} + +#dix_container { + width: 90%; + min-height: 100vh; + margin-left: auto; + margin-right: auto; +} + +nav.navbar { + width: 95%; + margin-left: auto; + margin-right: auto; + border-radius: 6px; +} + +nav#breadcrumb { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +#mode { + font-size: 1em; + margin-left: 1em; + margin-right: 1em; +} + +.bi-moon-stars-fill { + color: #ffe246; +} + +#breadcrumb { + /* color: black; */ + margin-top: 0.5em; + margin-bottom: 2em; + display: grid; + vertical-align: middle; +} + +.bi-sun-fill { + color: #ff7b00; +} + +#theme_icon_sun { + /* animation: rotate 4.5s linear infinite; */ + /* border: 1px solid red; */ + float: left; + width: 2.5em; + height: 1em; + position: relative; + display: grid; +} + +@keyframes rotate { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + +#cursor { + position: relative; + display: inline-block; + line-height: 24px; + font-size: 24px; + /* color: white; */ +} + +/* #ff7b00; */ +#cursor::after { + content: ""; + display: inline-block; + background-color: rgba(0, 0, 0, 1); + /* vertical-align: top; */ + width: 10px; + /* Set height to the line height of .text */ + height: 1em; + -webkit-animation: blink 1s step-end infinite; + animation: blink 1s step-end infinite; +} + +@-webkit-keyframes blink { + 0% { + opacity: 1; + } + + 50% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes blink { + 0% { + opacity: 1; + } + + 50% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +/* media querys */ +@media only screen and (max-width: 767px) { + /* Reglas CSS para pantallas de celulares */ + /* red */ + /* .navbar-brand { + background-color: red; + } */ +} + +@media only screen and (min-width: 768px) and (max-width: 991px) { + /* Reglas CSS para tablets */ + /* purple */ + /* .navbar-brand { + background-color: purple; + } */ +} + +@media only screen and (min-width: 992px) and (max-width: 1199px) { + /* Reglas CSS para laptops */ + /* green */ + /* .navbar-brand { + background-color: green; + } */ +} + +@media only screen and (min-width: 1200px) { + /* Reglas CSS para monitores ultra wide */ + /* yellow */ + /* .navbar-brand { + background-color: yellow; + } */ + + .cards_container { + display: grid; + grid-template-columns: repeat(5, 1fr); + } +} diff --git a/templates/ayuda_template.html b/templates/ayuda_template.html new file mode 100644 index 0000000..417aedd --- /dev/null +++ b/templates/ayuda_template.html @@ -0,0 +1,156 @@ +{% extends 'template.html' %} + +{% block css %} + + + + + + + + +{% endblock css %} + + + + +{% block body %} + + + + + + + + +{% include 'components/btn-toggle-see-code.html' %} + + + + + + + + + + + + + + + + + + + + + + + + + + +{% block js %} + + + + + + +{% endblock js %} + +{% endblock body %} \ No newline at end of file diff --git a/templates/components/alerts/linux.html b/templates/components/alerts/linux.html new file mode 100644 index 0000000..c3433b7 --- /dev/null +++ b/templates/components/alerts/linux.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/templates/components/alerts/linux_win_mac.html b/templates/components/alerts/linux_win_mac.html new file mode 100644 index 0000000..7a73ea4 --- /dev/null +++ b/templates/components/alerts/linux_win_mac.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/templates/components/alerts/linux_wsl.html b/templates/components/alerts/linux_wsl.html new file mode 100644 index 0000000..c7fcc66 --- /dev/null +++ b/templates/components/alerts/linux_wsl.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/templates/components/alerts/win.html b/templates/components/alerts/win.html new file mode 100644 index 0000000..e6d771f --- /dev/null +++ b/templates/components/alerts/win.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/components/alerts/win_mac.html b/templates/components/alerts/win_mac.html new file mode 100644 index 0000000..0b68475 --- /dev/null +++ b/templates/components/alerts/win_mac.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/components/btn-toggle-see-code.html b/templates/components/btn-toggle-see-code.html new file mode 100644 index 0000000..093bd6d --- /dev/null +++ b/templates/components/btn-toggle-see-code.html @@ -0,0 +1,4 @@ +
+ + +
\ No newline at end of file diff --git a/templates/components/copy-code.html b/templates/components/copy-code.html new file mode 100644 index 0000000..f8702be --- /dev/null +++ b/templates/components/copy-code.html @@ -0,0 +1,9 @@ + + + + + + +
{% if btn | default(true) %} Copiar código{% endif %}
+{{ codigo }}
+ diff --git a/templates/components/footer.html b/templates/components/footer.html new file mode 100644 index 0000000..1a0ab43 --- /dev/null +++ b/templates/components/footer.html @@ -0,0 +1,36 @@ + +
+ +
\ No newline at end of file diff --git a/templates/components/navbar.html b/templates/components/navbar.html new file mode 100644 index 0000000..3471c2f --- /dev/null +++ b/templates/components/navbar.html @@ -0,0 +1,57 @@ + + \ No newline at end of file diff --git a/templates/components/news_bar.html b/templates/components/news_bar.html new file mode 100644 index 0000000..fe479b0 --- /dev/null +++ b/templates/components/news_bar.html @@ -0,0 +1,79 @@ +
+
+ + + + + + + + + + + + +
+
    + +
+
+ + \ No newline at end of file diff --git a/templates/components/src_collapse.html b/templates/components/src_collapse.html new file mode 100644 index 0000000..f85a96c --- /dev/null +++ b/templates/components/src_collapse.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/templates/components/up_btn.html b/templates/components/up_btn.html new file mode 100644 index 0000000..8e3c976 --- /dev/null +++ b/templates/components/up_btn.html @@ -0,0 +1,102 @@ + + + + + \ No newline at end of file diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..8be92e6 --- /dev/null +++ b/templates/home.html @@ -0,0 +1,28 @@ +{% extends 'template.html' %} + +{% block n_server %} +{% if n_server %} + +{% endif %} +{% endblock n_server %} + +{% block css %} + +{% endblock css %} + +{% block body %} + +
+ {% for ele in dataHome %} +
+
+
{{ ele[1] }}
+ + {{ ele[2] | safe }} + +
+
+ {% endfor %} +
+ +{% endblock %} \ No newline at end of file diff --git a/templates/html_template/1.html b/templates/html_template/1.html new file mode 100644 index 0000000..155785b --- /dev/null +++ b/templates/html_template/1.html @@ -0,0 +1,84 @@ + + + +
+ +
+

+ El objetivo de esta subrutina + simple pero muy útil, generar un índice de todas las hojas que hay en el + archivo y a su vez a cada hoja colocarle el hipervínculo que te mande a la hoja principal de índices. +

+

+ Para el uso de la sibrutina debes a agregar una hoja nueva de cálculo en tu archivo de excel que quieres indexar, + esa hoja nueva debe estar completamente vacia y tener la primera posición de izquierda a derecha, dejas esa hoja + activa (Imágen A), posteriormente abres el editor de código de Visual + Basic for Applications (VBA) , una vez dentro, en un módulo pegas el código de abajo y lo ejecutas. +

+

+ Ejemplo:
Imagen A:
+

+
+ Imagen A +
+

+ Imágen B: +

+
+ Imagen B +
+ +{% set var1 %} +Sub asignar_link_hoja_mismo_libro() +' Declaración de las variables +Dim o As Object +Dim th, h As Integer +Dim lr As Long +Dim rng As Range + +' obtener el número total de hojas de excel en el archivo abierto +th = Sheets.Count + +' en la hoja 1, celda A1 se le asigna el valor de "índice hojas" +Sheets(1).Cells(1, 1).Value = "Índice Hojas" + +' En esta ciclo se recorre el total de hojas +For h = 1 To th + ' empezamos la iteración de hojas a partir del indice 2 para obtener el nombre de las hojas. + If h > 1 Then + ' el nombre de la hoja se coloca en la hoja 1 a partir de la celda 2 + Sheets(1).Cells(h, 1).Value = Sheets(h).Name + ' se selecciona la hoja para poder asignar el hipervínculo a la hoja 1 + Sheets(h).Activate + ' colocación del hipervínculo en la hoja activa + ActiveSheet.Hyperlinks.Add Anchor:=Selection, Address:="", SubAddress:="" & Sheets(1).Name & "!A1" & "", TextToDisplay:="Indice" + End If +Next h + +' Procesos en la hoja 1 +Sheets(1).Activate +' del ciclo anterior obtenemos la última fila con registro +lr = Sheets(1).Cells(1048576, 1).End(xlUp).Row +' establecemos el rango donde se colocaron los nombres de las hojas del archivo de excel +Set rng = Sheets(1).Range(Cells(2, 1), Cells(lr, 1)) + +'en este ciclo creamos los hipervínculos +For Each o In rng + ' creamos el indice por cada nombre de hoja con su respectiva hoja + Cells(o.Row, o.Column).Hyperlinks.Add Anchor:=Cells(o.Row, o.Column), Address:="", SubAddress:="'" & o.Value & "'!A1", TextToDisplay:=o.Value +Next o +' notificación al usuario del término del programa +MsgBox "Se ha creado el índice de hojas de forma exitosa", vbOKOnly, "INDICE HOJAS" + +End Sub +{% endset %} + +{% with codigo=var1.strip(), isEditable="false" %} +{% include 'components/copy-code.html' %} +{% endwith %} + + + + + diff --git a/templates/html_template/10.html b/templates/html_template/10.html new file mode 100644 index 0000000..bc4e405 --- /dev/null +++ b/templates/html_template/10.html @@ -0,0 +1,105 @@ + +
+ +
+

+ Para poder habilitar que el usuario se conecte y que se le requiera su contraseña necesitaremos modificar el archivo + pg_hba.conf el cual se encuentra en la ruta /etc/postgresql/[psql_version]/main/, para este + ejemplo práctico usaré la version 16, por lo tanto para editar el archivo debes ejecutar el siguiente comando con sudo y usaré el editor de nano:
+

+ +{% set i %} +sudo nano /etc/postgresql/16/main/pg_hba.conf +{% endset %} +{% with codigo=i.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Te recomiendo que siempre hagas una copia del archivo original.
+ El contenido de las últimas 15 filas del archivo pg_hba.conf debe ser muy similar al siguiente: +

+ +{% set ii %} +# Database administrative login by Unix domain socket +local all postgres peer + +# TYPE DATABASE USER ADDRESS METHOD + +# "local" is for Unix domain socket connections only +local all all peer +# IPv4 local connections: +host all all 127.0.0.1/32 scram-sha-256 +# IPv6 local connections: +host all all ::1/128 scram-sha-256 +# Allow replication connections from localhost, by a user with the +# replication privilege. +local replication all peer +host replication all 127.0.0.1/32 scram-sha-256 +host replication all ::1/128 scram-sha-256 +{% endset %} +{% with codigo=ii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

Nos movemos hasta las línea que dicen:

+ +{% set iii %} +# "local" is for Unix domain socket connections only +local all all peer +{% endset %} +{% with codigo=iii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Vamos a comentar la segunda línea del código anterior para ello necesitamos añadir el prefijo '#' en la linea, yo + suelo añadir al final otro signo '#'' y poner una pequeña nota de cuando la modifiqué, tambien añadiremos una + tercera linea donde indicaremos que todos los usuarios que se intenten conectar a la base de datos se deberan + autenticar por medio de contraseña. +

+ +{% set iv %} +# "local" is for Unix domain socket connections only +#local all all peer # 23.04.2024 13:14 hrs dix mod +local all all md5 +{% endset %} +{% with codigo=iv.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Diferencia en claves:
+ - peer: la autenticación basada en el nombre de usuario del sistema operativo.
+ - md5: la autenticación basada en contraseña almacenada en formato MD5 para el usuario específico `postgres`.
+ En resumen, la elección entre `peer` y `md5` depende de cómo deseas gestionar la autenticación de los usuarios + locales en PostgreSQL: si prefieres que se autentiquen automáticamente utilizando su nombre de usuario del sistema + operativo (`peer`) o si deseas que proporcionen una contraseña específica (`md5`). +

+

Luego nos vamos a las líneas:

+ +{% set v %} +# IPv6 local connections: +host all all ::1/128 scram-sha-256 +{% endset %} +{% with codigo=v.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Del código anterior comentamos la segunda linea y añadimos una tercera línea que requiera que el usuario se + auntentifique cada que se conecte al servidor de la base de datos: +

+ +{% set vi %} +# IPv6 local connections: +#host all all ::1/128 scram-sha-256 # 23.04.2024 13:14 hrs dix mod +host all all 0.0.0.0/0 md5 +{% endset %} +{% with codigo=vi.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Para guardar todos los cambios realizados en nano debes de oprimir la combinación de teclas CTRL + s y para + cerrar el editor oprime la combinación de teclas CTRL + x.

+ Finalmente para aplicar cambios debemos ejecutar el comando: +

+ +{% set vii %} +sudo service postgresql restart +{% endset %} +{% with codigo=vii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} diff --git a/templates/html_template/11.html b/templates/html_template/11.html new file mode 100644 index 0000000..d98fc6e --- /dev/null +++ b/templates/html_template/11.html @@ -0,0 +1,96 @@ + +
+ +
+

+ Tenemos que editar el archivo postgresql.conf el cual está en la ruta /etc/postgresql/[psql_version]/main/ para ello ejecutamos el + comando: +

+ +{% set i %} +sudo nano /etc/postgresql/16/main/postgresql.conf +{% endset %} +{% with codigo=i.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ El archivo es muy extenso, con muchas configuraciones, sin embargo la modicación que le harémos está en la primera + sección llamada CONNECTIONS AND AUTHENTICATION +

+ +{% set ii %} +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ +# - Connection Settings - +# listen_addresses = 'localhost' # what IP address(es) to listen on; +{% endset %} +{% with codigo=ii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Donde dice: +

+ +{% set iii %} +listen_addresses = 'localhost' # what IP address(es) to listen on; +{% endset %} +{% with codigo=iii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Debes cambiar el localhost por un *, ejemplo: +

+ +{% set iv %} +listen_addresses = '*' # what IP address(es) to listen on; +{% endset %} +{% with codigo=iv.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ En el editor de nano guardamos oprimiendo la combinación de teclas Ctrl + s y cerramos con la combinación de + teclas Ctrl + x, luego procedemos a reiniciar el servicio de postgres con el comando: +

+ +{% set v %} +sudo service postgresql restart +{% endset %} +{% with codigo=v.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ En los pasos anteriores logramos hacer que postgresql acepte peticiones de cualquier punto de nuestra red local, el + puerto por default por el que escucha las + peticiones es el 5432, sin embargo a pesar de haber aplicado los ajustes puede que la maquina en este caso linux o + wsl no tengan el puerto abierto por lo tanto ahora abriremos el puerto para hacer que las peticiones si llegue, para + ello ejecutamos el siguiente comando: +

+ +{% set vi %} +sudo ufw allow 5432/tcp +{% endset %} +{% with codigo=vi.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + + + +

+ Una vez hecho todo lo anterior ya podras hacer peticiones a tu servidor o equipo donde esta almacenada la base de + datos. +

\ No newline at end of file diff --git a/templates/html_template/12.html b/templates/html_template/12.html new file mode 100644 index 0000000..7b95d3b --- /dev/null +++ b/templates/html_template/12.html @@ -0,0 +1,75 @@ + +

+ Este script básico sirve para obtener los datos de la base de datos, la idea principal es que sepas como conectarte + y posteriormente manipules los datos ya sea con pandas, excel o cualquier herramienta de analisis de datos. +

+

+ Antes de ejecutar este Script, en caso de que la + base de datos este en otro equipo de la red local deberas asegurarte de que el servidor de base de datos acepte + peticiones dentro de la red local y no solo del localhost, además debes de conecer la IP del equipo y el nombre de la base de datos a la que que te + vas a conectar. +

+

+ Creamos un archivo nuevo con el nombre que quieras y con extensión .py o .ipynb, posteriormente abrimos la terminal + e instalamos la librería psycopg2, para ello el comando es: +

+ +{% set i %} +pip install psycopg2-binary +{% endset %} +{% with codigo=i.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Luego usamos el siguiente script: +

+ +{% set ii %} +# importamos la librería psycopg2 +import psycopg2 + +# mediante un try validamos la conexión de lo contrario que nos devuleva el except +try: + # creamos una instancia de psycopg2 + with psycopg2.connect( + + # host: es la ip del servidor de base de datos + host="192.168.1.11", + + # port: es el puerto por default de psql es el 5432 + port=5432, + + # database: es el nombre de la base de datos + database="db_sample", + + # nombre del usuario de postgresql + user="user_test", + + # contraseña de postgresql del usuario + password="!p4ssword_s4mpl3" + + ) as conexion: + with conexion.cursor() as cursor: + + # en este caso simple hacemos una consulta de obtener todos los datos de la tabla table_sample + cursor.execute("SELECT * FROM table_sample;") + + # la info obtenido de la consulta la almacenamos en una variable llamada resultados + resultados = cursor.fetchall() + + # iteramos la variable resultados e imprimimos en consola + for fila in resultados: + print(fila) + +except psycopg2.Error as e: + + # en caso de un error y que no se pueda ejecutar el try entonces imprimimos en consola el error + print(f"Error al conectar a la base de datos: {e}") +{% endset %} +{% with codigo=ii.strip(), isEditable="true" %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Como podemos ver al ejecutar el script anterior de la iteración obtendremos la información de la base de datos, pero + este script básico es el inicio más importante para que tu inicies tu análisis de datos en alguna herramienta y ahora + ya sabes como acceder a la base de datos y obtener tu insumo. +

\ No newline at end of file diff --git a/templates/html_template/13.html b/templates/html_template/13.html new file mode 100644 index 0000000..4423bb9 --- /dev/null +++ b/templates/html_template/13.html @@ -0,0 +1,110 @@ + +

+ Este manual aplica para darle permisos a usuarios de PostgreSQL (no usuario postgres) para la ejecución de + scripts, primero necesitamos el nombre de la base de datos a la que vamos a afectar, el nombre del usuario con + el cual nos + vamos a identificar en PostgreSQL y la IP del equipo. + Una vez conociendo esos datos, tendremos que modificar el archivo de configuración pg_hba.conf, que es crucial + para la seguridad y configuración de acceso en PostgreSQL, ya que define quién puede conectarse y cómo deben + autenticarse. Este archivo se encuentra ubicado en la ruta /etc/postgresql/[psql_version]/main/. Para modificarlo, ejecutamos el siguiente comando: +

+ +{% set i %} +sudo nano /etc/postgresql/16/main/pg_hba.conf +{% endset %} +{% with codigo=i.strip(), isEditable="true" %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Nos vamos hasta el final del archivo donde encontraremos el texto comentado +

+ +{% set ii %} +# TYPE DATABASE USER ADDRESS METHOD +{% endset %} +{% with codigo=ii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Debajo de ese texto agregaremos una nueva línea, asegurándonos de respetar la indentación, con la siguiente información.para ello el TYPE + hace referencia al tipo de conexión, nosotros le colocaremos el tipo local, en DATABASE el nombre de la base de datos, + USER es el nombre del usuario permitido, ADDRESS lo dejaremos vacio y METHOD md5 quedando el archivo de configuración + de la siguiente manera: +

+ +{% set iii %} +# TYPE DATABASE USER ADDRESS METHOD +local db_sample usr_1 md5 +{% endset %} +{% with codigo=iii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Guardamos los cambios con Ctrl + S y cerramos con Ctrl + X, despues reiniciamos el servicio. +

+ +{% set iv %} +sudo service postgresql restart +{% endset %} +{% with codigo=iv.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Una vez ejecutados todos los pasos anteriores, ya podremos ejecutar scripts de SQL., abriremos una terminal donde + tengas + tu archivo DUMP , como mencioné al inicio de la + nota, deberás conocer la ip del servidor de base de datos, el nombre del usuario que te vas a autenticar y + adicionalmente la ruta de tu archivo dump. +

+ +
+
+ + +{% set v %} +psql -h 172.20.86.252 -U user_1 -d db_sample -a -f dump_backup_sample.sql +{% endset %} +{% with codigo=v.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + + +
+
+ + {% set vi %} + psql -h 172.20.86.252 -U user_1 -a -f dump_backup_sample.sql + {% endset %} + {% with codigo=vi.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +
+
+
    +
  • + -h = host, la ip del servidor de base de datos +
  • +
  • + -U = nombre del usuario, , debe ser el mismo nombre que registraste en el archivo pg_hba.conf (user_1) +
  • +
  • + -d = nombre de la base de datos, debe ser el mismo nombre que registraste en el archivo pg_hba.conf + (db_sample) +
  • +
  • + -a = nos mostrará cada una de las respuestas de psql cuando se ejecuten +
  • +
  • + -f = nombre del archivo con extensión .sql para este caso usare un archivo ficticio dump_backup_sample.sql +
  • +
+ + +

+ Una vez ejecutado el comando anterior, vas a dar enter y te solicitará la contraseña, la ingresas y las instrucciones + de tu archivo .sql se empezarán a ejecutar. +

\ No newline at end of file diff --git a/templates/html_template/14.html b/templates/html_template/14.html new file mode 100644 index 0000000..ff950c9 --- /dev/null +++ b/templates/html_template/14.html @@ -0,0 +1,195 @@ + +

+ Los ambientes virtuales se ubican dentro de una + carpeta principal que en este caso mi + carpeta contenedora del proyecto será PROJECT_VENV la cual contendrá toda la información del proyecto, desde + dependencias y código fuente de la aplicación, + para este ejemplo crearemos una carpeta llamada init_venv con la ayuda de VENV: +

+ + +
+
+ + +{% set i %} +python3 -m venv init_venv +{% endset %} +{% with codigo=i.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + + +
+
+ + {% set ii %} + python -m venv init_venv + {% endset %} + {% with codigo=ii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +
+
+

+ Una vez creado el ambiente virtual debemos activarlo para poder instalar las dependencias necesarias: +

+ +
+
+ + + {% set iii %} + source init_venv/bin/activate + {% endset %} + {% with codigo=iii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Cuando actives el ambiente verás en consola una salida muy similar a esta:
+

+ +
+

+ +
+
+ + + {% set iv %} + .\init_venv\Scripts\activate + {% endset %} + {% with codigo=iv.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Cuando actives el ambiente verás en consola una salida muy similar a esta:
+

+ +
+

+ +
+
+

+ En este punto nuestro ambiente virtual está en blanco, no tenemos nada instalado, si queremos confirmarlo podemos + ejecutar el comando: +

+ +{% set v %} +pip freeze +{% endset %} +{% with codigo=v.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ La salida del comando anterior no retornará nada por lo tanto sabemos que no hay nada instalado, para instalar por + ejemplo pandas en ambos casos el comando base para instalar en windows y linux es exactamente el mismo, pip + install: +

+ +{% set vi %} +pip install pandas +{% endset %} +{% with codigo=vi.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

Cuando instalemos pandas veremos que se instalarán además de pandas tambien se agragan las dependencias básicas para + que funcione pandas y la salida del comando debe ser algo muy similar a esta:

+
+ +
+

+ si vuelves a ejecutar el comando pip freeze podrás ver ahora si todas las dependencias que se han instalado: +

+
+ +
+

+ Ahora si, en este supuesto que solo necesitaras pandas ya podrás empezar a trabajar en tu proyecto sin problemas, + dentro de tu carpeta y con el ambiente activo + deberás de abrir tu editor de código en mi caso VS Code y el comando es: +

+ +{% set vii %} +code . +{% endset %} +{% with codigo=vii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +
+ +
+

+ En tu editor de código ya podrás añadir archivos de python donde uses pandas y al momento de ejecutar el código por + primera vez, en este caso VS Code detectara que hay un ambiente activo y te pedira que selecciones el ambiente, da + click en la opción "Python Environments..." +

+
+ +
+

+ Despues selecciona el ambiente que este caso es "init_venv (Python 3.12.4)". +

+
+ +
+

+ Para desactivar el ambiente virtual en Windows o Linux solo necesitas ejecutar en la terminal el comando + deactivate. +

+ +{% set viii %} +deactivate +{% endset %} +{% with codigo=viii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Si llegaras a necesitar más dependencias, primero debes regresar a la consola e instalarlas ejecutar el comando base + pip install nombre_dependencia y despues + importarlas en tus archivos de python.

+ Te recomiendo que siempre antes de desactivar el ambiente virtual exportes una lista de las dependencias ya que si + compartes el proyecto la carpeta donde esta + ubicado + el ambiente virtual no se comparte ya que vuelve pesado el proyecto, la buena práctica es compartir solo la lista + con todos las carpetas necesarias sin el dictorio + del ambiente virtual (init_venv), para exportar la lista de dependencias debes ejecutar el comando: +

+ +{% set ix %} +pip freeze > requirements.txt +{% endset %} +{% with codigo=ix.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Del comando anterior lo que hace es que pip freeze lista las dependencias instaladas y + requirements.txt indica que almacenará la información que salga de pip freeze en un archivo llamado + requirements.txt, para este ejemplo práctico y sencillo si compartes tu proyecto solo compartirías la carpeta + contenedora del proyecto, el archivo donde importas pandas y el archivo requirements.txt +

+ + +{% set x %} +PROJECT_VENV + | + -- main.py + -- requirements.txt +{% endset %} +{% with btn=false, codigo=x.strip() %} + {% include 'components/copy-code.html' %} +{% endwith %} + + + + + \ No newline at end of file diff --git a/templates/html_template/15.html b/templates/html_template/15.html new file mode 100644 index 0000000..7fab6b9 --- /dev/null +++ b/templates/html_template/15.html @@ -0,0 +1,74 @@ + +

+ A veces cuando descargues un proyecto por ejemplo de GitHub o te compartan un proyecto de python, veras que tiene un + archivo comunmente llamado requirements.txt, entonces eso es una gran ayuda ya que no necesitas estar adivinando que + dependencias necesitas instalar, por lo tanto el proceso es muy simple, primero necesitaremos crear un ambiente + virtual en blanco, para este ejemplo lo llamaremos init_venv. +

+ +
+
+ +
 Copiar códigopython3 -m venv init_venv
+ +
+
+ +
 Copiar códigopython -m venv init_venv
+ +
+
+

Una vez creado el ambiente virtual lo activamos:

+ +
+
+ +
 Copiar códigosource  init_venv/bin/activate
+

+ Cuando actives el ambiente verás en consola una salida muy similar a esta:
+

+ +
+

+ +
+
+ +
 Copiar código.\init_venv\Scripts\activate
+

+ Cuando actives el ambiente verás en consola una salida muy similar a esta:
+

+ +
+

+ +
+
+

+ Una vez activado el ambiente ejecutamos el comando de instalación de las dependencias, con el comando: +

+
 Copiar códigopip install -r requirements.txt
+

+ Una vez hecho eso ya podrás ejecutar cualquier archivo de python sin estar instalando las dependencias una por una. +

+ + + \ No newline at end of file diff --git a/templates/html_template/16.html b/templates/html_template/16.html new file mode 100644 index 0000000..0ea47f7 --- /dev/null +++ b/templates/html_template/16.html @@ -0,0 +1,92 @@ + +

+ A veces vas a encontrar una función o crear tus propias funciones UDF y hago esta nota para que sepas como guardarla + y como importarla para usarla, por lo tanto digamos que encontramos esta función sencilla llamada saludar, la cual + recibe un parámetro denominado nombre de tipo cadena de texto (string): +

+
 Copiar código
+Function saludar(nombre As String)
+    saludar = "hola " & nombre
+End Function
+
+

+ Lo que tenemos que hacer es abrir una instancia nueva de excel, luego vamos a oprimir la combinación de teclas + Alt + F11 para abrir el editor de código de VBA, en la parte superior izquierda veremos un pequeño ícono de + cuadritos naranjas que parecen ventanitas, lo seleccionamos y en el menú que se despliega seleccionamos la opción de + "módulo". +

+
+ +
+

+ Nos aparecerá un nuevo elemento denominado módulo, que al darle doble clic veremos un lienzo en blanco en el cual + pegaremos el código de nuestra función: +

+
+ +
+

+ Ahora podemos cerrar el editor de código de VBA dando clic en el botón "X" de la parte superior derecha, para hacer + el llamado de la función es muy simple, solo en una celda escribimos el signo igual y ponemos el nombre de la + función que en este caso es saludar y cómo le debemos pasar el parámetro nombre le colocaré "David": +

+
+ +
+

+ Ya vimos que la función se ejecuta correctamente al llamarla, ahora bien esta función solo opera en nuestro libro + actual, si guardamos y cerramos y abrimos un nuevo libro y hacemos en llamado de la función veremos que no va a + funcionar, por lo tanto el libro de excel que contiene la función la guardaremos de la siguiente manera: +

+
    +
  1. + Oprime la combinación de teclas Ctrl + G, te aparecerá la ventana de "Guardar este archivo". +
  2. +
  3. + En nombre te recomiendo que pongas el nombre de la función que en este caso es saludar. +
  4. +
  5. + En el campo donde viene la extensión del archivo da clic y seleciona "Complemento de Excel (*.xlam)" +
  6. +
  7. + En la ubicación, te recomiendo que la guardes en una carpeta destinada para UDFs. +
  8. +
  9. + Cerramos el libro, te dirá que si quieres guardar el archivo de excel pero en este caso no lo guardaremos ya que + solo nos interesaba guardar la función como un complemento y ya lo hicimos en el paso anterior. +
  10. +
+

+ Una vez guardada la extensión y con el fin de poderla usar en cualquier libro existente o de nueva creación debemos + de + importarla en las funciones de excel para ello abrimos excel no importa si es un libro en blanco, y seguimos los + siguientes pasos: +

+
    +
  1. + En la barra de herramientas seleccionamos la pestaña de "Archivo". +
  2. +
  3. + En la nueva vista, en la parte inferior izquierda seleccionamos "Opciones". +
  4. +
  5. + En la ventana emergente, en el panel de la izquierda escogemos "Complementos". +
  6. +
  7. + Del menú de complementos, en la parte inferior veremos "Administrar" y seleccionamos "Complementos de Excel" y + damos clic en el botón de "Ir..." +
  8. +
  9. + Se nos mostrará la ventana de complementos, damos clic en el botón de "Examinar..." y busca la ruta donde + guardaste tu complemento de saludar.xlam, lo seleccionas y dar clic en aceptar. +
  10. +
  11. + Verifica que en la lista de complementos ya tenga activo el checkbox y das clic en el botón de "Aceptar". +
  12. +
+

+ Una vez hecho los pasos anteriores ya puedes usar la función de saludar en cualquier libro de ese equipo, considera + que si compartes ese archivo a una compañero de trabajo la función no servira por lo tanto debes compartir el + complemento (archivo con extensión .xlam) y repetir los pasos anteriores. +

\ No newline at end of file diff --git a/templates/html_template/17.html b/templates/html_template/17.html new file mode 100644 index 0000000..efb59ad --- /dev/null +++ b/templates/html_template/17.html @@ -0,0 +1,211 @@ + +

+ La UDF NumeroATextoMoneda sirve para + convertir + números a textos en formato moneda (pesos mexicanos), ejemplo:
+ En la celda C1 tenemos la cantidad en número 3050.8, y en la celda C2 ingresamos =NumeroATextoMoneda(C1) y + obtendremos el siguiente resultado: +

+
+ +
+

+ A continuación te dejo el código de la UDF para que la revises y la puedas usar o modificar. +

+ +
 Copiar código
+
+Function NumeroATextoMoneda(Numero)
+	Application.Volatile
+		Dim texto
+		Dim Billones
+		Dim Millones
+		Dim Miles
+		Dim Cientos
+		Dim Decimales
+		Dim Cadena
+		Dim CadBillones
+		Dim CadMillones
+		Dim CadMiles
+		Dim CadCientos
+		texto = Numero
+		texto = FormatNumber(texto, 2)
+		texto = Right(Space(18) & texto, 18)
+		Billones = Mid(texto, 1, 3)
+		Millones = Mid(texto, 5, 3)
+		Miles = Mid(texto, 9, 3)
+		Cientos = Mid(texto, 13, 3)
+		Decimales = Mid(texto, 17, 2)
+		CadBillones = ConvierteCifra(Billones, 1)
+		CadMillones = ConvierteCifra(Millones, 1)
+		CadMiles = ConvierteCifra(Miles, 1)
+		CadCientos = ConvierteCifra(Cientos, 0)
+		If Trim(CadBillones) > "" Then
+			If Trim(CadBillones) = "UN" Then
+				Cadena = CadBillones & " BILLON"
+			Else
+				Cadena = CadBillones & " BILLONES"
+			End If
+		End If
+		If Trim(CadMillones) > "" Then
+			If Trim(CadMillones) = "UN" Then
+				Cadena = CadMillones & " MILLON"
+			Else
+				Cadena = CadMillones & " MILLONES"
+			End If
+		End If
+		If Trim(CadMiles) > "" Then
+			Cadena = Cadena & " " & CadMiles & " MIL"
+		End If
+		If Trim(CadMiles & CadCientos) = "UN0" Then
+			Cadena = Cadena & "UN PESO " & Decimales & "/100 M.N."
+		Else
+			If Miles & Cientos = "000000" Then
+				Cadena = Cadena & " " & Trim(CadCientos) & " PESOS " & Decimales & "/100 M.N."
+			Else
+				Cadena = Cadena & " " & Trim(CadCientos) & " PESOS " & Decimales & "/100 M.N. "
+			End If
+		End If
+		Select Case Numero
+			Case Is < 0
+				Cadena = "El valor es negativo"
+			Case Is > 999999999999.99
+				Cadena = "El valor excede el limite de la función"
+			Case Is < 1
+				Cadena = "CERO PESOS " & Decimales & "/100 M.N."
+			Case Is < 2
+				Cadena = "UN PESO " & Decimales & "/100 M.N."
+		End Select
+		NumeroATextoMoneda = Trim(Cadena)
+		
+End Function
+	
+Function ConvierteCifra(texto, SW)
+    Dim Centena
+    Dim Decena
+    Dim Unidad
+    Dim txtCentena
+    Dim txtDecena
+    Dim txtUnidad
+    Centena = Mid(texto, 1, 1)
+    Decena = Mid(texto, 2, 1)
+    Unidad = Mid(texto, 3, 1)
+    Select Case Centena
+        Case "1"
+            txtCentena = "CIEN"
+            If Decena & Unidad <> "00" Then
+                txtCentena = "CIENTO"
+            End If
+        Case "2"
+            txtCentena = "DOSCIENTOS"
+        Case "3"
+            txtCentena = "TRESCIENTOS"
+        Case "4"
+            txtCentena = "CUATROCIENTOS"
+        Case "5"
+            txtCentena = "QUINIENTOS"
+        Case "6"
+            txtCentena = "SEISCIENTOS"
+        Case "7"
+            txtCentena = "SETECIENTOS"
+        Case "8"
+            txtCentena = "OCHOCIENTOS"
+        Case "9"
+            txtCentena = "NOVECIENTOS"
+    End Select
+
+    Select Case Decena
+        Case "1"
+            txtDecena = "DIEZ"
+            Select Case Unidad
+                Case "1"
+                    txtDecena = "ONCE"
+                Case "2"
+                    txtDecena = "DOCE"
+                Case "3"
+                    txtDecena = "TRECE"
+                Case "4"
+                    txtDecena = "CATORCE"
+                Case "5"
+                    txtDecena = "QUINCE"
+                Case "6"
+                    txtDecena = "DIECISEIS"
+                Case "7"
+                    txtDecena = "DIECISIETE"
+                Case "8"
+                    txtDecena = "DIECIOCHO"
+                Case "9"
+                    txtDecena = "DIECINUEVE"
+            End Select
+        Case "2"
+            txtDecena = "VEINTE"
+            If Unidad <> "0" Then
+                txtDecena = "VEINTI"
+            End If
+        Case "3"
+            txtDecena = "TREINTA"
+            If Unidad <> "0" Then
+                txtDecena = "TREINTA Y "
+            End If
+        Case "4"
+            txtDecena = "CUARENTA"
+            If Unidad <> "0" Then
+                txtDecena = "CUARENTA Y "
+            End If
+        Case "5"
+            txtDecena = "CINCUENTA"
+            If Unidad <> "0" Then
+                txtDecena = "CINCUENTA Y "
+            End If
+        Case "6"
+            txtDecena = "SESENTA"
+            If Unidad <> "0" Then
+                txtDecena = "SESENTA Y "
+            End If
+        Case "7"
+            txtDecena = "SETENTA"
+            If Unidad <> "0" Then
+                txtDecena = "SETENTA Y "
+            End If
+        Case "8"
+            txtDecena = "OCHENTA"
+            If Unidad <> "0" Then
+                txtDecena = "OCHENTA Y "
+            End If
+        Case "9"
+            txtDecena = "NOVENTA"
+            If Unidad <> "0" Then
+                txtDecena = "NOVENTA Y "
+            End If
+    End Select
+
+    If Decena <> "1" Then
+        Select Case Unidad
+            Case "1"
+                If SW Then
+                    txtUnidad = "UN"
+                Else
+                    txtUnidad = "UN"
+                End If
+            Case "2"
+                txtUnidad = "DOS"
+            Case "3"
+                txtUnidad = "TRES"
+            Case "4"
+                txtUnidad = "CUATRO"
+            Case "5"
+                txtUnidad = "CINCO"
+            Case "6"
+                txtUnidad = "SEIS"
+            Case "7"
+                txtUnidad = "SIETE"
+            Case "8"
+                txtUnidad = "OCHO"
+            Case "9"
+                txtUnidad = "NUEVE"
+        End Select
+    End If
+    ConvierteCifra = txtCentena & " " & txtDecena & txtUnidad
+End Function
+
+
\ No newline at end of file diff --git a/templates/html_template/18.html b/templates/html_template/18.html new file mode 100644 index 0000000..4172ce8 --- /dev/null +++ b/templates/html_template/18.html @@ -0,0 +1,40 @@ + +

+ Esta función al igual que la de sumar por color la hice por que en un trabajo llevaban un registro en excel donde la + clásificación era por dos categorías y + por colores del relleno de la celda, y a veces eran varios colores, como los colores eran una constante y el rango + era un rango variable pues lo mejor era hacer una + función en lugar de hacer siempre filtrados por cada color:

+ En esta función para poder funcionar necesita que ingreses dos parámetros, el primer parámetro es la celda que + contiene el color que deseas obtener el conteo, + el segundo parámetro es el rango que contiene las celdas con los colores a contar, ejemplo:

+ De la celda A1 a la celda A29 tenemos varios conceptos y varios colores pero nos interesa saber cuantas celdas + tienen el color verde de relleno, entonces + en la celda C1 colocamos el color verde (no importa si colocas texto en la celda solo valida el color de fondo), en + la celda D1 hacemos el llamado de la función + ingresando =contarPorColor(C1,$A$1:$A$29). +

+
+ +
+

+ Te dejo el código de la UDF en caso de que quieras + usarla o modificarla: +

+ +
 Copiar código
+
+Function contarPorColor(celda As Range, rango As Range) As Long
+	Application.Volatile
+	Dim conteo As Integer
+	Dim obj As Object
+	conteo = 0
+	For Each obj In rango
+	  If obj.Interior.Color = celda.Interior.Color Then
+		conteo = conteo + 1
+	  End If
+	Next obj
+	contarPorColor = conteo
+End Function
+
\ No newline at end of file diff --git a/templates/html_template/19.html b/templates/html_template/19.html new file mode 100644 index 0000000..b609541 --- /dev/null +++ b/templates/html_template/19.html @@ -0,0 +1,33 @@ + +

+ Cuando veía viáticos había automatizado el almacenamiento de unos documentos y dentro del nombre del archivo quería + que tuviera las iniciales del empleado, ejemplo:

+ La celda C1 tiene el valor de "José Hernández Pérez", y en la celda C2 hacemos el llamado de nuestra función + =INICIALES(C1), el resultado es JHP: +

+
+ +
+
 Copiar código
+
+Function INICIALES(texto As Range) As String
+	Application.Volatile
+		Dim resultado As String
+		Dim palabras() As String
+		
+		palabras = Split(AjustarEspacios(texto))
+		  For i = LBound(palabras) To UBound(palabras)
+			  resultado = resultado & Left(palabras(i), 1)
+		  Next
+		INICIALES = resultado
+End Function
+
+Private Function AjustarEspacios(ByVal texto As String) As String
+	Dim resultado As String
+	resultado = Trim(texto)
+	   Do While InStr(resultado, "  ")
+		  resultado = Replace(resultado, "  ", " ")
+	   Loop
+	AjustarEspacios = resultado
+End Function	
+
\ No newline at end of file diff --git a/templates/html_template/2.html b/templates/html_template/2.html new file mode 100644 index 0000000..9e314a2 --- /dev/null +++ b/templates/html_template/2.html @@ -0,0 +1,149 @@ + + + +
+ +
+ +

+ Yo aplico este método de crear variables de + entorno para almacenar datos sensibles en proyectos en desarrollo de los + cuales si compartes el proyecto en un repositorio público la información no sea visible. +

+

+ Para este ejemplo práctico vamos a crear una variable de entorno de tipo JSON muy sencilla donde la variable de + entorno se llamara 'usr_db' la cual tendra dos llaves, una será nombre y la otra será password: +

+ +{% set var1 %} +usr_db = { "nombre": "Saitama", "password": "OnePunchMan" } +{% endset %} +{% with codigo=var1.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Una vez conociendo los datos que queremos que tenga nuestra variable de entorno lo que sigue es guardarla en el + sistema y para ello tenemos dos opciones. +

+ +
+
+ +

+ En mi opinión es la más sencilla, para ello abrimos la terminal de windows o el shell de PowerShell y escribirmos + lo + siguiente: +

+ +{% set var2 %} +setx usr_db '{ "nombre": "Saitama", "password": "OnePunchMan" }'; +{% endset %} +{% with codigo=var2.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Despues de ingresar el código anterior y haber dado enter la consola te debe retornar un mensaje como el + siguiente: +

+
+ +
+

+ Del comando anterior podemos resaltar: +

+
    +
  1. Las variables de entorno establecidas a través del shell llevan el prefijo setx
  2. +
  3. En este caso que es un JSON lo debemos almacenar como cadena de texto, de lo contrario es indistinto si usas + comillas dobles o simples, siempre y cuando cierres la cadena de texto con el mismo tipo de comilla con la que + abriste.
  4. +
  5. No olvides quitar el operador de asignación (=) de lo contrario la consola te dará un error.
  6. +
+ +
+
+ +

+ Para este proceso sigue los siguientes pasos: +

+
    +
  1. En el botón inicio busca la opción "Editar las variables de entorno del sistema" y selecionala.
  2. +
  3. En la ventana emergente con título "Propiedades del sistema", en la pestaña "Opciones avanzadas" da click en + el + botón "Variables de entorno...".
  4. +
  5. Se sobrepone una nueva ventana con título "Variables de entorno" la cual tiene dos grupos, nosotros usaremos + el + primer grupo llamado "Variables de usuario para (nombre de tu usuario)" 1, en ese + grupo + damos + click + en el botón + "Nuevo...".
  6. +
  7. Se aparece una tercera ventana emergente que se sobrepone, la cual lleva el título de "Nueva variable de + usuario", la cual tiene dos campos: +
      +
    1. En el primer campo llamado "Nombre de la" ingresamos en valor de usr_db
    2. +
    3. En el segundo campo llamado "Valor de la" ingresamos { "nombre": "Saitama", "password": "OnePunchMan" + } +
    4. +
      + +
      +
    +
  8. +
+

+ Del proceso anterior podemos destacar los siguientes puntos: +

+
    +
  1. Las variables de entorno solo se registra el puro nombre sin prefijos.
  2. +
  3. El valor de la variable se puede almacenar como formato JSON sin necesidad de iniciar y terminar con comillas, + por + default lo almacena como cadena de texto.
  4. +
+

+ 1 Ya sea que añadas variables de entorno o a tráves de la interfaz gráfica, en ambos casos, + puedes ver + tus + variables de entorno en esta ventana, de igual forma las podrás editar o eliminar. +

+ +
+
+ +
    +
  1. Cerrar las terminales o consolas que tengas abiertas.
  2. +
  3. Cerrar el editor o editores de código que tengas abiertos.
  4. +
+

+ Una vez hecho lo anterior deberas de oprimir la combinación de teclas Windows + r y en la venta ejecutar escribiras lo + siguiente: +

+ +{% set var3 %} +cmd /c "set PATH=C: && exit" +{% endset %} +{% with codigo=var3.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Una vez escrito el comando y dado enter ya podrás usar las variables de entorno y podras abrir el editor de código y + las terminales que necesites. +

+ + \ No newline at end of file diff --git a/templates/html_template/20.html b/templates/html_template/20.html new file mode 100644 index 0000000..c5c5490 --- /dev/null +++ b/templates/html_template/20.html @@ -0,0 +1,34 @@ + +

+ Está función retorna una suma considerando un color dentro de un rango y los valores en cada una de ellas, ejemplo: +

+ En el rango C1:C20 tenemos múltiples valores y cada cenda dentro del rango tiene diferente color de fondo, en la celda + E1 ponemos un color de fondo del cual queremos obtener + la sumatorio a partir del color y en la celda F1 hacemos el llamado de la función =SUMARCOLOR(E1,$C$1:$C:20) y + nos debe dar la suma del rango considerando los valores + de cada celda y el color de fondo de las celdas: +

+
+ +
+ +
 Copiar código
+
+Function SUMARCOLOR(color As Range, rango As Range) As Long
+    'color: La celda que contiene el color a sumar
+    'rango: El rango de celdas a considerar en la suma
+        
+    Dim resultado       'Almacenará el resultado de la suma
+    Dim celda As Range
+    
+    'Recorrer cada celda del rango
+    For Each celda In rango
+        'Sumar si el color de la celda es igual al color especificado
+        If celda.Interior.ColorIndex = color.Interior.ColorIndex Then
+            resultado = resultado + celda.Value
+        End If
+    Next celda
+    
+    SUMARCOLOR = resultado
+End Function
+
\ No newline at end of file diff --git a/templates/html_template/21.html b/templates/html_template/21.html new file mode 100644 index 0000000..9a6454f --- /dev/null +++ b/templates/html_template/21.html @@ -0,0 +1,47 @@ + +

+ Esta función a partir de un criterio y un color de celda suma los valores de un rango, los parámetros que requiere + son 3: +

+
    +
  1. color_de_referencia: celda que contiene el color de relleno de referencia.
  2. +
  3. concepto_de_referencia: celda que contiene el criterio a ser sumado.
  4. +
  5. rango_de_colores_y_conceptos : Solo el rango de conceptos y colores.
  6. +
  7. columnas_de_desplazamiento : es un valor númerico entero, indica el número de columnas a la derecha del + rango_de_colores_y_conceptos donde estan los valores a ser sumados.
  8. +
+

+ Ejemplo, en el rango A2:A41 tengo diferentes conceptos con diferentes colores, y en el rango B2:B41 tengo los + valores + númericos a ser sumados, en la celda D1 tengo el criterio "Bolígrafo" con fondo verde, quiero saber cuanto es la + sumatorioa considerando ese concepto y ese color de relleno, entonces en la celda E1 hago el llamado de la función + donde: +

+
    +
  1. color_de_referencia: D1
  2. +
  3. concepto_de_referencia: D1
  4. +
  5. rango_de_colores_y_conceptos : A2:A41
  6. +
  7. columnas_de_desplazamiento : 1
  8. +
+

=sumarPorColorYConcepto(D1,D1,$A$2:$A$41,1)

+ +
+ +
+ +
 Copiar código
+
+Function sumarPorColorYConcepto(color_de_referencia, concepto_de_referencia, rango_de_colores_y_conceptos As Range, columnas_de_desplazamiento As Integer) As Double
+	'sumarPorColorYConcepto(Ctrl + Shift + A)-> para obtener en mini tooltip
+	Application.Volatile
+	Dim conteo As Double
+	Dim obj As Object
+	conteo = 0
+	For Each obj In rango_de_colores_y_conceptos
+	  If obj.Interior.Color = color_de_referencia.Interior.Color And obj.Value = concepto_de_referencia.Value Then
+		conteo = conteo + obj.Offset(0, columnas_de_desplazamiento).Value
+	  End If
+	Next obj
+	sumarPorColorYConcepto = conteo
+End Function
+
\ No newline at end of file diff --git a/templates/html_template/22.html b/templates/html_template/22.html new file mode 100644 index 0000000..b076eae --- /dev/null +++ b/templates/html_template/22.html @@ -0,0 +1,58 @@ + +

+ Esta función la hice hace ya varios años, en un foro sobre temas de Excel una chica + quería saber si existia alguna forma de obtener la IP + de un equipo en Excel y fue así que hice esta solución. +

+

+ El uso es muy sencillo, solo debes ingresar en cualquier celda =getMyIP(), una vez hecho te retornara la + información, ejemplo:
+

+
+ +
+
 Copiar código
+
+Function getMyIP() As String
+    Dim objWMI As Object
+    Dim objQuery As Object
+    Dim objQueryItem As Object
+    Dim vIpAddress As Variant
+    Dim res As String
+    
+    On Error GoTo ErrorHandler
+    
+    ' Crear objeto WMI
+    Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
+    
+    ' Consultar WMI
+    Set objQuery = objWMI.ExecQuery("Select * from Win32_NetworkAdapterConfiguration Where IPEnabled = True")
+    
+    res = ""
+    
+    ' Recorrer todas las direcciones IP asignadas
+    For Each objQueryItem In objQuery
+        For Each vIpAddress In objQueryItem.IPAddress
+            res = res & "User IP - " & vIpAddress & vbCrLf
+        Next
+    Next
+    
+    getMyIP = Trim(res)
+    
+Cleanup:
+    ' Limpiar objetos
+    On Error Resume Next
+    Set objQueryItem = Nothing
+    Set objQuery = Nothing
+    Set objWMI = Nothing
+    On Error GoTo 0
+    Exit Function
+    
+ErrorHandler:
+    ' Manejo de errores
+    res = "Error: " & Err.Description
+    getMyIP = res
+    Resume Cleanup
+    
+End Function
+
\ No newline at end of file diff --git a/templates/html_template/23.html b/templates/html_template/23.html new file mode 100644 index 0000000..f19aeb4 --- /dev/null +++ b/templates/html_template/23.html @@ -0,0 +1,77 @@ + +

+ Esta función la hice pensando en que a veces quiero obtener de forma recursiva el listado de archivos en mi equipo + para eliminarlos, renombrarlos u ordenarlos, por ello hice una función que retorna en un archivo txt: +

+
    +
  1. Nombre del archivo
  2. +
  3. Extensión
  4. +
  5. Fecha última modificación
  6. +
  7. Ruta del archivo
  8. +
  9. Tamaños del Archivo
  10. +
  11. Año
  12. +
  13. Mes
  14. +
  15. Día
  16. +
  17. Hora
  18. +
  19. Minuto
  20. +
  21. Segundo
  22. +
+

+ La forma de usarlo es muy sencilla, en tu terminal mueve a la carpeta que deseas obtener de forma recursiva el + listado + de archivos, luego llama a la función, ejemplo:

+ Quiero exportar la información de mi carpeta de descargas, y quiero que el archivo se llame carpeta_descargas. +

+
+ +
+

+ Cómo podemos ver en la imagen anterior, el archivo se guardará como carpeta_descargas y al final te dice en + que ruta se guardará el documento, te recomiendo que abras un nuevo archivo de excel e importes el documento para + que veas la información tabulada. +

+
    +
  1. Abrir un nuevo documento de Excel.
  2. +
  3. En la barra de herramientas seleccionar la opción de datos.
  4. +
  5. Seleccionar la opción "De texto/CSV".
  6. +
  7. Ir a la ruta donde se guardó el archivo y seleccionarlo.
  8. + En la ventana emergente seleccionar las opciones: +
  9. Origen de archivo: 1252: Europeo occidental (Windows)
  10. +
  11. Delimitador: --Personalizado-- y en la casilla de abajo coloca un pipe |
  12. +
  13. Detección del tipo de datos: Basado en las primeras 200 filas
  14. +
+ +
 Copiar código
+
+function exportDirectoryFilesTxt {
+	param(
+		[string]$fileName
+	)
+	
+	# Asegurarse de que el archivo tenga la extensión .txt
+	$fileName = "$fileName.txt"
+	
+	# Crear el archivo
+	New-Item -Path $fileName -ItemType File -Force
+	
+	# Obtener el directorio actual
+	$curPath = $pwd
+	
+	# Definir los encabezados
+	$headers = "File_Name|Extension_File|Date_Last_Write_Time|File_Path|MB_Size_File|Year|Month|Day|Hour|Minute|Second"
+	Set-Content -Path $fileName -Value $headers | Out-Null
+	
+	# Obtener los archivos y añadir su información al archivo
+	Get-ChildItem -File -Recurse -Path $curPath | ForEach-Object {
+		$iterObj = $_.LastWriteTime
+		$fileSizeMB = [math]::Round((Get-Item -Path $_.FullName).Length / 1MB, 2)  # Redondear a 2 decimales
+		$fileInfo = "$($_.Name)|$($_.Extension)|$($_.LastWriteTime)|$($_.FullName)|$fileSizeMB|$($iterObj.Year)|$($iterObj.Month)|$($iterObj.Day)|$($iterObj.Hour)|$($iterObj.Minute)|$($iterObj.Second)"
+		Add-Content -Path $fileName -Value $fileInfo | Out-Null
+	}
+	
+	# Mover el archivo al escritorio (asegúrate de que $desktop esté definido)
+	$desktop = [Environment]::GetFolderPath("Desktop")
+	Move-Item -Path $fileName -Destination $desktop
+	Write-Host "Archivo guardado en la ruta: $desktop\$fileName"
+}
+
\ No newline at end of file diff --git a/templates/html_template/24.html b/templates/html_template/24.html new file mode 100644 index 0000000..48edcdf --- /dev/null +++ b/templates/html_template/24.html @@ -0,0 +1,47 @@ + +

+ Esta función la hice para hacer conteo de archivos ya sea de forma recursiva o no de los archivos en un directorio, + la forma de usarlo es muy sencilla, en la terminal muevete al directorio donde quieres realizar el conteo, la función + tiene un parámetro llamado IsRecursive el cual es obligatorio + y puede ser T y F donde T = True, hace el conteo de todos los subdirectorios y F = False, hace el conteo solo de los + archivos en el direcotrio principal sin considerar subdirectorios, + ejemplo: +

+
+ +
+

+ De la imagen anterior podemos ver que no es recursivo el conteo, pero que encuentra 4 archivos y no hay subfolders, + hay 2 archivos pdf, 1 docx y 1 xlsx. +

+
 Copiar código
+
+function CountFilesAndFolders {
+	param(
+		[string]$IsRecursive
+	)
+	$curP = pwd
+	if ($IsRecursive -eq "") {
+		Write-Host "El parametro -IsRecursive es obligatorio y solo acepta T= true o F=false, ejemplo de sitaxis:" -ForegroundColor Red
+		Write-Host "CountFilesAndFolders -IsRecursive T"
+	}
+	elseif (($IsRecursive -eq "T") -or ($IsRecursive -eq "F")) {
+		if ($IsRecursive -eq "T") {
+			$totalFiles = (Get-ChildItem -Path $curP -Recurse -File | Measure-Object).Count
+			$totalFolders = (Get-ChildItem -Path $curP -Recurse -Directory  | Measure-Object).Count
+			Get-ChildItem -Path $curP -Recurse -File | group Extension -NoElement  | sort Count -Descending
+		}
+		elseif ($IsRecursive -eq "F") {
+			$totalFiles = (Get-ChildItem -Path $curP -File | Measure-Object).Count
+			$totalFolders = (Get-ChildItem -Path $curP -Directory | Measure-Object).Count
+			Get-ChildItem -Path $curP -File | group Extension -NoElement  | sort Count -Descending
+		}
+		
+		Write-Host "Total files: " $totalFiles -ForegroundColor Green
+		Write-Host "Total folders:" $totalFolders -ForegroundColor Green
+	}
+	elseif ($IsRecursive -ne "T" -and $IsRecursive -ne "F") {
+			Write-Host "Verifica el parametro ingresado" -ForegroundColor Red
+	}   
+}
+
\ No newline at end of file diff --git a/templates/html_template/25.html b/templates/html_template/25.html new file mode 100644 index 0000000..3b63714 --- /dev/null +++ b/templates/html_template/25.html @@ -0,0 +1,44 @@ + +

+ Esta función la hice por que a veces tengo que analizar archivos del INEGI y al momento de descargar los datos + abiertos vienen comprimidos en multiples archivos zip y como era una + tarea repetitiva ví que podía automatizarla de tal forma que pueda descomprimir todos los archivos zip en un + directorio, para usarla es muy sencillo, solo debes de moverte + en la terminal hasta el directorio donde están todos tus archivos zip y luego hacer el llamado de la función, + ejemplo:

+ Imaginemos que tenemos una carpeta con 32 archivos zip que queremos descomprimir y en la terminal ya estoy ubicado + en ese directorio, solo debo hacer el llamado de la función unzip: +

+
+ +
+

+ La salida del comando nos informa de forma iterativa el nombre del archivo que se va descomprimiendo y el número de + archivo del total de archivos a descomprimir, al final nos informa que ya se han descomprimido los archivos. +

+
 Copiar código
+
+function unzip {
+	# https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.archive/compress-archive?view=powershell-7.2
+	$curP = $pwd
+	$Zips = Get-ChildItem -path $curP  -filter "*.zip"  
+	$Destination = $curP
+	$numFiles = (Get-ChildItem -Filter "*.zip" | Measure-Object).Count
+	if ($numFiles -eq 0) {
+		Write-Warning "No hay archivos zips para ser descomprimidos"
+	}
+	elseif ($numFiles -eq 1) {
+		Write-Host "Descomprimiendo: " $Zips.name -ForegroundColor Yellow
+		Expand-Archive $Zips.name -Force
+		Write-Host "Proceso finalizado " -ForegroundColor Green
+	}
+	elseif ($numFiles -gt 1) {
+		foreach ($_ in $Zips) {
+		$nRound = [math]::Round(((($Zips.IndexOf($_) + 1) / $numFiles) * 100), 2)
+		Write-Host "" ($Zips.IndexOf($_) + 1) " de "  $numFiles  " | " $nRound "% | Descomprimiendo: "  $_.name -ForegroundColor Yellow
+		Expand-Archive $_ -Force
+		}
+		Write-Host "Proceso de descompresion finalizado exitosamente" -ForegroundColor Green
+	}
+}	
+
\ No newline at end of file diff --git a/templates/html_template/26.html b/templates/html_template/26.html new file mode 100644 index 0000000..8ab1e1c --- /dev/null +++ b/templates/html_template/26.html @@ -0,0 +1,88 @@ + +
+ +
+

+ Para generar una llave SSH primedo debemos + colocarnos en la carpeta .ssh que tanto en Ubuntu, Debian o Windows la ruta ~/.ssh: +

+
 Copiar código
+cd ~/.ssh
+

Una vez dentro de la carpeta de SSH de tu sistema, debemos ejecutar el comando que generara nuestra llave:

+
 Copiar código
+ssh-keygen -t rsa -b 4096 -C "tu_correo_electronico@email.com"
+

+ El comando genera una nueva clave SSH RSA de 4096 bits con el comentario "tu_correo_electronico@email.com", + explicación:
+

+
    +
  1. ssh-keygen: Es el comando que se utiliza para generar, administrar y convertir claves de autenticación + para + SSH (Secure Shell).
  2. +
  3. -t rsa: Esta opción especifica el tipo de clave que se va a crear. En este caso, 'rsa' indica que se está + generando una clave RSA (Rivest-Shamir-Adleman). RSA es uno de los algoritmos más comunes para generar claves + SSH.
  4. +
  5. -b 4096: Esta opción especifica el tamaño (en bits) de la clave que se va a generar. En este caso, '4096' + bits. Cuanto mayor sea el número de bits, más segura será la clave, pero también tomará más tiempo generarla y + usarla. 4096 bits es considerado un tamaño muy seguro para las claves RSA.
  6. +
  7. -C "tu_correo_electronico@email.com": Esta opción agrega un comentario a la clave pública generada. Este + comentario + se + usa generalmente para identificar la clave. En este caso, el comentario es "tu_correo_electronico@email.com", + que podría + ser una dirección de correo electrónico u otra forma de identificación del propietario de la clave.
  8. +
+

+ Como resultado del comando anterior son dos archivos con el mismo nombre id_rsa pero uno sin extensión + y otro con una + extensión .pub, debes copiar el contenido del archivo con extensión .pub. +

+ + +
+
+ +
 Copiar códigocat ~/.ssh/id_rsa.pub
+

+ Se mostrara en consola el contenido del archivo .pub, copialo. +

+ +
+
+ +
 Copiar códigoscb (cat ~\.ssh\id_rsa.pub)
+

El comando anterior copia el contenido a tu portapapeles.

+ +
+
+

+ Conectate a tu maquina Linux via SSH como normalmente lo haces y muevete a la carpeta .ssh, encontraras un archivo + llamado authorized_keys, lo modificaremos con ayuda de nano: +

+
 Copiar código
+nano ~/.ssh/authorized_keys
+

+ Una vez abierto el editor pegaras el contenido que copiaste del archivo id_rsa.pub, guarda los cambios con CTRL + + S y cierra el editor con CTRL + X, finalmente puedes salirte de la conexión SSH y volver a conectarte, + veras que ahora ya no te requiere contraseña + alguna para establecer la conexión. +

+ + + \ No newline at end of file diff --git a/templates/html_template/27.html b/templates/html_template/27.html new file mode 100644 index 0000000..8f0374c --- /dev/null +++ b/templates/html_template/27.html @@ -0,0 +1,72 @@ + +

+ Para poder añadir usuarios primero debemos cambiar a super usuario: +

+
 Copiar código
+sudo su
+ +
+
+ +

Para añadir un nuevo usuario simple sin permisos de administrador el comando es:

+
 Copiar códigoadduser usuario_nuevo
+

+ El sistema te pedirá que ingreses la contraseña para el usuario nuevo que estás creando y otros detalles, + como + el + nombre completo, el número de telefónico, etc. Puedes ingresar esta información opcionalmente, los puedes + dejar + vacios y dar enter. +

+

+ Después de configurar la contraseña y otros detalles, el comando 'adduser' creará el nuevo usuario y su + directorio + de inicio en '/home/nuevo_usuario'. También asignará permisos adecuados para el nuevo usuario. +

+

+ Para validar el acceso al usuario, puedes conectarte vía ssh y confirmarlo. +

+ +
+
+ +

Para asignar permisos de super usuario al nuevo usuario el comando es:

+
 Copiar códigosudo usermod -aG sudo usuario_nuevo
+

Esto permitirá que el nuevo usuario ejecute comandos con privilegios de administrador utilizando 'sudo'.

+ +
+
+ +

Para eliminar a un usuario el comando es:

+
 Copiar códigosudo deluser usuario_nuevo
+

+ Este comando eliminará el usuario llamado usuario_nuevo. Ten en cuenta que este comando eliminará el + usuario pero no + eliminará los archivos del usuario en el sistema. Si deseas eliminar también los archivos asociados al + usuario, puedes usar la opción --remove-home: +

+
 Copiar códigosudo deluser --remove-home usuario_nuevo
+

+ Recuerda que es importante tener cuidado al eliminar usuarios, especialmente si tienen archivos importantes + asociados. Asegúrate de realizar copias de seguridad o mover archivos importantes antes de eliminar un + usuario. +

+ +
+
+ + + \ No newline at end of file diff --git a/templates/html_template/28.html b/templates/html_template/28.html new file mode 100644 index 0000000..93cb42d --- /dev/null +++ b/templates/html_template/28.html @@ -0,0 +1,28 @@ + +

+ Para poder habilitar SSH deberas instalar + openssh-server con ayuda del comando: +

+
 Copiar código
+sudo apt install openssh-server -y
+

+ Una vez instalado openssh-server debemos verificar el estatus del servidor ssh: +

+
 Copiar código
+systemctl status ssh
+
+ +
+

+ Si te aparece que el servicio esta deshabilitado (disabled) entonces debemos ejecutar el comando para habilitarlo y + que acepte conexiones tanto de salida como de entrada: +

+
 Copiar código
+sudo systemctl enable ssh
+
+ +
+

El servidor SSH ya esta activo y finalmente nos queda abrir el puerto SSH en el firewall mediante el uso de ufw para + permitir conexiones SSH entrantes:

+
 Copiar código
+sudo  ufw allow ssh
\ No newline at end of file diff --git a/templates/html_template/29.html b/templates/html_template/29.html new file mode 100644 index 0000000..1a3a0e9 --- /dev/null +++ b/templates/html_template/29.html @@ -0,0 +1,43 @@ + +
 Copiar código
+sudo iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
+

+ Este comando añade una regla a la tabla de iptables para aceptar tráfico entrante en el puerto 8080: +

    +
  • sudo: Ejecuta el comando con privilegios de superusuario.
  • +
  • iptables: Herramienta para configurar las reglas del cortafuegos en Linux.
  • +
  • -A INPUT: Añade (-A) una regla a la cadena de entrada (INPUT).
  • +
  • -p tcp: Especifica que la regla aplica al protocolo TCP.
  • +
  • --dport 8080: Especifica que la regla aplica al puerto de destino 8080.
  • +
  • -j ACCEPT: Especifica que el tráfico que coincide con esta regla debe ser aceptado.
  • +
+

+

 Copiar código
+sudo ufw allow 8080/tcp
+

+ Este comando añade una regla al cortafuegos UFW (Uncomplicated Firewall) para permitir tráfico TCP en el puerto + 8080: +

    +
  • sudo: Ejecuta el comando con privilegios de superusuario.
  • +
  • ufw: Herramienta para gestionar el cortafuegos en Ubuntu.
  • +
  • allow 8080/tcp: Permite tráfico TCP en el puerto 8080.
  • +
+

+
 Copiar código
+  sudo iptables-save
+

+ Este comando guarda la configuración actual de iptables para que se mantenga después de reiniciar el sistema: +

    +
  • sudo: Ejecuta el comando con privilegios de superusuario.
  • +
  • iptables-save: Guarda las reglas actuales de iptables.
  • +
+

+
 Copiar código
+  sudo ufw reload
+

+ Este comando recarga la configuración de UFW para aplicar cualquier cambio reciente: +

    +
  • sudo: Ejecuta el comando con privilegios de superusuario.
  • +
  • ufw reload: Recarga la configuración del cortafuegos UFW.
  • +
+

\ No newline at end of file diff --git a/templates/html_template/3.html b/templates/html_template/3.html new file mode 100644 index 0000000..71b9219 --- /dev/null +++ b/templates/html_template/3.html @@ -0,0 +1,70 @@ + +

+ Yo aplico este método de crear variables de + entorno para almacenar datos sensibles en proyectos en desarrollo de los + cuales si compartes el proyecto en un repositorio público la información no sea visible. +

+

+ Para este ejemplo práctico vamos a crear una variable de entorno de tipo JSON muy sencilla donde la variable de + entorno se llamara 'usr_db' la cual tendra dos llaves, una será nombre y la otra será password: +

+ +{% set i %} +usr_db = { "nombre": "Saitama", "password": "OnePunchMan" } +{% endset %} +{% with codigo=i.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Una vez conociendo los datos que queremos que tenga nuestra variable de entorno lo que sigue es guardarla en el + sistema y para ello debemos abrir la consola oprimiendo la combinación de teclas Ctrl + Alt + T .

+ Una vez abierta la consola, debemos ir a home, así que ejecutamos el siguiente comando: +

+ +{% set ii %} +cd $home +{% endset %} +{% with codigo=ii.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Editamos el archivo de configuración del usuario, para ello debemos hacerlo a través de sudo por lo que te será requerida tu contraseña. +

+ +{% set iii %} +sudo nano .bashrc +{% endset %} +{% with codigo=iii.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Una vez abierto el archivo en modo edición nos vamos al final del documento y añadimos lo siguiente: +

+ +{% set iv %} +export usuario='{"nombre":"Saitama", "password": "OnePunchMan"}' +{% endset %} +{% with codigo=iv.strip(), isEditable='true' %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Dentro del entorno de nano presionamos la combinación de teclas CTRL + s para guardar y luego CTRL + x + para salir del modo de edición.
Para reiniciar la configuración del usuario solo debes ejecutar el comando: +

+ +{% set v %} +exec bash +{% endset %} +{% with codigo=v.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Una vez reiniciada la configuración del perfil del usuario podrás usar tu nueva variable de entorno. +

\ No newline at end of file diff --git a/templates/html_template/30.html b/templates/html_template/30.html new file mode 100644 index 0000000..50354c3 --- /dev/null +++ b/templates/html_template/30.html @@ -0,0 +1,50 @@ + +

+ CRONTAB es una super herramienta que te ayudará a automatizar tareas en tu equipo Linux o en tu servidor casero. + En mi caso, lo uso para automatizar tareas como la actualización del sistema operativo, back-ups de base de datos, e + incluso un back-up de una partida de Minecraft. Para instalarlo, los pasos son muy sencillos: +

+

Primero, verifica si está instalado CRON:

+
 Copiar código
+dpkg -l | grep cron
+

Si el comando no retorna información, deberás instalarlo y habilitarlo:

+
 Copiar código
+sudo apt-get install cron && sudo systemctl enable cron
+

En este punto, el servicio de cron debe indicar Active: active (running). + Ahora, solo queda automatizar tus tareas.

+
+
+
+
+ +
+
+
+ + Conocer el estado actual del servicio: +
 Copiar códigosystemctl status cron
+
+ Iniciar el servicio: +
 Copiar códigosudo systemctl start cron
+
+ Detener el servicio: +
 Copiar códigosudo service cron stop
+ +
+
+
+ + + \ No newline at end of file diff --git a/templates/html_template/31.html b/templates/html_template/31.html new file mode 100644 index 0000000..3f6c716 --- /dev/null +++ b/templates/html_template/31.html @@ -0,0 +1,181 @@ + +
+ +
+

+ Para crear un Servidor DNS en tu red local vamos a usar una herramienta + sencilla y práctica llamada dnsmasq el cual nos + ayudará a conectarnos a los dispositivos de la red local sin necesidad de aprendernos + las IPs de los dispositivos, adicionalmente es muy importante que tanto tu servidor dns y todos los equipos a los que + deseamos acceder mediante dns + tengan asignada una ip estática de lo + contrario, no funcionará de forma permanente.. +

+ + + +
+
+ +
 Copiar código
+ # actualizar el sistema e instalar dnsmasq
+sudo apt-get update && sudo apt-get upgrade -y && sudo apt install dnsmasq -y
+
+ +

+ Una vez instalado dnsmasq debemos modificar el archivo de configuración: +

+ +
 Copiar código
+ # modificar archivo de configuración de dnsmasq
+sudo nano /etc/dnsmasq.conf
+
+ +

+ Dentro del archivo, nos desplazamos hasta el final del documento y añadiremos los siguientes registros: +

+
 Copiar código
+ # añadir registros al final del documento
+interface=eth0                  # Interfaz de red del equipo, puedes obtenerla con el comando [ip a] (puede ser wlan0 si usas WiFi), en este caso uso eth0
+bind-interfaces                 # Asegura que dnsmasq solo escuche en la interfaz especificada
+server=8.8.8.8                  # Servidor DNS de respaldo (Google DNS u otro)
+domain-needed                   # Solo resuelve nombres de dominio completos
+bogus-priv                      # Bloquea consultas a direcciones privadas que no deberían salir de la red
+local=/mi-red.local/            # Dominio local personalizado
+expand-hosts                    # Expande nombres de host con el dominio especificado
+domain=mi-red.local             # Dominio DNS local
+addn-hosts=/etc/hosts.dnsmasq 	# Archivo donde defines los nombres de host y sus IPs
+
+

+ Creamos el archivo con los hosts personalizados para ello debemos conocer las ips de todos los dispositivos que + queremos acceder a través + de esta modalidad, considerando que en la configuración indicamos que los hosts iban a estar en la ruta + /etc/hosts.dnsmasq (addn-hosts=/etc/hosts.dnsmasq), + ejecutamos el siguiente comando: +

+ +
 Copiar código
+ # crear y editar el archivo con la lista de host e ips
+sudo nano /etc/hosts.dnsmasq
+
+ +

Una vez abierto el archivo añadimos la lista de ips de la red local y seguido, separado por un espacio, el nombre + de los hosts, ejemplo:

+
 Copiar código
+ # crear y editar el archivo con la lista de ips y los nombres de host deseados
+192.168.1.1   server1
+192.168.1.2   server2
+192.168.1.3   server3
+192.168.1.4   server4
+192.168.1.5   server5
+
+ +

+ Guardamos y cerramos el archivo, luego reiniciamos el servicio de dnsmasq: +

+ +
 Copiar código
+ # reiniciar dnsmasq
+sudo systemctl restart dnsmasq
+
+ +
+
+ +

En el equipo cliente donde usas alguna distribución de linux debes modificar el archivo resolv.conf, para ello + ejecutamos el comando:

+
 Copiar código
+ # en cliente modificar archivo resolv.conf
+sudo nano /etc/resolv.conf
+
+ +

Al final del archivo añadimos lo siguiente:

+
 Copiar código
+ # cliente conf resolv.conf
+nameserver 192.168.1.12 # ip local del servidor dns 
+nameserver 8.8.8.8 # dns google
+nameserver 8.8.4.4 # dns google
+
+ +

+ El archivo resolv.conf es un archivo que puede ser sobreescrito por el sistema por lo tanto debes de protegerlo de + escritura con el siguiente + comando: +

+
 Copiar código
+ # comando para hacer inmutable el archivo resolv.conf
+sudo chattr +i /etc/resolv.conf
+
+ +

+ En caso de que quieras volver a modificarlo es importante que primero lo vuelvas a hacer mutable con el comando: +

+
 Copiar código
+ # comando para hacer mutable el archivo resolv.conf
+sudo chattr -i /etc/resolv.conf
+
+ +

+ Finalmente para verificar que esta funcionando el servidor DNS lo que podemos hacer es un simple ping al server1 y + verificar que si hay + tráfico o en su defecto conectarnos mediante ssh ejemplo ssh usuario@server1 +

+ + + +
+
+ + + +
+
+ + + \ No newline at end of file diff --git a/templates/html_template/32.html b/templates/html_template/32.html new file mode 100644 index 0000000..0f84c62 --- /dev/null +++ b/templates/html_template/32.html @@ -0,0 +1,206 @@ + + +

+ Para poder subir cambios o descargar repositorios de nuestro servidor de control de versiones con Git (No host GitHub) + debemos primero haber creador el repositorio en nuestro servidor, para ello checa primero esa nota. +

+

+ Una vez creado el repositorio en nuestro servidor en nuestra maquina local podrémos hacer las siguientes + procedimientos: +

+ +
+
+ +
+

+ Imaginemos que vamos a crear un nuevo proyecto llamado new_project, por lo tanto creamos la carpeta: +

+
 Copiar código
+mkdir new_project; cd new_project
+

+ Una vez dentro de la carpeta de nuestro nuevo proyecto debemos inicializar un repositorio Git: +

+
 Copiar código
+# iniciar un entorno de git
+git init .
+
+

+ Para vincular la carpeta del proyecto a nuestro servidor debemos ejecutar los siguientes comandos: +

+
 Copiar código
+# vincular el proyecto a repositorio.
+git remote add origin nombre_usuario@ip_servidor_o_dns:/ruta/carpeta/repositorio/en/servidor
+# ejemplo: git remote add origin dave@192.162.1.18:/home/gitserver/repos/new_project
+
+

+ El comando git remote add origin vincula nuestro proyecto local a un repositorio remoto. El alias + origin se usa para + referirse a este repositorio en comandos futuros como git push o git pull. +

+

+ El comando anterior debe funcionar tanto en redes locales como externas. Para conexiones externas, asegúrate de + abrir y + redirigir el puerto SSH (22) en tu router hacia la IP del servidor. Si tienes una IP dinámica, considera usar + servicios + como DuckDNS. +

+

+ Cabe mencionar que no es recomendable aperturar el puerto 22 de tu tu router ISP, pero en caso de que así lo + desees esos son + los pasos a seguir, lo más recomendable es usar una VPN como wireguard y ya sea que tu hagas la instalación o + compres un router que + sea compatible con algunas VPN. +

+

+ En caso de que hayas cambiado el puerto por defecto de escucha que es el 22 debes cambiar el comando de + vinculación a tu proyecto y especificar el puerto, el comando + puede ser aplicado tanto para redes locales y externas. +

+
 Copiar código
+# comando para puerto de escucha modificado
+git remote add origin ssh://nombre_usuario@ip_publica_o_dns:puerto/ruta/carpeta/repositorio/en/servidor
+# ejemplo: ssh://dave@ip-casa-dns-sample.duckddns.org:2200/home/gitserver/repos/new_project
+
+

+ Una vez hecho todos los pasos anterior ya puedes continuar con tu proyecto sin importar en el lenguaje de + programación en que vayas a + trabajar, guardar cambios con git add ., guardar cambios git commit -m "mensaje relacionado + a los cambios hechos" + y subirlos a tu servidor git push -u origin master. +

+

+ Adicional a lo anterior, tambien es importante que conozcas que puedes vincular un proyecto a más de un servidor + en caso de que quieras + redundancias para backups, para hacerlo el proceso es similar al anterior, ejemplo del comando: +

+
 Copiar código
+git remote add server_1 dave1@192.168.16.10:/home/servergit1/new_project
+git remote add server_2 dave2@192.168.16.11:/home/servergit2/new_project
+
+

+ Para hacer el envio de cambios es importante que hagas push por cada servidor, en este caso serían dos push, + ejemplo: +

+
 Copiar código
+# Para enviar a servidor_1
+git push server_1 master	
+
+# Para enviar a servidor_2
+git push server_2 master	
+
+ +
+ +
+
+ +
+

+ Si deseas desvincular el repositorio remoto configurado en tu máquina local, el procedimiento es muy sencillo. + Primero, verifica las conexiones actuales de tu repositorio mediante el siguiente comando: +

+
 Copiar código
+# Listar los remotos configurados en el repositorio local
+git remote -v
+ 
+			
+

+ Este comando muestra las URL de los remotos configurados para operaciones de fetch (descarga) y + push (envío). + La salida será similar a la siguiente: +

+
$ git remote -v
+origin  dave@dns_server_git:/home/gitserver/repo/new_project (fetch)
+origin  dave@dns_server_git:/home/gitserver/repo/new_project (push)
+
+

+ Alternativamente, puedes usar el comando git remote show origin para obtener información más + detallada + sobre la configuración del remoto llamado origin, incluyendo su URL y el estado actual de las + conexiones. +

+

+ Una vez que hayas identificado el remoto que deseas eliminar, puedes desvincularlo usando el siguiente comando: +

+
 Copiar código
+# Eliminar un remoto llamado 'origin'
+git remote remove origin
+
+

+ Después de ejecutar este comando, el repositorio local ya no estará vinculado al remoto eliminado. + No podrás realizar operaciones como git push o git pull hasta que configures una nueva + vinculación. + Sin embargo, los cambios realizados en tu repositorio local permanecerán intactos. +

+

+ Si tienes múltiples remotos configurados, como se menciona en la sección "Vincular", asegúrate de especificar + correctamente el nombre del remoto que deseas eliminar. Por ejemplo: +

+
 Copiar código
+# Eliminar un remoto llamado 'server_1'
+git remote remove server_1
+
+
+ + +
+
+ +
+

+ Clonar un repositorio es el proceso de crear una copia local de un repositorio remoto. Este procedimiento es + sencillo, + ya sea que trabajes en una red local o externa, siempre y cuando hayas realizado las configuraciones necesarias, + como + la apertura de puertos o la configuración de claves SSH. +

+

+ Para clonar un repositorio utilizando el puerto SSH predeterminado (22), usa el siguiente comando: +

+
 Copiar código
+# Clonar repositorio con puerto predeterminado (22)
+git clone usuario@direccion_ip_servidor_o_dns:/ruta/carpeta/repositorio/en/servidor
+# Ejemplo: git clone dave@192.168.11.31:/home/gitserver/repos/new_project
+
+

+ Si tu servidor está configurado para usar un puerto SSH diferente, debes especificarlo en el comando. + A continuación, te mostramos el formato y un ejemplo: +

+
 Copiar código
+# Clonar repositorio con puerto modificado
+git clone ssh://usuario@direccion_ip_servidor_o_dns:puerto/ruta/carpeta/repositorio/en/servidor
+# Ejemplo: git clone ssh://dave@192.168.11.31:2200/home/gitserver/repos/new_project
+
+

+ En caso de que no tengas una llave SSH te solicitará autenticación, solo ingresa la contraseña y podrás + clonar el repositorio, + puedes consultar mi nota de como crear una llave SSH. +

+
+ +
+
+ + + \ No newline at end of file diff --git a/templates/html_template/33.html b/templates/html_template/33.html new file mode 100644 index 0000000..d18b343 --- /dev/null +++ b/templates/html_template/33.html @@ -0,0 +1,126 @@ + +
+ +
+ +

+ Primero debemos actualizar nuestro sistema + linux, una vez hecho eso, ya podemos iniciar con la instalación de Gitea: +

+ +

1. Instalar dependencias
Primero, vamos a Instalar SQLite3 para poder manejar la configuración inicial y + en local.

+
 Copiar código
+# es opcional ya que puedes usar otros gestores de bases de datos
+sudo apt install git sqlite3 -y
+
+

2. Crear un usuario para Gitea
Es una buena práctica ejecutar Gitea con un usuario dedicado:

+
 Copiar código
+# añadir un usuario ejemplo se llama gitea
+sudo adduser --system --group --home /home/gitea gitea
+
+

3. Descargar Gitea
Descargamos la última versión de Gitea, en mi caso yo uso una Raspberry Pi 4, te + recomiendo primero conocer la arquitectura de tu maquina linux para saber que versión necesitas, para ello ejecuta el + comando: +

+
 Copiar código
+# En mi caso la salida del comando es:  aarch64 lo que indica que es un procesador ARM de 64 bits.
+uname -m
+
+ +

Para Raspberry Pi 64-bit:

+ + +
 Copiar código
+# esto solo aplica para descargar el binario compatible con la raspberry pi ARM64
+URL_VRSNS="https://api.github.com/repos/go-gitea/gitea/releases/latest"
+GITEA_VERSION=$(curl -s "$URL_VRSNS" | grep -Po '"tag_name": "\K.*?(?=")')
+VERSION=$(echo "$GITEA_VERSION" | sed 's/^v//')
+wget -O gitea https://github.com/go-gitea/gitea/releases/download/${GITEA_VERSION}/gitea-${VERSION}-linux-arm64
+
+

Da permisos de ejecución:

+
 Copiar código
+chmod +x gitea
+sudo mv gitea /usr/local/bin/
+
+

4. Crear la estructura de directorios
Ahora, configura las carpetas necesarias para almacenar los datos y + configuraciones:

+
 Copiar código
+# dependiendo del nombre del usuario que le diste en el punto dos debes reemplazarlo en gitea
+sudo mkdir -p /var/lib/gitea/{custom,data,indexers,public,log}
+sudo chown -R gitea:gitea /var/lib/gitea
+sudo chmod -R 750 /var/lib/gitea
+sudo mkdir -p /etc/gitea
+sudo chown root:gitea /etc/gitea
+sudo chmod 770 /etc/gitea
+
+

5. Crear el servicio de systemd
Para que Gitea se ejecute automáticamente, crea un servicio de systemd: +

+
 Copiar código
+sudo nano /etc/systemd/system/gitea.service
+

Copia y pega el siguiente contenido:

+
 Copiar código
+[Unit]
+Description=Gitea
+After=network.target
+
+[Service]
+RestartSec=2s
+Type=simple
+User=gitea
+Group=gitea
+WorkingDirectory=/var/lib/gitea
+ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
+Restart=always
+Environment=USER=gitea HOME=/home/gitea GITEA_WORK_DIR=/var/lib/gitea
+
+[Install]
+WantedBy=multi-user.target
+
+

Guarda el archivo y sal (CTRL+S, CTRL+X, Enter). Luego, habilita e inicia el + servicio:

+ +
 Copiar código
+sudo systemctl daemon-reload
+sudo systemctl enable --now gitea
+
+

Verifica que Gitea esté corriendo:

+
 Copiar código
+sudo systemctl status gitea
+

6. Configuración inicial de Gitea
Accede a la interfaz web en el navegador:

+
 Copiar código
+http://IP-de-tu-equipo:3000
+

+ Cuando entres por primera vez al sitio tendrás que hacer la configuración inicial, los únicos + campos que yo modifico son:
+ "Tipo de base de datos": SQLite3
+ Usuario Configuración + SSH/Claves GPG
+ Le das clic en añadir una clave y le pegas tu llave pública SSH +

+

+ En tu servidor de Gitea en la terminal deberás de ejecutar el siguiente comando: +

+
 Copiar código
+sudo usermod -s /bin/bash gitea
+

En una maquina diferente, de donde copiaste la llave SSH deberás ejecutar el siguiente comando para ver si gitea ya + esta funcionando:

+
 Copiar código
+ssh -T gitea@ip-servidor-gitea
+ +

Completa la configuración y tendrás Gitea listo para usar.
+ "Nombre del usuario": *lo dejo tal y como está, recuerda que debes considerar el punto dos de este manual"
+ "Contraseña": *Asignale la contraseña o al igual que en github puedes agregarle tu llave SSH*
+ "Título del sitio": *Pon el nombre que tu quieras* +

+

Una vez que hayas hecho la configuración inicial, en tu servidor de Gitea deberás de iniciar sesión, ir a la ruta : +
+ http://ip-del-equipo:3000/user/settings/keys
+

\ No newline at end of file diff --git a/templates/html_template/34.html b/templates/html_template/34.html new file mode 100644 index 0000000..39e7e21 --- /dev/null +++ b/templates/html_template/34.html @@ -0,0 +1,153 @@ + + +

Para subir un proyecto a Apache HTTP Server que está hecho con Flask y VENV, debemos movernos mediante la terminal a + la ruta donde se almacenan los proyectos:

+
 Copiar código
+cd /var/www/
+
+ +

Clonamos el proyecto a desplegar

+
 Copiar código
+# sudo git clone user_serv@ip.del.serv.git:[puerto_si_no_es_el_22]/ruta/al/repositorio
+sudo git clone usr1@192.160.11.29:/home/git_server/repos/ejemplo.com
+
+ +

+ Una vez clonado el repositorio, nos movemos a la carpeta ejemplo.com, creamos un entorno virtual (VENV) e instalamos + todas las dependencias listadas en requirements.txt (esto es una buena práctica). Todo lo anterior debe realizarse con + permisos de superusuario: +

+
 Copiar código
+# Entramos en modo superusuario
+sudo su 
+
+# Nos cambiamos al directorio del proyecto a desplegar
+cd /var/www/ejemplo.com
+
+# Iniciamos una instancia de VENV (entorno virtual)
+python3 -m venv .venv
+
+# Activamos el nuevo entorno virtual
+source .venv/bin/activate
+
+# Instalamos todas las dependencias necesarias
+pip install -r requirements.txt
+
+# Una vez instaladas las dependencias, podemos desactivar el entorno virtual con:
+deactivate
+
+# Salimos del modo superusuario
+exit
+
+

+ Dentro del proyecto, asegúrate de que exista un archivo con la extensión .wsgi, cuyo contenido debe ser similar al + siguiente, considera:
+

+ +

+
 Copiar código
+# 1. sys: Se usa para manipular el entorno del sistema y agregar rutas a sys.path.
+import sys
+
+# 2. logging: Se usa para registrar eventos y errores, facilitando la depuración.
+import logging
+
+# 3. Ruta de Linux al proyecto Flask
+sys.path.insert(0, '/var/www/ejemplo.com')
+
+# 4. Ruta de Linux al entorno virtual (verifica la versión de Python en el servidor)
+sys.path.insert(0, '/var/www/ejemplo.com/.venv/lib/python3.11/site-packages')
+
+# 5. Configuración de logs
+logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
+
+# 6. Importamos la aplicación principal de Flask
+from main import app as application
+
+ +

+ Nos movemos al directorio de configuración de Apache: +

+
 Copiar código
+cd /etc/apache2/sites-available/
+
+ +

+ Creamos el archivo de configuración con extensión .conf +

+
 Copiar código
+# ejemplo
+sudo nano ejemplo.com.conf
+
+ +

+ Dentro del archivo debemos incluir una configuración similar a la siguiente. Consideraciones: +

    +
  • Ruta de tu proyecto
  • +
  • Nombre del proceso: Debe ser único y no repetirse en futuros proyectos, o el servicio de Apache se detendrá + (app-ejemplo). +
  • +
  • Hilos del procesador [threads=6]. +
  • +
+

+
 Copiar código
+# Se recomienda NO usar el puerto 80 por seguridad.
+# Si decides usar otro puerto, asegúrate de abrirlo en el firewall.
+Listen 8084
+
+<VirtualHost *:8084>
+	ServerAdmin tu-email@email.com
+	ServerName ejemplo.com
+	ServerAlias ejemplo.com
+	DocumentRoot /var/www/ejemplo.com
+	
+	WSGIDaemonProcess app-ejemplo user=www-data group=www-data threads=6 python-home=/var/www/ejemplo.com/.venv
+	WSGIScriptAlias / /var/www/ejemplo.com/ejemplo.com.wsgi
+	
+	# dentro de tu proyecto debe existir la carpeta log para que dentro de esa se almacenen los logs
+	ErrorLog /var/www/ejemplo.com/log/error.log
+	CustomLog /var/www/ejemplo.com/log/access.log combined
+	
+	<Directory /var/www/ejemplo.com>
+		WSGIProcessGroup app-ejemplo
+		WSGIApplicationGroup %{GLOBAL}
+		#Order deny,allow
+		Require all granted
+	</Directory>
+	
+</VirtualHost>
+
+

Para verificar la sintaxis del archivo de configuración, ejecuta:

+
 Copiar código
+sudo apachectl configtest
+# si todo esta bien deberas ver el mensaje: Syntax OK
+
+ +

Para aplicar los cambios, recarga el servicio de Apache:

+
 Copiar código
+sudo systemctl reload apache2
+ +

Finalmente, abre un navegador y coloca la IP del servidor de Apache. Si configuraste un puerto diferente al 80, debes + indicarlo:

+
 Copiar código
+192.160.11.35:8084
+ +

Si al recargar Apache aparecen errores o el sitio no carga en el navegador, revisa los logs de Apache o los del + proyecto:

+
 Copiar código
+# Logs de Apache
+sudo journalctl -u apache2 --no-pager --lines=50
+	
+# Logs del proyecto Flask
+cat /var/www/ejemplo.com/log/error.log
+ + \ No newline at end of file diff --git a/templates/html_template/35.html b/templates/html_template/35.html new file mode 100644 index 0000000..7fdbbcf --- /dev/null +++ b/templates/html_template/35.html @@ -0,0 +1,31 @@ + + +

A veces, en nuestro ambiente de pruebas, usamos variables de entorno en el front-end. Es común guardarlas en el + archivo `.bashrc` o en archivos `.env`. Si usas la segunda opción, siempre asegúrate de considerar el archivo + `.env` en `.gitignore` para evitar exponer información sensible y comprometer la seguridad de tu aplicación.

+

En producción, cuando usamos Apache, debemos agregar las variables de entorno correctamente para que nuestra + aplicación las reconozca. Para hacerlo, sigue estos pasos:

+ +

1. Editar el archivo de configuración de variables de entorno de Apache:

+
 Copiar código sudo nano /etc/apache2/envvars
+ +

2. Agregar las variables de entorno:

+

Al final del archivo, agrega la variable con la siguiente estructura:

+
 Copiar código 
+# export nombre_variable='valor' 
+export SALUDO="Hola mundo"
+ +

3. Guardar y reiniciar Apache

+

Guarda los cambios con `CTRL + S`, cierra el editor con `CTRL + X` y aplica los cambios reiniciando Apache:

+
 Copiar código
+sudo systemctl restart apache2
+

Una vez completados los pasos anteriores tu aplicación debe correr sin problemas, estas variables solo estarán + disponibles + para procesos manejados por Apache y no para sesiones de usuario en la terminal. +

+

En tu proyecto de Flask en producción ya podrás usar la variable como normalmente la estabas usando, ejemplo:

+
+import os  
+saludo = os.getenv("SALUDO")  
+print(saludo)  # Debería imprimir "Hola mundo"
+
\ No newline at end of file diff --git a/templates/html_template/36.html b/templates/html_template/36.html new file mode 100644 index 0000000..5d89e5a --- /dev/null +++ b/templates/html_template/36.html @@ -0,0 +1,141 @@ + +

Para el manejo de caché en Apache, debemos seguir los siguientes pasos:

+

1.- Habilitar los módulos de caché de Apache.

+
 Copiar código
+sudo chown -R www-data:www-data /var/cache/apache2/mod_cache_disk
+sudo chmod -R 755 /var/cache/apache2/mod_cache_disk
+sudo apt-get install apache2-utils
+sudo a2enmod expires
+sudo a2enmod cache
+sudo a2enmod cache_disk
+sudo a2enmod headers
+sudo systemctl restart apache2
+
+ +

2.- Configurar el caché en el disco:
Debemos movernos a la ruta donde están los archivos de configuración:

+
 Copiar código
+cd /etc/apache2/sites-available/
+ +

3.- Modificar el archivo de configuración del sitio, por ejemplo:

+
 Copiar código
+sudo nano ejemplo.com.conf
+ +

4.- Una vez en el modo de edición, veremos probablemente la configuración básica como esta:

+
+Listen 8084
+
+<VirtualHost *:8084>
+	ServerAdmin tu-email@email.com
+	ServerName ejemplo.com
+	ServerAlias ejemplo.com
+	DocumentRoot /var/www/ejemplo.com
+
+	WSGIDaemonProcess app-ejemplo user=www-data group=www-data threads=6 python-home=/var/www/ejemplo.com/.venv
+	WSGIScriptAlias / /var/www/ejemplo.com/ejemplo.com.wsgi
+
+	# dentro de tu proyecto debe existir la carpeta log para que dentro de esa se almacenen los logs
+	ErrorLog /var/www/ejemplo.com/log/error.log
+	CustomLog /var/www/ejemplo.com/log/access.log combined
+
+	<Directory /var/www/ejemplo.com>
+		WSGIProcessGroup app-ejemplo
+		WSGIApplicationGroup %{GLOBAL}
+		#Order deny,allow
+		Require all granted
+	</Directory>
+
+</VirtualHost>
+
+ +

Dentro de la configuración, de preferencia después del cierre del tag </Directory>, agregaremos la + siguiente información:

+ +
 Copiar código
+# Habilitar caché para todas las solicitudes
+CacheEnable disk /
+	
+# Configuración de caché
+<IfModule mod_cache_disk.c>
+	CacheRoot /var/cache/apache2/mod_cache_disk
+	CacheDirLevels 2
+	CacheDirLength 1
+	# [bytes] Tamaño máximo de archivo a almacenar en caché
+	CacheMaxFileSize 1000000
+	# CacheMinFileSize bytes
+	CacheMinFileSize 1
+	CacheIgnoreHeaders Set-Cookie
+	CacheIgnoreNoLastMod On
+		
+	<FilesMatch "\.(jpg|jpeg|png|gif|css|js)$">
+		# Indica si el caché está funcionando
+		Header set X-Cache "HIT from Apache"
+		# Expiración por defecto (1 hora)
+		CacheDefaultExpire 3600
+		# Expiración máxima (1 día)
+		CacheMaxExpire 86400
+		CacheLastModifiedFactor 0.5
+	</FilesMatch>
+</IfModule>
+	
+
+ +

5.- El nuevo archivo de configuración debería de verse como el siguiente:

+
 Copiar código
+Listen 8084
+
+<VirtualHost *:8084>
+	ServerAdmin tu-email@email.com
+	ServerName ejemplo.com
+	ServerAlias ejemplo.com
+	DocumentRoot /var/www/ejemplo.com
+	
+	WSGIDaemonProcess app-ejemplo user=www-data group=www-data threads=6 python-home=/var/www/ejemplo.com/.venv
+	WSGIScriptAlias / /var/www/ejemplo.com/ejemplo.com.wsgi
+	
+	# dentro de tu proyecto debe existir la carpeta log para que dentro de esa se almacenen los logs
+	ErrorLog /var/www/ejemplo.com/log/error.log
+	CustomLog /var/www/ejemplo.com/log/access.log combined
+	
+	<Directory /var/www/ejemplo.com>
+		WSGIProcessGroup app-ejemplo
+		WSGIApplicationGroup %{GLOBAL}
+		#Order deny,allow
+		Require all granted
+	</Directory>
+
+	# INICIO MANEJO CACHE APACHE
+	# Habilitar caché para todas las solicitudes
+	CacheEnable disk /
+		
+	# Configuración de caché
+	<IfModule mod_cache_disk.c>
+		CacheRoot /var/cache/apache2/mod_cache_disk
+		CacheDirLevels 2
+		CacheDirLength 1
+		# [bytes] Tamaño máximo de archivo a almacenar en caché
+		CacheMaxFileSize 1000000
+		# CacheMinFileSize bytes
+		CacheMinFileSize 1
+		CacheIgnoreHeaders Set-Cookie
+		CacheIgnoreNoLastMod On
+		
+		# archivos en especifico:
+		<FilesMatch "\.(jpg|jpeg|png|gif|css|js)$">
+			# Indica si el caché está funcionando
+			Header set X-Cache "HIT from Apache"
+			# Expiración por defecto (1 hora)
+			CacheDefaultExpire 3600
+			# Expiración máxima (1 día)
+			CacheMaxExpire 86400
+			CacheLastModifiedFactor 0.5
+		</FilesMatch>
+	</IfModule>
+	# FINAL MANEJO CACHE APACHE
+
+</VirtualHost>
+
+
+ +

6.- Para aplicar los cambios realizados en el archivo de configuración, debemos recargar Apache:

+
 Copiar código
+sudo systemctl reload apache2
\ No newline at end of file diff --git a/templates/html_template/37.html b/templates/html_template/37.html new file mode 100644 index 0000000..7e26199 --- /dev/null +++ b/templates/html_template/37.html @@ -0,0 +1,158 @@ + +

Metabase es una herramienta de inteligencia empresarial (BI) de código abierto que permite visualizar y analizar + datos de manera sencilla y accesible. Su principal fortaleza radica en su facilidad de uso, ya que no requiere + conocimientos técnicos avanzados para crear dashboards, consultas y visualizaciones interactivas. A diferencia de + herramientas más complejas como Tableau o Power BI, Metabase se destaca por su simplicidad y rapidez de + implementación, lo que la hace ideal para equipos pequeños o medianos que buscan una solución ágil y económica. + Además, su naturaleza de código abierto permite personalizaciones y adaptaciones según las necesidades específicas de + cada organización. Su potencial reside en democratizar el acceso a los datos, permitiendo que usuarios no técnicos + exploren y tomen decisiones basadas en información en tiempo real.

+

Para la instalación en un servidor Ubuntu, se deben seguir estos pasos:

+ +

+ 1. Actualizar el sistema e instalar Java + (requisito para Metabase):
+ Para ver las versiones disponibles, basta con ejecutar el comando: +

+
 Copiar código
+java -version
+

+ La salida de ese comando te enlistará las últimas versiones disponibles. Por ejemplo: +

dix@mb:~$ java -version
+Command 'java' not found, but can be installed with:
+sudo apt install default-jre                   # version 2:1.21-76, or
+sudo apt install openjdk-21-jre-headless       # version 21.0.5~8ea-1
+sudo apt install openjdk-11-jre-headless       # version 11.0.25~5ea-1ubuntu1
+sudo apt install openjdk-17-crac-jre-headless  # version 17.0.13+0-0ubuntu2
+sudo apt install openjdk-17-jre-headless       # version 17.0.12+7-2
+sudo apt install openjdk-21-crac-jre-headless  # version 21.0.5+0-0ubuntu2
+sudo apt install openjdk-22-jre-headless       # version 22.0.2+9-4
+sudo apt install openjdk-23-jre-headless       # version 23+37-1
+sudo apt install openjdk-24-jre-headless       # version 24~16ea-1
+sudo apt install openjdk-8-jre-headless        # version 8u422-b05-1ubuntu1
+

+La versión más reciente en este caso es la 24, así que procedemos a descargarla: +

+
 Copiar código
+sudo apt install openjdk-24-jre-headless -y
+ +

+ Verificamos la versión instalada: +

+
 Copiar código
+java -version
+ +

+ 2. Crear un usuario y carpeta para Metabase:
Por seguridad, es mejor ejecutar Metabase con un usuario sin + privilegios root: +

+
 Copiar código
+# crear un nuevo usuario llamado metabase
+sudo useradd -m -d /opt/metabase -s /bin/bash metabase
+
+# Opcional: Establece una contraseña para el usuario metabase
+# El sistema pedirá que ingreses una nueva contraseña para el usuario metabase.
+# Luego, te pedirá que confirmes la contraseña ingresándola nuevamente.
+# Si las dos entradas coinciden, la contraseña se actualizará y se almacenará de forma segura en el sistema.
+sudo passwd metabase  
+
+# se utiliza para cambiar al usuario metabase en un sistema Linux, iniciando una sesión de shell como ese usuario.
+sudo su - metabase
+ +

+ 3. Crear un directorio para Metabase: +

+
 Copiar código
+mkdir -p /opt/metabase && cd /opt/metabase
+ +

+ 4. Descargar el archivo .jar de Metabase:
Para ello, podemos consultar el repositorio en Github y ver cuál + es la versión más reciente. Para descargar el archivo desde la terminal, solo necesitamos ejecutar el comando wget y + el hipervínculo que aparece en la categoría Metabase Open Source con el concepto JAR download. +

+
+ +
+
 Copiar código
+wget https://downloads.metabase.com/v0.53.6.x/metabase.jar
+ + +

+ 5. Ejecutar Metabase de forma manual:
Para verificar que todo está funcionando correctamente: +

+
 Copiar código
+java -jar /opt/metabase/metabase.jar
+

+ Veremos muchos logs en la terminal, pero después de unos 30 segundos aproximadamente, podremos ir a nuestro navegador + e ingresar la IP del servidor e indicar que queremos el puerto 3000 (puerto por defecto del servicio de Metabase). Por + ejemplo: +

+
+ +
+
+http://IP_DEL_SERVIDOR:3000
+ +

Si ves los últimos logs en la terminal pero cuando vas al navegador no obtienes respuesta, entonces revisa que no + esté bloqueado el puerto 3000 por el firewall. Deberás salir del usuario Metabase y abrir el puerto con permisos de + superusuario.

+

+ En el caso de que veas el mensaje de bienvenida de Metabase en tu navegador, entonces todos los pasos ejecutados + anteriormente fueron + correctos. Así que interrumpiremos el servicio de Metabase para poder ejecutarlo en segundo plano y no tener que + hacerlo de forma manual. Seleccionamos la terminal donde se muestran los logs de Metabase y presionamos la combinación + de teclas CTRL + C para interrumpir el servicio. +

+ +

+ 6. Salir del usuario metabase: +

+
 Copiar código
+exit
+ +

+ 7. Configurar el servicio en segundo plano:
Mediante la edición del archivo metabase.service: +

+
 Copiar código
+sudo nano /etc/systemd/system/metabase.service
+

Pegamos la siguiente configuración:

+
 Copiar código
+[Unit]
+Description=Metabase
+After=network.target
+
+[Service]
+User=metabase
+ExecStart=/usr/bin/java -jar /opt/metabase/metabase.jar
+WorkingDirectory=/opt/metabase
+Restart=always
+
+[Install]
+WantedBy=multi-user.target
+
+ +

+ Guardamos (CTRL + S) y salimos (Ctrl + X). Luego, recargamos systemd y habilitamos el servicio: +

+
 Copiar código
+sudo systemctl daemon-reload
+sudo systemctl enable metabase
+sudo systemctl start metabase
+
+ +

+ 8. Verificar que el servicio esté activo: +

+
 Copiar código
+sudo systemctl status metabase
+
+ +
+ +
+

En este punto, ya hemos finalizado la instalación y configurado el servicio en segundo plano. Ahora, el servicio + estará activo cada vez que el servidor esté encendido. Finalmente, solo queda que inicies sesión a través de tu + navegador web para añadir la configuración inicial de idioma, nombre, email, contraseña, conexiones a bases de datos + cómo PostgreSQL o MySQL, etc. +

\ No newline at end of file diff --git a/templates/html_template/38.html b/templates/html_template/38.html new file mode 100644 index 0000000..eeb854a --- /dev/null +++ b/templates/html_template/38.html @@ -0,0 +1,236 @@ + + + + + +
+
+
+
+ +
+
+
+
+
    +
  1. +

    + Dominio de nuestra página web: Es el nombre que adquirimos a través de proveedores como + GoDaddy, Google Domains, Hostinger, entre otros. Este dominio será la dirección que + los usuarios utilizarán para acceder a nuestro sitio web. +

    +
  2. +
  3. +

    + La IP pública: En muchos casos, la IP pública es dinámica, lo que significa que puede + cambiar con frecuencia. Esto puede ser un problema, ya que necesitamos una IP estática para garantizar la + accesibilidad constante de nuestros servicios. Para solucionar esto, podemos utilizar servicios como Duck DNS, que monitorea y actualiza automáticamente + la IP dinámica. Este servicio es especialmente útil para servicios como VPN, hosting web o conexiones SSH + (aunque este último no es recomendable por razones de seguridad). +

    +
  4. +
  5. +

    + Configuración del router del ISP: En el router proporcionado por tu proveedor de + servicios de Internet (ISP), deberás abrir el puerto 80 (para este ejemplo). La configuración varía según + el modelo del router, pero generalmente debes buscar la opción de "Port Forwarding" o "Reenvío de + puertos". Allí, deberás agregar una regla que incluya: +

      +
    • La IP estática del balanceador de carga.
    • +
    • El puerto WAN (80 en este caso).
    • +
    • El puerto LAN (también 80).
    • +
    • El protocolo TCP.
    • +
    + Este proceso deberás repetirlo si tienes más de un router en tu red. +

    +
  6. +
  7. +

    + IP's estáticas de los servidores en la red local: Tanto los servidores web como el + balanceador de carga deben tener direcciones IP estáticas en la red local. Esto asegura que los + dispositivos siempre estén accesibles y que la configuración de red sea consistente. +

    +
  8. +
  9. +

    + SSL: Adicional a los puntos anteriores, no es obligatorio pero por seguridad debes + considerar usar un servicio SSL ya sea que lo contrates con el proveedor del dominio o con Cloudflare que tiene una versión gratuita. +

    +
  10. +
+
+
+
+
+ +

+ Para este ejemplo consideraremos la siguiente información para la configuración: +

+
    +
  • Dominio: ejemplo-dom.com
  • +
  • IP pública: Dinámica, pero gestionada con Duck DNS (ejemplo: ejemplos.duckdns.org).
  • +
  • Router del ISP: El puerto 80 está abierto y redirigido a la IP del balanceador de carga.
  • +
  • Servidores web: Supongamos que tenemos 4 servidores web y cada uno es un espejo del otro, las + IP´s son 198.162.10.4, 198.162.10.5, 198.162.10.6 y 198.162.10.7
  • +
+ +

+ Una vez instalado HAProxy podemos revisar si el servicio está activo con el comando: +

+ +{% set i %} +systemctl status haproxy +{% endset %} +{% with codigo=i.strip(), isEditable="false" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ La salida del comando debe ser algo similar a este: +

+ +{% set i %} +● haproxy.service - HAProxy Load Balancer + Loaded: loaded (/lib/systemd/system/haproxy.service; enabled; preset: enabled) + Active: active (running) since Mon 2025-03-17 12:12:45 CST; 2 days ago + Docs: man:haproxy(1) + file:/usr/share/doc/haproxy/configuration.txt.gz + Main PID: 2643 (haproxy) + Tasks: 5 (limit: 8742) + CPU: 5min 30.311s + CGroup: /system.slice/haproxy.service + ├─2643 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock + └─2645 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock +{% endset %} +{% with btn=false, codigo=i.strip(), isEditable="false" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Como podemos ver, HAProxy está activo, entonces podemos proceder a modificar el archivo de configuración con el editor + de nano y permisos de superusuario: +

+ +{% set ii %} +sudo nano /etc/haproxy/haproxy.cfg +{% endset %} +{% with codigo=ii.strip(), isEditable="false" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Nos vamos hasta el final del archivo de configuración y añadimos la configuración del "frontend": +

+ +{% set iii %} +# frontend nombre_que_quieras +frontend https_frontend + # puerto que manejará el 80 + bind *:80 + # protocolo a usar + mode http + + # nombre de nuestro dominio: ejemplo-dom.com + # validador ejemplo-dom.com = is_ejemplo_dom_com (colocar el prefijo "is_", además los guiones medios y puntos se cambian a guiones bajos) + acl is_ejemplo_dom_com hdr(host) -i ejemplo-dom.com + # el nombre del backend puede ser el que tú quieras + use_backend ejemplo_dom_com if is_ejemplo_dom_com +{% endset %} +{% with codigo=iii.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Una vez configurado el frontend, continuamos en el mismo archivo y añadimos el "backend": +

+ +{% set iv %} +# backend nombre backend usado en la configuración del frontend +backend ejemplo_dom_com + # protocolo de balanceo de carga (puedes dejarlo o googlear otros protocolos) + balance roundrobin + # protocolo para sitios web + mode http + # enlistar las IPs de los servidores web o coloca la DNS de cada uno de los servidores en caso de usar un servidor DNS. + server server1 198.162.10.4:80 check + server server2 198.162.10.5:80 check + server server3 198.162.10.6:80 check + server server4 198.162.10.7:80 check + # server{1-4} puede ser el nombre que tú quieras, solo es una referencia para HAProxy + # 198.162.10.X:80 = indica que el servidor con esa IP tiene un servicio web activo en el puerto 80, en caso de no usar el 80 solo reemplaza ese valor + # check = valida que el servidor esté operando antes de redirigir el tráfico +{% endset %} +{% with codigo=iv.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Una vez configurado el frontend y el backend en el archivo de HAProxy, guardamos CTRL + S, cerramos CTRL + + X. Para validar que la sintaxis de la configuración sea correcta puedes usar el comando: +

+ +{% set v %} +sudo haproxy -c -f /etc/haproxy/haproxy.cfg +{% endset %} +{% with codigo=v.strip(), isEditable="false" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Si todo está bien en términos de sintaxis, la salida del comando debe ser: Configuration file + is valid
+ Procedemos a reiniciar el servicio de HAProxy con el comando: +

+ +{% set vi %} +sudo service haproxy restart +{% endset %} +{% with codigo=vi.strip(), isEditable="false" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Listo, ahora podrás entrar a tu navegador web e ingresar el dominio ejemplo-dom.com. Tu proveedor de dominio + redirigirá a Duck DNS que tiene tu IP y mandará el tráfico a tu router ISP, este a su vez lo mandará a tu balanceador + de carga para finalmente redirigir el tráfico al servidor web + que esté con menos carga.
+ De forma práctica y simple, te recomiendo que en cada servidor web, en el HTML del sitio, en la página de inicio + coloques un comentario con el número de servidor, y desde el lado del navegador web refresques la página y veas cómo + cambia el número del servidor. Esto con la finalidad de saber si el balanceo de carga está funcionando, ejemplo diagrama de flujo: +

+ +{% set vii %} +[Usuario] + | + v +[Dominio ejemplo-dom.com] + | + v +[DuckDNS (resuelve IP dinámica)] + | + v +[Router ISP (Port Forwarding 80 -> HAProxy)] + | + v +[HAProxy] + | + +--> [Backend ejemplo_dom_com] + | + +--> [Servidor 1] + | + +--> [Servidor 2] + | + +--> [Servidor 3] + | + +--> [Servidor 4] +{% endset %} +{% with btn=false, codigo=vii.strip(), isEditable="false" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +{% include 'components/src_collapse.html' %} \ No newline at end of file diff --git a/templates/html_template/4.html b/templates/html_template/4.html new file mode 100644 index 0000000..788a583 --- /dev/null +++ b/templates/html_template/4.html @@ -0,0 +1,37 @@ + +

+ Suponiendo que ya has creado la variable de entorno, ahora toca ver como leerla, ello el siguiente código de ejemplo: +

+ +{% set i %} +# la librería os nos ayudará a interactuar con las variables de entorno +import os +# la librería json nos ayudará a convertir la cadena de texto (str) a un formato JSON +import json + +# en seguimiento a mis notas de linux y de windows obtendremos la información de nuestra variable de entorno "usr_db" +usuario_env = os.getenv('usr_db') + +# Verifica si la variable de entorno existe +if usuario_env: + # si existe la variable de entorno la convertimos en formato JSON + usuario_json = json.loads(usuario_env) + # para este ejemplo solo imprimimos el valor de la variable + print(usuario_json) +else: + # en caso de que no exista que la consola nos imprima el siguiente texto + print("La variable de entorno 'usuario' no está definida.") +{% endset %} +{% with codigo=i.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Puedes ejecutar el código en un archivo con extensión .py o directamente en la terminal usando + el interprete de python como yo lo hago en este ejemplo: +

+
+ +
+ + diff --git a/templates/html_template/5.html b/templates/html_template/5.html new file mode 100644 index 0000000..db36936 --- /dev/null +++ b/templates/html_template/5.html @@ -0,0 +1,80 @@ + +
+ +
+

+ Para crear un repositorio de Git donde vamos a enviar + nuestros cambios, primero necesitamos instalar el programa con permisos + de superusuario (sudo). Te pedirá tu contraseña, por lo que deberás proporcionarla; en caso de que no lo + hagas, no se + instalará: +

+ +{% set i %} +sudo apt install git -y +{% endset %} +{% with codigo=i.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Una vez instalado, debes moverte al directorio donde almacenarás tu repositorio. En mi caso, el repositorio lo + colocaré en home. +

+ +{% set ii %} +cd $HOME +{% endset %} +{% with codigo=ii.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Para este ejemplo, voy a crear una carpeta llamada repo_ejemplo y me ubicaré dentro de ella: +

+ +{% set iii %} +mkdir repo_ejemplo && cd repo_ejemplo +{% endset %} +{% with codigo=iii.strip(), isEditable='true' %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Una vez dentro de la carpeta, debemos iniciar el repositorio ejecutando el siguiente comando: + git init --bare. + Esta es una forma de configurar un repositorio Git centralizado y sin área de trabajo, que se utiliza típicamente para + facilitar la colaboración y el intercambio de código entre múltiples desarrolladores. +

+ +{% set iv %} +git init --bare +{% endset %} +{% with codigo=iv.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ En caso de que no conozcas el nombre del usuario activo, deberás ejecutar el siguiente comando: +

+ +{% set v %} +whoami +{% endset %} +{% with codigo=v.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Con los siguientes dos comandos, debes elevar los privilegios de la carpeta. Para ello, necesitaremos el nombre del + usuario obtenido con el comando anterior. En los comandos de abajo, deberás sustituir la palabra USER por + tu + nombre de usuario. +

+ +{% set vi %} +sudo chown -R USER:USER /home/USER/repo_ejemplo && sudo chmod -R u+w /home/USER/repo_ejemplo +{% endset %} +{% with codigo=vi.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} diff --git a/templates/html_template/6.html b/templates/html_template/6.html new file mode 100644 index 0000000..d114840 --- /dev/null +++ b/templates/html_template/6.html @@ -0,0 +1,85 @@ + +
+ +
+

Antes de instalar todas las herramientas recuerda que siempre es muy importante actualizar el sistema operativo, + para ello ejecuta los siguientes comandos:

+ +{% set i %}sudo apt update && sudo apt upgrade -y{% endset %} +{% with codigo=i.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

Apache Server es un software que nos ayudará a manejar todos los sitios web de nuestros servidores, en mi caso + integro Apache con ambientes virtuales de Python 3 y Flask, pero para esta nota lo unico que te enseñaré será a + instalarlo. Una vez abierta tu terminal ya sea en Ubuntu o en WLS, debes seguir los siguientes pasos:

+

1.- Instalar el servidor de Apache que es la herramienta principal para hacer el hosting y manejo de peticiones por + sitio web:

+ +{% set ii %}sudo apt install apache2 -y{% endset %} +{% with codigo=ii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

2.- Para poder integrar Python 3 (Flask) con Apache vamos a necesitar instalar el módulo WSGI de Apache para Python + 3.

+ +{% set iii %}sudo apt-get install libapache2-mod-wsgi-py3 -y{% endset %} +{% with codigo=iii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

3.- Habilitar el módulo WSGI el cual es muy importante para poder leer los archivos de la aplicación de Flask:

+ +{% set iii %}sudo a2enmod wsgi{% endset %} +{% with codigo=iii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

4.- Habilitar y activar Uncomplicated Firewall (UFW). UFW es una interfaz de usuario para iptables y está diseñado + para simplificar el proceso de administración de firewalls, haciendo más accesible la configuración de reglas de + firewall para los usuarios.

+ +{% set iv %}sudo ufw enable{% endset %} +{% with codigo=iv.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

5.- Hábilitar el puerto 80 del equipo para el tráfico de datos y poder acceder al sitio web desde otros equipos (NO + ES LO MISMO HABILITAR EL PUERTO 80 DEL EQUIPO QUE DEL ISP O ROUTER).

+ +{% set v %}sudo ufw allow 80{% endset %} +{% with codigo=v.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

6.- Una vez instalado el Apache Server (paso 1) podemos saber si esta activo el servidor ejecutando el comando:

+ +{% set vi %}sudo service apache2 status{% endset %} +{% with codigo=vi.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

6.1.- En caso de que la consola te de la respuesta "apache2 is not running":

+
+ +
+

Lo que debes de hacer es levantar el servicio ejecutando el comando:

+ +{% set vii %}sudo service apache2 start{% endset %} +{% with codigo=vii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

Una vez que el servicio este activo:

+
+ +
+

Debemos conocer la ip del equipo, para ello ejecutamos el comando:

+ +{% set viii %}ifconfig{% endset %} +{% with codigo=viii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +
+ +
+

Del comando anterior tenemos que la ip de la interfaz "eth0", en la red local "inet" es la 172.30.29.141, por lo + tanto en tu navegador web ingresaras la ip y podras ver que tu servidor apache ya esta operando y listo para ser + modificado con tu sitio de interes.

+
+ +
+

Otros comando básico sobre el servicio de apache:

+

Reiniciar el sistema:

+ +{% set ix %}sudo service apache2 restart{% endset %} +{% with codigo=ix.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

Detener el servicio:

+ +{% set x %}sudo service apache2 stop{% endset %} +{% with codigo=x.strip() %}{% include 'components/copy-code.html' %}{% endwith %} \ No newline at end of file diff --git a/templates/html_template/7.html b/templates/html_template/7.html new file mode 100644 index 0000000..80e49ee --- /dev/null +++ b/templates/html_template/7.html @@ -0,0 +1,20 @@ + +

+ En esta pequeña nota solo verémos la instalación, es muy sencilla, para proceder con la instalación ejecutamos el + comando de instalación dentro de la terminal: +

+ +{% set i %}sudo apt install haproxy -y{% endset %} +{% with codigo=i.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + +

+ Una vez instalado HAProxy puedes ver el archivo de configuración que está en la ruta:
+ /etc/haproxy/haproxy.cfg +

+

+ Te recomiendo que antes de hacer alguna modificación al archivo primero realices una copia del mismo ya sea en una + carpeta de tu preferencia o en el directorio origen del archivo ejecutando el siguiente comando: +

+ +{% set i %}sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy_backup_usr.cfg{% endset %} +{% with codigo=i.strip() %}{% include 'components/copy-code.html' %}{% endwith %} \ No newline at end of file diff --git a/templates/html_template/8.html b/templates/html_template/8.html new file mode 100644 index 0000000..7862d4c --- /dev/null +++ b/templates/html_template/8.html @@ -0,0 +1,102 @@ + + + + +
+ +
+

La instalación de PostgreSQL es muy sencilla, para ello debemos de abrir una terminal en el sistema.

+
+ +
+
+ +

Comando de instalación en Linux:

+ + {% set i %}sudo apt install postgresql -y{% endset %} + {% with codigo=i.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + + +
+
+

Comando de instalación en WSL:

+ + {% set ii %}sudo apt install postgresql postgresql-contrib -y{% endset %} + {% with codigo=ii.strip() %}{% include 'components/copy-code.html' %}{% endwith %} + + +
+
+
+

Una vez instalado deberas saber que los archivos de configuración se guardan en la ruta: +

/etc/postgresql/[version]/main/ +

+ +

+ Por seguridad asignale una contraseña al usuario postgres para ello debes ejecutar en la terminal el comando: +

+ +{% set iii %}sudo -u postgres psql{% endset %} +{% with codigo=iii.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

+ Despues le asignamos la contraseña con el comando: +

+ +{% set iv %}ALTER USER postgres WITH PASSWORD 'la_contraseña_que_quieras';{% endset %} +{% with codigo=iv.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + + +

Salimos de la terminal de postgresql con el comando:

+ +{% set v %}\q{% endset %} +{% with codigo=v.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

Para saber si el servicio de postgresql esta activo puedes ejecutar el comando:

+ +{% set v %}sudo service postgresql status{% endset %} +{% with codigo=v.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

Si el servicio esta inactivo se te mostrará la siguiente información:

+
+ +
+

Para iniciar el servicio el comando es el siguiente:

+ +{% set vi %}sudo service postgresql start{% endset %} +{% with codigo=vi.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

Para detener el servicio el comando es el siguiente:

+ +{% set vii %}sudo service postgresql start{% endset %} +{% with codigo=vii.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

Más documentación importante: +

+

+ + \ No newline at end of file diff --git a/templates/html_template/9.html b/templates/html_template/9.html new file mode 100644 index 0000000..5cb4019 --- /dev/null +++ b/templates/html_template/9.html @@ -0,0 +1,92 @@ + +
+ +
+

+ Para crear un usuario en PostgreSQL que pueda realizar operaciones CRUD en todas las tablas de una base de datos específica, sigue estos pasos: +

+ +

1. Acceder a PostgreSQL como superusuario
Abrimos una terminal y accedemos a PostgreSQL:

+ +{% set i %} +sudo -u postgres psql +{% endset %} +{% with codigo=i.strip() %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

2. Crear el usuario
Creamos un usuario con una contraseña:

+ +{% set ii %} +CREATE USER testusr WITH PASSWORD 'test1991'; +{% endset %} +{% with codigo=ii.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

3. Conceder acceso a la base de datos
Damos acceso al usuario para que pueda conectarse a la base de datos: +

+ +{% set iii %} +GRANT CONNECT ON DATABASE db_test TO testusr; +{% endset %} +{% with codigo=iii.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

4. Otorgar permisos sobre el esquema public
Primero, nos conectamos a la base de datos y damos permisos + sobre el esquema:

+ +{% set iv %} +\c db_test; +GRANT USAGE ON SCHEMA public TO testusr; +GRANT CREATE ON SCHEMA public TO testusr; +{% endset %} +{% with codigo=iv.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

5. Conceder permisos en tablas y secuencias existentes
Otorgamos permisos CRUD sobre todas las tablas y acceso a + secuencias existentes:

+ +{% set v %} +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO testusr; +GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO testusr; +{% endset %} +{% with codigo=v.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

6. Conceder permisos para futuras tablas y secuencias
Para asegurarnos de que los objetos creados en el + futuro también sean accesibles por testusr:

+ +{% set vi %} +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO testusr; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO testusr; +{% endset %} +{% with codigo=vi.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

7. Salir de psql

+ +{% set vii %} +\q +{% endset %} +{% with codigo=vii.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} + +

8. Probar la conexión con el nuevo usuario
Antes de conectarnos, debemos conocer la IP del servidor de base + de datos. Si el servidor está en el mismo equipo, la IP será 127.0.0.1:
+ # psql -U testusr -d db_test -h localhost (te pedirá la contraseña por lo que deberás ingresarla) + psql -U testusr -d db_test -h 127.0.0.1 +

+ +{% set viii %} +psql -U testusr -d db_test -h 127.0.0.1 +{% endset %} +{% with codigo=viii.strip(), isEditable="true" %} +{% include 'components/copy-code.html' %} +{% endwith %} \ No newline at end of file diff --git a/templates/more_info/about_me.html b/templates/more_info/about_me.html new file mode 100644 index 0000000..5dcaeb1 --- /dev/null +++ b/templates/more_info/about_me.html @@ -0,0 +1,50 @@ +{% extends 'template.html' %} + +{% block css %} + +{% endblock %} + +{% block usr_path %} + +{% endblock %} + +{% block body %} + + +{% for ele in f_lst_glosary %} +{% for e in ele %} + +{% endfor %} +{% endfor %} + + + +{{data[0][0] | safe}} + +
+ +
+{% endblock body %} diff --git a/templates/more_info/personal_projects.html b/templates/more_info/personal_projects.html new file mode 100644 index 0000000..f61e7ce --- /dev/null +++ b/templates/more_info/personal_projects.html @@ -0,0 +1,99 @@ +{% extends 'template.html' %} + +{% block css %} + + + + + + + + + + + +{% endblock css %} + + +{% block usr_path %} + +{% endblock usr_path %} + +{% block body %} + + + +
+
+
+
+ Card image cap +
+
Bienestar Digital
+

Portal estático, sitio hecho para la SICT en espera de ser integrado a la sección principal.

+ Sitio +
+
+
+
+
+ Card image cap +
+
Repositorio COOPERASUR
+

Portal dinámico, aportación del gobierno mexicano al Proyecto Mesoamérica.

+ Sitio +
+
+
+
+
+ Card image cap +
+
Aldeas Inteligentes
+

Portal dinamico, sitio donde los beneficiarios de la conectividad generan sus reportes trimestrales.

+ Sitio +
+
+
+
+
+ Card image cap +
+
TAC Consulting (DB)
+

Portal dinámico que almacena la información de candidatos el área de reclutamiento y selección.

+ Sitio +
+
+
+
+
+ Card image cap +
+
Estrategia de Alfabetización Digital
+

Portal dinámico que muestra de forma sencilla tutoriales de gobierno.

+ Sitio +
+
+
+
+
+ Card image cap +
+
Consultora Formha
+

Consultora de RRHH especializada en reclutamiento, selección y evaluaciónes.

+ Sitio +
+
+
+
+
+ +{% endblock body %} \ No newline at end of file diff --git a/templates/new_posts.html b/templates/new_posts.html new file mode 100644 index 0000000..3949092 --- /dev/null +++ b/templates/new_posts.html @@ -0,0 +1,110 @@ +{% extends 'template.html' %} + +{% block css %} + +{% endblock %} + + +{% block usr_path %} + +{% endblock %} + + +{% block body %} + + +

Todas las notas

+ + +
+ {% for ele in db_mainSections %} +
+ + +
+ {% endfor %} +
+ +
+
+ Buscar nota +
+ +
+ +
+ {% for ele in db_newPosts %} + + {% endfor %} +
+ + +
+ + + + + + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/templates/seccion/a_seccion.html b/templates/seccion/a_seccion.html new file mode 100644 index 0000000..24f20d6 --- /dev/null +++ b/templates/seccion/a_seccion.html @@ -0,0 +1,84 @@ +{% extends 'template.html' %} + +{% block css %} + +{% endblock %} + +{% block usr_path %} + +{% endblock %} + +{% block body %} + +
+ + + {{dataHome[0][4] | safe}} + + + + + {{dataHome[0][3]}} + + +
+ +
+
+ +
+
+ + +
+ {% for ele in db_subtemas %} +
+ +

+ + {{dataHome[0][2] | safe}} + + | + +

+
+
+

+ {{ele[3]}} +

+
+
+
+ {% endfor %} +
+ + + +{% endblock %} diff --git a/templates/seccion/b_tema.html b/templates/seccion/b_tema.html new file mode 100644 index 0000000..da6895f --- /dev/null +++ b/templates/seccion/b_tema.html @@ -0,0 +1,120 @@ +{% extends 'template.html' %} + +{% block css %} + + + +{% endblock css %} + + + +{% block usr_path %} + + +{% endblock usr_path %} + + +{% block body %} + + + +{% for ele in f_lst_glosary %} +{% for e in ele %} + +{% endfor %} +{% endfor %} + + +
+ {{data[0][0]}}    + {{data[0][1]}} +
+ +
+

{{data[0][2]}}

+
+ +{% if alert != 'no_template' %} +{% set alert_template = "/components/alerts/" + alert %} +{% include alert_template %} +{% endif %} + + +{% include 'components/btn-toggle-see-code.html' %} + + + +{% include template_path %} + + +{% if f_lst_data_links | length > 0 %} +
+
+

+ +

+
+
+ {% for item in f_lst_data_links %} + + {% endfor %} +
+
+
+
+{% endif %} + + + + + +{% block js %} + + + + + +{% endblock js %} + +{% endblock body %} \ No newline at end of file diff --git a/templates/template.html b/templates/template.html new file mode 100644 index 0000000..36e2786 --- /dev/null +++ b/templates/template.html @@ -0,0 +1,120 @@ + + +{% block n_server %} +{% endblock n_server %} + + + + + + David Itehua Xalamihua - Blog Personal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% block css %} + {% endblock %} + + + + + + + {% include "components/navbar.html" %} + + + + {% include "components/news_bar.html" %} + + + + + + + {% include "components/up_btn.html" %} + + +
+ {% block body %} + {% endblock body %} +
+ + + + {% include "components/footer.html" %} + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xala.dev.conf b/xala.dev.conf new file mode 100644 index 0000000..998b519 --- /dev/null +++ b/xala.dev.conf @@ -0,0 +1,48 @@ +# sudo apachectl configtest +Listen 8084 + + ServerAdmin davidix1991@gmail.com + ServerName xala.dev + ServerAlias xala.dev + DocumentRoot /var/www/xala.dev + + WSGIDaemonProcess app6 user=www-data group=www-data threads=6 python-home=/var/www/xala.dev/.venv + WSGIScriptAlias / /var/www/xala.dev/xala.dev.wsgi + + ErrorLog /var/www/xala.dev/log/error.log + CustomLog /var/www/xala.dev/log/access.log combined + + + WSGIProcessGroup app6 + WSGIApplicationGroup %{GLOBAL} + Order deny,allow + Require all granted + + + # Habilitar caché para todas las solicitudes + CacheEnable disk / + + # Configuración de caché + + CacheRoot /var/cache/apache2/mod_cache_disk + CacheDirLevels 2 + CacheDirLength 1 + # [bytes] Tamaño máximo de archivo a almacenar en caché + CacheMaxFileSize 1000000 + # CacheMinFileSize bytes + CacheMinFileSize 1 + CacheIgnoreHeaders Set-Cookie + CacheIgnoreNoLastMod On + + + # Indica si el caché está funcionando + Header set X-Cache "HIT from Apache" + # Expiración por defecto (1 hora) + CacheDefaultExpire 3600 + # Expiración máxima (1 día) + CacheMaxExpire 86400 + CacheLastModifiedFactor 0.5 + + + + diff --git a/xala.dev.wsgi b/xala.dev.wsgi new file mode 100644 index 0000000..e71158f --- /dev/null +++ b/xala.dev.wsgi @@ -0,0 +1,14 @@ +import sys +import logging + +# ruta de linux al proyecto de flask +sys.path.insert(0, '/var/www/xala.dev') + +# ruta de linux al ambiente virtual de flask +sys.path.insert(0, '/var/www/xala.dev/.venv/lib/python3.11/site-packages') + +# Set up logging +logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) + +# Import and run the Flask app +from main import app as application