@saulwade/swl-ses 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +238 -0
- package/README.md +560 -0
- package/_userland/agentes/.gitkeep +0 -0
- package/_userland/habilidades/.gitkeep +0 -0
- package/agentes/.evolved.json +9 -0
- package/agentes/accesibilidad-wcag-swl.md +692 -0
- package/agentes/arquitecto-swl.md +238 -0
- package/agentes/auto-evolucion-swl.md +854 -0
- package/agentes/backend-api-swl.md +470 -0
- package/agentes/backend-csharp-swl.md +418 -0
- package/agentes/backend-go-swl.md +388 -0
- package/agentes/backend-java-swl.md +279 -0
- package/agentes/backend-node-swl.md +477 -0
- package/agentes/backend-python-swl.md +608 -0
- package/agentes/backend-rust-swl.md +362 -0
- package/agentes/backend-workers-swl.md +480 -0
- package/agentes/cloud-infra-swl.md +485 -0
- package/agentes/consolidador-swl.md +539 -0
- package/agentes/datos-swl.md +584 -0
- package/agentes/depurador-swl.md +349 -0
- package/agentes/devops-ci-swl.md +374 -0
- package/agentes/disenador-ui-swl.md +558 -0
- package/agentes/documentador-swl.md +343 -0
- package/agentes/evals/arquitecto-swl.evals.json +56 -0
- package/agentes/evals/auto-evolucion-swl.evals.json +68 -0
- package/agentes/evals/implementador-swl.evals.json +56 -0
- package/agentes/evals/orquestador-swl.evals.json +60 -0
- package/agentes/evals/perfilador-usuario-swl.evals.json +60 -0
- package/agentes/evals/red-team-swl.evals.json +59 -0
- package/agentes/evals/revisor-codigo-swl.evals.json +59 -0
- package/agentes/frontend-angular-swl.md +627 -0
- package/agentes/frontend-css-swl.md +720 -0
- package/agentes/frontend-react-swl.md +696 -0
- package/agentes/frontend-swl.md +500 -0
- package/agentes/frontend-tailwind-swl.md +830 -0
- package/agentes/implementador-swl.md +328 -0
- package/agentes/investigador-swl.md +430 -0
- package/agentes/investigador-ux-swl.md +500 -0
- package/agentes/llm-apps-swl.md +276 -0
- package/agentes/migrador-swl.md +417 -0
- package/agentes/mobile-android-swl.md +509 -0
- package/agentes/mobile-cross-swl.md +539 -0
- package/agentes/mobile-ios-swl.md +500 -0
- package/agentes/mobile-testing-swl.md +300 -0
- package/agentes/notificador-swl.md +916 -0
- package/agentes/observabilidad-swl.md +436 -0
- package/agentes/orquestador-swl.md +884 -0
- package/agentes/pagos-swl.md +283 -0
- package/agentes/perfilador-usuario-swl.md +306 -0
- package/agentes/planificador-swl.md +402 -0
- package/agentes/producto-prd-swl.md +587 -0
- package/agentes/red-team-swl.md +216 -0
- package/agentes/release-manager-swl.md +568 -0
- package/agentes/rendimiento-swl.md +714 -0
- package/agentes/resolutor-build-swl.md +243 -0
- package/agentes/revisor-angular-swl.md +276 -0
- package/agentes/revisor-codigo-swl.md +348 -0
- package/agentes/revisor-csharp-swl.md +262 -0
- package/agentes/revisor-go-swl.md +257 -0
- package/agentes/revisor-java-swl.md +255 -0
- package/agentes/revisor-kotlin-swl.md +271 -0
- package/agentes/revisor-nextjs-swl.md +279 -0
- package/agentes/revisor-php-swl.md +269 -0
- package/agentes/revisor-react-swl.md +276 -0
- package/agentes/revisor-rust-swl.md +344 -0
- package/agentes/revisor-seguridad-swl.md +390 -0
- package/agentes/revisor-swift-swl.md +266 -0
- package/agentes/revisor-typescript-swl.md +344 -0
- package/agentes/sre-swl.md +265 -0
- package/agentes/tdd-qa-swl.md +354 -0
- package/agentes/ux-disenador-swl.md +501 -0
- package/bin/lib/bot-comandos.js +1030 -0
- package/bin/lib/bot-discovery.js +182 -0
- package/bin/lib/bot-git.js +142 -0
- package/bin/swl-ses.js +325 -0
- package/bin/swl-telegram-bot.js +442 -0
- package/bin/swl-telegram-bot.plist +21 -0
- package/bin/swl-telegram-bot.service +14 -0
- package/comandos/swl/.evolved.json +23 -0
- package/comandos/swl/actualizar.md +174 -0
- package/comandos/swl/adoptar-proyecto.md +207 -0
- package/comandos/swl/aprender.md +701 -0
- package/comandos/swl/auditar-deps.md +134 -0
- package/comandos/swl/autoresearch.md +170 -0
- package/comandos/swl/ayuda.md +224 -0
- package/comandos/swl/brainstorm.md +50 -0
- package/comandos/swl/checkpoint.md +330 -0
- package/comandos/swl/compactar.md +283 -0
- package/comandos/swl/configurar-ci.md +227 -0
- package/comandos/swl/contexto.md +112 -0
- package/comandos/swl/contribuir.md +233 -0
- package/comandos/swl/crear-skill.md +292 -0
- package/comandos/swl/cron.md +196 -0
- package/comandos/swl/dashboard.md +146 -0
- package/comandos/swl/discutir-fase.md +230 -0
- package/comandos/swl/ejecutar-fase.md +135 -0
- package/comandos/swl/evaluar-skill.md +487 -0
- package/comandos/swl/evolucion-estado.md +142 -0
- package/comandos/swl/evolucionar.md +259 -0
- package/comandos/swl/exportar-vault.md +189 -0
- package/comandos/swl/gateway.md +158 -0
- package/comandos/swl/inbox.md +116 -0
- package/comandos/swl/instalar.md +220 -0
- package/comandos/swl/instintos.md +86 -0
- package/comandos/swl/mapear-codebase.md +312 -0
- package/comandos/swl/mcp-status.md +175 -0
- package/comandos/swl/metricas.md +270 -0
- package/comandos/swl/modelo.md +102 -0
- package/comandos/swl/notificaciones.md +396 -0
- package/comandos/swl/nuevo-proyecto.md +154 -0
- package/comandos/swl/planear-fase.md +221 -0
- package/comandos/swl/plugins.md +256 -0
- package/comandos/swl/reflect-skills.md +125 -0
- package/comandos/swl/release.md +217 -0
- package/comandos/swl/revisar-impacto.md +206 -0
- package/comandos/swl/revisar.md +330 -0
- package/comandos/swl/salud.md +363 -0
- package/comandos/swl/sesiones.md +200 -0
- package/comandos/swl/skill-search.md +113 -0
- package/comandos/swl/verificar.md +585 -0
- package/comandos/swl/wiki.md +620 -0
- package/contextos/dev.md +32 -0
- package/contextos/research.md +30 -0
- package/contextos/review.md +31 -0
- package/habilidades/accesibilidad-a11y/SKILL.md +201 -0
- package/habilidades/accesibilidad-a11y/evals/evals.json +56 -0
- package/habilidades/accesibilidad-a11y/recursos/ejemplos-y-checklist-completo.md +441 -0
- package/habilidades/agent-browser/SKILL.md +218 -0
- package/habilidades/agentes-como-servicio/SKILL.md +218 -0
- package/habilidades/ai-runtime-security/SKILL.md +273 -0
- package/habilidades/angular-avanzado/SKILL.md +164 -0
- package/habilidades/angular-avanzado/recursos/ejemplos-avanzados.md +219 -0
- package/habilidades/angular-moderno/SKILL.md +186 -0
- package/habilidades/angular-moderno/evals/evals.json +45 -0
- package/habilidades/angular-moderno/recursos/ejemplos-avanzados.md +106 -0
- package/habilidades/api-rest-diseno/SKILL.md +191 -0
- package/habilidades/api-rest-diseno/recursos/openapi-template.yaml +506 -0
- package/habilidades/api-rest-diseno/recursos/referencia-api.md +140 -0
- package/habilidades/aprendizaje-continuo/SKILL.md +151 -0
- package/habilidades/aprendizaje-continuo/evals/evals.json +53 -0
- package/habilidades/aprendizaje-continuo/recursos/referencia-instintos.md +290 -0
- package/habilidades/async-python/SKILL.md +149 -0
- package/habilidades/async-python/evals/evals.json +47 -0
- package/habilidades/async-python/recursos/patrones-y-ejemplos-completos.md +292 -0
- package/habilidades/auth-patrones/.evolved.json +9 -0
- package/habilidades/auth-patrones/SKILL.md +413 -0
- package/habilidades/auth-patrones/recursos/implementaciones-completas.md +229 -0
- package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -0
- package/habilidades/auto-evolucion-protocolo/evals/evals.json +55 -0
- package/habilidades/auto-evolucion-protocolo/recursos/referencia-completa.md +145 -0
- package/habilidades/autoresearch/SKILL.md +268 -0
- package/habilidades/autoresearch/evals/evals.json +41 -0
- package/habilidades/autoresearch/recursos/checklist-template.md +191 -0
- package/habilidades/autoresearch/scripts/calcular-score.js +88 -0
- package/habilidades/azure-cloud/SKILL.md +308 -0
- package/habilidades/azure-cloud/recursos/aks.md +327 -0
- package/habilidades/backend-mcp-servidor/SKILL.md +270 -0
- package/habilidades/backend-production-resilience/SKILL.md +288 -0
- package/habilidades/brainstorming/SKILL.md +295 -0
- package/habilidades/brainstorming/recursos/componentes-html.md +247 -0
- package/habilidades/build-errors-cpp/SKILL.md +270 -0
- package/habilidades/build-errors-csharp/SKILL.md +265 -0
- package/habilidades/build-errors-go/SKILL.md +306 -0
- package/habilidades/build-errors-java/SKILL.md +278 -0
- package/habilidades/build-errors-kotlin/SKILL.md +303 -0
- package/habilidades/build-errors-nextjs/SKILL.md +312 -0
- package/habilidades/build-errors-php/SKILL.md +270 -0
- package/habilidades/build-errors-python/SKILL.md +292 -0
- package/habilidades/build-errors-rust/SKILL.md +284 -0
- package/habilidades/build-errors-swift/SKILL.md +272 -0
- package/habilidades/build-errors-typescript/SKILL.md +369 -0
- package/habilidades/checklist-calidad/SKILL.md +271 -0
- package/habilidades/checklist-calidad/recursos/quality-report-template.md +148 -0
- package/habilidades/checklist-seguridad/SKILL.md +285 -0
- package/habilidades/checkpoints-verificacion/SKILL.md +298 -0
- package/habilidades/checkpoints-verificacion/recursos/checkpoint-templates.md +360 -0
- package/habilidades/ci-cd-pipelines/SKILL.md +157 -0
- package/habilidades/ci-cd-pipelines/recursos/github-actions-template.yaml +403 -0
- package/habilidades/ci-cd-pipelines/recursos/pipelines-completos.md +487 -0
- package/habilidades/cloud-aws/SKILL.md +142 -0
- package/habilidades/cloud-aws/recursos/servicios-aws-referencia.md +321 -0
- package/habilidades/compactacion-contexto/SKILL.md +247 -0
- package/habilidades/contenedores-docker/SKILL.md +137 -0
- package/habilidades/contenedores-docker/recursos/dockerfile-template.dockerfile +160 -0
- package/habilidades/contenedores-docker/recursos/ejemplos-y-configuraciones.md +327 -0
- package/habilidades/context-builder/SKILL.md +170 -0
- package/habilidades/control-profundidad/SKILL.md +128 -0
- package/habilidades/csharp-experto/SKILL.md +322 -0
- package/habilidades/csharp-patrones/SKILL.md +316 -0
- package/habilidades/csharp-testing/SKILL.md +286 -0
- package/habilidades/css-moderno/SKILL.md +166 -0
- package/habilidades/css-moderno/evals/evals.json +43 -0
- package/habilidades/css-moderno/recursos/ejemplos-y-patrones-completos.md +337 -0
- package/habilidades/datos-etl/SKILL.md +129 -0
- package/habilidades/datos-etl/recursos/implementaciones-completas.md +322 -0
- package/habilidades/dbml-experto/SKILL.md +339 -0
- package/habilidades/dbml-experto/evals/evals.json +56 -0
- package/habilidades/dependencias-auditoria/SKILL.md +320 -0
- package/habilidades/deprecacion-migracion/SKILL.md +169 -0
- package/habilidades/deprecacion-migracion/recursos/implementaciones-completas.md +220 -0
- package/habilidades/design-tokens/SKILL.md +158 -0
- package/habilidades/design-tokens/recursos/tokens-y-configuracion.md +363 -0
- package/habilidades/devsecops-pipeline-security/SKILL.md +309 -0
- package/habilidades/diagrama-arquitectura/SKILL.md +165 -0
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -0
- package/habilidades/discutir-fase/SKILL.md +188 -0
- package/habilidades/diseno-herramientas-agente/SKILL.md +199 -0
- package/habilidades/diseno-responsivo/SKILL.md +186 -0
- package/habilidades/diseno-responsivo/recursos/ejemplos-layouts.md +156 -0
- package/habilidades/django-experto/SKILL.md +205 -0
- package/habilidades/django-experto/recursos/async-django.md +390 -0
- package/habilidades/django-experto/recursos/drf-patrones.md +438 -0
- package/habilidades/django-experto/recursos/orm-avanzado.md +382 -0
- package/habilidades/django-experto/recursos/referencia-completa.md +188 -0
- package/habilidades/django-experto/recursos/testing-django.md +415 -0
- package/habilidades/doc-sync/SKILL.md +280 -0
- package/habilidades/drift-detection/SKILL.md +179 -0
- package/habilidades/ejecutar-fase/SKILL.md +468 -0
- package/habilidades/estilo-sin-ai-isms/SKILL.md +775 -0
- package/habilidades/estilo-sin-ai-isms/evals/evals.json +63 -0
- package/habilidades/estilo-sin-ai-isms/scripts/detectar_aiisms.py +500 -0
- package/habilidades/estructura-proyecto-claude/SKILL.md +215 -0
- package/habilidades/estructura-proyecto-claude/recursos/claude-md-template.md +261 -0
- package/habilidades/estructura-proyecto-claude/recursos/configuracion-y-extensiones.md +176 -0
- package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +289 -0
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +77 -0
- package/habilidades/estructura-proyecto-claude/recursos/variantes-por-stack.md +177 -0
- package/habilidades/evaluacion-agentes/SKILL.md +314 -0
- package/habilidades/event-driven/SKILL.md +153 -0
- package/habilidades/event-driven/recursos/implementaciones-completas.md +423 -0
- package/habilidades/extraccion-documentos/SKILL.md +221 -0
- package/habilidades/extractor-de-aprendizajes/.evolved.json +9 -0
- package/habilidades/extractor-de-aprendizajes/SKILL.md +311 -0
- package/habilidades/extractor-de-aprendizajes/evals/evals.json +55 -0
- package/habilidades/fastapi-experto/SKILL.md +221 -0
- package/habilidades/fastapi-experto/recursos/async-patterns.md +438 -0
- package/habilidades/fastapi-experto/recursos/dependency-injection.md +330 -0
- package/habilidades/fastapi-experto/recursos/referencia-completa.md +79 -0
- package/habilidades/fastapi-experto/recursos/testing-httpx.md +420 -0
- package/habilidades/filament-admin/SKILL.md +290 -0
- package/habilidades/frontend-avanzado/SKILL.md +257 -0
- package/habilidades/frontend-avanzado/recursos/apis-nativas-ejemplos.md +341 -0
- package/habilidades/gcp-cloud/SKILL.md +260 -0
- package/habilidades/gcp-cloud/recursos/gke.md +234 -0
- package/habilidades/gcp-cloud/recursos/terraform-gcp.md +307 -0
- package/habilidades/generacion-mermaid/SKILL.md +229 -0
- package/habilidades/git-worktrees-paralelo/SKILL.md +270 -0
- package/habilidades/go-experto/SKILL.md +305 -0
- package/habilidades/go-patrones/SKILL.md +299 -0
- package/habilidades/go-testing/SKILL.md +291 -0
- package/habilidades/graphql-experto/SKILL.md +323 -0
- package/habilidades/guardrail-semantico/SKILL.md +282 -0
- package/habilidades/harness-claude-code/SKILL.md +299 -0
- package/habilidades/iam-secretos/SKILL.md +265 -0
- package/habilidades/iam-secretos/recursos/implementaciones-completas.md +356 -0
- package/habilidades/infra-github-actions/SKILL.md +166 -0
- package/habilidades/instalar-sistema/.evolved.json +9 -0
- package/habilidades/instalar-sistema/SKILL.md +221 -0
- package/habilidades/java-experto/SKILL.md +290 -0
- package/habilidades/java-patrones/SKILL.md +275 -0
- package/habilidades/java-testing/SKILL.md +288 -0
- package/habilidades/kotlin-compose/SKILL.md +278 -0
- package/habilidades/kotlin-compose/recursos/animaciones-performance.md +93 -0
- package/habilidades/kotlin-experto/SKILL.md +318 -0
- package/habilidades/kotlin-testing/SKILL.md +267 -0
- package/habilidades/kotlin-testing/recursos/testing-avanzado.md +74 -0
- package/habilidades/kubernetes-orquestacion/SKILL.md +152 -0
- package/habilidades/kubernetes-orquestacion/recursos/manifiestos-completos.md +452 -0
- package/habilidades/langchain-langraph/SKILL.md +386 -0
- package/habilidades/langchain-langraph/recursos/evaluacion-rag.md +321 -0
- package/habilidades/langchain-langraph/recursos/rag-maturity-model.md +225 -0
- package/habilidades/langchain-langraph/recursos/vectorstores.md +306 -0
- package/habilidades/legacy-code-rescue/SKILL.md +267 -0
- package/habilidades/likec4-experto/SKILL.md +412 -0
- package/habilidades/likec4-experto/evals/evals.json +69 -0
- package/habilidades/manejo-errores/.evolved.json +9 -0
- package/habilidades/manejo-errores/SKILL.md +407 -0
- package/habilidades/manejo-errores/recursos/implementaciones-completas.md +248 -0
- package/habilidades/mapear-codebase/SKILL.md +275 -0
- package/habilidades/memoria-busqueda/SKILL.md +194 -0
- package/habilidades/memoria-busqueda/evals/evals.json +44 -0
- package/habilidades/meta-skills-estandar/SKILL.md +298 -0
- package/habilidades/meta-skills-estandar/recursos/anti-patrones-y-leyes.md +205 -0
- package/habilidades/meta-skills-estandar/recursos/frameworks-seguridad.md +107 -0
- package/habilidades/meta-skills-estandar/recursos/idiomas-framework.md +60 -0
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -0
- package/habilidades/microservicios/SKILL.md +155 -0
- package/habilidades/microservicios/recursos/patrones-y-ejemplos-completos.md +325 -0
- package/habilidades/mobile-flutter/SKILL.md +199 -0
- package/habilidades/mobile-flutter/recursos/ejemplos-completos.md +319 -0
- package/habilidades/mobile-react-native/SKILL.md +176 -0
- package/habilidades/mobile-react-native/recursos/ejemplos-completos.md +216 -0
- package/habilidades/mongodb-experto/SKILL.md +302 -0
- package/habilidades/monitoring-alertas/SKILL.md +201 -0
- package/habilidades/monitoring-alertas/recursos/instrumentacion-y-alertas.md +301 -0
- package/habilidades/nestjs-experto/SKILL.md +307 -0
- package/habilidades/nestjs-experto/recursos/guards-interceptors.md +339 -0
- package/habilidades/nestjs-experto/recursos/modulos-di.md +287 -0
- package/habilidades/nestjs-experto/recursos/testing-nestjs.md +354 -0
- package/habilidades/nextjs-experto/SKILL.md +335 -0
- package/habilidades/nextjs-patrones/SKILL.md +303 -0
- package/habilidades/nextjs-testing/SKILL.md +331 -0
- package/habilidades/node-experto/.evolved.json +9 -0
- package/habilidades/node-experto/SKILL.md +266 -0
- package/habilidades/node-experto/recursos/patrones-completos.md +283 -0
- package/habilidades/notificaciones-multicanal/SKILL.md +159 -0
- package/habilidades/notificaciones-multicanal/recursos/config-template.json +115 -0
- package/habilidades/notificaciones-multicanal/recursos/configuracion-y-templates.md +303 -0
- package/habilidades/nuevo-proyecto/SKILL.md +204 -0
- package/habilidades/orquestacion-async/SKILL.md +303 -0
- package/habilidades/paid-media-tracking/SKILL.md +269 -0
- package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +220 -0
- package/habilidades/paid-media-tracking/recursos/google-ads-api.md +215 -0
- package/habilidades/patrones-python/SKILL.md +228 -0
- package/habilidades/patrones-python/evals/evals.json +56 -0
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -0
- package/habilidades/patrones-python/recursos/referencia-completa.md +202 -0
- package/habilidades/perfil-usuario/SKILL.md +200 -0
- package/habilidades/perfil-usuario/evals/evals.json +55 -0
- package/habilidades/performance-baseline/SKILL.md +297 -0
- package/habilidades/php-experto/SKILL.md +291 -0
- package/habilidades/php-patrones/SKILL.md +306 -0
- package/habilidades/php-testing/SKILL.md +280 -0
- package/habilidades/planear-fase/SKILL.md +269 -0
- package/habilidades/postgresql-experto/SKILL.md +151 -0
- package/habilidades/postgresql-experto/evals/evals.json +53 -0
- package/habilidades/postgresql-experto/recursos/referencia-completa.md +215 -0
- package/habilidades/prevencion-racionalizacion/SKILL.md +175 -0
- package/habilidades/prevencion-sobreingenieria/SKILL.md +323 -0
- package/habilidades/privacy-memoria/SKILL.md +141 -0
- package/habilidades/privacy-memoria/evals/evals.json +43 -0
- package/habilidades/prompt-engineering/SKILL.md +518 -0
- package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +467 -0
- package/habilidades/rag-arquitectura/SKILL.md +338 -0
- package/habilidades/rails-experto/SKILL.md +237 -0
- package/habilidades/rails-experto/recursos/active-record.md +260 -0
- package/habilidades/rails-experto/recursos/hotwire-turbo.md +293 -0
- package/habilidades/rails-experto/recursos/testing-rspec.md +362 -0
- package/habilidades/react-experto/SKILL.md +209 -0
- package/habilidades/react-experto/evals/evals.json +55 -0
- package/habilidades/react-experto/recursos/patrones-y-ejemplos-completos.md +240 -0
- package/habilidades/react-optimizacion/SKILL.md +174 -0
- package/habilidades/react-optimizacion/recursos/patrones-avanzados.md +138 -0
- package/habilidades/redis-experto/SKILL.md +305 -0
- package/habilidades/release-semver/.evolved.json +9 -0
- package/habilidades/release-semver/SKILL.md +248 -0
- package/habilidades/release-semver/scripts/generar-changelog.sh +238 -0
- package/habilidades/rust-experto/SKILL.md +400 -0
- package/habilidades/rust-patrones/SKILL.md +296 -0
- package/habilidades/rust-testing/SKILL.md +311 -0
- package/habilidades/seguridad-skills-ia/SKILL.md +262 -0
- package/habilidades/sql-optimizacion/SKILL.md +200 -0
- package/habilidades/sql-optimizacion/evals/evals.json +54 -0
- package/habilidades/sql-optimizacion/recursos/patrones-sql-avanzados.md +131 -0
- package/habilidades/sre-patrones/SKILL.md +333 -0
- package/habilidades/sre-patrones/recursos/chaos-engineering.md +241 -0
- package/habilidades/sre-patrones/recursos/oncall-design.md +236 -0
- package/habilidades/stripe-pagos/SKILL.md +550 -0
- package/habilidades/stripe-pagos/recursos/errores-reintentos.md +390 -0
- package/habilidades/stripe-pagos/recursos/stripe-connect.md +290 -0
- package/habilidades/structured-outputs/SKILL.md +343 -0
- package/habilidades/swift-experto/SKILL.md +320 -0
- package/habilidades/swift-experto/recursos/keychain-y-wrappers.md +110 -0
- package/habilidades/swift-patrones/SKILL.md +313 -0
- package/habilidades/swift-patrones/recursos/tca-ejemplo-completo.md +113 -0
- package/habilidades/swift-testing/SKILL.md +254 -0
- package/habilidades/swift-testing/recursos/xcuitest-planes.md +143 -0
- package/habilidades/swl-dashboard/SKILL.md +370 -0
- package/habilidades/swl-markitdown/SKILL.md +285 -0
- package/habilidades/swl-markitdown/evals/evals.json +52 -0
- package/habilidades/swl-revisar-impacto/SKILL.md +233 -0
- package/habilidades/tailwind-experto/SKILL.md +240 -0
- package/habilidades/tailwind-experto/recursos/referencia-completa.md +184 -0
- package/habilidades/tdd-workflow/SKILL.md +293 -0
- package/habilidades/terraform-experto/SKILL.md +321 -0
- package/habilidades/testing-python/SKILL.md +340 -0
- package/habilidades/testing-python/recursos/ejemplos-completos.md +167 -0
- package/habilidades/threat-model-lite/SKILL.md +246 -0
- package/habilidades/tracing-processor/SKILL.md +212 -0
- package/habilidades/tracking-measurement/SKILL.md +239 -0
- package/habilidades/tracking-measurement/recursos/consent-mode.md +231 -0
- package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +216 -0
- package/habilidades/tracking-measurement/recursos/meta-capi.md +262 -0
- package/habilidades/typescript-avanzado/SKILL.md +144 -0
- package/habilidades/typescript-avanzado/evals/evals.json +55 -0
- package/habilidades/typescript-avanzado/recursos/patrones-y-ejemplos-completos.md +298 -0
- package/habilidades/typescript-diagnosticos/SKILL.md +513 -0
- package/habilidades/ux-diseno/SKILL.md +116 -0
- package/habilidades/ux-diseno/evals/evals.json +43 -0
- package/habilidades/ux-diseno/recursos/patrones-ux-referencia.md +214 -0
- package/habilidades/validacion-ci-sistema/SKILL.md +136 -0
- package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +369 -0
- package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +286 -0
- package/habilidades/verificacion-evidencia/SKILL.md +160 -0
- package/habilidades/verificar-trabajo/SKILL.md +303 -0
- package/habilidades/verificar-trabajo/recursos/plantilla-verificacion.md +60 -0
- package/habilidades/wiki-conocimiento/SKILL.md +276 -0
- package/habilidades/wireframes-flujos/SKILL.md +212 -0
- package/habilidades/wireframes-flujos/recursos/referencia-completa.md +192 -0
- package/habilidades/workflow-claude-code/SKILL.md +260 -0
- package/habilidades/workflow-claude-code/recursos/referencia-completa.md +109 -0
- package/hooks/_run-hook.sh +57 -0
- package/hooks/actualizar-perfil-usuario.js +364 -0
- package/hooks/agente-lifecycle.js +71 -0
- package/hooks/aiisms-detector.js +173 -0
- package/hooks/audit-trail.js +204 -0
- package/hooks/auto-background.js +97 -0
- package/hooks/auto-consolidacion.js +178 -0
- package/hooks/auto-evolucion.js +666 -0
- package/hooks/auto-restaurar-settings.js +360 -0
- package/hooks/calidad-pre-commit.js +929 -0
- package/hooks/calidad-typescript.js +511 -0
- package/hooks/captura-feedback-usuario.js +148 -0
- package/hooks/check-update.js +211 -0
- package/hooks/clasificador-mensajes.js +271 -0
- package/hooks/degradacion-instintos.js +272 -0
- package/hooks/escaneo-secretos.js +389 -0
- package/hooks/extraccion-aprendizajes.js +763 -0
- package/hooks/grafo-contexto.js +129 -0
- package/hooks/graph-update.js +67 -0
- package/hooks/guardrail-modelo.js +247 -0
- package/hooks/inbox-aviso.js +75 -0
- package/hooks/inyeccion-contexto.js +246 -0
- package/hooks/lib/abort-registry.js +214 -0
- package/hooks/lib/agent-backend.js +210 -0
- package/hooks/lib/agent-comms.js +263 -0
- package/hooks/lib/agent-issue-codes.js +284 -0
- package/hooks/lib/agent-matcher.js +189 -0
- package/hooks/lib/async-hook-registry.js +252 -0
- package/hooks/lib/atomic-write.js +130 -0
- package/hooks/lib/auto-consolidator.js +335 -0
- package/hooks/lib/canary-skills.js +187 -0
- package/hooks/lib/consolidation-lock.js +291 -0
- package/hooks/lib/context-builder.js +430 -0
- package/hooks/lib/context-compressor.js +657 -0
- package/hooks/lib/convergence-detector.js +105 -0
- package/hooks/lib/delegation-tracker.js +198 -0
- package/hooks/lib/detectar-package-manager.js +423 -0
- package/hooks/lib/edit-accumulator.js +171 -0
- package/hooks/lib/error-classifier.js +308 -0
- package/hooks/lib/event-bus.js +112 -0
- package/hooks/lib/evolution-tracker.js +442 -0
- package/hooks/lib/execution-state.js +316 -0
- package/hooks/lib/fingerprint-id.js +135 -0
- package/hooks/lib/gateway-notify.js +116 -0
- package/hooks/lib/graph-security.js +75 -0
- package/hooks/lib/guardrail-metrics.js +202 -0
- package/hooks/lib/hook-circuit-breaker.js +206 -0
- package/hooks/lib/loop-detector.js +267 -0
- package/hooks/lib/mcp-health.js +184 -0
- package/hooks/lib/mcp-pool.js +436 -0
- package/hooks/lib/memory-search.js +506 -0
- package/hooks/lib/merkle-audit.js +96 -0
- package/hooks/lib/model-router.js +222 -0
- package/hooks/lib/normalize-error.js +324 -0
- package/hooks/lib/normalize-input.js +65 -0
- package/hooks/lib/nudge-tracker.js +306 -0
- package/hooks/lib/otlp-exporter.js +365 -0
- package/hooks/lib/performance-marks.js +239 -0
- package/hooks/lib/privacy-filter.js +128 -0
- package/hooks/lib/prompt-injection-scanner.js +209 -0
- package/hooks/lib/provenance-tracker.js +183 -0
- package/hooks/lib/rate-limit-tracker.js +253 -0
- package/hooks/lib/reflect-classifier.js +164 -0
- package/hooks/lib/resource-quota.js +122 -0
- package/hooks/lib/retry-jitter.js +165 -0
- package/hooks/lib/risk-engine.js +368 -0
- package/hooks/lib/run-log.js +408 -0
- package/hooks/lib/session-fts.js +379 -0
- package/hooks/lib/session-store.js +293 -0
- package/hooks/lib/singleton-guard.js +159 -0
- package/hooks/lib/skill-auditor.js +588 -0
- package/hooks/lib/sync-status.js +228 -0
- package/hooks/lib/taint-tracker.js +107 -0
- package/hooks/lib/task-service.js +295 -0
- package/hooks/lib/tech-skills-map.js +146 -0
- package/hooks/lib/telegram-cliente.js +159 -0
- package/hooks/lib/telegram-config.js +170 -0
- package/hooks/lib/token-budget.js +156 -0
- package/hooks/lib/token-estimator.js +420 -0
- package/hooks/lib/toon-compressor.js +245 -0
- package/hooks/lib/usage-model.js +183 -0
- package/hooks/lib/variable-resolver.js +230 -0
- package/hooks/linea-estado.js +324 -0
- package/hooks/metricas-evolucion.js +209 -0
- package/hooks/monitor-contexto.js +325 -0
- package/hooks/notificacion-sesion-stop.js +198 -0
- package/hooks/notificacion-telegram-notification.js +4 -0
- package/hooks/notificacion-telegram-subagent.js +4 -0
- package/hooks/notificacion-telegram.js +267 -0
- package/hooks/preservar-estado-pre-compact.js +150 -0
- package/hooks/proteccion-rutas.js +366 -0
- package/hooks/registro-turnos.js +209 -0
- package/hooks/resumen-sesion.js +249 -0
- package/hooks/risk-scoring.js +323 -0
- package/hooks/rotar-audit-auto.js +122 -0
- package/hooks/sugerir-regenerar-inventario.js +170 -0
- package/hooks/telemetria-agentes.js +167 -0
- package/hooks/tracking-costos.js +688 -0
- package/instintos/global.yaml +8 -0
- package/instintos/perfil-usuario.yaml +53 -0
- package/instintos/prompt-appendices.yaml +57 -0
- package/instintos/proyecto.yaml +372 -0
- package/manifiestos/gateway-config.json +77 -0
- package/manifiestos/handoff-context.json +223 -0
- package/manifiestos/hook-profiles.json +44 -0
- package/manifiestos/hooks-config.json +360 -0
- package/manifiestos/modulos.json +1173 -0
- package/manifiestos/perfiles.json +404 -0
- package/package.json +86 -0
- package/plantillas/ESTADO.md +109 -0
- package/plantillas/HOJA-RUTA.md +143 -0
- package/plantillas/PROYECTO.md +122 -0
- package/plantillas/REQUISITOS.md +132 -0
- package/plantillas/auditor-veto-template.md +105 -0
- package/plantillas/github-workflows/README.md +47 -0
- package/plantillas/github-workflows/release-please.yml +44 -0
- package/plantillas/github-workflows/swl-ci.yml +107 -0
- package/plantillas/github-workflows/swl-security.yml +51 -0
- package/plantillas/mcp-mineru.json +13 -0
- package/plantillas/research/ARQUITECTURA.md +220 -0
- package/plantillas/research/FUNCIONALIDADES.md +175 -0
- package/plantillas/research/RESUMEN.md +165 -0
- package/plantillas/research/STACK.md +233 -0
- package/plantillas/research/TRAMPAS.md +299 -0
- package/plantillas/skill-evals-template.json +44 -0
- package/plugin.json +343 -0
- package/reglas/accesibilidad.md +269 -0
- package/reglas/api-diseno.md +400 -0
- package/reglas/arquitectura.md +352 -0
- package/reglas/brevedad-output.md +124 -0
- package/reglas/cloud-infra.md +247 -0
- package/reglas/docs.md +245 -0
- package/reglas/estilo-codigo.md +201 -0
- package/reglas/git-workflow.md +245 -0
- package/reglas/gobernanza.md +271 -0
- package/reglas/harness-claude-code.md +213 -0
- package/reglas/hooks.md +186 -0
- package/reglas/lenguajes/csharp/estilo-codigo.md +231 -0
- package/reglas/lenguajes/csharp/hooks.md +281 -0
- package/reglas/lenguajes/csharp/patrones.md +226 -0
- package/reglas/lenguajes/csharp/seguridad.md +258 -0
- package/reglas/lenguajes/csharp/testing.md +176 -0
- package/reglas/lenguajes/go/estilo-codigo.md +195 -0
- package/reglas/lenguajes/go/hooks.md +249 -0
- package/reglas/lenguajes/go/patrones.md +249 -0
- package/reglas/lenguajes/go/seguridad.md +225 -0
- package/reglas/lenguajes/go/testing.md +272 -0
- package/reglas/lenguajes/java/estilo-codigo.md +217 -0
- package/reglas/lenguajes/java/hooks.md +251 -0
- package/reglas/lenguajes/java/patrones.md +226 -0
- package/reglas/lenguajes/java/seguridad.md +233 -0
- package/reglas/lenguajes/java/testing.md +238 -0
- package/reglas/lenguajes/kotlin/estilo-codigo.md +208 -0
- package/reglas/lenguajes/kotlin/hooks.md +245 -0
- package/reglas/lenguajes/kotlin/patrones.md +201 -0
- package/reglas/lenguajes/kotlin/seguridad.md +202 -0
- package/reglas/lenguajes/kotlin/testing.md +236 -0
- package/reglas/lenguajes/nextjs/estilo-codigo.md +175 -0
- package/reglas/lenguajes/nextjs/hooks.md +186 -0
- package/reglas/lenguajes/nextjs/patrones.md +225 -0
- package/reglas/lenguajes/nextjs/seguridad.md +216 -0
- package/reglas/lenguajes/nextjs/testing.md +193 -0
- package/reglas/lenguajes/php/estilo-codigo.md +228 -0
- package/reglas/lenguajes/php/hooks.md +165 -0
- package/reglas/lenguajes/php/patrones.md +233 -0
- package/reglas/lenguajes/php/seguridad.md +186 -0
- package/reglas/lenguajes/php/testing.md +205 -0
- package/reglas/lenguajes/rust/estilo-codigo.md +207 -0
- package/reglas/lenguajes/rust/hooks.md +240 -0
- package/reglas/lenguajes/rust/patrones.md +250 -0
- package/reglas/lenguajes/rust/seguridad.md +221 -0
- package/reglas/lenguajes/rust/testing.md +194 -0
- package/reglas/lenguajes/swift/estilo-codigo.md +238 -0
- package/reglas/lenguajes/swift/hooks.md +257 -0
- package/reglas/lenguajes/swift/patrones.md +235 -0
- package/reglas/lenguajes/swift/seguridad.md +248 -0
- package/reglas/lenguajes/swift/testing.md +242 -0
- package/reglas/markitdown.md +60 -0
- package/reglas/memoria-consolidada.md +209 -0
- package/reglas/patrones.md +225 -0
- package/reglas/performance.md +195 -0
- package/reglas/pruebas.md +159 -0
- package/reglas/seguridad-agentes.md +351 -0
- package/reglas/seguridad.md +151 -0
- package/reglas/skills-estandar.md +373 -0
- package/reglas/testing.md +193 -0
- package/schemas/agent-contract.json +176 -0
- package/schemas/agent-frontmatter.schema.json +149 -0
- package/schemas/agent-message.schema.json +53 -0
- package/schemas/agent-output-implementacion.schema.json +85 -0
- package/schemas/agent-output-planificacion.schema.json +113 -0
- package/schemas/agent-output-review.schema.json +78 -0
- package/schemas/diary-entry.schema.json +80 -0
- package/schemas/hook-profiles.schema.json +39 -0
- package/schemas/hooks-config.schema.json +74 -0
- package/schemas/instinct.schema.json +115 -0
- package/schemas/modulos.schema.json +29 -0
- package/schemas/perfiles.schema.json +28 -0
- package/schemas/plugin.schema.json +64 -0
- package/schemas/skill-evals.schema.json +95 -0
- package/schemas/skill-frontmatter.schema.json +170 -0
- package/scripts/actualizar.js +145 -0
- package/scripts/audit-skills.sh +78 -0
- package/scripts/auditar-agentes-gaps.js +149 -0
- package/scripts/auditar-cobertura-frameworks.js +241 -0
- package/scripts/auditar-skills-gaps.js +206 -0
- package/scripts/bootstrap-instintos.js +259 -0
- package/scripts/check-update.js +109 -0
- package/scripts/comandos/agents.js +105 -0
- package/scripts/comandos/info.js +108 -0
- package/scripts/comandos/install-asistido.js +186 -0
- package/scripts/comandos/skills.js +211 -0
- package/scripts/configurar-branch-protection.js +418 -0
- package/scripts/daemon-swl.py +388 -0
- package/scripts/desinstalar.js +130 -0
- package/scripts/doctor.js +559 -0
- package/scripts/field-report.js +199 -0
- package/scripts/generar-inventario.js +317 -0
- package/scripts/inbox-tmux-inject.js +161 -0
- package/scripts/inferir-herramientas-permitidas.js +586 -0
- package/scripts/inicializar.js +133 -0
- package/scripts/instalador.js +1031 -0
- package/scripts/instalar-git-hook.js +122 -0
- package/scripts/lib/agp-frontmatter.js +222 -0
- package/scripts/lib/append-con-marcadores.js +199 -0
- package/scripts/lib/artefactos-python.js +43 -0
- package/scripts/lib/audit-query.js +221 -0
- package/scripts/lib/autostart-linux.js +347 -0
- package/scripts/lib/autostart-macos.js +360 -0
- package/scripts/lib/autostart-windows.js +307 -0
- package/scripts/lib/budget-enforcer.js +252 -0
- package/scripts/lib/claude-sessions.js +285 -0
- package/scripts/lib/configurar-ci.js +380 -0
- package/scripts/lib/console-span-exporter.js +92 -0
- package/scripts/lib/contadores-inventario.js +217 -0
- package/scripts/lib/dashboard-widgets.js +290 -0
- package/scripts/lib/detectar-runtime.js +279 -0
- package/scripts/lib/detectar-stack.js +187 -0
- package/scripts/lib/diary-entry.js +234 -0
- package/scripts/lib/drift-detector.js +545 -0
- package/scripts/lib/estado.js +124 -0
- package/scripts/lib/gestor-componentes.js +243 -0
- package/scripts/lib/gitignore-manifest.js +305 -0
- package/scripts/lib/graph-analyze.py +556 -0
- package/scripts/lib/graph-builder.py +485 -0
- package/scripts/lib/graph-cluster.py +259 -0
- package/scripts/lib/health-row.js +168 -0
- package/scripts/lib/hooks-settings.js +789 -0
- package/scripts/lib/manifiestos.js +138 -0
- package/scripts/lib/mc-client.js +137 -0
- package/scripts/lib/notificaciones-telegram.js +1107 -0
- package/scripts/lib/npm-version.js +261 -0
- package/scripts/lib/paquetes-conocidos.js +50 -0
- package/scripts/lib/preservar-usuario.js +586 -0
- package/scripts/lib/prompt-builder.js +264 -0
- package/scripts/lib/resolver-externo.js +332 -0
- package/scripts/lib/schedule-parser.js +305 -0
- package/scripts/lib/scoring-instintos.js +240 -0
- package/scripts/lib/seguridad.js +160 -0
- package/scripts/lib/selector-interactivo.js +152 -0
- package/scripts/lib/semantic-search.js +242 -0
- package/scripts/lib/skill-discovery.js +234 -0
- package/scripts/lib/skill-metrics.js +246 -0
- package/scripts/lib/skill-normalizer.js +112 -0
- package/scripts/lib/skills-hub.js +340 -0
- package/scripts/lib/span-schema.js +134 -0
- package/scripts/lib/tool-cost-analyzer.js +255 -0
- package/scripts/lib/tracing-processor-interface.js +286 -0
- package/scripts/lib/transformadores/base.js +80 -0
- package/scripts/lib/transformadores/claude.js +124 -0
- package/scripts/lib/transformadores/codex.js +115 -0
- package/scripts/lib/transformadores/copilot.js +106 -0
- package/scripts/lib/transformadores/gemini.js +74 -0
- package/scripts/lib/transformadores/index.js +35 -0
- package/scripts/lib/transformadores/opencode.js +75 -0
- package/scripts/lib/ui.js +259 -0
- package/scripts/limpiar-artefactos-python.js +131 -0
- package/scripts/mcp-orchestrator.py +386 -0
- package/scripts/mcp-pool-manager.py +352 -0
- package/scripts/mcp-telemetry.py +378 -0
- package/scripts/poblar-evolvable.js +226 -0
- package/scripts/publicar.js +287 -0
- package/scripts/reflect-skills.js +403 -0
- package/scripts/rotar-audit-logs.js +185 -0
- package/scripts/run-skill-evals.js +242 -0
- package/scripts/smoke-test.js +374 -0
- package/scripts/token-analysis.py +471 -0
- package/scripts/validar-manifest.js +195 -0
- package/scripts/validar-memoria.js +321 -0
- package/scripts/validar-tests-aislamiento.js +184 -0
- package/scripts/validar-tokens-test.js +208 -0
- package/scripts/validar.js +147 -0
- package/scripts/validate-markdown.py +339 -0
- package/scripts/validate-skills.py +385 -0
- package/scripts/vendor/claude-usage/README.md +116 -0
- package/scripts/vendor/claude-usage/cli.py +334 -0
- package/scripts/vendor/claude-usage/dashboard.py +795 -0
- package/scripts/vendor/claude-usage/scanner.py +467 -0
- package/scripts/vendor/markitdown/cli.py +194 -0
- package/scripts/verificar-evolucion.js +289 -0
- package/scripts/verificar-release.js +494 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: django-experto
|
|
3
|
+
description: >
|
|
4
|
+
Patrones de producción para Django: ORM avanzado (select_related, prefetch_related,
|
|
5
|
+
F/Q expressions, anotaciones), migraciones con datos, signals, Django REST Framework,
|
|
6
|
+
Channels y testing con pytest-django. Cargar cuando se implemente cualquier
|
|
7
|
+
componente Django: modelos, vistas, serializers, APIs REST, WebSockets o tareas Celery.
|
|
8
|
+
version: "1.0.0"
|
|
9
|
+
herramientasPermitidas: [Read]
|
|
10
|
+
exclusiones:
|
|
11
|
+
- "No cargar para proyectos FastAPI — el ORM sync de Django y el sistema de migrations no aplican a SQLAlchemy async; cargar `fastapi-experto`."
|
|
12
|
+
- "No cargar para scripting Python standalone que no use el stack de Django (manage.py, settings, apps) — si no hay `DJANGO_SETTINGS_MODULE`, los patrones de este skill no aplican; cargar `patrones-python`."
|
|
13
|
+
- "No cargar para optimizar queries SQL en bases de datos no gestionadas por el ORM de Django — para PostgreSQL raw o sin Django ORM, cargar `sql-optimizacion` o `postgresql-experto`."
|
|
14
|
+
- "No cargar para tareas Celery que no tienen relación con modelos Django — si la tarea Celery es procesamiento puro sin acceso al ORM, cargar `async-python` o `orquestacion-async`."
|
|
15
|
+
evolvable: true # default para skill estandar
|
|
16
|
+
---
|
|
17
|
+
# Django Experto
|
|
18
|
+
|
|
19
|
+
## Cuándo NO cargar
|
|
20
|
+
|
|
21
|
+
- El proyecto usa FastAPI — los patrones de ORM async y DI son distintos; cargar `fastapi-experto`.
|
|
22
|
+
- No hay `DJANGO_SETTINGS_MODULE` en el contexto — si no es un proyecto Django, los patrones no aplican.
|
|
23
|
+
- La optimización de queries es para PostgreSQL sin ORM de Django — cargar `sql-optimizacion`.
|
|
24
|
+
|
|
25
|
+
## Estructura de proyecto recomendada
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
proyecto/
|
|
29
|
+
├── config/
|
|
30
|
+
│ ├── settings/
|
|
31
|
+
│ │ ├── base.py # Configuración base
|
|
32
|
+
│ │ ├── development.py
|
|
33
|
+
│ │ └── production.py
|
|
34
|
+
│ ├── urls.py
|
|
35
|
+
│ └── wsgi.py / asgi.py
|
|
36
|
+
├── apps/
|
|
37
|
+
│ ├── usuarios/ # Cada app es un módulo de negocio
|
|
38
|
+
│ │ ├── models.py
|
|
39
|
+
│ │ ├── views.py
|
|
40
|
+
│ │ ├── serializers.py
|
|
41
|
+
│ │ ├── urls.py
|
|
42
|
+
│ │ ├── signals.py
|
|
43
|
+
│ │ ├── admin.py
|
|
44
|
+
│ │ └── tests/
|
|
45
|
+
│ └── facturas/
|
|
46
|
+
├── common/ # Código compartido entre apps
|
|
47
|
+
│ ├── models.py # Modelos abstractos base
|
|
48
|
+
│ ├── permissions.py
|
|
49
|
+
│ └── pagination.py
|
|
50
|
+
└── manage.py
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## ORM Avanzado
|
|
56
|
+
|
|
57
|
+
### select_related y prefetch_related
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
# NUNCA: N+1 queries
|
|
61
|
+
facturas = Factura.objects.all()
|
|
62
|
+
for factura in facturas:
|
|
63
|
+
print(factura.cliente.nombre) # 1 query por factura
|
|
64
|
+
|
|
65
|
+
# BIEN: select_related para FK/OneToOne (JOIN)
|
|
66
|
+
facturas = Factura.objects.select_related("cliente", "empresa").all()
|
|
67
|
+
|
|
68
|
+
# BIEN: prefetch_related para ManyToMany y relaciones inversas
|
|
69
|
+
facturas = Factura.objects.prefetch_related("items", "items__producto").all()
|
|
70
|
+
|
|
71
|
+
# Prefetch con queryset personalizado
|
|
72
|
+
from django.db.models import Prefetch
|
|
73
|
+
|
|
74
|
+
facturas = Factura.objects.prefetch_related(
|
|
75
|
+
Prefetch(
|
|
76
|
+
"items",
|
|
77
|
+
queryset=ItemFactura.objects.filter(activo=True).select_related("producto"),
|
|
78
|
+
to_attr="items_activos",
|
|
79
|
+
)
|
|
80
|
+
).all()
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Anotaciones y agregaciones
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from django.db.models import Sum, Count, F, Q
|
|
87
|
+
|
|
88
|
+
# Anotar totales calculados en la base de datos
|
|
89
|
+
facturas = Factura.objects.annotate(
|
|
90
|
+
total_calculado=Sum(F("items__cantidad") * F("items__precio_unitario")),
|
|
91
|
+
num_items=Count("items"),
|
|
92
|
+
).filter(total_calculado__gt=1000)
|
|
93
|
+
|
|
94
|
+
# Filtros complejos con Q
|
|
95
|
+
facturas_pendientes = Factura.objects.filter(
|
|
96
|
+
Q(estatus="pendiente") | Q(estatus="vencida"),
|
|
97
|
+
empresa=empresa_actual,
|
|
98
|
+
).exclude(cancelada=True)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### F expressions para actualizaciones atómicas
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from django.db.models import F
|
|
105
|
+
|
|
106
|
+
# Incrementar sin race condition
|
|
107
|
+
Producto.objects.filter(id=producto_id).update(
|
|
108
|
+
stock=F("stock") - cantidad_vendida
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Modelo base abstracto
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
# common/models.py
|
|
116
|
+
import uuid
|
|
117
|
+
from django.db import models
|
|
118
|
+
|
|
119
|
+
class ModeloBase(models.Model):
|
|
120
|
+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
121
|
+
creado_en = models.DateTimeField(auto_now_add=True)
|
|
122
|
+
actualizado_en = models.DateTimeField(auto_now=True)
|
|
123
|
+
creado_por = models.EmailField(max_length=255, blank=True)
|
|
124
|
+
actualizado_por = models.EmailField(max_length=255, blank=True)
|
|
125
|
+
|
|
126
|
+
class Meta:
|
|
127
|
+
abstract = True
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Migraciones
|
|
133
|
+
|
|
134
|
+
### Migraciones con datos (data migrations)
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
# migrations/0023_migrar_estatus_factura.py
|
|
138
|
+
from django.db import migrations
|
|
139
|
+
|
|
140
|
+
def migrar_estatus(apps, schema_editor):
|
|
141
|
+
Factura = apps.get_model("facturas", "Factura")
|
|
142
|
+
Factura.objects.filter(estatus="activa").update(estatus="emitida")
|
|
143
|
+
|
|
144
|
+
def revertir_migrar_estatus(apps, schema_editor):
|
|
145
|
+
Factura = apps.get_model("facturas", "Factura")
|
|
146
|
+
Factura.objects.filter(estatus="emitida").update(estatus="activa")
|
|
147
|
+
|
|
148
|
+
class Migration(migrations.Migration):
|
|
149
|
+
dependencies = [("facturas", "0022_factura_estatus_choices")]
|
|
150
|
+
operations = [
|
|
151
|
+
migrations.RunPython(migrar_estatus, revertir_migrar_estatus),
|
|
152
|
+
]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Optimizaciones en migraciones de producción
|
|
156
|
+
|
|
157
|
+
Para tablas grandes: agregar columna nullable primero, luego data migration,
|
|
158
|
+
luego NOT NULL en siguiente deploy. Esto evita bloqueo de tabla.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Signals
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
# apps/facturas/signals.py
|
|
166
|
+
from django.db.models.signals import post_save
|
|
167
|
+
from django.dispatch import receiver
|
|
168
|
+
from .models import Factura
|
|
169
|
+
|
|
170
|
+
@receiver(post_save, sender=Factura)
|
|
171
|
+
def notificar_cambio_estatus(sender, instance, created, **kwargs):
|
|
172
|
+
if not created and instance.tracker.has_changed("estatus"):
|
|
173
|
+
estatus_anterior = instance.tracker.previous("estatus")
|
|
174
|
+
notificacion_service.enviar(
|
|
175
|
+
usuario_id=instance.cliente_id,
|
|
176
|
+
tipo="CAMBIO_ESTATUS_FACTURA",
|
|
177
|
+
datos={"anterior": estatus_anterior, "nuevo": instance.estatus},
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Conectar signals en AppConfig (apps/facturas/apps.py)
|
|
181
|
+
class FacturasConfig(AppConfig):
|
|
182
|
+
default_auto_field = "django.db.models.BigAutoField"
|
|
183
|
+
name = "apps.facturas"
|
|
184
|
+
|
|
185
|
+
def ready(self):
|
|
186
|
+
import apps.facturas.signals # noqa: F401
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Gotchas / Errores comunes no obvios
|
|
192
|
+
|
|
193
|
+
- **`select_related('empresa__ciudad')` carga la relación pero la query sigue siendo lenta en producción**: `select_related` hace un JOIN en SQL, pero si la tabla `empresa` tiene millones de registros, el JOIN puede ser más caro que dos queries separadas. Causa: `select_related` no siempre es más eficiente que `prefetch_related` — para relaciones de muchos-a-uno con alta cardinalidad, `prefetch_related` puede ser mejor porque hace una query separada con `IN`. Solución: comparar con `EXPLAIN ANALYZE` ambas aproximaciones antes de decidir — la elección depende de los datos reales, no de una regla absoluta.
|
|
194
|
+
- **Migration que agrega columna `NOT NULL` sin default falla en producción con millones de filas pero pasa en tests con datos vacíos**: `ALTER TABLE ADD COLUMN NOT NULL` en PostgreSQL adquiere un `ACCESS EXCLUSIVE LOCK` sobre la tabla completa durante la reescritura. Causa: en tests la tabla está vacía y la operación es instantánea; en producción con millones de filas, la tabla queda bloqueada segundos o minutos. Solución: para columnas NOT NULL en tablas grandes: (1) agregar columna `NULL` primero, (2) hacer backfill con `update_fields`, (3) agregar constraint NOT NULL con `NOT VALID`, (4) validar en una ventana de mantenimiento.
|
|
195
|
+
- **Signal `post_save` llama a código que lanza excepción, dejando la transacción en estado inconsistente**: la signal se ejecuta dentro de la transacción de `save()`, y si la signal falla, la transacción puede quedar en un estado de error que impide más queries. Causa: Django ejecuta signals síncronamente dentro de la transacción activa — si el código de la signal falla, la transacción original también puede fallar. Solución: usar `transaction.on_commit(lambda: mi_tarea.delay())` para code que debe ejecutarse después del commit exitoso, especialmente para llamadas a Celery o servicios externos.
|
|
196
|
+
- **DRF `SerializerMethodField` que accede a `request.user` devuelve `AnonymousUser` en tests aunque el test está autenticado**: el contexto del serializer no se pasa automáticamente cuando se instancia el serializer directamente en tests. Causa: en la vista, DRF pasa `context={'request': request}` al serializer automáticamente, pero al instanciar `MySerializer(instance)` directamente en tests sin el contexto, `self.context.get('request')` devuelve `None`. Solución: pasar el contexto explícitamente: `MySerializer(instance, context={'request': mock_request})`.
|
|
197
|
+
|
|
198
|
+
## Referencias especializadas
|
|
199
|
+
|
|
200
|
+
| Tema | Archivo |
|
|
201
|
+
|------|---------|
|
|
202
|
+
| Custom QuerySets, F/Q expressions, window functions, bulk ops | [recursos/orm-avanzado.md](recursos/orm-avanzado.md) |
|
|
203
|
+
| ViewSets, Serializers, filtros, paginación, throttling, versioning | [recursos/drf-patrones.md](recursos/drf-patrones.md) |
|
|
204
|
+
| pytest-django, Factory Boy, fixtures, mocking, performance | [recursos/testing-django.md](recursos/testing-django.md) |
|
|
205
|
+
| Async views, Channels, WebSockets, Celery, ASGI | [recursos/async-django.md](recursos/async-django.md) |
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# Django Async y Channels — Patrones de Producción
|
|
2
|
+
|
|
3
|
+
## Async views: cuándo y cómo usarlas
|
|
4
|
+
|
|
5
|
+
Django soporta vistas async desde la versión 3.1. Una vista async permite liberar
|
|
6
|
+
el hilo mientras espera I/O (llamadas HTTP externas, operaciones de archivos),
|
|
7
|
+
pero el ORM de Django es **síncrono**: cada query bloquea. La solución es
|
|
8
|
+
`sync_to_async`.
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
# apps/reportes/views.py
|
|
12
|
+
import asyncio
|
|
13
|
+
from django.http import JsonResponse
|
|
14
|
+
from django.views import View
|
|
15
|
+
from asgiref.sync import sync_to_async
|
|
16
|
+
|
|
17
|
+
from apps.clientes.models import Cliente
|
|
18
|
+
from apps.facturas.services import calcular_resumen
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ResumenClienteView(View):
|
|
22
|
+
"""
|
|
23
|
+
Vista async que hace consultas ORM y llamadas HTTP externas en paralelo.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
async def get(self, request, cliente_id: int) -> JsonResponse:
|
|
27
|
+
# sync_to_async envuelve una función síncrona para llamarla desde async
|
|
28
|
+
obtener_cliente = sync_to_async(
|
|
29
|
+
Cliente.objects.select_related("empresa").get
|
|
30
|
+
)
|
|
31
|
+
try:
|
|
32
|
+
cliente = await obtener_cliente(id=cliente_id)
|
|
33
|
+
except Cliente.DoesNotExist:
|
|
34
|
+
return JsonResponse({"error": "Cliente no encontrado."}, status=404)
|
|
35
|
+
|
|
36
|
+
# Ejecutar ORM y llamada HTTP externa en paralelo
|
|
37
|
+
resumen, datos_externos = await asyncio.gather(
|
|
38
|
+
sync_to_async(calcular_resumen)(cliente.id),
|
|
39
|
+
self._obtener_datos_externos(cliente.rfc),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
return JsonResponse({"resumen": resumen, "externos": datos_externos})
|
|
43
|
+
|
|
44
|
+
async def _obtener_datos_externos(self, rfc: str) -> dict:
|
|
45
|
+
import httpx
|
|
46
|
+
async with httpx.AsyncClient(timeout=5.0) as client:
|
|
47
|
+
respuesta = await client.get(f"https://api.sat.mx/rfc/{rfc}")
|
|
48
|
+
respuesta.raise_for_status()
|
|
49
|
+
return respuesta.json()
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## sync_to_async y async_to_sync
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from asgiref.sync import sync_to_async, async_to_sync
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# sync_to_async — llamar código síncrono desde un contexto async
|
|
61
|
+
# thread_sensitive=True (default): corre en el mismo hilo principal
|
|
62
|
+
# thread_sensitive=False: corre en un hilo de pool — para operaciones CPU-bound
|
|
63
|
+
|
|
64
|
+
obtener_facturas = sync_to_async(
|
|
65
|
+
lambda: list(Factura.objects.filter(estatus="pendiente").select_related("cliente")),
|
|
66
|
+
thread_sensitive=False, # pool de hilos para operaciones pesadas
|
|
67
|
+
)
|
|
68
|
+
facturas = await obtener_facturas()
|
|
69
|
+
|
|
70
|
+
# Decorador: convierte función síncrona completa en async
|
|
71
|
+
@sync_to_async
|
|
72
|
+
def crear_usuario_sync(email: str, nombre: str) -> Usuario:
|
|
73
|
+
return Usuario.objects.create_user(email=email, nombre=nombre)
|
|
74
|
+
|
|
75
|
+
usuario = await crear_usuario_sync("a@b.com", "Ana")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# async_to_sync — llamar código async desde un contexto síncrono
|
|
79
|
+
# Útil en management commands, scripts de migración o Celery tasks
|
|
80
|
+
|
|
81
|
+
@async_to_sync
|
|
82
|
+
async def tarea_async_en_celery():
|
|
83
|
+
async with httpx.AsyncClient() as client:
|
|
84
|
+
await client.post("https://webhook.ejemplo.com/evento", json={"tipo": "SYNC"})
|
|
85
|
+
|
|
86
|
+
# En una tarea Celery síncrona:
|
|
87
|
+
@shared_task
|
|
88
|
+
def notificar_webhook():
|
|
89
|
+
tarea_async_en_celery() # compatible con Celery síncrono
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Django Channels: configuración y channel layers
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# config/asgi.py
|
|
98
|
+
import os
|
|
99
|
+
from django.core.asgi import get_asgi_application
|
|
100
|
+
from channels.routing import ProtocolTypeRouter, URLRouter
|
|
101
|
+
from channels.auth import AuthMiddlewareStack
|
|
102
|
+
from channels.security.websocket import AllowedHostsOriginValidator
|
|
103
|
+
|
|
104
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
|
|
105
|
+
|
|
106
|
+
django_asgi_app = get_asgi_application()
|
|
107
|
+
|
|
108
|
+
from apps.notificaciones.routing import websocket_urlpatterns # noqa: E402
|
|
109
|
+
|
|
110
|
+
application = ProtocolTypeRouter({
|
|
111
|
+
"http": django_asgi_app,
|
|
112
|
+
"websocket": AllowedHostsOriginValidator(
|
|
113
|
+
AuthMiddlewareStack(
|
|
114
|
+
URLRouter(websocket_urlpatterns)
|
|
115
|
+
)
|
|
116
|
+
),
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# config/settings/base.py
|
|
121
|
+
CHANNEL_LAYERS = {
|
|
122
|
+
"default": {
|
|
123
|
+
"BACKEND": "channels_redis.core.RedisChannelLayer",
|
|
124
|
+
"CONFIG": {
|
|
125
|
+
"hosts": [("redis", 6379)],
|
|
126
|
+
"capacity": 1500, # mensajes máximos en cola por canal
|
|
127
|
+
"expiry": 60, # segundos de TTL para mensajes
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# apps/notificaciones/routing.py
|
|
133
|
+
from django.urls import re_path
|
|
134
|
+
from . import consumers
|
|
135
|
+
|
|
136
|
+
websocket_urlpatterns = [
|
|
137
|
+
re_path(r"ws/notificaciones/(?P<empresa_id>\d+)/$", consumers.NotificacionConsumer.as_asgi()),
|
|
138
|
+
]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## WebSocket consumer completo
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
# apps/notificaciones/consumers.py
|
|
147
|
+
import json
|
|
148
|
+
from channels.generic.websocket import AsyncWebsocketConsumer
|
|
149
|
+
from channels.db import database_sync_to_async
|
|
150
|
+
from django.contrib.auth.models import AnonymousUser
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class NotificacionConsumer(AsyncWebsocketConsumer):
|
|
154
|
+
"""
|
|
155
|
+
Consumer WebSocket para notificaciones en tiempo real por empresa.
|
|
156
|
+
Grupo: notificaciones_empresa_{empresa_id}
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
async def connect(self) -> None:
|
|
160
|
+
# Rechazar conexiones no autenticadas
|
|
161
|
+
if isinstance(self.scope["user"], AnonymousUser):
|
|
162
|
+
await self.close(code=4001)
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
self.empresa_id = self.scope["url_route"]["kwargs"]["empresa_id"]
|
|
166
|
+
self.nombre_grupo = f"notificaciones_empresa_{self.empresa_id}"
|
|
167
|
+
|
|
168
|
+
# Verificar que el usuario pertenece a la empresa
|
|
169
|
+
tiene_acceso = await self._verificar_acceso()
|
|
170
|
+
if not tiene_acceso:
|
|
171
|
+
await self.close(code=4003)
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
# Unirse al grupo del channel layer (Redis)
|
|
175
|
+
await self.channel_layer.group_add(self.nombre_grupo, self.channel_name)
|
|
176
|
+
await self.accept()
|
|
177
|
+
|
|
178
|
+
# Enviar notificaciones pendientes al conectar
|
|
179
|
+
pendientes = await self._obtener_pendientes()
|
|
180
|
+
if pendientes:
|
|
181
|
+
await self.send(text_data=json.dumps({
|
|
182
|
+
"tipo": "pendientes",
|
|
183
|
+
"datos": pendientes,
|
|
184
|
+
}))
|
|
185
|
+
|
|
186
|
+
async def disconnect(self, close_code: int) -> None:
|
|
187
|
+
if hasattr(self, "nombre_grupo"):
|
|
188
|
+
await self.channel_layer.group_discard(self.nombre_grupo, self.channel_name)
|
|
189
|
+
|
|
190
|
+
async def receive(self, text_data: str) -> None:
|
|
191
|
+
"""Manejar mensajes entrantes del cliente WebSocket."""
|
|
192
|
+
try:
|
|
193
|
+
datos = json.loads(text_data)
|
|
194
|
+
except json.JSONDecodeError:
|
|
195
|
+
await self.send(text_data=json.dumps({"error": "JSON inválido"}))
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
tipo = datos.get("tipo")
|
|
199
|
+
if tipo == "marcar_leida":
|
|
200
|
+
notificacion_id = datos.get("id")
|
|
201
|
+
await self._marcar_leida(notificacion_id)
|
|
202
|
+
await self.send(text_data=json.dumps({
|
|
203
|
+
"tipo": "confirmacion",
|
|
204
|
+
"id": notificacion_id,
|
|
205
|
+
}))
|
|
206
|
+
|
|
207
|
+
# Manejador de eventos del channel layer — llamado por group_send
|
|
208
|
+
async def notificacion_nueva(self, evento: dict) -> None:
|
|
209
|
+
"""Recibe mensaje del channel layer y lo reenvía al WebSocket."""
|
|
210
|
+
await self.send(text_data=json.dumps({
|
|
211
|
+
"tipo": "nueva",
|
|
212
|
+
"datos": evento["datos"],
|
|
213
|
+
}))
|
|
214
|
+
|
|
215
|
+
@database_sync_to_async
|
|
216
|
+
def _verificar_acceso(self) -> bool:
|
|
217
|
+
usuario = self.scope["user"]
|
|
218
|
+
return str(usuario.empresa_id) == str(self.empresa_id)
|
|
219
|
+
|
|
220
|
+
@database_sync_to_async
|
|
221
|
+
def _obtener_pendientes(self) -> list:
|
|
222
|
+
from apps.notificaciones.models import Notificacion
|
|
223
|
+
return list(
|
|
224
|
+
Notificacion.objects
|
|
225
|
+
.filter(empresa_id=self.empresa_id, leida=False)
|
|
226
|
+
.order_by("-creado_en")[:20]
|
|
227
|
+
.values("id", "mensaje", "tipo", "creado_en")
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
@database_sync_to_async
|
|
231
|
+
def _marcar_leida(self, notificacion_id: int) -> None:
|
|
232
|
+
from apps.notificaciones.models import Notificacion
|
|
233
|
+
Notificacion.objects.filter(id=notificacion_id).update(leida=True)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
# Enviar desde cualquier parte del sistema (no solo desde consumers)
|
|
237
|
+
# apps/notificaciones/services.py
|
|
238
|
+
from channels.layers import get_channel_layer
|
|
239
|
+
from asgiref.sync import async_to_sync
|
|
240
|
+
|
|
241
|
+
def enviar_notificacion_ws(empresa_id: int, mensaje: str, tipo: str = "info") -> None:
|
|
242
|
+
"""Envía un evento a todos los WebSockets conectados de la empresa."""
|
|
243
|
+
channel_layer = get_channel_layer()
|
|
244
|
+
async_to_sync(channel_layer.group_send)(
|
|
245
|
+
f"notificaciones_empresa_{empresa_id}",
|
|
246
|
+
{
|
|
247
|
+
"type": "notificacion_nueva", # mapea al método del consumer
|
|
248
|
+
"datos": {"mensaje": mensaje, "tipo": tipo},
|
|
249
|
+
},
|
|
250
|
+
)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Celery con Django: configuración completa
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
# config/celery.py
|
|
259
|
+
import os
|
|
260
|
+
from celery import Celery
|
|
261
|
+
from celery.schedules import crontab
|
|
262
|
+
|
|
263
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
|
|
264
|
+
|
|
265
|
+
app = Celery("mi_proyecto")
|
|
266
|
+
app.config_from_object("django.conf:settings", namespace="CELERY")
|
|
267
|
+
app.autodiscover_tasks() # descubre tasks.py en todas las INSTALLED_APPS
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
# config/settings/base.py — configuración de Celery
|
|
271
|
+
CELERY_BROKER_URL = env("CELERY_BROKER_URL", default="redis://localhost:6379/0")
|
|
272
|
+
CELERY_RESULT_BACKEND = env("CELERY_RESULT_BACKEND", default="redis://localhost:6379/1")
|
|
273
|
+
CELERY_ACCEPT_CONTENT = ["json"]
|
|
274
|
+
CELERY_TASK_SERIALIZER = "json"
|
|
275
|
+
CELERY_RESULT_SERIALIZER = "json"
|
|
276
|
+
CELERY_TIMEZONE = "America/Mexico_City"
|
|
277
|
+
|
|
278
|
+
# Routing: separar colas por prioridad
|
|
279
|
+
CELERY_TASK_ROUTES = {
|
|
280
|
+
"apps.facturas.tasks.*": {"queue": "facturas"},
|
|
281
|
+
"apps.reportes.tasks.*": {"queue": "reportes"},
|
|
282
|
+
"apps.notificaciones.tasks.*": {"queue": "emails", "priority": 9},
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
# Beat schedule: tareas periódicas
|
|
286
|
+
CELERY_BEAT_SCHEDULE = {
|
|
287
|
+
"cerrar-facturas-vencidas": {
|
|
288
|
+
"task": "apps.facturas.tasks.cerrar_facturas_vencidas",
|
|
289
|
+
"schedule": crontab(hour=2, minute=0), # cada día a las 2 AM
|
|
290
|
+
},
|
|
291
|
+
"limpiar-sesiones-expiradas": {
|
|
292
|
+
"task": "apps.usuarios.tasks.limpiar_sesiones",
|
|
293
|
+
"schedule": crontab(hour=3, minute=30, day_of_week="sunday"),
|
|
294
|
+
},
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# Chord y Chain para flujos de tareas
|
|
299
|
+
# apps/reportes/tasks.py
|
|
300
|
+
from celery import shared_task, chord, chain
|
|
301
|
+
from celery.utils.log import get_task_logger
|
|
302
|
+
|
|
303
|
+
logger = get_task_logger(__name__)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
@shared_task(bind=True, max_retries=3)
|
|
307
|
+
def generar_reporte_mensual(self, empresa_id: int, mes: int, anio: int) -> dict:
|
|
308
|
+
"""Genera reporte mensual con retry automático."""
|
|
309
|
+
try:
|
|
310
|
+
datos = ReporteService().generar(empresa_id=empresa_id, mes=mes, anio=anio)
|
|
311
|
+
return {"estado": "ok", "registros": len(datos)}
|
|
312
|
+
except Exception as exc:
|
|
313
|
+
logger.error("Error generando reporte", exc_info=exc)
|
|
314
|
+
raise self.retry(exc=exc, countdown=60 * (2 ** self.request.retries))
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
@shared_task
|
|
318
|
+
def enviar_reporte_por_email(resultado: dict, destinatario: str) -> None:
|
|
319
|
+
"""Callback de chord: recibe resultados de tareas paralelas."""
|
|
320
|
+
logger.info("Enviando reporte", resultado=resultado, destinatario=destinatario)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def orquestar_reportes_mensuales(empresa_ids: list[int], mes: int, anio: int) -> None:
|
|
324
|
+
"""Genera reportes en paralelo y envía resumen al terminar."""
|
|
325
|
+
tareas = [generar_reporte_mensual.s(eid, mes, anio) for eid in empresa_ids]
|
|
326
|
+
workflow = chord(tareas)(
|
|
327
|
+
enviar_reporte_por_email.s(destinatario="ops@empresa.com")
|
|
328
|
+
)
|
|
329
|
+
workflow.delay()
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## ASGI deployment: Daphne, Uvicorn, Hypercorn
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
# Opción 1: Daphne — servidor oficial de Django Channels
|
|
338
|
+
# pip install daphne
|
|
339
|
+
# Procfile / supervisord:
|
|
340
|
+
# web: daphne -b 0.0.0.0 -p 8000 config.asgi:application
|
|
341
|
+
# channels: celery -A config worker -Q facturas,emails --concurrency=4
|
|
342
|
+
# beat: celery -A config beat --scheduler django_celery_beat.schedulers:DatabaseScheduler
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
# Opción 2: Uvicorn con Gunicorn como gestor de procesos
|
|
346
|
+
# pip install uvicorn[standard] gunicorn
|
|
347
|
+
# gunicorn config.asgi:application \
|
|
348
|
+
# --worker-class uvicorn.workers.UvicornWorker \
|
|
349
|
+
# --workers 4 \
|
|
350
|
+
# --bind 0.0.0.0:8000 \
|
|
351
|
+
# --timeout 60
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
# Opción 3: Hypercorn — soporte HTTP/2 y HTTP/3
|
|
355
|
+
# pip install hypercorn
|
|
356
|
+
# hypercorn config.asgi:application --bind 0.0.0.0:8000 --workers 4
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
# docker-compose.yml (fragmento para producción con Channels)
|
|
360
|
+
# services:
|
|
361
|
+
# web:
|
|
362
|
+
# command: daphne -b 0.0.0.0 -p 8000 config.asgi:application
|
|
363
|
+
# environment:
|
|
364
|
+
# - DJANGO_SETTINGS_MODULE=config.settings.production
|
|
365
|
+
# worker:
|
|
366
|
+
# command: celery -A config worker -Q default,facturas,emails -c 4
|
|
367
|
+
# beat:
|
|
368
|
+
# command: celery -A config beat -l info
|
|
369
|
+
# redis:
|
|
370
|
+
# image: redis:7-alpine
|
|
371
|
+
# command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Reglas clave para async en Django
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
BIEN: async def vista(request) con await sync_to_async(qs.get)()
|
|
380
|
+
MAL: async def vista(request) con Cliente.objects.get() directo — bloquea el event loop
|
|
381
|
+
|
|
382
|
+
BIEN: @database_sync_to_async en métodos de Consumer
|
|
383
|
+
MAL: queries ORM directas en métodos async de Consumer sin envoltura
|
|
384
|
+
|
|
385
|
+
BIEN: CELERY_TASK_ALWAYS_EAGER=True en settings de test
|
|
386
|
+
MAL: tests que dependen de Redis real para verificar tareas Celery
|
|
387
|
+
|
|
388
|
+
BIEN: channel_layer.group_send() para fan-out a múltiples WebSockets
|
|
389
|
+
MAL: iterar sobre conexiones activas manualmente sin channel layers
|
|
390
|
+
```
|