From 781f202c90e3f3cbc027cbe62aeb702e303a5508 Mon Sep 17 00:00:00 2001 From: Bogdan2005-19 Date: Wed, 29 Apr 2026 10:15:05 +0200 Subject: [PATCH 1/5] Test --- web_app/prueba.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 web_app/prueba.py diff --git a/web_app/prueba.py b/web_app/prueba.py new file mode 100644 index 0000000..8745983 --- /dev/null +++ b/web_app/prueba.py @@ -0,0 +1,77 @@ +# views_prueba.py + +def funcion_simple(request): + """ + Complejidad esperada: 2 + Comportamiento: Silencio. Pasa la validación sin imprimir nada. + Desglose: 1 (base) + 1 (if) = 2. + """ + if request.method == 'POST': + return "Guardado" + return "Formulario" + + +def funcion_mediana(usuario): + """ + Complejidad esperada: 11 + Comportamiento: Advertencia (⚠️ AVISO). Está en el rango de (Límite - 5). + Desglose: + 1 (base) + 1(if) + 1(if) + 1(elif) + 1(for) + 1(if) + 1(elif) + + 1(elif) + 1(and) + 1(except) + 1(if) = 11. + """ + nivel = 0 + if usuario.is_authenticated: + if usuario.is_staff: + nivel = 1 + elif usuario.is_superuser: + nivel = 2 + + for permiso in usuario.permisos: + if permiso == 'leer': + pass + elif permiso == 'escribir': + pass + elif permiso == 'borrar' and usuario.is_admin: + pass + + try: + procesar(usuario) + except Exception: + if usuario.reintentos > 0: + pass + + return nivel + + +def funcion_compleja(request): + """ + Complejidad esperada: 17 + Comportamiento: Fallo (❌ FALLO). Supera el límite de 15. Rompe la Pull Request. + Desglose: + 1 (base) + 1(if) + 1(if) + 1(if) + 1(and) + 1(for) + 1(if) + 1(elif) + + 1(elif) + 1(or) + 1(elif) + 1(if) + 1(elif) + 1(elif) + 1(for) + + 1(if) + 1(if) = 17. + """ + if request.method == 'GET': + if request.GET.get('filtro1'): + if request.GET.get('filtro2') and request.GET.get('filtro3'): + for item in request.items: + if item.valido: + pass + elif item.pendiente: + pass + elif item.rechazado or item.expirado: + pass + elif request.GET.get('orden'): + if request.GET['orden'] == 'asc': + pass + elif request.GET['orden'] == 'desc': + pass + elif request.method == 'POST': + for x in range(10): + if x == 5: + continue + if x == 9: + break + + return "Fin" \ No newline at end of file From 3601d0d58b8517a925f6c39488dfd370d6a8dcb8 Mon Sep 17 00:00:00 2001 From: Bogdan2005-19 Date: Wed, 29 Apr 2026 10:51:19 +0200 Subject: [PATCH 2/5] Yes --- .github/workflows/CyclomaticComplexity.yml | 23 ++--- CyclomaticMetric.py | 111 +++++++++++++++------ 2 files changed, 94 insertions(+), 40 deletions(-) diff --git a/.github/workflows/CyclomaticComplexity.yml b/.github/workflows/CyclomaticComplexity.yml index 312f5df..fc794ad 100644 --- a/.github/workflows/CyclomaticComplexity.yml +++ b/.github/workflows/CyclomaticComplexity.yml @@ -1,36 +1,35 @@ -name: Check Complexity PR +name: Python Complexity Check on: - push: pull_request: types: [opened, synchronize, reopened] jobs: - check-complexity: + complexity-analysis: runs-on: ubuntu-latest steps: - name: Checkout del código uses: actions/checkout@v4 with: - fetch-depth: 0 # Obligatorio para descargar el historial de Git y poder comparar ramas + fetch-depth: 0 # Necesario para comparar ramas y detectar archivos cambiados - name: Configurar Python uses: actions/setup-python@v5 with: python-version: '3.12' - - name: Analizar solo archivos modificados + - name: Ejecutar análisis de complejidad run: | - RAMA_DESTINO="origin/${{ github.base_ref }}" - - ARCHIVOS=$(git diff --name-only --diff-filter=AM $RAMA_DESTINO HEAD | grep '\.py$' || true) + ARCHIVOS=$(git diff --name-only --diff-filter=AM origin/${{ github.base_ref }} HEAD | grep '\.py$' || true) if [ -z "$ARCHIVOS" ]; then - echo "No hay archivos Python modificados en esta Pull Request." + echo "No se encontraron archivos Python modificados en esta Pull Request." exit 0 fi - - echo "Archivos detectados para analizar:" + + echo "Analizando los siguientes archivos:" echo "$ARCHIVOS" - python CyclomaticMetric.py $ARCHIVOS \ No newline at end of file + python mi_complejidad.py $ARCHIVOS > resultados_complejidad.json + + cat resultados_complejidad.json \ No newline at end of file diff --git a/CyclomaticMetric.py b/CyclomaticMetric.py index 7291045..2d835c4 100644 --- a/CyclomaticMetric.py +++ b/CyclomaticMetric.py @@ -1,5 +1,7 @@ import os -import sys, ast +import sys +import ast +import json class AnalizadorComplejidad(ast.NodeVisitor): def __init__(self): @@ -46,7 +48,6 @@ def visit_GeneratorExp(self, node): self.generic_visit(node) def calcular(codigo_fuente: str) -> int: - """Parsea el código y devuelve su complejidad ciclomática.""" try: arbol = ast.parse(codigo_fuente) except SyntaxError: @@ -56,37 +57,91 @@ def calcular(codigo_fuente: str) -> int: visitante.visit(arbol) return visitante.complejidad -def analizar_proyecto(ruta_directorio: str, limite_complejidad: int = 15): - exito = True +DIRECTORIOS_IGNORADOS = {'venv', 'env', '.venv', 'migrations', '__pycache__', '.git', 'tests'} +ARCHIVOS_IGNORADOS = {'manage.py', 'settings.py', 'wsgi.py', 'asgi.py'} + +def es_archivo_valido(ruta): + nombre = os.path.basename(ruta) + + if nombre in ARCHIVOS_IGNORADOS or not nombre.endswith('.py'): + return False + + partes_ruta = ruta.split(os.sep) + for ignorado in DIRECTORIOS_IGNORADOS: + if ignorado in partes_ruta: + return False + + return True + +def analizar_archivos(rutas_archivos: list, limite_complejidad: int = 15): + resultados_array = [] + archivos_procesados = 0 + archivos_fallidos = 0 - for raiz, _, archivos in os.walk(ruta_directorio): - if 'migrations' in raiz or 'venv' in raiz or '__pycache__' in raiz or '.git' in raiz: + for ruta in rutas_archivos: + if not os.path.exists(ruta): + continue + + if not es_archivo_valido(ruta): continue - for archivo in archivos: - if '__init__' in archivo: - continue - if archivo.endswith('.py'): - ruta_completa = os.path.join(raiz, archivo) - - with open(ruta_completa, 'r', encoding='utf-8') as f: - contenido = f.read() - - complejidad = calcular(contenido) - - if complejidad > limite_complejidad: - print(f"❌ PELIGRO: {ruta_completa} tiene complejidad {complejidad}") - exito = False - elif complejidad >= limite_complejidad - 5: - print(f"⚠️ AVISO: {ruta_completa} tiene complejidad {complejidad}") - else: - # Añade esto para ver el escaneo completo - print(f"✅ OK: {ruta_completa} (Complejidad: {complejidad})") - if not exito: + with open(ruta, 'r', encoding='utf-8') as f: + contenido = f.read() + + complejidad = calcular(contenido) + archivos_procesados += 1 + + # Asignar el status_code que solicitaste + if complejidad > limite_complejidad: + status_code = "DANGER" + archivos_fallidos += 1 + elif complejidad >= limite_complejidad - 5: + status_code = "WARN" + else: + status_code = "OK" + + resultados_array.append({ + "file": ruta, + "complexity": complejidad, + "status_code": status_code + }) + + # Construcción del diccionario final con la estructura requerida + salida_json = { + "analysis_type": "Cyclomatic Complexity", + "threshold": limite_complejidad, + "summary": { + "total_files": archivos_procesados, + "failed_files": archivos_fallidos + }, + "results": resultados_array + } + + # Imprimir el JSON formateado con indentación + print(json.dumps(salida_json, indent=4)) + + # Mantener el código de salida del sistema para que falle en el CI si es necesario + if archivos_fallidos > 0: sys.exit(1) else: - print("✅ Análisis completado. El código cumple con los estándares.") sys.exit(0) if __name__ == "__main__": - analizar_proyecto('./') \ No newline at end of file + archivos_a_analizar = sys.argv[1:] + limite = 15 + + if not archivos_a_analizar: + # Estructura vacía consistente si no hay archivos + salida_vacia = { + "analysis_type": "Cyclomatic Complexity", + "threshold": limite, + "summary": { + "total_files": 0, + "failed_files": 0 + }, + "results": [] + } + print(json.dumps(salida_vacia, indent=4)) + sys.exit(0) + + analizar_archivos(archivos_a_analizar, limite) \ No newline at end of file From 6e46b73a4dd5df520a2b663e6464fab4fd521256 Mon Sep 17 00:00:00 2001 From: Bogdan Nicolae Stefanescu <159245364+Bogdan2005-19@users.noreply.github.com> Date: Wed, 29 Apr 2026 10:54:37 +0200 Subject: [PATCH 3/5] Change script name for cyclomatic complexity analysis --- .github/workflows/CyclomaticComplexity.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CyclomaticComplexity.yml b/.github/workflows/CyclomaticComplexity.yml index fc794ad..b7a1f71 100644 --- a/.github/workflows/CyclomaticComplexity.yml +++ b/.github/workflows/CyclomaticComplexity.yml @@ -30,6 +30,6 @@ jobs: echo "Analizando los siguientes archivos:" echo "$ARCHIVOS" - python mi_complejidad.py $ARCHIVOS > resultados_complejidad.json + python CyclomaticMetric.py $ARCHIVOS > resultados_complejidad.json - cat resultados_complejidad.json \ No newline at end of file + cat resultados_complejidad.json From 8bdaafbebd76bf58ed707d1c0aa305ce2f646835 Mon Sep 17 00:00:00 2001 From: Bogdan Nicolae Stefanescu <159245364+Bogdan2005-19@users.noreply.github.com> Date: Wed, 29 Apr 2026 10:58:58 +0200 Subject: [PATCH 4/5] Simplify exit code handling in CyclomaticMetric.py Remove conditional exit code handling for failed files. --- CyclomaticMetric.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/CyclomaticMetric.py b/CyclomaticMetric.py index 2d835c4..169a977 100644 --- a/CyclomaticMetric.py +++ b/CyclomaticMetric.py @@ -119,12 +119,7 @@ def analizar_archivos(rutas_archivos: list, limite_complejidad: int = 15): # Imprimir el JSON formateado con indentación print(json.dumps(salida_json, indent=4)) - - # Mantener el código de salida del sistema para que falle en el CI si es necesario - if archivos_fallidos > 0: - sys.exit(1) - else: - sys.exit(0) + sys.exit(0) if __name__ == "__main__": archivos_a_analizar = sys.argv[1:] @@ -144,4 +139,4 @@ def analizar_archivos(rutas_archivos: list, limite_complejidad: int = 15): print(json.dumps(salida_vacia, indent=4)) sys.exit(0) - analizar_archivos(archivos_a_analizar, limite) \ No newline at end of file + analizar_archivos(archivos_a_analizar, limite) From 609dccee0bd310b67d64b208428aabd05b1030a8 Mon Sep 17 00:00:00 2001 From: Bogdan Nicolae Stefanescu <159245364+Bogdan2005-19@users.noreply.github.com> Date: Wed, 29 Apr 2026 11:00:51 +0200 Subject: [PATCH 5/5] Simplify Cyclomatic Complexity workflow Removed check for modified Python files before analysis. --- .github/workflows/CyclomaticComplexity.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/CyclomaticComplexity.yml b/.github/workflows/CyclomaticComplexity.yml index b7a1f71..b7f4f9d 100644 --- a/.github/workflows/CyclomaticComplexity.yml +++ b/.github/workflows/CyclomaticComplexity.yml @@ -22,14 +22,6 @@ jobs: run: | ARCHIVOS=$(git diff --name-only --diff-filter=AM origin/${{ github.base_ref }} HEAD | grep '\.py$' || true) - if [ -z "$ARCHIVOS" ]; then - echo "No se encontraron archivos Python modificados en esta Pull Request." - exit 0 - fi - - echo "Analizando los siguientes archivos:" - echo "$ARCHIVOS" - python CyclomaticMetric.py $ARCHIVOS > resultados_complejidad.json cat resultados_complejidad.json