Skip to content

Commit d8ba733

Browse files
authored
chore(management): Pending task table tracker (#2494)
* Update pending-tasks.yml * Update pending-tasks.yml * Update pending-tasks.yml * Update pending-tasks.yml * Update pending-tasks.yml * separate script * Update pending-tasks.yml * Update pending-tasks.py * Update pending-tasks.py * Update pending-tasks.py * Update pending-tasks.py * Update pending-tasks.py * change directory * Update pending-tasks.py
1 parent f95135f commit d8ba733

File tree

4 files changed

+172
-50
lines changed

4 files changed

+172
-50
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import os
2+
from github import Github
3+
from datetime import datetime
4+
5+
def count_checkboxes(text):
6+
"""Count checked and unchecked checkboxes in a text"""
7+
if text is None:
8+
return 0, 0
9+
10+
checked = text.count('- [x]') + text.count('- [X]')
11+
unchecked = text.count('- [ ]')
12+
return checked, unchecked
13+
14+
def format_time_difference(days):
15+
"""Format time difference in years, months and days"""
16+
years = days // 365
17+
remaining_days = days % 365
18+
months = remaining_days // 30
19+
remaining_days = remaining_days % 30
20+
21+
# Build the formatted string based on the components
22+
formatted_time = []
23+
if years > 0:
24+
formatted_time.append(f"{years}yr{'s' if years > 1 else ''}")
25+
if months > 0:
26+
formatted_time.append(f"{months}mo")
27+
if remaining_days > 0 or (years == 0 and months == 0):
28+
formatted_time.append(f"{remaining_days}d")
29+
if remaining_days == 0 and years == 0 and months == 0:
30+
return "Today"
31+
32+
return ", ".join(formatted_time)
33+
34+
def analyze_issues_with_pending_tasks(token, owner, repo, target_issue_number):
35+
"""Analyze closed issues with pending checkboxes and update the report"""
36+
37+
# Repository base URL
38+
repo_url = f'https://github.com/{owner}/{repo}'
39+
40+
# Create a Github class instance using the access token
41+
g = Github(token)
42+
43+
# Get the repository
44+
repository = g.get_repo(f'{owner}/{repo}')
45+
46+
# Search for closed issues in the repository
47+
closed_issues = repository.get_issues(state='closed')
48+
49+
# Lista para almacenar las issues con checkboxes desactivados
50+
issues_con_checkboxes_desactivados = []
51+
52+
# Itera sobre las issues cerradas
53+
for issue in closed_issues:
54+
# Exclude the report issue (where this report is displayed)
55+
if issue.number == target_issue_number:
56+
continue
57+
58+
# Only include issues closed as completed (exclude duplicates and not planned)
59+
if hasattr(issue, 'state_reason') and issue.state_reason != 'completed':
60+
continue
61+
62+
has_unchecked = False
63+
64+
# Verify that the issue body is not None
65+
if issue.body is not None and '- [ ]' in issue.body:
66+
has_unchecked = True
67+
68+
# If not found in the body, search in comments
69+
if not has_unchecked:
70+
comments = issue.get_comments()
71+
for comment in comments:
72+
if comment.body is not None and '- [ ]' in comment.body:
73+
has_unchecked = True
74+
break
75+
76+
if has_unchecked:
77+
issues_con_checkboxes_desactivados.append(issue)
78+
79+
# Sort issues by last modification date (most recent first)
80+
issues_con_checkboxes_desactivados.sort(key=lambda x: x.updated_at, reverse=True)
81+
82+
# Print the list of issues with unchecked checkboxes
83+
print("Issues con checkboxes desactivados:")
84+
for issue in issues_con_checkboxes_desactivados:
85+
issue_url = f'{repo_url}/issues/{issue.number}'
86+
print(f"Issue #{issue.number}: {issue.title} ({issue_url})")
87+
88+
# Create markdown table
89+
table_header = "| Issue | Issue name | Progress | Last Modification |\n"
90+
table_separator = "|-------|:------|:----------|:------------------|\n"
91+
table_rows = ""
92+
93+
for issue in issues_con_checkboxes_desactivados:
94+
issue_link = f"[#{issue.number}]({repo_url}/issues/{issue.number})"
95+
issue_name = issue.title.replace("|", "\\|") # Escape pipes in the title
96+
97+
# Calculate days since last modification
98+
current_time = datetime.now(issue.updated_at.tzinfo)
99+
days_since_modification = (current_time - issue.updated_at).days
100+
last_modified = format_time_difference(days_since_modification)
101+
102+
# Contar checkboxes en el body de la issue
103+
checked_body, unchecked_body = count_checkboxes(issue.body)
104+
105+
# Contar checkboxes en los comentarios
106+
checked_comments = 0
107+
unchecked_comments = 0
108+
comments = issue.get_comments()
109+
for comment in comments:
110+
c_checked, c_unchecked = count_checkboxes(comment.body)
111+
checked_comments += c_checked
112+
unchecked_comments += c_unchecked
113+
114+
# Total checkboxes
115+
total_checked = checked_body + checked_comments
116+
total_unchecked = unchecked_body + unchecked_comments
117+
total_checkboxes = total_checked + total_unchecked
118+
119+
progress = f"{total_checked} / {total_checkboxes}"
120+
121+
table_rows += f"| {issue_link} | {issue_name} | {progress} | {last_modified} |\n"
122+
123+
# Create the new issue body
124+
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S UTC")
125+
new_body = f"""
126+
127+
{table_header}{table_separator}{table_rows}
128+
129+
**Last Updated:** {current_time}
130+
**Total Issues Found:** {len(issues_con_checkboxes_desactivados)}
131+
132+
---
133+
*This report shows closed issues (completed only) that still contain unchecked checkboxes in their description or comments.*
134+
*Progress column shows: completed tasks / total tasks*
135+
*Note: Issues closed as duplicate or not planned are excluded from this report.*
136+
"""
137+
138+
# Update the report issue
139+
try:
140+
target_issue = repository.get_issue(target_issue_number)
141+
target_issue.edit(body=new_body)
142+
print(f"\n✅ Issue #{target_issue_number} updated successfully!")
143+
print(f"Total issues with unchecked checkboxes: {len(issues_con_checkboxes_desactivados)}")
144+
return True
145+
except Exception as e:
146+
print(f"\n❌ Error updating issue #{target_issue_number}: {str(e)}")
147+
return False
148+
149+
def main():
150+
"""Main function"""
151+
# Get environment variables
152+
token = os.environ.get('TOKEN')
153+
if not token:
154+
print("❌ Error: TOKEN environment variable not found")
155+
return False
156+
157+
# Repository configuration
158+
owner = 'Telefonica'
159+
repo = 'mistica-design'
160+
target_issue_number = 2490
161+
162+
return analyze_issues_with_pending_tasks(token, owner, repo, target_issue_number)
163+
164+
if __name__ == "__main__":
165+
success = main()
166+
exit(0 if success else 1)

.github/workflows/open-branches.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ jobs:
2929
env:
3030
FIGMA_TOKEN: ${{ secrets.FIGMA_TOKEN }}
3131
GITHUB_TOKEN: ${{ secrets.NOVUM_PRIVATE_REPOS }}
32-
working-directory: .github/branch-table
32+
working-directory: .github/issue-scripts
3333
run: python3 branch-table.py

.github/workflows/pending-tasks.yml

Lines changed: 5 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: Pending tasks in issues
22

33
on:
44
workflow_dispatch:
5+
schedule:
6+
- cron: 0 8 * * *
57

68
jobs:
79
pending-tasks:
@@ -20,54 +22,8 @@ jobs:
2022
python -m pip install --upgrade pip
2123
pip install PyGithub
2224
23-
- name: Ejecutar script de búsqueda
25+
- name: Ejecutar script de búsqueda y actualizar issue
2426
env:
2527
TOKEN: ${{ secrets.NOVUM_PRIVATE_REPOS }}
26-
run: |
27-
python - <<EOF
28-
import os
29-
from github import Github
30-
31-
TOKEN = os.environ['TOKEN']
32-
33-
# Nombre de usuario y nombre del repositorio
34-
OWNER = 'Telefonica'
35-
REPO = 'mistica-design'
36-
37-
# URL base del repositorio
38-
REPO_URL = f'https://github.com/{OWNER}/{REPO}'
39-
40-
# Crea una instancia de la clase Github usando tu token de acceso
41-
g = Github(TOKEN)
42-
43-
# Obtiene el repositorio
44-
repo = g.get_repo(f'{OWNER}/{REPO}')
45-
46-
# Busca las issues cerradas en el repositorio
47-
closed_issues = repo.get_issues(state='closed')
48-
49-
# Lista para almacenar las issues con checkboxes desactivados
50-
issues_con_checkboxes_desactivados = []
51-
52-
# Itera sobre las issues cerradas
53-
for issue in closed_issues:
54-
# Verifica que el cuerpo de la issue no sea None
55-
if issue.body is not None and '- [ ]' in issue.body:
56-
issues_con_checkboxes_desactivados.append(issue)
57-
continue
58-
59-
# Obtiene los comentarios de la issue
60-
comments = issue.get_comments()
61-
62-
# Busca checkboxes desactivados en los comentarios
63-
for comment in comments:
64-
if '- [ ]' in comment.body:
65-
issues_con_checkboxes_desactivados.append(issue)
66-
break
67-
68-
# Imprime el listado de issues con checkboxes desactivados
69-
print("Issues con checkboxes desactivados:")
70-
for issue in issues_con_checkboxes_desactivados:
71-
issue_url = f'{REPO_URL}/issues/{issue.number}'
72-
print(f"Issue #{issue.number}: {issue.title} ({issue_url})")
73-
EOF
28+
working-directory: .github/issue-scripts
29+
run: python3 pending-tasks.py

0 commit comments

Comments
 (0)