├── projectify ├── requirements-dev.txt ├── tests │ └── test_basic.py ├── dashboard │ ├── modules │ │ ├── __init__.py │ │ ├── dashboard.py │ │ └── api.py │ ├── requirements_dashboard.txt │ ├── Makefile │ └── main.py ├── models │ ├── __init__.py │ ├── folders.py │ ├── ide_files.py │ └── base_files.py ├── modules │ ├── __init__.py │ ├── git_repository.py │ ├── header.py │ ├── ide_configuration.py │ ├── project_structure.py │ ├── setup_project.py │ ├── environment.py │ └── dependencies.py ├── __init__.py ├── scripts │ └── __init__.py └── core.py ├── requirements ├── requirements.txt └── requirements-docs.txt ├── .bumpversion.cfg ├── docs ├── license.md ├── contributing.md ├── getting-started.md ├── install.md ├── index.md ├── roadmap.md ├── commands.md ├── changelog.md ├── api.md ├── faq.md └── templates.md ├── mkdocs.yml ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug-report.md ├── workflows │ ├── python-package.yml │ ├── docs-to-gh-pages.yml │ └── publish-to-pypi.yml ├── CODE_OF_CONDUCT.md ├── CODE_OF_CONDUCT.es.md └── CONTRIBUTING.md ├── setup.py ├── LICENSE ├── .gitignore ├── Makefile ├── pyproject.toml └── README.md /projectify/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements/requirements.txt: -------------------------------------------------------------------------------- 1 | bump2version 2 | twine 3 | -------------------------------------------------------------------------------- /projectify/tests/test_basic.py: -------------------------------------------------------------------------------- 1 | def test_basic(): 2 | assert True 3 | -------------------------------------------------------------------------------- /requirements/requirements-docs.txt: -------------------------------------------------------------------------------- 1 | mkdocs 2 | mkdocs-rtd-dropdown 3 | -------------------------------------------------------------------------------- /projectify/dashboard/modules/__init__.py: -------------------------------------------------------------------------------- 1 | from .api import * 2 | from .dashboard import * 3 | -------------------------------------------------------------------------------- /projectify/dashboard/requirements_dashboard.txt: -------------------------------------------------------------------------------- 1 | streamlit 2 | requests 3 | pandas 4 | altair 5 | plotly 6 | matplotlib 7 | seaborn 8 | -------------------------------------------------------------------------------- /projectify/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_files import base_files 2 | from .folders import directories 3 | from .ide_files import vscode_files 4 | -------------------------------------------------------------------------------- /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.1.19 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | 8 | [bumpversion:file:pyproject.toml] 9 | -------------------------------------------------------------------------------- /projectify/modules/__init__.py: -------------------------------------------------------------------------------- 1 | from .dependencies import check_and_install_dependencies 2 | from .environment import get_installed_python_versions 3 | from .header import print_header 4 | from .setup_project import setup_project 5 | -------------------------------------------------------------------------------- /projectify/models/folders.py: -------------------------------------------------------------------------------- 1 | directories = [ 2 | "notebooks", 3 | "app", 4 | "utils", 5 | "modules", 6 | "models", 7 | "images", 8 | "data", 9 | "artifacts", 10 | "docs", 11 | "scripts", 12 | "tests", 13 | "logs", 14 | ] 15 | -------------------------------------------------------------------------------- /projectify/dashboard/Makefile: -------------------------------------------------------------------------------- 1 | # Nombre del archivo principal de streamlit 2 | APP = main.py 3 | 4 | # Comando para ejecutar Streamlit 5 | run: 6 | streamlit run $(APP) 7 | 8 | # Instalar las dependencias del proyecto desde requirements.txt 9 | install: 10 | pip install -r requirements_dashboard.txt 11 | -------------------------------------------------------------------------------- /docs/license.md: -------------------------------------------------------------------------------- 1 | # Projectify! License 2 | 3 | Projectify! is licensed under the MIT License. You can find a copy of the license in the LICENSE file in the root directory of this project. The MIT License is a permissive open-source license that allows you to use, modify, and distribute the software for any purpose. It requires that you include the original copyright notice and disclaimer in any redistribution of the software. 4 | -------------------------------------------------------------------------------- /projectify/modules/git_repository.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | def initialize_git_repo(project_name): 5 | """Initialize a new Git repository in the specified project directory. 6 | 7 | Parameters 8 | ---------- 9 | project_name : str 10 | The name of the project directory where the Git repository will be initialized. 11 | """ 12 | subprocess.run(["git", "init"], cwd=project_name) 13 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Projectify! 2 | 3 | ## How to Contribute? 4 | 5 | Thank you for your interest in contributing to **Projectify!**! Here are some ways you can help: 6 | 7 | - Report bugs or issues. 8 | - Suggest new features. 9 | - Submit code through pull requests. 10 | 11 | ## Rules and Conventions 12 | 13 | - Use descriptive branches like `feat/new-feature` or `fix/bug-fix`. 14 | - Follow code conventions and make sure everything passes tests before submitting a PR. 15 | -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Create a New Project 4 | 5 | To create a new project, run: 6 | 7 | ```bash 8 | projectify init 9 | ``` 10 | 11 | ## Initial Setup 12 | 13 | Projectify! will automatically configure: 14 | 15 | - A virtual environment. 16 | - Ruff as a linter and formatter. 17 | - Basic project structure with essential folders and files. 18 | 19 | ## Basic Commands 20 | 21 | - Clean the project: `projectify clean` 22 | - Run tests: `projectify run-tests` 23 | - Generate documentation: `projectify generate-docs` 24 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Projectify! 2 | site_url: https://cyberingeniero.github.io/projectify/ 3 | theme: 4 | name: readthedocs 5 | language: es 6 | features: 7 | - navigation.tabs 8 | - navigation.expand 9 | nav: 10 | - Home: index.md 11 | - Getting Started: getting-started.md 12 | - Installation: install.md 13 | - Commands: commands.md 14 | - API Reference: api.md 15 | - Templates: templates.md 16 | - FAQ: faq.md 17 | - Contributing: contributing.md 18 | - Changelog: changelog.md 19 | - Roadmap: roadmap.md 20 | - License: license.md 21 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Prerequisites 4 | 5 | Before installing **Projectify!**, make sure you have: 6 | 7 | - Python 3.8 or higher. 8 | - pip (Python package manager). 9 | - Optional: Git for version control integration. 10 | 11 | ## Install 12 | 13 | Install **Projectify!** using `pip`: 14 | 15 | ```bash 16 | pip install projectify 17 | ``` 18 | 19 | ## First Steps 20 | 21 | After installation, you can start a new project with the following command: 22 | 23 | ```bash 24 | projectify init 25 | ``` 26 | 27 | For more details, check out the [Getting Started Guide](getting-started.md). 28 | -------------------------------------------------------------------------------- /projectify/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Nibaldo 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /projectify/scripts/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Nibaldo 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Command '....' 17 | 3. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | - OS: [e.g. Window] 26 | - Version [ projectify -v ] 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /projectify/modules/header.py: -------------------------------------------------------------------------------- 1 | from art import text2art 2 | from colorama import Fore, Style 3 | 4 | 5 | def print_header(): 6 | """ 7 | This function prints a header using the text2art library. 8 | The header text is "Projectify!" and it's printed in cyan color. 9 | """ 10 | header = text2art("Projectify!") 11 | byline = Fore.YELLOW + Style.BRIGHT + "by CyberIngeniero" + Style.RESET_ALL 12 | header_lines = header.split("\n") 13 | max_length = max(len(line) for line in header_lines) 14 | header_with_byline = [ 15 | Fore.CYAN + line + " " * (max_length - len(line)) + byline if i == 0 else Fore.CYAN + line 16 | for i, line in enumerate(header_lines) 17 | ] 18 | print("\n".join(header_with_byline) + Style.RESET_ALL) 19 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | setup( 4 | name="Projectify", 5 | version="0.1.19", 6 | description="A tool to set up a Python project structure", 7 | author="CyberIngeniero", 8 | author_email="npinoa.ai@gmail.com", 9 | packages=find_packages(include=["projectify", "projectify.*"]), 10 | include_package_data=True, 11 | install_requires=[ 12 | "colorama", 13 | "art", 14 | "packaging", 15 | ], 16 | entry_points={ 17 | "console_scripts": [ 18 | "projectify=projectify.core:main", 19 | ], 20 | }, 21 | classifiers=[ 22 | "Programming Language :: Python :: 3", 23 | "License :: OSI Approved :: MIT License", 24 | "Operating System :: OS Independent", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /projectify/models/ide_files.py: -------------------------------------------------------------------------------- 1 | vscode_files = { 2 | "settings.json": """{ 3 | "python.pythonPath": "${workspaceFolder}/venv/bin/python", 4 | "editor.formatOnSave": true, 5 | "editor.codeActionsOnSave": { 6 | "source.organizeImports": true, 7 | "source.fixAll": true 8 | }, 9 | "python.linting.enabled": true, 10 | "python.linting.ruffEnabled": true, 11 | "python.formatting.provider": "black" 12 | }""", 13 | "keybindings.json": """[ 14 | { 15 | "key": "ctrl+shift+b", 16 | "command": "workbench.action.tasks.build", 17 | "when": "editorTextFocus" 18 | } 19 | ]""", 20 | "snippets.json": """{ 21 | "Print to console": { 22 | "prefix": "log", 23 | "body": [ 24 | "console.log('$1');", 25 | "$2" 26 | ], 27 | "description": "Log output to console" 28 | } 29 | }""", 30 | } 31 | -------------------------------------------------------------------------------- /projectify/modules/ide_configuration.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from projectify.models import vscode_files 4 | 5 | 6 | def create_ide_configuration(project_name, ide_choice): 7 | """ 8 | This function creates IDE configuration files for a project. 9 | 10 | Parameters 11 | ---------- 12 | project_name : str 13 | The name of the project. 14 | ide_choice : int 15 | The choice of IDE. 1 for VS Code, 2 for PyCharm. 16 | """ 17 | if ide_choice == 1: 18 | vscode_dir = os.path.join(project_name, ".vscode") 19 | os.makedirs(vscode_dir, exist_ok=True) 20 | 21 | # Creando archivos de configuración de VS Code 22 | for file, content in vscode_files.items(): 23 | with open(os.path.join(vscode_dir, file), "w") as f: 24 | f.write(content) 25 | elif ide_choice == 2: 26 | os.makedirs(os.path.join(project_name, ".idea"), exist_ok=True) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Nibaldo A. Pino Araya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | name: Python package 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | python-version: ["3.9", "3.10", "3.11"] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Set up Python ${{ matrix.python-version }} 21 | uses: actions/setup-python@v3 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | python -m pip install flake8 pytest 28 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 29 | - name: Lint with flake8 30 | run: | 31 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=__init__.py 32 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude=__init__.py 33 | - name: Test with pytest 34 | run: | 35 | pytest 36 | 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # Distribution / packaging 7 | .Python 8 | build/ 9 | develop-eggs/ 10 | dist/ 11 | downloads/ 12 | eggs/ 13 | .eggs/ 14 | lib/ 15 | lib64/ 16 | parts/ 17 | sdist/ 18 | var/ 19 | wheels/ 20 | share/python-wheels/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | MANIFEST 25 | 26 | # Virtual environment 27 | venv/ 28 | ENV/ 29 | env/ 30 | .venv/ 31 | ENV/ 32 | env.bak/ 33 | venv.bak/ 34 | 35 | # Editor and IDE files 36 | .vscode/ 37 | .idea/ 38 | 39 | # Jupyter Notebook 40 | .ipynb_checkpoints 41 | 42 | # Pyenv 43 | .python-version 44 | 45 | # pip 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # PyInstaller 50 | *.spec 51 | 52 | # Installer logs 53 | pip-log.txt 54 | pip-delete-this-directory.txt 55 | 56 | # mkdocs documentation 57 | /site 58 | 59 | # mypy 60 | .mypy_cache/ 61 | 62 | # Pyre 63 | .pyre/ 64 | 65 | # Coverage reports 66 | htmlcov/ 67 | .tox/ 68 | .nox/ 69 | 70 | # Environments 71 | .env 72 | .envrc 73 | .spyderproject 74 | .spyproject 75 | .ropeproject 76 | 77 | # Mypy 78 | .mypy_cache/ 79 | .dmypy.json 80 | dmypy.json 81 | 82 | # Testing 83 | .coverage 84 | .cache/ 85 | nosetests.xml 86 | coverage.xml 87 | *.cover 88 | *.py,cover 89 | .hypothesis/ 90 | .pytest_cache/ 91 | -------------------------------------------------------------------------------- /.github/workflows/docs-to-gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Projectify! Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | permissions: 9 | contents: write 10 | pages: write 11 | id-token: write 12 | 13 | jobs: 14 | build_mkdocs: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | 21 | - name: Set up Python 22 | uses: actions/setup-python@v5 23 | with: 24 | python-version: '3.11' 25 | 26 | - name: Install dependencies 27 | run: pip install -r requirements/requirements-docs.txt 28 | 29 | - name: Deploy to GitHub Pages 30 | run: mkdocs gh-deploy --force 31 | 32 | deploy_mkdocs: 33 | needs: build_mkdocs 34 | environment: 35 | name: github-pages 36 | url: ${{ steps.deployment.outputs.page_url }} 37 | runs-on: ubuntu-latest 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v4 41 | with: 42 | ref: gh-pages 43 | 44 | - name: Setup Pages 45 | uses: actions/configure-pages@v5 46 | 47 | - name: Upload artifact 48 | uses: actions/upload-pages-artifact@v3 49 | with: 50 | path: '.' 51 | 52 | - name: Deploy to GitHub Pages 53 | id: deployment 54 | uses: actions/deploy-pages@v4 55 | -------------------------------------------------------------------------------- /projectify/modules/project_structure.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from projectify.models import base_files, directories 4 | 5 | 6 | def create_project_structure(project_name, python_version): 7 | """ 8 | This function creates a basic structure for a Python project. 9 | 10 | Parameters 11 | ---------- 12 | project_name : str 13 | The name of the project. This will be used to create the main directory and replace placeholders in files. 14 | python_version : str 15 | The version of Python to be used in the project. This will be used to replace placeholders in the Dockerfile. 16 | """ 17 | # Reemplazar variables en los archivos base 18 | base_files["README.md"] = base_files["README.md"].replace( 19 | "{project_name}", project_name 20 | ) 21 | base_files["Dockerfile"] = base_files["Dockerfile"].replace( 22 | "{python_version}", python_version 23 | ) 24 | base_files["mkdocs.yml"] = base_files["mkdocs.yml"].replace( 25 | "{project_name}", project_name 26 | ) 27 | 28 | # Crear directorios base 29 | for directory in directories: 30 | os.makedirs(os.path.join(project_name, directory), exist_ok=True) 31 | 32 | for file, content in base_files.items(): 33 | file_path = os.path.join(project_name, file) 34 | os.makedirs(os.path.dirname(file_path), exist_ok=True) 35 | with open(file_path, "w") as f: 36 | f.write(content) 37 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Projectify! 2 | 3 | Welcome to **Projectify!**, the ultimate tool for quickly and efficiently setting up Python project structures. 4 | 5 | ## What is Projectify!? 6 | 7 | Projectify! is a tool that helps you generate and configure the structure of a Python project with all the necessary configurations, including virtual environment setup, IDE configuration, Git integration, and automatic documentation generation with MkDocs. 8 | 9 | ## Key Features 10 | 11 | - Automatic virtual environment setup with uv. 12 | - Git integration. 13 | - Initial configuration of linter and formatter with Ruff. 14 | - Automatic documentation generation with MkDocs. 15 | - Initial Test setup with Pytest. 16 | - Code quality checks with pre-commit hooks. 17 | - Easy project management with Makefile commands. 18 | - Dockerfile setup for containerization. 19 | 20 | ## How to Get Started? 21 | 22 | To start using Projectify!, follow our [Installation Guide](install.md). 23 | 24 | ## Roadmap 25 | 26 | For upcoming features and long-term plans, check out our [Roadmap](roadmap.md). 27 | 28 | ## Contributing 29 | 30 | If you are interested in contributing to Projectify!, please read our [Contributing Guide](contributing.md). 31 | 32 | ## License 33 | 34 | Projectify! is licensed under the [MIT License](license.md). 35 | 36 | --- 37 | 38 | **Note:** This project is still under development. Please refer to the [Roadmap](roadmap.md) for upcoming features and releases. 39 | 40 | --- 41 | 42 | [Back to Top](#projectify) 43 | -------------------------------------------------------------------------------- /docs/roadmap.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | ## Upcoming Releases 4 | 5 | ### v0.2.0 - October 2024 6 | 7 | - **New Features:** 8 | - Advanced template support. 9 | - Integration with CI/CD for automatic deployments. 10 | - Documentation improvements with detailed usage examples. 11 | - Code Inspector integration for code quality checks and security analysis. 12 | 13 | ### v0.3.0 - December 2024 14 | 15 | - **New Features:** 16 | - Security analysis integration using Bandit. 17 | - Performance optimization for handling large projects. 18 | - Extended IDE support (e.g., integration with additional IDEs). 19 | 20 | ## Long-Term Plans 21 | 22 | ### Version 1.0 - Target: Q2 2025 23 | 24 | - **Stabilization:** 25 | - Stabilize all existing features and ensure compatibility across platforms. 26 | - Comprehensive documentation covering all aspects of the tool. 27 | - Establish a strong community for support and contributions. 28 | 29 | ### Future Ideas 30 | 31 | - **Template Marketplace:** 32 | - Allow users to create and share custom project templates. 33 | - Integration with a template marketplace for easy access and use. 34 | 35 | - **Project Dashboard:** 36 | - Introduce a project management dashboard within the tool. 37 | - Track progress, manage tasks, and monitor project health. 38 | 39 | - **Enhanced Git Integration:** 40 | - Provide advanced Git workflows and integration with popular Git services. 41 | - Include GitHub Actions or GitLab CI templates out-of-the-box. 42 | 43 | - **Visual Studio Code Extension:** 44 | - Develop an extension for Visual Studio Code to enhance the IDE integration. 45 | - Provide features like project creation, linting, and debugging directly from the editor. 46 | -------------------------------------------------------------------------------- /docs/commands.md: -------------------------------------------------------------------------------- 1 | # Projectify! Commands 2 | 3 | **Projectify!** offers a variety of commands to facilitate the setup and management of your Python projects. 4 | 5 | | Command | Description | 6 | | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | 7 | | `projectify init` | Creates a new project with the basic structure. | 8 | | `projectify clean` | Cleans the files generated by the project (build, dist, etc.). | 9 | | `projectify install-dependencies` | Installs dependencies from the `requirements.txt` file inside the virtual environment. | 10 | | `projectify run-tests` | Runs the tests located in the `tests/` folder. | 11 | | `projectify lint` | Runs Ruff as a linter to check the code. | 12 | | `projectify format` | Formats the code using Ruff. | 13 | | `projectify generate-docs` | Automatically generates documentation using MkDocs. | 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Projectify! 2 | 3 | # Variables 4 | VERSION_PART := patch 5 | PACKAGE_NAME := projectify 6 | 7 | # Determina el sistema operativo (Windows o no) 8 | ifeq ($(OS),Windows_NT) 9 | RM := rmdir /S /Q 10 | SHELL := cmd 11 | FIND := findstr 12 | else 13 | RM := rm -rf 14 | SHELL := /bin/sh 15 | FIND := grep 16 | endif 17 | 18 | # Incrementa la versión del paquete 19 | bump_version: 20 | @bump2version $(VERSION_PART) --allow-dirty || echo "Error: bump2version command failed." 21 | 22 | # Verifica si el directorio de trabajo está limpio (Git) 23 | check_git_clean: 24 | @if git status --porcelain | $(FIND) . ; then \ 25 | echo "Error: Git working directory is not clean."; \ 26 | echo "Please commit or stash your changes before running this command."; \ 27 | exit 1; \ 28 | else \ 29 | echo "Git working directory is clean."; \ 30 | fi 31 | 32 | # Crea y envía un tag al repositorio 33 | tag_release: bump_version 34 | @git tag $(shell git describe --tags --abbrev=0) || echo "Error: Failed to create tag." 35 | @git push origin $(shell git describe --tags --abbrev=0) || echo "Error: Failed to push tag." 36 | 37 | # Verifica la configuración del paquete 38 | check: 39 | @python setup.py check || echo "Error: setup.py check failed." 40 | 41 | # Limpia los archivos generados por la construcción 42 | clean: 43 | @$(RM) build dist $(PACKAGE_NAME).egg-info || echo "Nothing to clean." 44 | 45 | # Construye el paquete (sdist y bdist_wheel) 46 | build: clean 47 | @python setup.py sdist bdist_wheel || echo "Error: Failed to build the package." 48 | 49 | # Sube el paquete a PyPI 50 | upload: build 51 | @twine upload dist/* || echo "Error: Failed to upload to PyPI." 52 | 53 | # Publica automáticamente una nueva versión 54 | release: check_git_clean tag_release check upload 55 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "projectify" 7 | version = "0.1.19" 8 | description = "A simple tool to create a Python project structure" 9 | readme = "README.md" 10 | authors = [{ name = "CyberIngeniero", email = "npinoa.ai@gmail.com" }] 11 | requires-python = ">=3.8" 12 | keywords = ["project", "structure", "python"] 13 | classifiers = [ 14 | "Development Status :: 4 - Beta", 15 | "Environment :: Console", 16 | "Intended Audience :: Developers", 17 | "Operating System :: OS Independent", 18 | "License :: OSI Approved :: MIT License", 19 | "Programming Language :: Python", 20 | "Programming Language :: Python :: 3.8", 21 | "Programming Language :: Python :: 3.9", 22 | "Programming Language :: Python :: 3.10", 23 | "Programming Language :: Python :: 3.11", 24 | "Programming Language :: Python :: 3.12", 25 | "Programming Language :: Python :: 3.13", 26 | "Programming Language :: Python :: 3 :: Only", 27 | "Topic :: Software Development :: Libraries", 28 | "Topic :: Software Development :: Libraries :: Python Modules", 29 | "Topic :: Utilities", 30 | ] 31 | dependencies = [ 32 | "colorama", 33 | "art", 34 | "packaging" 35 | ] 36 | 37 | [project.urls] 38 | Repository = "https://github.com/CyberIngeniero/projectify" 39 | Documentation = "https://github.com/CyberIngeniero/projectify" 40 | 41 | [project.scripts] 42 | projectify = "projectify.core:main" 43 | 44 | [tool.flake8] 45 | exclude = "__init__.py" 46 | max-line-length = 127 47 | ignore = ["E203", "E501", "W503"] 48 | 49 | [tool.ruff] 50 | exclude = ["__init__.py"] 51 | 52 | [tool.ruff.lint] 53 | select = ["E4", "E7", "E9", "F"] 54 | 55 | [tool.ruff.format] 56 | quote-style = "double" 57 | indent-style = "space" 58 | skip-magic-trailing-comma = false 59 | line-ending = "auto" 60 | 61 | [tool.pytest.ini_options] 62 | testpaths = ["tests"] 63 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## v0.1.8 - September 2024 6 | 7 | - Initial public release of Projectify!. 8 | - Basic project structure creation for Python projects. 9 | - Automatic virtual environment setup. 10 | - Git repository initialization. 11 | - IDE configuration support (VSCode, PyCharm, and others). 12 | - Initial configuration of linter and formatter with Ruff. 13 | - Automatic documentation generation with MkDocs. 14 | 15 | ## v0.1.7 - August 2024 16 | 17 | - Added support for pytest integration. 18 | - Improved error handling and user prompts. 19 | - Enhanced project setup with additional configurations. 20 | 21 | ## v0.1.6 - July 2024 22 | 23 | - Introduced support for custom templates. 24 | - Added new commands for cleaning project directories and running tests. 25 | - Refined virtual environment handling across different OS platforms. 26 | 27 | ## v0.1.5 - June 2024 28 | 29 | - Added Ruff linter and formatter integration. 30 | - Simplified configuration steps for project setup. 31 | - Improved command-line interface (CLI) experience. 32 | 33 | ## v0.1.4 - May 2024 34 | 35 | - Added support for multiple Python versions during project setup. 36 | - Enhanced project initialization with pre-defined structure templates. 37 | - Minor bug fixes and performance improvements. 38 | 39 | ## v0.1.3 - April 2024 40 | 41 | - Improved installation script and dependency management. 42 | - Added support for custom project name input. 43 | - Enhanced compatibility with various operating systems. 44 | 45 | ## v0.1.2 - March 2024 46 | 47 | - Initial CLI interface and basic project structure setup. 48 | - Added color-coded terminal output for better user experience. 49 | - Added support for Git repository initialization. 50 | 51 | ## v0.1.1 - February 2024 52 | 53 | - Introduced basic command structure for project setup. 54 | - Integrated initial version of the tool with basic functionality. 55 | -------------------------------------------------------------------------------- /.github/workflows/publish-to-pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | build: 10 | name: Build distribution 📦 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up Python 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: "3.x" 19 | - name: Install pypa/build 20 | run: | 21 | python -m pip install --upgrade pip 22 | pip install build --user 23 | - name: Build a binary wheel and a source tarball 24 | run: python -m build 25 | - name: Store the distribution packages 26 | uses: actions/upload-artifact@v3 27 | with: 28 | name: python-package-distributions 29 | path: dist/ 30 | 31 | publish-to-testpypi: 32 | name: Publish Python 🐍 distribution 📦 to TestPyPI 33 | needs: build 34 | runs-on: ubuntu-latest 35 | environment: 36 | name: testpypi 37 | url: https://test.pypi.org/project/projectify/ 38 | permissions: 39 | id-token: write # Obligatorio para la publicación de confianza 40 | steps: 41 | - name: Download all the dists 42 | uses: actions/download-artifact@v3 43 | with: 44 | name: python-package-distributions 45 | path: dist/ 46 | - name: Publish distribution 📦 to TestPyPI 47 | uses: pypa/gh-action-pypi-publish@release/v1 48 | with: 49 | repository-url: https://test.pypi.org/legacy/ 50 | password: ${{ secrets.TEST_PYPI }} 51 | 52 | publish-to-pypi: 53 | name: Publish Python 🐍 distribution 📦 to PyPI 54 | if: startsWith(github.ref, 'refs/tags/') # Solo publica en PyPI en los pushes con tags 55 | needs: build 56 | runs-on: ubuntu-latest 57 | environment: 58 | name: pypi 59 | url: https://pypi.org/project/projectify/ # Reemplaza con el nombre de tu proyecto 60 | permissions: 61 | id-token: write # Obligatorio para la publicación de confianza 62 | steps: 63 | - name: Download all the dists 64 | uses: actions/download-artifact@v3 65 | with: 66 | name: python-package-distributions 67 | path: dist/ 68 | - name: Publish distribution 📦 to PyPI 69 | uses: pypa/gh-action-pypi-publish@release/v1 70 | with: 71 | password: ${{ secrets.PIPY_PROJECTIFY }} 72 | -------------------------------------------------------------------------------- /projectify/dashboard/modules/dashboard.py: -------------------------------------------------------------------------------- 1 | # dashboard.py 2 | import altair as alt 3 | import matplotlib.pyplot as plt 4 | import seaborn as sns 5 | import streamlit as st 6 | 7 | 8 | def display_total_downloads(df): 9 | st.subheader("Gráfico de Descargas Totales") 10 | plt.figure(figsize=(10, 6)) 11 | sns.lineplot(x="date", y="downloads", data=df, marker="o") 12 | plt.title("Descargas Totales a lo Largo del Tiempo") 13 | plt.xlabel("Fecha") 14 | plt.ylabel("Número de Descargas") 15 | st.pyplot(plt) 16 | 17 | 18 | def display_recent_downloads(recent_downloads_df): 19 | st.subheader("Descargas recientes") 20 | if not recent_downloads_df.empty: 21 | st.table(recent_downloads_df) 22 | 23 | 24 | def display_overall_downloads(overall_downloads_df): 25 | st.subheader("Descargas totales a lo largo del tiempo") 26 | if not overall_downloads_df.empty: 27 | overall_chart = ( 28 | alt.Chart(overall_downloads_df) 29 | .mark_line() 30 | .encode(x="date:T", y="downloads:Q", color="category:N") 31 | .properties(width=700, height=400) 32 | ) 33 | st.altair_chart(overall_chart) 34 | 35 | 36 | def display_downloads_by_python_major(python_major_df): 37 | st.subheader("Descargas por versión mayor de Python") 38 | if not python_major_df.empty: 39 | python_major_chart = ( 40 | alt.Chart(python_major_df) 41 | .mark_line() 42 | .encode(x="date:T", y="downloads:Q", color="category:N") 43 | .properties(width=700, height=400) 44 | ) 45 | st.altair_chart(python_major_chart) 46 | 47 | 48 | def display_downloads_by_python_minor(python_minor_df): 49 | st.subheader("Descargas por versión menor de Python") 50 | if not python_minor_df.empty: 51 | python_minor_chart = ( 52 | alt.Chart(python_minor_df) 53 | .mark_line() 54 | .encode(x="date:T", y="downloads:Q", color="category:N") 55 | .properties(width=700, height=400) 56 | ) 57 | st.altair_chart(python_minor_chart) 58 | 59 | 60 | def display_downloads_by_system(system_downloads_df): 61 | st.subheader("Descargas por sistema operativo") 62 | if not system_downloads_df.empty: 63 | system_chart = ( 64 | alt.Chart(system_downloads_df) 65 | .mark_line() 66 | .encode(x="date:T", y="downloads:Q", color="category:N") 67 | .properties(width=700, height=400) 68 | ) 69 | st.altair_chart(system_chart) 70 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # API Documentation 2 | 3 | This section provides an overview of the main functions available in the `Projectify!` tool. Each function is explained with its purpose, parameters, and usage examples. 4 | 5 | ## Functions 6 | 7 | ### `get_int_input(prompt, min_val, max_val)` 8 | 9 | **Description:** 10 | 11 | Prompts the user for an integer input within a specified range. 12 | 13 | **Parameters:** 14 | 15 | - `prompt` (str): The message to display to the user when asking for input. 16 | - `min_val` (int): The minimum acceptable value for the input. 17 | - `max_val` (int): The maximum acceptable value for the input. 18 | 19 | **Returns:** 20 | 21 | - `int`: The user's input, which is an integer within the specified range. 22 | 23 | **Example:** 24 | 25 | ```python 26 | choice = get_int_input("Enter your choice: ", 1, 5) 27 | ``` 28 | 29 | --- 30 | 31 | ### clean_project() 32 | 33 | **Description:** 34 | 35 | Cleans up generated directories such as dist, build, and *.egg-info. 36 | 37 | **Usage:** 38 | 39 | ```python 40 | clean_project() 41 | ``` 42 | 43 | --- 44 | 45 | ### install_dependencies() 46 | 47 | **Description:** 48 | 49 | Installs the dependencies listed in the requirements.txt file using uv inside a virtual environment if it exists. 50 | 51 | **Usage:** 52 | 53 | ```python 54 | install_dependencies() 55 | ``` 56 | 57 | --- 58 | 59 | ### run_tests() 60 | 61 | **Description:** 62 | 63 | Runs the tests using pytest if the tests folder and the test configuration file exist. 64 | 65 | **Usage:** 66 | 67 | ```python 68 | run_tests() 69 | ``` 70 | 71 | --- 72 | 73 | ### run_linter() 74 | 75 | **Description:** 76 | 77 | Runs the ruff linter on the project codebase. 78 | 79 | **Usage:** 80 | 81 | ```python 82 | run_linter() 83 | ``` 84 | 85 | --- 86 | 87 | ### format_code() 88 | 89 | **Description:** 90 | 91 | Formats the project codebase using ruff with the --fix option. 92 | 93 | **Usage:** 94 | 95 | ```python 96 | format_code() 97 | ``` 98 | 99 | --- 100 | 101 | ### generate_docs() 102 | 103 | **Description:** 104 | 105 | Generates the project documentation using mkdocs. 106 | 107 | **Usage:** 108 | 109 | ```python 110 | generate_docs() 111 | ``` 112 | 113 | --- 114 | 115 | ### Init() 116 | 117 | **Description:** 118 | 119 | This is the entry point for the project setup process. It prints the header, checks and installs dependencies, and then sets up the project based on user input. 120 | 121 | **Usage:** 122 | 123 | ```python 124 | Init() 125 | ``` 126 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions (FAQs) 2 | 3 | ## Which Python versions are compatible with Projectify!? 4 | 5 | **Projectify!** is compatible with Python 3.8 and above. 6 | 7 | ## How can I customize the project structure? 8 | 9 | You can customize the initial structure by modifying the configuration files generated in your project. 10 | 11 | ## How can I add more features to the project? 12 | 13 | You can add more features to the project by modifying the configuration files generated in your project. You can also add your own custom scripts and configurations to the project. 14 | 15 | ## How can I contribute to the project? 16 | 17 | You can contribute to the project by submitting a pull request on the GitHub repository. 18 | 19 | ## How can I report a bug or request a feature? 20 | 21 | You can report a bug or request a feature by opening an issue on the GitHub repository. 22 | 23 | ## How can I contact the project maintainers? 24 | 25 | You can contact the project maintainers by sending an email to [npinoa.ai@gmail.com](mailto:npinoa.ai@gmail.com). 26 | 27 | ## How can I get help with using Projectify!? 28 | 29 | You can get help with using **Projectify!** by reading the documentation or opening an issue on the GitHub repository. You can also join the community on Discord for more support and discussions. 30 | 31 | ## How can I contribute to the documentation? 32 | 33 | You can contribute to the documentation by submitting a pull request on the GitHub repository. The documentation is written in Markdown and is located in the `docs` directory of the repository. 34 | 35 | ## How can I install the project dependencies? 36 | 37 | You can install the project dependencies by running the `install_dependencies()` function. This will install the dependencies listed in the `requirements.txt` file using `uv` inside a virtual environment if it exists. If a virtual environment does not exist, it will install the dependencies globally using `pip`. 38 | 39 | ## How can I run the tests for the project? 40 | 41 | You can run the tests for the project by running the `run_tests()` function. This will run the tests using `pytest` if the tests folder and the test configuration file exist. 42 | 43 | ## How can I run the linter on the project codebase? 44 | 45 | You can run the linter on the project codebase by running the `run_linter()` function. This will run the `ruff` linter on the project codebase. 46 | 47 | ## How can I format the project codebase? 48 | 49 | You can format the project codebase by running the `format_code()` function. This will format the project codebase using `ruff` with the `--fix` option. This will fix any formatting issues in the codebase and make it consistent with the project's style guide. 50 | 51 | ## How can I generate the project documentation? 52 | 53 | You can generate the project documentation by running the `generate_docs()` function. This will generate the project documentation using `mkdocs`. The documentation will be generated in the `site` directory of the project. You can view the documentation by opening the `index.html` file in a web browser. 54 | -------------------------------------------------------------------------------- /projectify/dashboard/modules/api.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import requests 3 | import streamlit as st 4 | 5 | BASE_URL = "https://pypistats.org/api/packages/projectify" 6 | 7 | 8 | def fetch_data(endpoint, params=None): 9 | url = f"{BASE_URL}/{endpoint}" 10 | response = requests.get(url, params=params) 11 | if response.status_code == 200: 12 | return response.json() 13 | else: 14 | st.error(f"Error fetching data from {endpoint}: {response.status_code}") 15 | return None 16 | 17 | 18 | def get_recent_downloads(): 19 | data = fetch_data("recent") 20 | if data: 21 | return pd.DataFrame.from_dict( 22 | data["data"], orient="index", columns=["Descargas"] 23 | ).reset_index() 24 | return pd.DataFrame() 25 | 26 | 27 | def get_overall_downloads(include_mirrors=False): 28 | params = {"mirrors": "true"} if include_mirrors else None 29 | data = fetch_data("overall", params=params) 30 | if data: 31 | df = pd.DataFrame(data["data"]) 32 | df["date"] = pd.to_datetime(df["date"]) 33 | return df 34 | return pd.DataFrame() 35 | 36 | 37 | def get_downloads_by_python_major(version=None): 38 | params = {"version": version} if version else None 39 | data = fetch_data("python_major", params=params) 40 | if data: 41 | df = pd.DataFrame(data["data"]) 42 | df["date"] = pd.to_datetime(df["date"]) 43 | return df 44 | return pd.DataFrame() 45 | 46 | 47 | def get_downloads_by_python_minor(version=None): 48 | params = {"version": version} if version else None 49 | data = fetch_data("python_minor", params=params) 50 | if data: 51 | df = pd.DataFrame(data["data"]) 52 | df["date"] = pd.to_datetime(df["date"]) 53 | return df 54 | return pd.DataFrame() 55 | 56 | 57 | def get_downloads_by_system(os_name=None): 58 | params = {"os": os_name} if os_name else None 59 | data = fetch_data("system", params=params) 60 | if data: 61 | df = pd.DataFrame(data["data"]) 62 | df["date"] = pd.to_datetime(df["date"]) 63 | return df 64 | return pd.DataFrame() 65 | 66 | 67 | def get_downloads_by_project_version(): 68 | response = requests.get("https://pypistats.org/api/packages/projectify/overall") 69 | if response.status_code == 200: 70 | data = response.json() 71 | df = pd.DataFrame(data["data"]) 72 | return df[df["category"] != "null"] 73 | else: 74 | return pd.DataFrame(columns=["category", "date", "downloads"]) 75 | 76 | 77 | # Funciones adicionales para agrupar por mes y año 78 | 79 | 80 | def get_monthly_downloads(df): 81 | """ 82 | Agrupa las descargas por mes. 83 | """ 84 | if not df.empty: 85 | df["month"] = df["date"].dt.to_period("M") 86 | monthly_df = df.groupby("month").agg({"downloads": "sum"}).reset_index() 87 | monthly_df["month"] = monthly_df["month"].dt.to_timestamp() 88 | return monthly_df 89 | return pd.DataFrame() 90 | 91 | 92 | def get_yearly_downloads(df): 93 | """ 94 | Agrupa las descargas por año. 95 | """ 96 | if not df.empty: 97 | df["year"] = df["date"].dt.to_period("Y") 98 | yearly_df = df.groupby("year").agg({"downloads": "sum"}).reset_index() 99 | yearly_df["year"] = yearly_df["year"].dt.to_timestamp() 100 | return yearly_df 101 | return pd.DataFrame() 102 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Being respectful of differing viewpoints and experiences 18 | - Gracefully accepting constructive criticism 19 | - Focusing on what is best for the community 20 | - Showing empathy towards other community members 21 | 22 | Examples of unacceptable behavior by participants include: 23 | 24 | - The use of sexualized language or imagery and unwelcome sexual attention or 25 | advances 26 | - Trolling, insulting/derogatory comments, and personal or political attacks 27 | - Public or private harassment 28 | - Publishing others' private information, such as a physical or electronic 29 | address, without explicit permission 30 | - Other conduct which could reasonably be considered inappropriate in a 31 | professional setting 32 | 33 | ## Our Responsibilities 34 | 35 | Project maintainers are responsible for clarifying the standards of acceptable 36 | behavior and are expected to take appropriate and fair corrective action in 37 | response to any instances of unacceptable behavior. 38 | 39 | Project maintainers have the right and responsibility to remove, edit, or 40 | reject comments, commits, code, wiki edits, issues, and other contributions 41 | that are not aligned to this Code of Conduct, or to ban temporarily or 42 | permanently any contributor for other behaviors that they deem inappropriate, 43 | threatening, offensive, or harmful. 44 | 45 | ## Scope 46 | 47 | This Code of Conduct applies both within project spaces and in public spaces 48 | when an individual is representing the project or its community. Examples of 49 | representing a project or community include using an official project e-mail 50 | address, posting via an official social media account, or acting as an appointed 51 | representative at an online or offline event. Representation of a project may be 52 | further defined and clarified by project maintainers. 53 | 54 | ## Enforcement 55 | 56 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 57 | reported by contacting the project team at npinoa.ai@gmail.com. 58 | 59 | All complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.es.md: -------------------------------------------------------------------------------- 1 | # Código de Conducta 2 | 3 | ## Nuestro Compromiso 4 | 5 | En el interés de fomentar un entorno abierto y acogedor, nosotros como colaboradores 6 | y mantenedores nos comprometemos a hacer que la participación en nuestro proyecto y 7 | comunidad sea una experiencia libre de acoso para todos, independientemente de edad, 8 | tamaño corporal, discapacidad, etnia, características sexuales, identidad de género 9 | y expresión, nivel de experiencia, educación, estatus socioeconómico, nacionalidad, 10 | apariencia personal, raza, religión o identidad y orientación sexual. 11 | 12 | ## Nuestras Normas 13 | 14 | Ejemplos de comportamiento que contribuyen a crear un entorno positivo son: 15 | 16 | * Utilizar lenguaje inclusivo 17 | * Ser respetuoso con experiencias y puntos de vista distintos al nuestro 18 | * Aceptar críticas contructivas de forma cortés 19 | * Centrarnos en lo que sea mejor para la comunidad 20 | * Mostrar empatía hacia otros miembros de la comunidad 21 | 22 | Ejemplos de comportamiento inaceptable son: 23 | 24 | * Uso de lenguaje sexualizado o imágenes sexuales, así como avances sexuales 25 | indeseados 26 | * Trolling, comentarios insultantes/despectivos, y ataques personales o políticos 27 | * Acoso público o privado 28 | * Publicar la información privada de terceros, como direcciones físicas o electrónicas, 29 | sin permiso explícito. 30 | * Cualquier conducta que, de forma razonable, se considere inapropiada en un ámbito 31 | profesional. 32 | 33 | 34 | ## Nuestras Responsabilidades 35 | 36 | Los mantenedores del proyecto son responsables de aclarar las normas de 37 | comportamiento aceptable y se espera de ellos que tomen medidas apropiadas 38 | en respuesta a cualquier instancia de comportamiento inaceptable. 39 | 40 | Los mantenedores del proyecto tienen el derecho y la responsabilidad de eliminar, 41 | editar o rechazar comentarios, commits, código, ediciones de wiki, issues y 42 | cualquier otra contribución que incumpla este código de conducta, o de banear 43 | temporal o peramenentemente a cualquier colaborador por cualquier comportamiento 44 | que se considere inapropiado, amenazador, ofensivo o dañino. 45 | 46 | 47 | ## Ámbito 48 | 49 | Este Código de Conducta se aplica tanto en el entorno del proyecto como en espacios 50 | públicos donde un individuo representa al proyecto o a su comunidad. Ejemplos de 51 | representar un proyecto o comunidad incluyen utilizar un e-mail oficial del proyecto, 52 | publicaciones hechas vía una cuenta oficial en una red social, o actuar como un 53 | representante oficial en cualquier evento online u offline. La representación de un 54 | proyecto puede ser ampliada o aclarada por los mantenedores del proyecto. 55 | 56 | ## Aplicación 57 | 58 | Instancias de comportamiento abusivo, acosador o inaceptable en cualquier otro sentido 59 | pueden ser comunicadas al equipo del proyecto, a través de las direcciones de correo 60 | electrónico npinoa.ai@gmail.com. Todas las quejas serán 61 | revisadas e investigadas y resultarán en la respuesta que se considere necesaria y 62 | apropiada según las circunstancias. El equipo del proyecto está obligado a mantener 63 | la confidencialidad de cualquier persona que informe de un incidente. 64 | 65 | Los mantenedores del proyecto que no sigan ni apliquen el Código de Conducta pueden 66 | enfrentarse a repercusiones temporales o permanentes, determinadas por otros miembros 67 | del equipo del proyecto. 68 | 69 | ## Atribución 70 | 71 | Este Código de Conducta está adaptado del [Contributor Covenant][homepage], version 1.4, 72 | disponible en https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 73 | 74 | [homepage]: https://www.contributor-covenant.org 75 | 76 | Para ver respuestas a preguntas comunes sobre este código de conducta, véase 77 | https://www.contributor-covenant.org/faq 78 | -------------------------------------------------------------------------------- /docs/templates.md: -------------------------------------------------------------------------------- 1 | # Projectify! Templates 2 | 3 | **Projectify!** supports the use of YAML templates to customize the setup of your Python projects. This allows you to define reusable configurations that can be quickly applied when starting a new project. 4 | 5 | ## What are Templates? 6 | 7 | Templates in **Projectify!** are predefined configurations that specify the structure, dependencies, and settings for a new project. By using templates, you can ensure consistency across your projects and save time on repetitive setup tasks. 8 | 9 | ## Creating a Template 10 | 11 | Templates are written in YAML format and stored in the `templates` directory within your project or in a central repository for reuse across multiple projects. 12 | 13 | ### Example Template 14 | 15 | Here’s an example of a simple YAML template: 16 | 17 | ```yaml 18 | # Template Name: basic-python-project 19 | 20 | # Project structure 21 | structure: 22 | - src/ 23 | - src/__init__.py 24 | - tests/ 25 | - tests/__init__.py 26 | - README.md 27 | - setup.py 28 | 29 | # Dependencies 30 | dependencies: 31 | - colorama 32 | - pytest 33 | - ruff 34 | 35 | # Optional: Define a Python version 36 | python_version: "3.9" 37 | 38 | # Optional: Initialize Git repository 39 | git_init: true 40 | 41 | # Optional: Add pre-commit hooks 42 | pre_commit_hooks: 43 | - id: ruff 44 | name: Ruff Linter 45 | entry: ruff 46 | language: python 47 | files: \.py$ 48 | - id: black 49 | name: Black Formatter 50 | entry: black 51 | language: python 52 | files: \.py$ 53 | 54 | # Dockerfile setup 55 | dockerfile: 56 | from: python:3.9-slim 57 | workdir: /app 58 | copy: 59 | - requirements.txt . 60 | run: pip install --no-cache-dir -r requirements.txt 61 | cmd: python main.py 62 | 63 | # Makefile setup 64 | makefile: 65 | install: 66 | - pip install -r requirements.txt 67 | test: 68 | - pytest 69 | lint: 70 | - ruff . 71 | ``` 72 | 73 | ## Template Breakdown 74 | 75 | - **structure**: Specifies the directories and files to create in the new project. 76 | - **dependencies**: Lists the Python packages to install in the virtual environment. 77 | - **python_version**: Optionally sets the Python version for the virtual environment. 78 | - **git_init**: If set to `true`, initializes a Git repository in the new project. 79 | - **pre_commit_hooks**: Defines pre-commit hooks to automate code quality checks. 80 | - **dockerfile**: Provides instructions for generating a `Dockerfile` in the project. 81 | - **makefile**: Defines common `Makefile` commands for easy project management. 82 | 83 | ## Using a Template 84 | 85 | To use a template with Projectify!, specify the template name during the project initialization: 86 | 87 | ```bash 88 | projectify init --template basic-python-project 89 | ``` 90 | 91 | This command will create a new project using the basic-python template, generating the structure, installing dependencies, and setting up any other configurations defined in the template. 92 | 93 | ## Best Practices 94 | 95 | - **Reuse Templates**: Store templates in a central repository or share them across teams to promote consistency. 96 | - **Customize as Needed**: Modify templates to suit the specific needs of different projects or teams. 97 | - **Document Templates**: Provide clear documentation for each template, so others can easily understand and use them. 98 | 99 | ## Sharing Templates 100 | 101 | You can share your templates with others by publishing them in a central repository or including them in your project's repository. This allows you to collaborate with others and reuse configurations across different projects. 102 | 103 | ## Final Thoughts 104 | 105 | Templates in Projectify! provide a powerful way to standardize and streamline the setup of Python projects. By defining reusable configurations, you can ensure that every project starts with the right structure, dependencies, and tools, saving time and reducing setup errors. 106 | -------------------------------------------------------------------------------- /projectify/dashboard/main.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import plotly.express as px 3 | import streamlit as st 4 | from modules import ( 5 | get_downloads_by_system, 6 | get_monthly_downloads, 7 | get_overall_downloads, 8 | ) 9 | 10 | # Título y Descripción 11 | st.set_page_config(page_title="Projectify! Dashboard", layout="wide") 12 | st.title("📊 Projectify! Dashboard") 13 | 14 | # Mostrar el total de descargas acumuladas 15 | overall_df = get_overall_downloads() 16 | total_downloads = overall_df["downloads"].sum() 17 | st.write("### Descargas Acumuladas") 18 | st.metric( 19 | label="Descargas Acumuladas", 20 | value=f"{total_downloads:,}", 21 | label_visibility="collapsed", 22 | ) 23 | 24 | # Gráfico de Descargas Totales a lo largo del tiempo (sin filtro de tipo de descarga) 25 | st.write("### Descargas Totales") 26 | fig_total_downloads = px.area( 27 | overall_df, 28 | x="date", 29 | y="downloads", 30 | title="Descargas Totales a lo Largo del Tiempo", 31 | labels={"date": "Fecha", "downloads": "Número de Descargas"}, 32 | line_shape="linear", 33 | markers=True, 34 | ) 35 | fig_total_downloads.update_traces(line=dict(color="royalblue", width=2)) 36 | st.plotly_chart(fig_total_downloads) 37 | 38 | # Gráfico de Descargas Agrupadas por Mes (sin filtro de tipo de descarga) 39 | st.write("### Descargas Agrupadas por Mes") 40 | monthly_df = get_monthly_downloads(overall_df) 41 | 42 | if not monthly_df.empty: 43 | monthly_df["year"] = monthly_df["month"].dt.year 44 | available_years = monthly_df["year"].unique() 45 | selected_year = st.selectbox( 46 | "Seleccionar Año", ["Todos"] + list(available_years), key="monthly_year" 47 | ) 48 | 49 | if selected_year != "Todos": 50 | filtered_monthly_df = monthly_df[monthly_df["year"] == selected_year] 51 | else: 52 | filtered_monthly_df = monthly_df 53 | 54 | filtered_monthly_df["month_name"] = filtered_monthly_df["month"].dt.strftime("%B") 55 | 56 | fig_monthly_downloads = px.line( 57 | filtered_monthly_df, 58 | x="month_name", 59 | y="downloads", 60 | title="Descargas Agrupadas por Mes", 61 | labels={"month_name": "Mes", "downloads": "Número de Descargas"}, 62 | ) 63 | fig_monthly_downloads.update_traces(line=dict(color="blue", width=2)) 64 | st.plotly_chart(fig_monthly_downloads) 65 | else: 66 | st.write( 67 | "No hay datos disponibles para mostrar en el gráfico de descargas agrupadas por mes." 68 | ) 69 | 70 | # Gráfico de Descargas por Sistema Operativo (con filtro de tipo de descarga) 71 | st.write("### Descargas por Sistema Operativo") 72 | 73 | # Filtro: with_mirrors/without_mirrors para gráficos 74 | mirror_filter = st.selectbox( 75 | "Seleccionar Tipo de Descarga", ["Todos"] + list(overall_df["category"].unique()) 76 | ) 77 | 78 | # Filtrar el dataframe general según el filtro seleccionado 79 | if mirror_filter != "Todos": 80 | filtered_overall_df = overall_df[overall_df["category"] == mirror_filter] 81 | else: 82 | filtered_overall_df = overall_df 83 | 84 | os_df = get_downloads_by_system() 85 | 86 | # Filtro de año y mes para el gráfico de sistema operativo 87 | os_df["year"] = pd.to_datetime(os_df["date"]).dt.year 88 | os_df["month"] = pd.to_datetime(os_df["date"]).dt.month_name() 89 | 90 | selected_year_os = st.selectbox( 91 | "Seleccionar Año", ["Todos"] + list(os_df["year"].unique()), key="os_year" 92 | ) 93 | selected_month_os = st.selectbox( 94 | "Seleccionar Mes", ["Todos"] + list(os_df["month"].unique()), key="os_month" 95 | ) 96 | 97 | if selected_year_os != "Todos": 98 | filtered_os_df = os_df[os_df["year"] == selected_year_os] 99 | else: 100 | filtered_os_df = os_df 101 | 102 | if selected_month_os != "Todos": 103 | filtered_os_df = filtered_os_df[filtered_os_df["month"] == selected_month_os] 104 | 105 | fig_os_downloads = px.bar( 106 | filtered_os_df, 107 | x="category", 108 | y="downloads", 109 | labels={"category": "Sistema Operativo", "downloads": "Número de Descargas"}, 110 | color="category", 111 | opacity=1, 112 | ) 113 | fig_os_downloads.update_traces(marker=dict(line=dict(width=0))) 114 | st.plotly_chart(fig_os_downloads) 115 | -------------------------------------------------------------------------------- /projectify/modules/setup_project.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import sys 4 | 5 | from colorama import Fore, Style 6 | 7 | from .dependencies import ( 8 | install_packages, 9 | install_tool, 10 | install_uv, 11 | is_tool_installed, 12 | is_uv_installed, 13 | ) 14 | from .environment import create_virtual_environment 15 | from .git_repository import initialize_git_repo 16 | from .ide_configuration import create_ide_configuration 17 | from .project_structure import create_project_structure 18 | 19 | 20 | def separator(): 21 | """ 22 | The separator function prints a separator line to the console. 23 | """ 24 | print(Fore.MAGENTA + "\n" + "=" * 80 + "\n") 25 | 26 | 27 | def setup_project(project_name, python_version, ide_choice): 28 | """ 29 | This function sets up a new Python project with the specified name, Python version, and IDE choice. 30 | It creates the project structure, installs necessary tools and packages, initializes a Git repository, 31 | sets up a virtual environment, and configures the selected IDE. 32 | 33 | Parameters 34 | ---------- 35 | project_name : str 36 | The name of the project. 37 | python_version : str 38 | The version of Python to be used for the project. 39 | ide_choice : str 40 | The IDE to be used for the project. 41 | """ 42 | # Creando estructura del proyecto 43 | if os.path.exists(project_name): 44 | print( 45 | Fore.RED 46 | + f"El directorio '{project_name}' ya existe. Por favor, elija otro nombre para el proyecto." + Style.RESET_ALL 47 | ) 48 | sys.exit(1) 49 | 50 | separator() 51 | print(Fore.CYAN + "Creando estructura del proyecto..." + Style.RESET_ALL) 52 | create_project_structure(project_name, python_version) 53 | 54 | # Instalando uv 55 | separator() 56 | if not is_uv_installed(): 57 | print(Fore.YELLOW + "uv no está instalado. Instalando uv..." + Style.RESET_ALL) 58 | install_uv() 59 | else: 60 | print(Fore.GREEN + "uv ya está instalado." + Style.RESET_ALL) 61 | 62 | # Instalando git 63 | separator() 64 | if not is_tool_installed("git"): 65 | print(Fore.YELLOW + "git no está instalado. Instalando git..." + Style.RESET_ALL) 66 | install_tool("git") 67 | else: 68 | print(Fore.GREEN + "git ya está instalado." + Style.RESET_ALL) 69 | 70 | # Instalando make 71 | separator() 72 | if platform.system() == "Windows": 73 | if not is_tool_installed("make"): 74 | print(Fore.RED + "make no está instalado." + Style.RESET_ALL) 75 | install_tool("make") 76 | if not is_tool_installed("make"): 77 | print( 78 | Fore.YELLOW 79 | + "Instálelo desde PowerShell con permisos de administrador: choco install make" + Style.RESET_ALL 80 | ) 81 | else: 82 | print(Fore.GREEN + "make ya está instalado." + Style.RESET_ALL) 83 | else: 84 | if not is_tool_installed("make"): 85 | print(Fore.YELLOW + "make no está instalado. Instalando make..." + Style.RESET_ALL) 86 | install_tool("make") 87 | else: 88 | print(Fore.GREEN + "make ya está instalado." + Style.RESET_ALL) 89 | 90 | # Creando entorno virtual 91 | separator() 92 | print(Fore.CYAN + "Creando entorno virtual..." + Style.RESET_ALL) 93 | create_virtual_environment(project_name, python_version) 94 | 95 | # Inicializando repositorio Git 96 | separator() 97 | print(Fore.CYAN + "Inicializando repositorio Git..." + Style.RESET_ALL) 98 | initialize_git_repo(project_name) 99 | 100 | # Instalando dependencias 101 | separator() 102 | print(Fore.CYAN + "Instalando paquetes necesarios..." + Style.RESET_ALL) 103 | install_packages( 104 | project_name, ["ruff", "pre-commit", "mkdocs", "mkdocstrings", "pytest"] 105 | ) 106 | 107 | # Configurando IDE 108 | separator() 109 | print(Fore.CYAN + "Configurando IDE seleccionado..." + Style.RESET_ALL) 110 | create_ide_configuration(project_name, ide_choice) 111 | 112 | # Mensaje final 113 | separator() 114 | print(Fore.CYAN + f"Proyecto {project_name} creado y configurado exitosamente." + Style.RESET_ALL) 115 | -------------------------------------------------------------------------------- /projectify/modules/environment.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import re 3 | import subprocess 4 | import sys 5 | 6 | from colorama import Fore, Style 7 | from packaging import version 8 | 9 | from .dependencies import ensure_uv_installed 10 | 11 | 12 | def get_installed_python_versions(): 13 | """ 14 | This function retrieves the installed Python versions on the system. 15 | 16 | Returns 17 | ------- 18 | list 19 | A list of installed Python versions in descending order. 20 | Each version is a string in the format 'major.minor.patch'. 21 | If no Python versions are found, an empty list is returned. 22 | """ 23 | versions = set() 24 | try: 25 | if platform.system() == "Windows": 26 | result = subprocess.run( 27 | ["py", "-0p"], stdout=subprocess.PIPE, stderr=subprocess.PIPE 28 | ) 29 | output = result.stdout.decode("utf-8") 30 | matches = re.findall(r" -V:(\d+\.\d+)", output) 31 | versions.update(matches) 32 | else: 33 | possible_commands = ["python3", "python"] 34 | for cmd in possible_commands: 35 | result = subprocess.run( 36 | [cmd, "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE 37 | ) 38 | output = ( 39 | result.stdout.decode("utf-8").strip() 40 | or result.stderr.decode("utf-8").strip() 41 | ) 42 | match = re.search(r"(\d+\.\d+\.\d+)", output) 43 | if match: 44 | versions.add(match.group(1)) 45 | except FileNotFoundError: 46 | pass 47 | 48 | return sorted(versions, key=lambda v: version.parse(v), reverse=True) 49 | 50 | 51 | def create_virtual_environment(project_name, python_version): 52 | """ 53 | This function sets up a virtual environment for a project with a specified Python version. 54 | 55 | Parameters 56 | ---------- 57 | project_name : str 58 | The name of the project. 59 | python_version : str 60 | The desired Python version for the virtual environment. 61 | """ 62 | # check if uv is installed 63 | ensure_uv_installed() 64 | 65 | result = subprocess.run( 66 | ["uv", "venv", f"--python=python{python_version}"], 67 | cwd=project_name, 68 | stdout=subprocess.PIPE, 69 | stderr=subprocess.PIPE, 70 | ) 71 | if result.returncode != 0: 72 | print( 73 | Fore.RED 74 | + f"No se encontró el intérprete para Python {python_version}. Intentando encontrar la versión más cercana disponible..." 75 | + Style.RESET_ALL 76 | ) 77 | closest_version = find_closest_python_version(python_version) 78 | if closest_version: 79 | print(Fore.YELLOW + f"Utilizando Python {closest_version} en su lugar." + Style.RESET_ALL) 80 | subprocess.run( 81 | ["uv", "venv", f"--python=python{closest_version}"], cwd=project_name 82 | ) 83 | else: 84 | print( 85 | Fore.RED 86 | + "No se pudo encontrar una versión de Python cercana. Por favor, elija otra versión." 87 | + Style.RESET_ALL 88 | ) 89 | sys.exit(1) 90 | 91 | 92 | def find_closest_python_version(target_version): 93 | """ 94 | This function finds the closest installed Python version to the target version. 95 | 96 | Parameters 97 | ---------- 98 | target_version : str 99 | The target Python version to compare with the installed versions. 100 | This should be a string in the format of 'major.minor.patch'. 101 | 102 | Returns 103 | ------- 104 | str 105 | The closest installed Python version to the target version. 106 | This is also a string in the format of 'major.minor.patch'. 107 | """ 108 | available_versions = get_installed_python_versions() 109 | target_version = version.parse(target_version) 110 | closest_version = None 111 | smallest_diff = None 112 | for v in available_versions: 113 | v_parsed = version.parse(v) 114 | diff = abs(target_version - v_parsed) 115 | if smallest_diff is None or diff < smallest_diff: 116 | smallest_diff = diff 117 | closest_version = v 118 | return closest_version 119 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Projectify! 🤖📦 2 | 3 | Thank you for your interest in contributing to Projectify! Whether you're here to report issues, suggest improvements, or contribute code, your help is greatly appreciated. This document provides guidelines and best practices for contributing to the project. 4 | 5 | --- 6 | 7 | ## Common Considerations 8 | 9 | - **Avoid Redundant Work**: Before starting work on a new feature or bug fix, please check existing issues and projects to avoid duplicating effort. 10 | - **Open an Issue**: For significant changes or new features, please open an issue first to discuss your ideas with the community and ensure alignment. 11 | - **Minimal Changes**: Limit your changes to the necessary lines to avoid conflicts and simplify the review process. 12 | - **Run `pip install -r requirements-dev.txt`**: This ensures you have all the development dependencies, including linters and formatters, to maintain code consistency. 13 | - **Write Tests**: Whenever possible, add tests for your changes. Tests help ensure that new code works as expected and prevent future regressions. 14 | 15 | ## New Feature 16 | 17 | 1. **Check Existing Issues**: Ensure there isn't an existing issue or pull request for the feature you want to add. 18 | 2. **Open an Issue**: Describe the feature, the files you plan to modify, and any other relevant details. 19 | 3. **Community Feedback**: Wait for feedback and approval from the community or maintainers. 20 | 4. **Fork the Project**: Fork the repository to your GitHub account. 21 | 5. **Create a Branch**: Create a new branch using the naming conventions below. 22 | 6. **Write Code and Commit**: Make regular commits following the commit message conventions. 23 | 7. **Pull Request**: Submit a pull request to the `main` branch. Use the issue title or a similar one as the pull request title. In the description, link the related issue and use "close #issueNumber" to automatically close the issue upon merging. 24 | 25 | ## Conventions 26 | 27 | ### Git Branch Naming 28 | 29 | Use the following naming conventions for branches: 30 | 31 | - `feat/short-description` for new features 32 | - `fix/short-description` for bug fixes 33 | - `docs/short-description` for documentation improvements 34 | - `chore/short-description` for maintenance tasks 35 | - `refactor/short-description` for code refactoring 36 | 37 | > 📘 Example branch naming: 38 | > `feat/add-login-functionality` 39 | > `fix/typo-in-readme` 40 | > `docs/update-contributing-guide` 41 | > `chore/update-dependencies` 42 | > `refactor/improve-code-readability` 43 | 44 | ### Git Commit Messages 45 | 46 | Follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification: 47 | 48 | - Use the present tense ("add feature" not "added feature"). 49 | - Use the imperative mood ("move cursor to..." not "moves cursor to..."). 50 | - Limit the subject line to 72 characters or less. 51 | - Reference issues and pull requests liberally in the body of the commit message. 52 | - Structure your commit messages as follows: 53 | 54 | ```plaintext 55 | (): 56 | 57 | 58 | 59 | 60 | ``` 61 | 62 | > 📘 Example commit message: 63 | > feat: add virtual environment setup script 64 | > Adds a script to automate the setup of a virtual environment for the project. This improves the onboarding experience for new contributors and ensures consistency across development environments. 65 | > Closes #123 66 | 67 | ### Code Quality 68 | 69 | Adhere to clean code principles and ensure your code is readable, maintainable, and well-documented. 70 | 71 | If you use `VS Code`, consider installing the following extensions: 72 | 73 | - **Pylint**: Helps enforce coding standards and detect errors. 74 | - **Python Docstring Generator**: Simplifies the creation of docstrings for your functions and classes. 75 | - **GitLens**: Enhances the Git capabilities within VS Code. 76 | 77 | For other IDEs, look for similar extensions to maintain code quality and consistency. 78 | 79 | ## Running Tests 80 | 81 | 1. **Install Test Dependencies**: Make sure to install the development dependencies: 82 | 83 | ```sh 84 | pip install -r requirements-dev.txt 85 | ``` 86 | 87 | 2. **Run Tests**: Use `pytest` to run the tests: 88 | 89 | ```sh 90 | pytest 91 | ``` 92 | 93 | ## Code of Conduct 94 | 95 | By participating in this project, you agree to abide by our [Code of Conduct](CODE_OF_CONDUCT.md). Please read it to understand the expected behavior in our community. 96 | 97 | --- 98 | 99 | Thank you for contributing to Projectify! Your help is essential in making this project better for everyone. 100 | 101 | Happy coding! 🚀 102 | -------------------------------------------------------------------------------- /projectify/models/base_files.py: -------------------------------------------------------------------------------- 1 | base_files = { 2 | "pyproject.toml": """[project] 3 | name = "{ project_name }" 4 | version = "0.1.0" 5 | description = "" 6 | readme = "README.md" 7 | requires-python = "{ python_version }" 8 | dependencies = [] 9 | 10 | [tool.ruff] 11 | line-length = 88 12 | indent-width = 4 13 | include = ["pyproject.toml"] 14 | extend-include = ["*.ipynb"] 15 | 16 | [tool.ruff.lint] 17 | select = ["E4", "E7", "E9", "F"] 18 | fixable = ["ALL"] 19 | unfixable = [] 20 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" 21 | 22 | [tool.ruff.lint.pydocstyle] 23 | convention = "numpy" 24 | 25 | [tool.ruff.format] 26 | exclude = ["__init__.py"] 27 | quote-style = "single" 28 | skip-magic-trailing-comma = false 29 | line-ending = "auto" 30 | docstring-code-format = true 31 | docstring-code-line-length = "dynamic" 32 | 33 | [tool.pytest.ini_options] 34 | testpaths = ["tests"] 35 | addopts = "-v -ra -q" 36 | log_cli = true 37 | log_level = "DEBUG" 38 | log_format = "%(asctime)s %(levelname)s %(message)s" 39 | log_date_format = "%Y-%m-%d %H:%M:%S" 40 | log_file = "logs/pytest-logs.txt" 41 | log_file_level = "INFO" 42 | filterwarnings = "ignore" 43 | 44 | """, 45 | ".gitignore": """*.venv/ 46 | app/__pycache__/ 47 | modules/__pycache__/ 48 | utils/__pycache__/ 49 | notebooks/.ipynb_checkpoints/ 50 | data/ 51 | .vscode 52 | .idea 53 | """, 54 | "README.md": """# {project_name} 55 | 56 | ## Description 57 | 58 | ## Installation 59 | 60 | ## Usage 61 | 62 | ## Contributing 63 | 64 | ## License 65 | 66 | ## Contact 67 | 68 | """, 69 | "Makefile": """SHELL := /bin/bash 70 | 71 | # Determine the OS and set the activation command accordingly 72 | ifeq ($(OS),Windows_NT) 73 | ACTIVATE = .venv\\Scripts\\activate 74 | else 75 | ACTIVATE = source .venv/bin/activate 76 | endif 77 | 78 | .PHONY: activate install lint 79 | 80 | activate: ## Activate the virtual environment 81 | \t$(ACTIVATE) 82 | 83 | install: activate ## Install the dependencies 84 | \tuv pip install -r requirements.txt 85 | 86 | lint: activate ## Run Ruff to lint the code 87 | \truff . 88 | 89 | docs: activate 90 | \tmkdocs serve 91 | 92 | generate-docs: activate 93 | \tpython scripts/generate_docs.py 94 | """, 95 | "app/main.py": """#! /usr/bin/env python 96 | import os 97 | import sys 98 | 99 | if __name__ == "__main__": 100 | print("Hello, World!") 101 | """, 102 | "modules/__init__.py": "", 103 | "utils/__init__.py": "", 104 | ".env": "", 105 | ".pre-commit-config.yaml": """repos: 106 | - repo: https://github.com/charliermarsh/ruff-pre-commit 107 | rev: v0.0.241 108 | hooks: 109 | - id: ruff 110 | - repo: https://github.com/pre-commit/pre-commit-hooks 111 | rev: v3.4.0 112 | hooks: 113 | - id: trailing-whitespace 114 | - id: end-of-file-fixer 115 | - id: check-yaml 116 | - id: check-json 117 | """, 118 | "Dockerfile": """FROM python:{python_version}-slim 119 | WORKDIR /app 120 | COPY . . 121 | RUN pip install --no-cache-dir -r requirements.txt 122 | CMD ["python", "./app/main.py"] 123 | """, 124 | ".dockerignore": """.venv 125 | app/__pycache__/ 126 | modules/__pycache__/ 127 | utils/__pycache__/ 128 | notebooks/.ipynb_checkpoints/ 129 | .vscode 130 | .idea 131 | """, 132 | "mkdocs.yml": """site_name: {project_name} Documentation 133 | nav: 134 | - Home: index.md 135 | - Notebooks: notebooks.md 136 | 137 | plugins: 138 | - mkdocstrings 139 | 140 | theme: readthedocs 141 | """, 142 | "docs/index.md": """# Welcome to the documentation\n 143 | ## Getting Started 144 | ### Prerequisites 145 | - Python 3.8 or higher 146 | 147 | ### Installation 148 | 1. Clone the repository 149 | 2. Create a virtual environment 150 | 3. Install the dependencies 151 | 152 | ### Usage 153 | - Run the app 154 | - Explore the notebooks 155 | - Check the documentation 156 | """, 157 | "scripts/generate_docs.py": """import os 158 | def generate_docs_for_folder(folder_name): 159 | docs_folder = 'docs' 160 | folder_path = os.path.join(docs_folder, folder_name) 161 | os.makedirs(folder_path, exist_ok=True) 162 | 163 | # Listar archivos .py en la carpeta especificada 164 | folder_files = [f for f in os.listdir(folder_name) if f.endswith('.py') and not f.startswith('__')] 165 | 166 | # Crear el contenido del archivo .md 167 | content_lines = [f"# {folder_name.capitalize()} Documentation\n"] 168 | for file in folder_files: 169 | module_name = file.replace('.py', '') 170 | content_lines.append(f"::: {folder_name}.{module_name}\n") 171 | 172 | # Guardar el archivo .md en la carpeta de documentación 173 | md_file_path = os.path.join(docs_folder, f"{folder_name}.md") 174 | with open(md_file_path, 'w') as md_file: 175 | md_file.write('\\n'.join(content_lines)) 176 | 177 | if __name__ == "__main__": 178 | folders_to_process = ['modules', 'utils'] 179 | for folder in folders_to_process: 180 | if os.path.exists(folder): 181 | generate_docs_for_folder(folder) 182 | """, 183 | "requirements.txt": "", 184 | "tests/__init__.py": "", 185 | "tests/test_example.py": """# you can import your modules and test them here 186 | import pytest 187 | 188 | @pytest.fixture 189 | def sample_data(): 190 | return [1, 2, 3, 4] 191 | 192 | def test_addition(): 193 | assert 1 + 1 == 2 194 | 195 | def test_subtraction(): 196 | assert 2 - 1 == 1 197 | 198 | def test_sum(sample_data): 199 | assert sum(sample_data) == 10 200 | 201 | class TestMathOperations: 202 | def test_multiplication(self): 203 | assert 2 * 2 == 4 204 | 205 | def test_division(self): 206 | assert 4 / 2 == 2 207 | """, 208 | } 209 | -------------------------------------------------------------------------------- /projectify/modules/dependencies.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import subprocess 4 | import sys 5 | 6 | from colorama import Fore, Style 7 | 8 | 9 | def is_uv_installed(): 10 | """ 11 | Check if the 'uv' command line tool is installed on the system. 12 | 13 | This function tries to run the 'uv --version' command and checks if it 14 | executes successfully. If the command executes successfully, the function 15 | returns True, indicating that 'uv' is installed. If the command fails to 16 | execute, the function returns False, indicating that 'uv' is not installed. 17 | 18 | Returns 19 | ------- 20 | bool 21 | True if 'uv' is installed, False otherwise. 22 | """ 23 | try: 24 | result = subprocess.run( 25 | ["uv", "--version"], 26 | check=True, 27 | stdout=subprocess.PIPE, 28 | stderr=subprocess.PIPE, 29 | ) 30 | return result.returncode == 0 31 | except (subprocess.CalledProcessError, FileNotFoundError): 32 | return False 33 | 34 | 35 | def install_uv(): 36 | """ 37 | This function is used to install the 'uv' tool based on the operating system. 38 | 39 | If the operating system is Linux or macOS, it first checks if 'brew' is installed and uses it to install 'uv'. 40 | If 'brew' is not available, it falls back to using 'curl'. 41 | If the operating system is Windows, it sets the execution policy to RemoteSigned and installs 'uv' using PowerShell. 42 | If the operating system is not supported, it prints an error message and exits the program. 43 | 44 | After the installation, it adds the installation path to the system's PATH environment variable. 45 | """ 46 | os_name = platform.system() 47 | if os_name == "Linux": 48 | print(Fore.YELLOW + "Installing uv with curl..." + Style.RESET_ALL) 49 | subprocess.run( 50 | ["curl", "-LsSf", "https://astral.sh/uv/install.sh | sh"], shell=True 51 | ) 52 | elif os_name == "Darwin": # macOS is 'Darwin' 53 | try: 54 | # Check if brew is installed 55 | subprocess.run( 56 | ["brew", "--version"], 57 | check=True, 58 | stdout=subprocess.PIPE, 59 | stderr=subprocess.PIPE, 60 | ) 61 | print(Fore.GREEN + "Brew is installed. Installing uv with brew..." + Style.RESET_ALL) 62 | subprocess.run(["brew", "install", "uv"], check=True) 63 | except subprocess.CalledProcessError: 64 | print(Fore.YELLOW + "Brew is not installed. Installing uv with curl..." + Style.RESET_ALL) 65 | subprocess.run( 66 | ["curl", "-LsSf", "https://astral.sh/uv/install.sh | sh"], shell=True 67 | ) 68 | elif os_name == "Windows": 69 | try: 70 | print( 71 | Fore.YELLOW + "Setting PowerShell execution policy to RemoteSigned..." 72 | + Style.RESET_ALL 73 | ) 74 | subprocess.run( 75 | [ 76 | "powershell", 77 | "-Command", 78 | "Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force", 79 | ], 80 | check=True, 81 | ) 82 | print( 83 | Fore.GREEN + 84 | "Execution policy set. Installing uv with PowerShell..." + 85 | Style.RESET_ALL) 86 | subprocess.run( 87 | ["powershell", "-c", "irm https://astral.sh/uv/install.ps1 | iex"], 88 | check=True, 89 | ) 90 | except subprocess.CalledProcessError: 91 | print( 92 | Fore.RED 93 | + "No se pudo configurar la política de ejecución de PowerShell. " 94 | "Por favor, configúrela manualmente y vuelva a intentar." + Style.RESET_ALL 95 | ) 96 | sys.exit(1) 97 | else: 98 | print( 99 | Fore.RED 100 | + f"Sistema operativo {os_name} no soportado para la instalación automática de uv." + Style.RESET_ALL 101 | ) 102 | sys.exit(1) 103 | 104 | # Añadir la ruta de instalación al PATH 105 | os.environ["PATH"] += os.pathsep + os.path.expanduser("~/.cargo/bin") 106 | 107 | 108 | def is_tool_installed(tool): 109 | """ 110 | Check if a given tool is installed on the system. 111 | 112 | This function attempts to run the tool with the '--version' argument. If the tool 113 | is installed and runs successfully, the function returns True. If the tool is not 114 | installed or if it fails to run, the function returns False. 115 | 116 | Parameters 117 | ---------- 118 | tool : str 119 | The name of the tool to check. 120 | 121 | Returns 122 | ------- 123 | bool 124 | True if the tool is installed, False otherwise. 125 | """ 126 | try: 127 | subprocess.run( 128 | [tool, "--version"], 129 | check=True, 130 | stdout=subprocess.PIPE, 131 | stderr=subprocess.PIPE, 132 | ) 133 | return True 134 | except subprocess.CalledProcessError: 135 | return False 136 | except FileNotFoundError: 137 | return False 138 | 139 | 140 | def ensure_uv_installed(): 141 | """ 142 | Verify if iv is installed in your systems. 143 | """ 144 | if not is_uv_installed(): 145 | print(Fore.YELLOW + "uv no está instalado. Instalando uv..." + Style.RESET_ALL) 146 | install_uv() 147 | if not is_uv_installed(): 148 | print( 149 | Fore.RED + "No se pudo instalar uv automáticamente. " 150 | "Por favor, instálelo manualmente y vuelva a intentar." + Style.RESET_ALL 151 | ) 152 | sys.exit(1) 153 | else: 154 | print(Fore.GREEN + "uv instalado correctamente." + Style.RESET_ALL) 155 | else: 156 | print(Fore.GREEN + "uv ya está instalado." + Style.RESET_ALL) 157 | 158 | 159 | def install_tool(tool): 160 | """ 161 | This function installs a specified tool on the system. 162 | 163 | The function first determines the operating system of the machine. 164 | If the OS is Linux, it uses the 'apt-get' command to install the tool. 165 | If the OS is macOS, it uses the 'brew' command to install the tool. 166 | If the OS is Windows, it also uses the 'brew' command to install the tool (assuming that Homebrew is installed on Windows). 167 | If the OS is not one of these three, it prints an error message and exits the program. 168 | 169 | After the installation, the function checks if the tool was installed successfully. 170 | If it was, it prints a success message. If it wasn't, it prints an error message. 171 | 172 | Parameters 173 | ---------- 174 | tool : str 175 | The name of the tool to be installed. 176 | 177 | Returns 178 | ------- 179 | None 180 | """ 181 | os_name = platform.system() 182 | if os_name == "Linux": 183 | subprocess.run(["sudo", "apt-get", "install", "-y", tool]) 184 | elif os_name == "Darwin": 185 | subprocess.run(["brew", "install", tool]) 186 | elif os_name == "Windows": 187 | if tool == "make": 188 | print(Fore.RED + "make no está instalado." + Style.RESET_ALL) 189 | print( 190 | Fore.YELLOW 191 | + "Instálelo desde PowerShell con permisos de administrador: choco install make" + Style.RESET_ALL 192 | ) 193 | else: 194 | print(Fore.RED + f"{tool} no está instalado.") 195 | print( 196 | Fore.YELLOW 197 | + f"Instálelo desde PowerShell con permisos de administrador: choco install {tool}" + Style.RESET_ALL 198 | ) 199 | else: 200 | print( 201 | Fore.RED 202 | + f"Sistema operativo {os_name} no soportado para la instalación automática de {tool}." + Style.RESET_ALL 203 | ) 204 | sys.exit(1) 205 | if is_tool_installed(tool): 206 | print(Fore.GREEN + f"{tool} instalado correctamente." + Style.RESET_ALL) 207 | else: 208 | print(Fore.RED + f"Error al instalar {tool}. Por favor, instálelo manualmente." + Style.RESET_ALL) 209 | 210 | 211 | def install_packages(project_name, packages): 212 | """ 213 | This function installs the specified packages for a given project. 214 | 215 | The function uses the subprocess module to run the pip install command for each package in the list. 216 | The command is run in the working directory specified by the project_name parameter. 217 | 218 | Parameters 219 | ---------- 220 | project_name :str 221 | The name of the project. This is used as the working directory for the installation. 222 | packages : list 223 | A list of package names to be installed. 224 | 225 | Returns 226 | ------- 227 | None 228 | """ 229 | for package in packages: 230 | subprocess.run(["uv", "pip", "install", package], cwd=project_name) 231 | 232 | 233 | def check_and_install_dependencies(): 234 | """ 235 | This function checks if the required packages are installed and installs them if necessary. 236 | """ 237 | required_packages = ["colorama", "art", "packaging"] 238 | for package in required_packages: 239 | try: 240 | __import__(package) 241 | except ImportError: 242 | print( 243 | Fore.YELLOW 244 | + f"El paquete '{package}' no está instalado. Instalándolo..." + Style.RESET_ALL 245 | ) 246 | subprocess.check_call([sys.executable, "-m", "pip", "install", package]) 247 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🤖📦 Pojectify!: A Command-Line Tool for Quick Python Project Setup 2 | 3 | Proyectify is a command-line tool that generates and sets up a Python project structure with all necessary configurations, including a virtual environment, IDE setup, Git integration, and documentation generation with MkDocs. 4 | 5 | ## ✅ Features 6 | 7 | - Creation of the basic project structure with predefined folders. 8 | - Creation of virtual environments with the specified Python version using `uv` . 9 | - Configuration of base files such as pyproject.toml, .gitignore, Makefile, Dockerfile, etc. 10 | - Creation of a virtual environment with the specified Python version. 11 | - Initialization of a Git repository. 12 | - Installation of necessary packages such as ruff, pre-commit, mkdocs, mkdocstrings, pytest. 13 | - Setup for popular IDEs (VScode, Pycharm). 14 | - Automatic documentation generation for modules using `mkdocs` and `mkdocstrings`. 15 | - Automatic configuration for Testing with Pytest. 16 | 17 | ## 📦 Requirements 18 | 19 | - Python 3.8 or higher. 20 | - In macOS, DevsTools must be installed. 21 | 22 | ## 🚀 Quick Start 23 | 24 | ### ☁️ Instalation 25 | 26 | To run the package and set up your project, use the following command: 27 | 28 | ```python 29 | pip install projectify 30 | ``` 31 | 32 | ### Command-Line Options 33 | 34 | During script execution, you will be prompted for the following options: 35 | 36 | - **Project Name**: Enter the name of your new project. 37 | - **IDE Selection**: Select your favorite IDE (VScode, Pycharm, Other). 38 | - **Python Version**: Select the Python version to use. If only one version is installed, it will be used automatically. 39 | 40 | #### Options 41 | 42 | | ARGUMENT | DESCRIPTION | 43 | | -------------------------------- | ------------------------------------------------------------------------------------------------------- | 44 | | -i, --init | Create a new project | 45 | | -c, --clean | Clean generated files and directories | 46 | | -id, --install-dependencies | Install dependencies from `requirements.txt` | 47 | | -t, --run-tests | Run tests in the `tests` directory | 48 | | -l, --lint | Run linter (Ruff) on the project | 49 | | -f, --format | Format the code using Ruff | 50 | | -g, --generate-docs | Generate documentation using MkDocs | 51 | | -h, --help | Show this help page and exit | 52 | | -v, --version | Show the current version of Projectify | 53 | 54 | **Warning:** _In future versions some commands may change_ 55 | 56 | ## Dependency Installation 57 | 58 | ### Installing `uv` 59 | 60 | `uv` os installed automatically when running the package. If you want to install it manually, follow the instructions below. 61 | 62 | #### macOS and Linux 63 | 64 | ```sh 65 | curl -LsSf https://astral.sh/uv/install.sh | sh 66 | ``` 67 | 68 | #### Windows 69 | 70 | ```sh 71 | irm https://astral.sh/uv/install.ps1 | iex 72 | ``` 73 | 74 | ### Installing `make` (only on Windows) 75 | 76 | `make` is required to run the Makefile commands. To install `make` on Windows, use the following command: 77 | 78 | ```sh 79 | choco install make 80 | ``` 81 | 82 | In Linux and macOS, `make` is installed by default. 83 | 84 | ### 📂 Project Structure 85 | 86 | The package will generate the following folder structure: 87 | 88 | ```sh 89 | / 90 | │── .venv/ 91 | │── .vscode/ 92 | │ └── keybindings.json 93 | │ └── settings.json 94 | │ └── snippets.json 95 | ├── app/ 96 | │ └── main.py 97 | ├── artifacts/ 98 | ├── data/ 99 | ├── docs/ 100 | │ └── index.md 101 | ├── images/ 102 | ├── modules/ 103 | │ └── __init__.py 104 | ├── notebooks/ 105 | ├── logs/ 106 | ├── scripts/ 107 | │ └── generate_docs.py 108 | ├── utils/ 109 | │ └── __init__.py 110 | ├── .tests/ 111 | │ └── __init__.py 112 | │ └── test_example.py 113 | ├── .dockerignore 114 | ├── .env 115 | ├── .gitignore 116 | ├── .pre-commit-config.yaml 117 | ├── Dockerfile 118 | ├── Makefile 119 | ├── mkdocs.yml 120 | ├── pyproject.toml 121 | └── README.md 122 | ``` 123 | 124 | ### 📝 Documentation Generation 125 | 126 | The package generates documentation for each module using MkDocs and the MkDocstrings plugin. The documentation is generated in the `docs` folder. 127 | 128 | To generate and serve documentation with MkDocs, use the following commands: 129 | 130 | ```sh 131 | make docs 132 | ``` 133 | 134 | To generate documentation for modules, run: 135 | 136 | ```sh 137 | make generate-docs 138 | ``` 139 | 140 | ## 📚 Configuration Detail 141 | 142 | `uv` for Virtual Environments 143 | 144 | Proyectify uses uv, an extremely fast Python package installer and resolver written in Rust, as the virtual environment manager. For more information, refer to the [uv documentation](https://github.com/astral-sh/uv). 145 | 146 | `ruff` for Linting and Formatting 147 | 148 | Proyectify uses ruff as the linter and formatter. Ruff is An extremely fast Python linter and code formatter, written in Rust. For more information, refer to the [ruff documentation](https://github.com/astral-sh/ruff) 149 | 150 | A base configuration is provided in the pyproject.toml file: 151 | 152 | ```toml 153 | [tool.ruff] 154 | line-length = 88 155 | indent-width = 4 156 | include = ["pyproject.toml", "src/**/*.py"] 157 | extend-include = ["*.ipynb"] 158 | 159 | [tool.ruff.lint] 160 | select = ["E4", "E7", "E9", "F"] 161 | ignore = [] 162 | fixable = ["ALL"] 163 | unfixable = [] 164 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" 165 | 166 | [tool.ruff.format] 167 | quote-style = "single" 168 | skip-magic-trailing-comma = false 169 | line-ending = "auto" 170 | docstring-code-format = true 171 | docstring-code-line-length = "dynamic" 172 | ``` 173 | 174 | `mkdocs` for Documentation 175 | 176 | Proyectify automates documentation generation using `mkdocs` and the `mkdocstrings` plugin to incorporate the docstrings of each module into the documentation. The `mkdocs.yml` configuration is provided to get you started: 177 | 178 | ```yaml 179 | site_name: Project Documentation 180 | nav: 181 | - Home: index.md 182 | - Notebooks: notebooks.md 183 | 184 | plugins: 185 | - mkdocstrings 186 | 187 | theme: readthedocs 188 | ``` 189 | 190 | ## Scripts 191 | 192 | `generate_docs.py` 193 | 194 | This script traverses the `modules` and `utils` folders and creates `.md` files in the docs directory with the documentation content of each module. 195 | 196 | ```python 197 | import os 198 | 199 | def generate_docs_for_folder(folder_name): 200 | docs_folder = 'docs' 201 | folder_path = os.path.join(docs_folder, folder_name) 202 | os.makedirs(folder_path, exist_ok=True) 203 | 204 | folder_files = [f for f in os.listdir(folder_name) if f.endswith('.py') and not f.startswith('__')] 205 | 206 | content_lines = [f"# {folder_name.capitalize()} Documentation\n"] 207 | for file in folder_files: 208 | module_name = file.replace('.py', '') 209 | content_lines.append(f"::: {folder_name}.{module_name}\n") 210 | 211 | md_file_path = os.path.join(docs_folder, f"{folder_name}.md") 212 | with open(md_file_path, 'w') as md_file: 213 | md_file.write('\n'.join(content_lines)) 214 | 215 | if __name__ == "__main__": 216 | folders_to_process = ['modules', 'utils'] 217 | for folder in folders_to_process: 218 | if os.path.exists(folder): 219 | generate_docs_for_folder(folder) 220 | ``` 221 | 222 | This script is automatically executed when running the `make generate-docs` command. (Feel free to modify it according to your needs). 223 | 224 | ## 🔮 Roadmap 225 | 226 | - [x] Create a basic and standar project structure. 227 | - [ ] Add more configuration options. (e.g. pre-commit hooks, etc.) 228 | - [ ] Add more IDEs to the setup options. 229 | - [ ] Add devcontainer configuration for VScode. 230 | - [ ] Add more documentation generation options. (e.g. Sphinx, etc.) 231 | - [ ] Add CI/CD options. (e.g. GitHub Actions, etc.) 232 | 233 | ## 🐛 Known bugs 234 | 235 | - (SOLVED) The package does not work on Windows when the Python version is specified. 236 | - (SOLVED) The dependencies are not installed when the package is installed with pip. 237 | - (SOLVED) The color output is not displayed correctly in the terminal. 238 | 239 | > If you find any bugs, don't hesitate and open an issue :) 240 | 241 | ## 📝 Contributing 242 | 243 | If you want to contribute check the [CONTRIBUTING.md](.github/CONTRIBUTING.md) 244 | 245 | ## ☕ Buy me a coffee 246 | 247 | We have developed npkill in our free time, because we are passionate about the programming sector. 248 | Tomorrow we would like to dedicate ourselves to this, but first, we have a long way to go. 249 | 250 | We will continue to do things anyway, but donations are one of the many ways to support what we do. 251 | 252 | Buy Me A Coffee 253 | 254 | ### Thanks! 🙏 255 | 256 | ## 📜 License 257 | 258 | This project is licensed under the MIT License. For more details, please refer to the LICENSE file. 259 | -------------------------------------------------------------------------------- /projectify/core.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import glob 3 | import importlib.metadata 4 | import os 5 | import platform 6 | import shutil 7 | import subprocess 8 | import sys 9 | 10 | from colorama import Fore, Style 11 | 12 | from projectify.modules import ( 13 | check_and_install_dependencies, 14 | get_installed_python_versions, 15 | print_header, 16 | setup_project, 17 | ) 18 | 19 | 20 | def get_int_input(prompt, min_val, max_val): 21 | """Prompt the user for an integer input within a specified range. 22 | 23 | Args: 24 | prompt (str): The message to display to the user when asking for input. 25 | min_val (int): The minimum acceptable value for the input. 26 | max_val (int): The maximum acceptable value for the input. 27 | 28 | Returns: 29 | int: The user's input, which is an integer within the specified range. 30 | """ 31 | while True: 32 | try: 33 | value = int(input(prompt)) 34 | if value < min_val or value > max_val: 35 | raise ValueError 36 | return value 37 | except ValueError: 38 | print( 39 | Fore.RED + f"Por favor, ingrese un número entre {min_val} y {max_val}." + Style.RESET_ALL 40 | ) 41 | 42 | 43 | def clean_project(): 44 | """ 45 | This function cleans up generated directories. 46 | """ 47 | print(Fore.CYAN + "Limpiando archivos generados..." + Style.RESET_ALL) 48 | 49 | directories_to_remove = ["dist", "build", "*.egg-info"] 50 | 51 | for directory in directories_to_remove: 52 | full_path = os.path.join(os.getcwd(), directory) 53 | if os.path.isdir(full_path): 54 | try: 55 | shutil.rmtree(full_path) 56 | print(Fore.GREEN + f"Directorio {directory} eliminado exitosamente." + Style.RESET_ALL) 57 | except Exception as e: 58 | print(Fore.RED + f"Error al eliminar {directory}: {e}") 59 | elif os.path.isfile(full_path) or os.path.islink(full_path): 60 | try: 61 | os.remove(full_path) 62 | print(Fore.GREEN + f"Archivo {directory} eliminado exitosamente." + Style.RESET_ALL) 63 | except Exception as e: 64 | print(Fore.RED + f"Error al eliminar {directory}: {e}" + Style.RESET_ALL) 65 | else: 66 | # En caso de que sea un patrón, como "*.egg-info" 67 | for file_or_dir in glob.glob(full_path): 68 | if os.path.isdir(file_or_dir): 69 | try: 70 | shutil.rmtree(file_or_dir) 71 | print( 72 | Fore.GREEN 73 | + f"Directorio {file_or_dir} eliminado exitosamente." + Style.RESET_ALL 74 | ) 75 | except Exception as e: 76 | print(Fore.RED + f"Error al eliminar {file_or_dir}: {e}" + Style.RESET_ALL) 77 | elif os.path.isfile(file_or_dir) or os.path.islink(file_or_dir): 78 | try: 79 | os.remove(file_or_dir) 80 | print( 81 | Fore.GREEN + f"Archivo {directory} eliminado exitosamente." + Style.RESET_ALL 82 | ) 83 | except Exception as e: 84 | print(Fore.RED + f"Error al eliminar {directory}: {e}" + Style.RESET_ALL) 85 | else: 86 | print( 87 | Fore.YELLOW 88 | + f"{directory} no existe o no es un directorio/archivo válido." + Style.RESET_ALL 89 | ) 90 | 91 | 92 | def install_dependencies(): 93 | """ 94 | This function installs the dependencies listed in the requirements.txt file 95 | using 'uv' inside a virtual environment if it exists. 96 | """ 97 | print(Fore.CYAN + "Instalando dependencias..." + Style.RESET_ALL) 98 | 99 | venv_path = os.path.join(os.getcwd(), ".venv") 100 | requirements_file = "requirements.txt" 101 | 102 | # Verificar si el entorno virtual existe 103 | if os.path.exists(venv_path): 104 | print( 105 | Fore.GREEN 106 | + "Entorno virtual encontrado. Instalando dependencias en el entorno virtual." + Style.RESET_ALL 107 | ) 108 | uv_executable = os.path.join( 109 | venv_path, 110 | "bin", 111 | "uv" if platform.system() != "Windows" else "Scripts\\uv.exe", 112 | ) 113 | 114 | if os.path.exists(uv_executable): 115 | if os.path.exists(requirements_file): 116 | try: 117 | subprocess.check_call( 118 | [uv_executable, "pip", "install", "-r", requirements_file] 119 | ) 120 | subprocess.check_call([uv_executable, "pip", "install", "ruff"]) 121 | print(Fore.GREEN + "Dependencias instaladas exitosamente." + Style.RESET_ALL) 122 | except subprocess.CalledProcessError as e: 123 | print(f"Error al instalar dependencias: {e}") 124 | sys.exit(1) 125 | else: 126 | print( 127 | Fore.RED 128 | + "No se encontró el archivo requirements.txt. Por favor, asegúrese de que exista." + Style.RESET_ALL 129 | ) 130 | sys.exit(1) 131 | else: 132 | print( 133 | Fore.RED + "No se encontró el ejecutable de 'uv' en el entorno virtual." + Style.RESET_ALL 134 | ) 135 | sys.exit(1) 136 | else: 137 | print( 138 | Fore.RED 139 | + "No se encontró un entorno virtual. Por favor, cree un entorno virtual primero." + Style.RESET_ALL 140 | ) 141 | sys.exit(1) 142 | 143 | 144 | def run_tests(): 145 | """ 146 | This function runs the tests using 'pytest' if the 'tests' folder 147 | and the test configuration file exist. 148 | """ 149 | print(Fore.CYAN + "Ejecutando pruebas..." + Style.RESET_ALL) 150 | 151 | tests_path = os.path.join(os.getcwd(), "tests") 152 | pytest_ini_path = os.path.join(os.getcwd(), "pytest.ini") 153 | 154 | if os.path.exists(tests_path) and os.path.isdir(tests_path): 155 | if os.path.exists(pytest_ini_path): 156 | subprocess.run(["pytest"]) 157 | print(Fore.GREEN + "Pruebas ejecutadas exitosamente." + Style.RESET_ALL) 158 | else: 159 | print( 160 | Fore.RED 161 | + "No se encontró el archivo de configuración de pruebas 'pytest.ini'." + Style.RESET_ALL 162 | ) 163 | else: 164 | print(Fore.RED + "No se encontró la carpeta 'tests'." + Style.RESET_ALL) 165 | 166 | 167 | def run_linter(): 168 | venv_path = ".venv" 169 | if not os.path.exists(venv_path): 170 | print( 171 | "No se encontró un entorno virtual. Por favor, cree uno antes de ejecutar el linter." + Style.RESET_ALL 172 | ) 173 | sys.exit(1) 174 | 175 | try: 176 | subprocess.run([os.path.join(venv_path, "bin", "ruff"), "."], check=True) 177 | except subprocess.CalledProcessError as e: 178 | print(f"Error al ejecutar el linter: {e}" + Style.RESET_ALL) 179 | sys.exit(1) 180 | print("Linter ejecutado correctamente." + Style.RESET_ALL) 181 | 182 | 183 | def format_code(): 184 | venv_path = ".venv" 185 | if not os.path.exists(venv_path): 186 | print( 187 | "No se encontró un entorno virtual. Por favor, cree uno antes de formatear el código." + Style.RESET_ALL 188 | ) 189 | sys.exit(1) 190 | 191 | try: 192 | subprocess.run( 193 | [os.path.join(venv_path, "bin", "ruff"), "check", "--fix", "."], check=True 194 | ) 195 | except subprocess.CalledProcessError as e: 196 | print(f"Error al formatear el código: {e}" + Style.RESET_ALL) 197 | sys.exit(1) 198 | print("Código formateado correctamente." + Style.RESET_ALL) 199 | 200 | 201 | def generate_docs(): 202 | try: 203 | subprocess.run(["mkdocs", "build"], check=True) 204 | except subprocess.CalledProcessError as e: 205 | print(f"Error al generar la documentación: {e}" + Style.RESET_ALL) 206 | sys.exit(1) 207 | print("Documentación generada correctamente." + Style.RESET_ALL) 208 | 209 | 210 | def Init(): 211 | """ 212 | This function is the entry point for the project setup process. 213 | It prints the header, checks and installs dependencies, and then sets up 214 | the project based on user input. 215 | """ 216 | try: 217 | # Print the header 218 | print_header() 219 | 220 | # check and install dependencies 221 | check_and_install_dependencies() 222 | 223 | project_name = input(Fore.CYAN + "Ingrese el nombre del proyecto: " + Style.RESET_ALL) 224 | 225 | print(Fore.CYAN + "\nSeleccione su IDE:" + Style.RESET_ALL) 226 | print(Fore.CYAN + "1. VScode" + Style.RESET_ALL) 227 | print(Fore.CYAN + "2. Pycharm" + Style.RESET_ALL) 228 | print(Fore.CYAN + "3. Other" + Style.RESET_ALL) 229 | ide_choice = get_int_input( 230 | Fore.CYAN + "Ingrese la opción de su IDE favorito: ", 1, 3 231 | ) 232 | 233 | installed_versions = get_installed_python_versions() 234 | if len(installed_versions) == 0: 235 | print( 236 | Fore.RED 237 | + "\nNo se encontró ninguna versión de Python instalada. Por favor, instale Python y vuelva a intentarlo." + Style.RESET_ALL 238 | ) 239 | sys.exit(1) 240 | elif len(installed_versions) == 1: 241 | python_version = installed_versions[0] 242 | print( 243 | Fore.YELLOW 244 | + f"\nSe encontró solo la versión de Python {python_version}. Creando venv con esta versión." + Style.RESET_ALL 245 | ) 246 | else: 247 | print(Fore.CYAN + "\nSeleccione la versión de Python:") 248 | for i, v in enumerate(installed_versions, 1): 249 | print(Fore.CYAN + f"{i}. Python {v}") 250 | version_choice = get_int_input( 251 | Fore.CYAN 252 | + "Ingrese la opción correspondiente a la versión de Python: ", 253 | 1, 254 | len(installed_versions), 255 | ) 256 | python_version = installed_versions[version_choice - 1] 257 | 258 | setup_project(project_name, python_version, ide_choice) 259 | except KeyboardInterrupt: 260 | print(Fore.RED + "\nProceso interrumpido por el usuario. Saliendo..." + Style.RESET_ALL) 261 | 262 | 263 | def main(): 264 | """ 265 | The main function that runs the project setup script. 266 | """ 267 | parser = argparse.ArgumentParser( 268 | description="Proyectify: Una herramienta para configurar estructuras de proyectos en Python" 269 | ) 270 | parser.add_argument( 271 | "--version", 272 | "-v", 273 | action="version", 274 | version=f"Proyectify {importlib.metadata.version('projectify')}", 275 | ) 276 | 277 | subparsers = parser.add_subparsers(dest="command") 278 | 279 | init_parser = subparsers.add_parser( 280 | "init", aliases=["i"], help="Crear un nuevo proyecto" 281 | ) 282 | init_parser.set_defaults(func=Init) 283 | 284 | clean_parser = subparsers.add_parser( 285 | "clean", aliases=["c"], help="Limpiar archivos generados" 286 | ) 287 | clean_parser.set_defaults(func=clean_project) 288 | 289 | install_dependencies_parser = subparsers.add_parser( 290 | "install-dependencies", aliases=["id"], help="Instalar dependencias" 291 | ) 292 | install_dependencies_parser.set_defaults(func=install_dependencies) 293 | 294 | run_tests_parser = subparsers.add_parser( 295 | "run-tests", aliases=["t"], help="Ejecutar pruebas" 296 | ) 297 | run_tests_parser.set_defaults(func=run_tests) 298 | 299 | lint_parser = subparsers.add_parser("lint", aliases=["l"], help="Ejecutar linter") 300 | lint_parser.set_defaults(func=run_linter) 301 | 302 | format_parser = subparsers.add_parser( 303 | "format", aliases=["f"], help="Formatear código" 304 | ) 305 | format_parser.set_defaults(func=format_code) 306 | 307 | generate_docs_parser = subparsers.add_parser( 308 | "generate-docs", aliases=["g"], help="Generar documentación" 309 | ) 310 | generate_docs_parser.set_defaults(func=generate_docs) 311 | 312 | args = parser.parse_args() 313 | 314 | if args.command is None: 315 | parser.print_help() 316 | sys.exit(1) 317 | 318 | args.func() 319 | 320 | 321 | if __name__ == "__main__": 322 | main() 323 | --------------------------------------------------------------------------------