diff --git a/.gitignore b/.gitignore index bf3ff3d..b2d0cb0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ dmp_2/__pycache__/* .env env/* - +venv __pycache__/* diff --git a/app.py b/app.py index 086d896..c7f0d62 100644 --- a/app.py +++ b/app.py @@ -5,7 +5,7 @@ import re,os,traceback from utils import * from flask_cors import CORS,cross_origin -from functools import wraps +from v2_app import v2 app = Flask(__name__) @@ -45,15 +45,6 @@ def greeting(): -# Custom decorator to validate secret key -def require_secret_key(f): - @wraps(f) - def decorated_function(*args, **kwargs): - secret_key = request.headers.get('X-Secret-Key') - if secret_key != SECRET_KEY: - return jsonify({'message': 'Unauthorized access'}), 401 - return f(*args, **kwargs) - return decorated_function @app.route('/get-data', methods=['GET']) @cross_origin(supports_credentials=True) @@ -82,7 +73,7 @@ def get_data(): data = response.data return jsonify(data) except Exception as e: - return jsonify({'error': str(e)}), 500 + return jsonify({'error': str(e)}), 200 @@ -108,7 +99,7 @@ def v1get_issues(): except Exception as e: error_traceback = traceback.format_exc() - return jsonify({'error': str(e), 'traceback': error_traceback}), 500 + return jsonify({'error': str(e), 'traceback': error_traceback}), 200 @app.route('/issues', methods=['GET']) @@ -136,88 +127,99 @@ def get_issues(): type: string """ try: - dmp_issue =SupabaseInterface().get_instance().client.table('dmp_issues').select('*').execute().data + # Fetch all issues with their details + dmp_issues = SupabaseInterface().get_instance().client.table('dmp_issues').select('*').execute().data - updated_issues = [] - - for i in dmp_issue: - val = SupabaseInterface().get_instance().client.table('dmp_issue_updates').select('*').eq('dmp_issue_url',i['repo_url']).execute().data - if val!=[]: - i['issues'] = val[0] #append first obj ie all are reder same issue - i['org_id'] = val[0]['org_id'] - i['org_name'] = val[0]['org_name'] - - updated_issues.append(i) - - # Create a defaultdict of lists + # Create a defaultdict of lists to group issues by 'org_id' grouped_data = defaultdict(list) - # Group data by 'org_name' - for item in updated_issues: - grouped_data[item['org_name']].append(item) + for issue in dmp_issues: + # Fetch organization details for the issue + org_details = SupabaseInterface().get_instance().client.table('dmp_orgs').select('*').eq('id', issue['org_id']).execute().data + if org_details: + issue['org_name'] = org_details[0]['name'] + + grouped_data[issue['org_id']].append(issue) + # Prepare response in the required format response = [] - for org_name, items in grouped_data.items(): + for org_id, items in grouped_data.items(): issues = [ { - "html_url": item['issues']['html_issue_url'], - "id": item['issues']['comment_id'], - "issue_number": item['issues']['issue_number'], - "name": item['issues']['title'] + "id": item['issue_number'], + "name": item['title'] } for item in items ] response.append({ - "issues": issues, - "org_id": items[0]['org_id'], - "org_name": org_name + "org_id": org_id, + "org_name": items[0]['org_name'], # Assuming all items in the group have the same org_name + "issues": issues }) - return jsonify(response) + return jsonify({"issues": response}) except Exception as e: error_traceback = traceback.format_exc() return jsonify({'error': str(e), 'traceback': error_traceback}), 500 - + @app.route('/issues/', methods=['GET']) @cross_origin(supports_credentials=True) @require_secret_key def get_issues_by_owner(owner): """ - Fetch issues by owner. + Fetch organization details by owner's GitHub URL. --- parameters: - name: owner in: path type: string required: true - description: The owner of the issues + description: The owner of the GitHub URL (e.g., organization owner) responses: 200: - description: Issues fetched successfully + description: Organization details fetched successfully schema: - type: array - items: - type: object + type: object + properties: + name: + type: string + description: Name of the organization + description: + type: string + description: Description of the organization + 404: + description: Organization not found + schema: + type: object + properties: + error: + type: string + description: Error message 500: - description: Error fetching issues + description: Error fetching organization details schema: type: object properties: error: type: string + description: Error message """ try: - response = SupabaseInterface().get_instance().client.table('dmp_issue_updates').select('*').eq('owner', owner).order('comment_updated_at', desc=True).execute() + # Construct the GitHub URL based on the owner parameter + org_link = f"https://github.com/{owner}" + + # Fetch organization details from dmp_orgs table + response = SupabaseInterface().get_instance().client.table('dmp_orgs').select('name', 'description').eq('link', org_link).execute() + if not response.data: - return jsonify({'error': "No data found"}), 500 - data = response.data[0] - return jsonify({"name": data['org_name'], "description": data['org_description']}) + return jsonify({'error': "Organization not found"}), 404 + + return jsonify(response.data) except Exception as e: error_traceback = traceback.format_exc() return jsonify({'error': str(e), 'traceback': error_traceback}), 500 - @app.route('/issues//', methods=['GET']) @@ -259,7 +261,7 @@ def get_issues_by_owner_id(owner, issue): SUPABASE_DB = SupabaseInterface().get_instance() response = SUPABASE_DB.client.table('dmp_issue_updates').select('*').eq('owner', owner).eq('issue_number', issue).execute() if not response.data: - return jsonify({'error': "No data found"}), 500 + return jsonify({'error': "No data found"}), 200 data = response.data final_data = [] @@ -329,7 +331,7 @@ def get_issues_by_owner_id(owner, issue): except Exception as e: error_traceback = traceback.format_exc() - return jsonify({'error': str(e), 'traceback': error_traceback}), 500 + return jsonify({'error': str(e), 'traceback': error_traceback}), 200 @@ -344,5 +346,9 @@ def check_secret_key(): break # Stop checking if the current route matches + +# Register the v2 Blueprint +app.register_blueprint(v2, url_prefix='/v2') + if __name__ == '__main__': app.run(debug=True) \ No newline at end of file diff --git a/utils.py b/utils.py index 0a8e085..37c1e54 100644 --- a/utils.py +++ b/utils.py @@ -2,9 +2,13 @@ from collections import defaultdict from datetime import datetime, timedelta from dateutil import parser +from flask import jsonify,request +from functools import wraps GITHUB_TOKEN =os.getenv('GITHUB_TOKEN') +SECRET_KEY =os.getenv('SECRET_KEY') + headers = { "Accept": "application/vnd.github+json", @@ -14,6 +18,17 @@ +# Custom decorator to validate secret key +def require_secret_key(f): + @wraps(f) + def decorated_function(*args, **kwargs): + secret_key = request.headers.get('X-Secret-Key') + if secret_key != SECRET_KEY: + return jsonify({'message': 'Unauthorized access'}), 401 + return f(*args, **kwargs) + return decorated_function + + def find_org_data(url): try: url_parts = url.split("/") diff --git a/v2_app.py b/v2_app.py new file mode 100644 index 0000000..a6cbd11 --- /dev/null +++ b/v2_app.py @@ -0,0 +1,97 @@ +import traceback,re +from flask import Blueprint, jsonify, request +import markdown2 +from utils import require_secret_key +from db import SupabaseInterface +from utils import determine_week +from v2_utils import calculate_overall_progress, define_link_data, week_data_formatter + +v2 = Blueprint('v2', __name__) + + +@v2.route('/issues//', methods=['GET']) +@require_secret_key +def get_issues_by_owner_id_v2(owner, issue): + try: + SUPABASE_DB = SupabaseInterface().get_instance() + # Fetch issue updates based on owner and issue number + + url = f"https://github.com/{owner}" + dmp_issue_id = SUPABASE_DB.client.table('dmp_issues').select('*').like('issue_url', f'%{url}%').eq('issue_number', issue).execute() + if not dmp_issue_id.data: + return jsonify({'error': "No data found"}), 500 + + dmp_issue_id = dmp_issue_id.data[0] + response = SUPABASE_DB.client.table('dmp_issue_updates').select('*').eq('dmp_id', dmp_issue_id['id']).execute() + + if not response.data: + return jsonify({'error': "No data found"}), 500 + + data = response.data + + final_data = [] + w_learn_url,w_goal_url,avg,cont_details,plain_text_body,plain_text_wurl = None,None,None,None,None,None + + + for val in data: + # issue_url = "https://api.github.com/repos/{}/{}/issues/comments".format(val['owner'],val['repo']) + # week_avg ,cont_name,cont_id,w_goal,w_learn,weekby_avgs,org_link = find_week_avg(issue_url) + # mentors_data = find_mentors(val['issue_url']) if val['issue_url'] else {'mentors': [], 'mentor_usernames': []} + + if val['body_text']: + if ("Weekly Goals" in val['body_text'] and not w_goal_url) and ("@"+val['created_by'].lower() == dmp_issue_id['mentor_username'].lower()): + w_goal_url = val['body_text'] + plain_text_body = markdown2.markdown(val['body_text']) + tasks = re.findall(r'\[(x| )\]', plain_text_body) + total_tasks = len(tasks) + completed_tasks = tasks.count('x') + avg = round((completed_tasks/total_tasks)*100) if total_tasks!=0 else 0 + + if ("Weekly Learnings" in val['body_text'] and not w_learn_url) and ((val['created_by'] == dmp_issue_id['contributor_username'])): + w_learn_url = val['body_text'] + plain_text_wurl = markdown2.markdown(val['body_text']) + + + # mentors = mentors_data['mentors'] + # ment_usernames = mentors_data['mentor_usernames'] + if not cont_details: + cont_details = dmp_issue_id['contributor_username'] + week_data = week_data_formatter(plain_text_body,"Goals") + + res = { + "name": owner, + "description": dmp_issue_id['description'], + "mentor": define_link_data(dmp_issue_id['mentor_username']), + "mentor_id": dmp_issue_id['mentor_username'] , + "contributor":define_link_data(cont_details), + # "contributor_id": cont_details[0]['contributor_id'], + "org": define_link_data(dmp_issue_id['mentor_username'])[0] if dmp_issue_id['mentor_username'] else [], + "weekly_goals_html": w_goal_url, + "weekly_learnings_html": w_learn_url, + "overall_progress":calculate_overall_progress(week_data,12), + "issue_url":dmp_issue_id['issue_url'], + "pr_details":None, + "weekly_goals":week_data, + "weekly_learnings":week_data_formatter(plain_text_wurl,"Learnings") + } + + + pr_Data = SUPABASE_DB.client.table('dmp_pr_updates').select('*').eq('dmp_id', dmp_issue_id['id']).like('title', f'%#{issue} - %').execute() + transformed = {"pr_details": []} + if pr_Data.data: + for pr in pr_Data.data: + transformed["pr_details"].append({ + "id": pr.get("pr_id", ""), + "name": pr.get("title", ""), + "week": determine_week(pr['created_at']), + "link": pr.get("link", ""), + "status": pr.get("status", ""), + }) + + res['pr_details'] = transformed['pr_details'] + + return jsonify(res),200 + + except Exception as e: + error_traceback = traceback.format_exc() + return jsonify({'error': str(e), 'traceback': error_traceback}), 200 diff --git a/v2_utils.py b/v2_utils.py new file mode 100644 index 0000000..5c9d55c --- /dev/null +++ b/v2_utils.py @@ -0,0 +1,94 @@ +import logging,re,markdown2 + +# Func to create name and link for all mentors and contributors +def define_link_data(usernames): + try: + res = [] + if type(usernames) == list: + for username in usernames: + val = {} + val['name'] = username + val['link'] = "https://github.com/" + username + res.append(val) + if type(usernames) == str: + if usernames[0]=="@": + usernames = usernames[1:] + val = {} + val['name'] = usernames + val['link'] = "https://github.com/" + usernames + res.append(val) + + return res + + except Exception as e: + logging.info(f"{e}---define_link_data") + return [] + + + +def week_data_formatter(html_content, type): + + try: + # Use regex to find week titles (e.g., Week 1, Week 2) and their corresponding task lists + week_matches = re.findall(r'(Week \d+)', html_content) + tasks_per_week = re.split(r'Week \d+', html_content)[1:] # Split the content by weeks and skip the first empty split + + weekly_updates = [] + + if type == "Learnings": + for i, week in enumerate(week_matches): + task_list_html = tasks_per_week[i] if i < len(tasks_per_week) else "" + weekly_updates.append({ + 'week': i + 1, + 'content': task_list_html.strip() + }) + return weekly_updates + + else: + for i, week in enumerate(week_matches): + task_list_html = tasks_per_week[i] if i < len(tasks_per_week) else "" + + # Adjust regex to capture tasks regardless of the tags around them + tasks = re.findall(r'\[(x|X| )\]\s*(.*?)', task_list_html, re.DOTALL) + + total_tasks = len(tasks) + completed_tasks = sum(1 for task in tasks if task[0] in ['x', 'X']) + task_list = [{"content": task[1].strip(), "checked": task[0] in ['x', 'X']} for task in tasks] + + avg = round((completed_tasks / total_tasks) * 100) if total_tasks != 0 else 0 + + weekly_updates.append({ + 'week': i + 1, + 'progress': avg, + 'tasks': task_list + }) + + return weekly_updates + + except Exception as e: + print(f"Error: {e}") + return [] + + +def calculate_overall_progress(weekly_updates, default_weeks=12): + try: + total_progress = 0 + provided_weeks = len(weekly_updates) + + # Sum the progress of each provided week + for week in weekly_updates: + total_progress += week.get('progress', 0) + + # Add zero progress for the remaining weeks to reach the default weeks + total_weeks = default_weeks + remaining_weeks = default_weeks - provided_weeks + total_progress += remaining_weeks * 0 # Adding zero progress for the remaining weeks + + # Calculate the average progress over the total number of weeks + overall_progress = total_progress / total_weeks if total_weeks > 0 else 0 + + return round(overall_progress, 2) + except Exception as e: + print(f"Error: {e}") + return 0 + \ No newline at end of file