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 )
0 commit comments