Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Profile + Github + PraiseBot #87

Merged
merged 5 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 103 additions & 3 deletions api/messages/messages_service.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from common.utils import safe_get_env_var
from common.utils.slack import send_slack_audit, create_slack_channel, send_slack, invite_user_to_channel
from common.utils.firebase import get_hackathon_by_event_id, upsert_news
from common.utils.firebase import get_hackathon_by_event_id, upsert_news, upsert_praise, get_github_contributions_for_user
from common.utils.openai_api import generate_and_save_image_to_cdn
from common.utils.github import create_github_repo
from api.messages.message import Message
Expand Down Expand Up @@ -30,7 +30,8 @@


logger = logging.getLogger("myapp")
logger.setLevel(logging.INFO)
logger.setLevel(logging.DEBUG)
logger.setLevel(logging.DEBUG)


google_recaptcha_key = safe_get_env_var("GOOGLE_CAPTCHA_SECRET_KEY")
Expand Down Expand Up @@ -1235,6 +1236,26 @@ def save_news(json):

return Message("Saved News")

def save_praise(json):
logger.debug(f"Attempting to save the praise with the json object {json}")
# Take in Slack message and summarize it using GPT-3.5
# Make sure these fields exist praise_receiver, praise_channel, praise_message
check_fields = ["praise_receiver", "praise_channel", "praise_message"]
for field in check_fields:
if field not in json:
logger.error(f"Missing field {field} in {json}")
return Message("Missing field")

logger.debug(f"Detected required fields, attempting to save praise")
upsert_praise(json)

logger.info("Updated praise successfully")

#get_news.cache_clear()
#logger.info("Cleared cache for get_news")

return Message("Saved praise")

async def save_lead(json):
token = json["token"]

Expand Down Expand Up @@ -1713,6 +1734,16 @@ def get_problem_statement_list_old():
logger.debug(results)
return { "problem_statements": results }

@cached(cache=TTLCache(maxsize=100, ttl=600))
@limits(calls=100, period=ONE_MINUTE)
def get_github_profile(github_username):
logger.debug(f"Getting Github Profile for {github_username}")

return {
"github_history": get_github_contributions_for_user(github_username)
}


# -------------------- User functions to be deleted ---------------------------------------- #

# 10 minute cache for 100 objects LRU
Expand Down Expand Up @@ -1816,6 +1847,8 @@ def get_history_old(db_id):
"expertise": res["expertise"] if "expertise" in res else "",
"education": res["education"] if "education" in res else "",
"shirt_size": res["shirt_size"] if "shirt_size" in res else "",
"linkedin_url": res["linkedin_url"] if "linkedin_url" in res else "",
"instagram_url": res["instagram_url"] if "instagram_url" in res else "",
"github": res["github"] if "github" in res else "",
"why": res["why"] if "why" in res else "",
"role": res["role"] if "role" in res else "",
Expand Down Expand Up @@ -1907,7 +1940,7 @@ def save_profile_metadata_old(propel_id, json):
logger.info(f"User exists: {user.id}")

# Only update metadata that is in the json
metadataList = ["role", "expertise", "education", "company", "why", "shirt_size", "github"]
metadataList = ["role", "expertise", "education", "company", "why", "shirt_size", "github", "linkedin_url", "instagram_url"]

d = {}

Expand Down Expand Up @@ -1944,3 +1977,70 @@ def get_user_by_id_old(id):
logger.debug(f"Get User By ID Result: {res}")
return res


@limits(calls=50, period=ONE_MINUTE)
def save_feedback(propel_user_id, json):
db = get_db()
logger.info("Saving Feedback")
send_slack_audit(action="save_feedback", message="Saving", payload=json)

slack_user = get_slack_user_from_propel_user_id(propel_user_id)
user_db_id = get_user_from_slack_id(slack_user["sub"]).id
feedback_giver_id = slack_user["sub"]

doc_id = uuid.uuid1().hex
feedback_receiver_id = json.get("feedback_receiver_id")
relationship = json.get("relationship")
duration = json.get("duration")
confidence_level = json.get("confidence_level")
is_anonymous = json.get("is_anonymous", False)
feedback_data = json.get("feedback", {})

collection = db.collection('feedback')

insert_res = collection.document(doc_id).set({
"feedback_giver_slack_id": feedback_giver_id,
"feedback_giver_id": user_db_id,
"feedback_receiver_id": feedback_receiver_id,
"relationship": relationship,
"duration": duration,
"confidence_level": confidence_level,
"is_anonymous": is_anonymous,
"feedback": feedback_data,
"timestamp": datetime.now().isoformat()
})

logger.info(f"Insert Result: {insert_res}")

# Notify the feedback receiver
notify_feedback_receiver(feedback_receiver_id)

# Clear cache
get_user_feedback.cache_clear()

return Message("Feedback saved successfully")

def notify_feedback_receiver(feedback_receiver_id):
# Implement notification logic here
# This could be sending an email, a Slack message, or updating a notification field in the user's document
pass

@cached(cache=TTLCache(maxsize=100, ttl=600))
@limits(calls=100, period=ONE_MINUTE)
def get_user_feedback(propel_user_id):
logger.info(f"Getting feedback for propel_user_id: {propel_user_id}")
db = get_db()

slack_user = get_slack_user_from_propel_user_id(propel_user_id)
db_user_id = get_user_from_slack_id(slack_user["sub"]).id

feedback_docs = db.collection('feedback').where("feedback_receiver_id", "==", db_user_id).stream()

feedback_list = []
for doc in feedback_docs:
feedback = doc.to_dict()
if feedback.get("is_anonymous", False):
feedback.pop("feedback_giver_id", None)
feedback_list.append(feedback)

return {"feedback": feedback_list}
55 changes: 52 additions & 3 deletions api/messages/messages_views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os

import logging
import json
from common.auth import auth, auth_user

from flask import (
Expand Down Expand Up @@ -42,10 +43,15 @@
get_all_profiles,
save_npo_application,
get_npo_applications,
update_npo_application
update_npo_application,
get_github_profile,
save_praise,
save_feedback,
get_user_feedback
)


logger = logging.getLogger("myapp")
logger.setLevel(logging.DEBUG)


bp_name = 'api-messages'
Expand Down Expand Up @@ -261,6 +267,27 @@ async def store_lead():
else:
return "OK", 200

# -------------------- Praises routes begin here --------------------------- #
@bp.route("/praise", methods=["POST"])
def store_praise():
# Check header for token
# if token is valid, store news
# else return 401

token = request.headers.get("X-Api-Key")

# Check BACKEND_NEWS_TOKEN
if token == None or token != os.getenv("BACKEND_PRAISE_TOKEN"):
return "Unauthorized", 401
else:
logger.debug(f"Hre is the request object {request.get_json()}")
# try:
# logger.debug(f"Here is the request object: {request.get_json()}")
# except Exception as e:
# logger.error(f"Error logging request object: {e}")
return vars(save_praise(request.get_json()))

# -------------------- Praises routes end here --------------------------- #

# -------------------- Problem Statement routes to be deleted --------------------------- #
@auth.require_user
Expand Down Expand Up @@ -316,6 +343,10 @@ def save_profile():
else:
return None

@bp.route("/profile/github/<username>", methods=["GET"])
def get_github_profile_api(username):
return get_github_profile(username)

# Get user profile by user id
@bp.route("/profile/<id>", methods=["GET"])
def get_profile_by_id(id):
Expand All @@ -329,3 +360,21 @@ def all_profiles():
return get_all_profiles()


@bp.route("/feedback", methods=["POST"])
@auth.require_user
def submit_feedback():
if auth_user and auth_user.user_id:
return vars(save_feedback(auth_user.user_id, request.get_json()))
else:
return {"error": "Unauthorized"}, 401

@bp.route("/feedback", methods=["GET"])
@auth.require_user
def get_feedback():
"""
Get feedback for a user - note that you can only get this for the current logged in user
"""
if auth_user and auth_user.user_id:
return get_user_feedback(auth_user.user_id)
else:
return {"error": "Unauthorized"}, 401
46 changes: 44 additions & 2 deletions common/utils/firebase.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

# add logger
import logging
logger = logging.getLogger(__name__)
logger = logging.getLogger("myapp")
# set log level
logger.setLevel(logging.INFO)
logger.setLevel(logging.DEBUG)


def get_db():
Expand Down Expand Up @@ -88,6 +88,34 @@ def get_user_by_email(email_address):
return adict


def get_github_contributions_for_user(login):
logger.info(f"Getting github contributions for user {login}")

db = get_db() # this connects to our Firestore database

# Use a collection group query to search across all contributor subcollections
contributors_ref = db.collection_group('github_contributors').where("login", "==", login)

docs = contributors_ref.stream()

github_history = []
for doc in docs:
contribution = doc.to_dict()
# Add document ID
contribution["id"] = doc.id
# Add organization and repository information
repo_ref = doc.reference.parent.parent
org_ref = repo_ref.parent.parent
contribution["repo_name"] = repo_ref.id
contribution["org_name"] = org_ref.id
github_history.append(contribution)

logger.info(f"Found {len(github_history)} contributions for user {login}")

return github_history



def create_user(name, email_address, slack_id):
db = get_db() # this connects to our Firestore database
logger.info(f"Creating user {name} {email_address}")
Expand Down Expand Up @@ -1017,3 +1045,17 @@ def upsert_news(news):
logger.info("news does not exist")

db.collection("news").add(news)

def upsert_praise(praise):
db = get_db() # this connects to our Firestore database
logger.info(f"Adding praise {praise}")

#Check if the vibe field exists.
if "praise_gif" not in praise:
logger.info("vibe field not found, setting praise['vibe'] to None")
praise["praise_gif"] = None

logger.info(f"Adding refined praise {praise} into the database")

db.collection("praises").add(praise)
logger.info("praise successfully saved")
4 changes: 3 additions & 1 deletion model/user.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
metadata_list = ["role", "expertise", "education", "company", "why", "shirt_size", "github", "volunteering"]
metadata_list = ["role", "expertise", "education", "company", "why", "shirt_size", "github", "volunteering", "linkedin_url", "instagram_url"]

class User:
id = None
Expand Down Expand Up @@ -34,6 +34,8 @@ def deserialize(cls, d):
u.expertise = d['expertise'] if 'expertise' in d else ''
u.education = d['education'] if 'education' in d else ''
u.shirt_size = d['shirt_size'] if 'shirt_size' in d else ''
u.linkedin_url = d['linkedin_url'] if 'linkedin_url' in d else ''
u.instagram_url = d['instagram_url'] if 'instagram_url' in d else ''
u.github = d['github'] if 'github' in d else ''
u.role = d['role'] if 'role' in d else ''
u.company = d['company'] if 'company' in d else ''
Expand Down
Loading