diff --git a/app.py b/app.py index b759f90..136c126 100644 --- a/app.py +++ b/app.py @@ -1,14 +1,16 @@ -from flask import Flask, jsonify +from flask import Flask, jsonify,request,url_for from db import SupabaseInterface from collections import defaultdict from flasgger import Swagger -import re,markdown2,requests,os +import re,os +from utils import * +from flask_cors import CORS -app = Flask(__name__) +app = Flask(__name__) +CORS(app) Swagger(app) - GITHUB_TOKEN =os.getenv('GITHUB_TOKEN') headers = { @@ -17,8 +19,22 @@ "X-GitHub-Api-Version": "2022-11-28" } -@app.route('/api/greeting', methods=['GET']) -def greeting(): + +# Define a list of routes that should be protected +protected_routes = ['/greeting', '/get-data', '/issues', '/issues/', '/issues//'] +SECRET_KEY =os.getenv('SECRET_KEY') + +protected_routes = [ + re.compile(r'^/greeting$'), + re.compile(r'^/get-data$'), + re.compile(r'^/issues$'), + re.compile(r'^/issues/[^/]+$'), # Matches '/issues/' + re.compile(r'^/issues/[^/]+/[^/]+$') # Matches '/issues//' +] + + +@app.route('/greeting', methods=['GET']) +def greeting(): """ A simple greeting endpoint. --- @@ -37,7 +53,7 @@ def greeting(): } return jsonify(response) -@app.route('/api/get-data', methods=['GET']) +@app.route('/get-data', methods=['GET']) def get_data(): """ Fetch data from Supabase. @@ -64,25 +80,9 @@ def get_data(): except Exception as e: return jsonify({'error': str(e)}), 500 -def group_by_owner(data): - grouped_data = defaultdict(list) - for record in data: - owner = record['owner'] - grouped_data[owner].append(record) - - - #Arrange data as reponse format - res = [] - for val in grouped_data: - dict_ = {} - dict_['org_name'] = val - dict_['issues'] = grouped_data[val] - - res.append(dict_) - - return {"issues":res} -@app.route('/api/issues', methods=['GET']) + +@app.route('/issues', methods=['GET']) def get_issues(): """ Fetch all issues and group by owner. @@ -107,12 +107,25 @@ def get_issues(): try: response = SupabaseInterface().get_instance().client.table('dmp_issue_updates').select('*').execute() data = response.data - grouped_data = group_by_owner(data) + + #group data based on issues + grouped_data = defaultdict(list) + for record in data: + issue_url = record['issue_url'] + grouped_data[issue_url].append({ + 'id': record['id'], + 'name': record['body_text'] + }) + + result = [{'issue_url': issue_url, 'issues': issues} for issue_url, issues in grouped_data.items()] + + grouped_data = group_by_owner(result) return jsonify(grouped_data) + except Exception as e: return jsonify({'error': str(e)}), 500 -@app.route('/api/issues/', methods=['GET']) +@app.route('/issues/', methods=['GET']) def get_issues_by_owner(owner): """ Fetch issues by owner. @@ -139,7 +152,8 @@ def get_issues_by_owner(owner): type: string """ try: - response = SupabaseInterface().get_instance().client.table('dmp_issue_updates').select('*').eq('owner', owner).execute() + response = SupabaseInterface().get_instance().client.table('dmp_issue_updates').select('*').eq('owner', owner).order('comment_updated_at', desc=True).execute() + if not response.data: return jsonify({'error': "No data found"}), 500 data = response.data @@ -151,88 +165,8 @@ def get_issues_by_owner(owner): -def find_week_avg(url): - # url = "https://api.github.com/repos/VedantKhairnar/dmp-backend-test-repo/issues/comments" - - response = requests.get(url,headers=headers) - - if response.status_code == 200: - issue_details = response.json() - plain_text_body = markdown2.markdown(issue_details[0]['body']) - - 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 - - #find weekly goal html urls - w_goal_url = None - w_learn_url = None - - for item in issue_details: - if "Weekly Goals" in item['body']: - w_goal_url = item['html_url'] - if "Weekly Learnings" in item['body']: - w_learn_url = item['html_url'] - - return avg,issue_details[0]['user']['login'],issue_details[0]['user']['id'],w_goal_url,w_learn_url - -@app.route('/api/mentors', methods=['GET']) -def find_mentors(url): - response = requests.get(url,headers=headers) - - if response.status_code == 200: - issue_details = response.json() - - issue_body = issue_details['body'] - pattern = r"## Mentors\s*([\s\S]+?)\s*##" - match = re.search(pattern, issue_body) - - if match: - mentors_text = match.group(1).strip() - # Extract individual mentor usernames - mentors = [mentor.strip() for mentor in mentors_text.split(',')] - else: - mentors = [] - api_base_url = "https://api.github.com/users/" - - ment_username = [] - for val in mentors: - url = f"{api_base_url}{val[1:]}" - username = requests.get(url) - ment_username.append(username.json()['login']) - - return mentors,ment_username - else: - return [],[] - -def get_pr_details(url): - try: - issue_url = url - url_parts = issue_url.split("/") - owner = url_parts[4] - repo = url_parts[5] - issue_number = url_parts[7] - - # GitHub API endpoint to get pull requests for the repository - pulls_url = f"https://api.github.com/repos/{owner}/{repo}/pulls" - - # Send GET request to GitHub API with authentication - response = requests.get(pulls_url, headers=headers) - if response.status_code == 200: - pulls = response.json() - return pulls - else: - return [] - - - except Exception as e: - raise Exception - - -@app.route('/api/issues//', methods=['GET']) +@app.route('/issues//', methods=['GET']) def get_issues_by_owner_id(owner, issue): """ Fetch issues by owner and issue number. @@ -272,15 +206,17 @@ def get_issues_by_owner_id(owner, issue): data = response.data final_data = [] - 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 = find_week_avg(issue_url) + week_avg ,cont_name,cont_id,w_goal,w_learn,weekby_avgs = find_week_avg(issue_url) + mentors_data = find_mentors(val['issue_url']) if val['issue_url'] else {'mentors': [], 'mentor_usernames': []} + + mentors = mentors_data['mentors'] + ment_usernames = mentors_data['mentor_usernames'] - mentors,ment_usernames = find_mentors(val['issue_url']) if val['issue_url'] else [],[] res = { "name": owner, - "description": None, + "description": mentors_data['desc'], "mentor_name": ment_usernames, "mentor_id": mentors, "contributor_name":cont_name , @@ -293,12 +229,42 @@ def get_issues_by_owner_id(owner, issue): "issue_url":val['issue_url'], "pr_details":get_pr_details(val['issue_url']) } + + transformed = {"pr_details": []} + + for pr in res.get("pr_details", []): + transformed["pr_details"].append({ + "id": pr.get("id", ""), + "name": pr.get("title", ""), + "week": pr.get("week", ""), + "link": pr.get("html_url", ""), + "status": pr.get("state", "") + }) + + res['pr_details'] = transformed + # Adding each week as a separate key + # for week in weekby_avgs: + # res.update(week) + # final_data.append(res) return jsonify(res),200 except Exception as e: return jsonify({'error': str(e)}), 500 + + +# Before request handler to check for the presence of the secret key +@app.before_request +def check_secret_key(): + for route_pattern in protected_routes: + if route_pattern.match(request.path): + secret_key = request.headers.get('X-Secret-Key') + if secret_key != SECRET_KEY: + return jsonify({'message': 'Unauthorized access'}), 401 + break # Stop checking if the current route matches + + if __name__ == '__main__': app.run(debug=True) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 8054db6..98244a7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,5 @@ supabase==2.4.5 gunicorn==22.0.0 flasgger==0.9.7.1 markdown2==2.4.13 -requests==2.32.2 \ No newline at end of file +requests==2.32.2 +flask-cors==4.0.1 \ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..4360c8d --- /dev/null +++ b/utils.py @@ -0,0 +1,217 @@ +import requests,re,markdown2,os +from collections import defaultdict + +GITHUB_TOKEN =os.getenv('GITHUB_TOKEN') + +headers = { + "Accept": "application/vnd.github+json", + "Authorization": f"Bearer {GITHUB_TOKEN}", + "X-GitHub-Api-Version": "2022-11-28" + } + + + +def find_org_data(url): + try: + url_parts = url.split("/") + owner = url_parts[4] + repo = url_parts[5] + + # Fetch repository details to get organization info + repo_url = f"https://api.github.com/repos/{owner}/{repo}" + repo_response = requests.get(repo_url, headers=headers) + repo_data = repo_response.json() + if repo_data: + org_name = repo_data['owner']['login'] + org_id = repo_data['owner']['id'] + else: + org_name = None + org_id = None + return {"org_id":org_id,"org_name":org_name} + + except Exception as e: + return {"org_id":None,"org_name":None} + + + + +def get_issue_details(issue_url): + url_parts = issue_url.split("/") + owner = url_parts[4] + repo = url_parts[5] + issue_number = url_parts[6] + + # GitHub API endpoint to get the issue details + issue_api_url = f"https://api.github.com/repos/{owner}/{repo}/issues" + + # Send GET request to GitHub API with authentication + response = requests.get(issue_api_url, headers=headers) + if response.status_code == 200: + issue_data = response.json() + return [{'id': issue['id'], 'name': issue['title'],'html_url':issue['html_url']} for issue in issue_data if "pull_request" not in issue] + else: + return {'id': None, 'name': None} + + + +def group_by_owner(data): + res = [] + for record in data: + org_data = find_org_data(record['issue_url']) + dict_ = {} + dict_['org_name'] = org_data['org_name'] + dict_['org_id'] = org_data['org_id'] + dict_['issues'] = get_issue_details(record['issue_url']) + res.append(dict_) + + + org_dict = defaultdict(lambda: {'issues': [], 'org_id': None, 'org_name': None}) + for entry in res: + org_id = entry['org_id'] + org_name = entry['org_name'] + + org_dict[org_id]['issues'].extend(entry['issues']) + org_dict[org_id]['org_id'] = org_id + org_dict[org_id]['org_name'] = org_name + + return list(org_dict.values()) + + +def find_week_data(issue_details): + try: + #find how many weeks in reponse + weekly_updates = [] + for item in issue_details: + if "Weekly Goals" in item["body"]: + week_match = re.search(r'Week \d+', item["body"]) + if week_match: + weekly_updates.append({ + "id": item["id"], + "val":item, + "week": week_match.group(0) + }) + + val = [] + + for week in weekly_updates: + + plain_text_body = markdown2.markdown(week['val']['body']) + + 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 + + # week['avg'] = avg + # week['val'] = None + week[str(week['week'])+' percentage'] = avg + del week['val'] + del week['id'] + del week['week'] + val.append(week) + + return val + + except Exception as e: + return {} + + + +def find_week_avg(url): + + response = requests.get(url,headers=headers) + if response.status_code == 200: + issue_details = response.json() + + # week_avgs = find_week_data(issue_details) phase 2 + week_avgs = None + + w_learn_url = None + w_goal_url = None + avg = 0 + for item in issue_details: + + if "Weekly Goals" in item['body']: + w_goal_url = item['html_url'] + plain_text_body = markdown2.markdown(issue_details[0]['body']) + + 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 item['body']: + w_learn_url = item['html_url'] + + + return avg,issue_details[0]['user']['login'],issue_details[0]['user']['id'],w_goal_url,w_learn_url,week_avgs + + +def find_mentors(url): + response = requests.get(url,headers=headers) + + if response.status_code == 200: + issue_details = response.json() + + issue_body = issue_details['body'] + pattern = r"## Mentors\s*([\s\S]+?)\s*##" + disc_pattern = r"## Desc 1\s*([\s\S]+?)\s*##" + disc_match = re.search(disc_pattern, issue_body) + + disc_text = disc_match.group(1).strip() if disc_match else None + + match = re.search(pattern, issue_body) + + if match: + mentors_text = match.group(1).strip() + # Extract individual mentor usernames + mentors = [mentor.strip() for mentor in mentors_text.split(',')] + else: + mentors = [] + api_base_url = "https://api.github.com/users/" + + ment_username = [] + for val in mentors: + url = f"{api_base_url}{val[1:]}" + username = requests.get(url) + + ment_username.append(username.json()['login']) + return { + 'mentors': mentors, + 'mentor_usernames': ment_username, + 'desc':disc_text + } + else: + return { + 'mentors': [], + 'mentor_usernames': [], + 'desc':None + } + +def get_pr_details(url): + try: + issue_url = url + url_parts = issue_url.split("/") + owner = url_parts[4] + repo = url_parts[5] + issue_number = url_parts[7] + + # GitHub API endpoint to get pull requests for the repository + pulls_url = f"https://api.github.com/repos/{owner}/{repo}/pulls" + + # Send GET request to GitHub API with authentication + response = requests.get(pulls_url, headers=headers) + if response.status_code == 200: + pulls = response.json() + return pulls + else: + return [] + + + except Exception as e: + raise Exception + + +