Skip to content
Merged

Dev #1357

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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ pyproject.toml000
app.log
backend/app.log
tests/.env
tests/cypress/screenshots/
tests/cypress/screenshots/

.cursor
24 changes: 15 additions & 9 deletions backend/api/utils/profile_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,11 @@ def edit_profile(data: dict = {}, profile: object = None):
title=video_data.get("title"),
url=video_data.get("url"),
tag=video_data.get("tag"),
date_uploaded=video_data.get("date_uploaded")["$date"]
if "$date" in video_data.get("date_uploaded")
else video_data.get("date_uploaded"),
date_uploaded=(
video_data.get("date_uploaded")["$date"]
if "$date" in video_data.get("date_uploaded")
else video_data.get("date_uploaded")
),
)
if profile.videos:
profile.videos[0] = profile.video
Expand All @@ -353,9 +355,11 @@ def edit_profile(data: dict = {}, profile: object = None):
title=video.get("title"),
url=video.get("url"),
tag=video.get("tag"),
date_uploaded=video.get("date_uploaded")["$date"]
if "$date" in video.get("date_uploaded")
else video.get("date_uploaded"),
date_uploaded=(
video.get("date_uploaded")["$date"]
if "$date" in video.get("date_uploaded")
else video.get("date_uploaded")
),
)
for video in video_data
]
Expand Down Expand Up @@ -413,9 +417,11 @@ def edit_profile(data: dict = {}, profile: object = None):
title=video_data.get("title"),
url=video_data.get("url"),
tag=video_data.get("tag"),
date_uploaded=video_data.get("date_uploaded")["$date"]
if "$date" in video_data.get("date_uploaded")
else video_data.get("date_uploaded"),
date_uploaded=(
video_data.get("date_uploaded")["$date"]
if "$date" in video_data.get("date_uploaded")
else video_data.get("date_uploaded")
),
)
else:
profile.video = None
Expand Down
316 changes: 315 additions & 1 deletion backend/api/views/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ def generate_sheet(sheet_name, row_data, columns):
worksheet = writer.sheets[sheet_name]
format = workbook.add_format()
format.set_bg_color("#eeeeee")
worksheet.set_column(0, len(row_data[0]), 28)
worksheet.set_column(0, len(row_data[0]) if row_data else 0, 28)

writer.close()
output.seek(0)
Expand All @@ -544,3 +544,317 @@ def generate_sheet(sheet_name, row_data, columns):
msg = "Downloads failed"
logger.info(msg)
return create_response(status=422, message=msg)


def generate_file(file_name, row_data, columns, file_format="xlsx"):
"""Generate either CSV or Excel file based on format parameter."""
df = pd.DataFrame(row_data, columns=columns)
output = BytesIO()

if file_format == "csv":
# Generate CSV
csv_data = df.to_csv(index=False)
output.write(csv_data.encode("utf-8"))
output.seek(0)

try:
return send_file(
output,
mimetype="text/csv",
download_name="{0}.csv".format(file_name),
as_attachment=True,
)
except FileNotFoundError:
msg = "Downloads failed"
logger.info(msg)
return create_response(status=422, message=msg)
else:
# Generate Excel (default)
writer = pd.ExcelWriter(output, engine="xlsxwriter")

df.to_excel(
writer, startrow=0, merge_cells=False, sheet_name=file_name, index=False
)
workbook = writer.book
worksheet = writer.sheets[file_name]
format = workbook.add_format()
format.set_bg_color("#eeeeee")
worksheet.set_column(0, len(row_data[0]) if row_data else 0, 28)

writer.close()
output.seek(0)

try:
return send_file(
output,
mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
download_name="{0}.xlsx".format(file_name),
as_attachment=True,
)
except FileNotFoundError:
msg = "Downloads failed"
logger.info(msg)
return create_response(status=422, message=msg)


@download.route("/partner/<partner_id>/accounts", methods=["GET"])
@admin_only
def download_partner_accounts_data(partner_id):
"""Download mentors or mentees assigned to a specific partner with activity status."""
data = request.args
account_type = int(data.get("account_type", 0))
file_format = data.get("format", "xlsx") # 'xlsx' or 'csv'

try:
partner = PartnerProfile.objects.get(id=partner_id)
except:
msg = "Partner not found"
logger.info(msg)
return create_response(status=422, message=msg)

messages = DirectMessage.objects()

if account_type == Account.MENTOR:
return download_partner_mentor_accounts(partner, messages, file_format)
elif account_type == Account.MENTEE:
return download_partner_mentee_accounts(partner, messages, file_format)

msg = "Invalid account type"
logger.info(msg)
return create_response(status=422, message=msg)


def download_partner_mentor_accounts(partner, messages, file_format="xlsx"):
"""Download mentors assigned to a partner with activity status."""
if not partner.assign_mentors:
return generate_file(
"partner_mentors", [], get_mentor_columns_with_activity(), file_format
)

accts = []

for mentor_ref in partner.assign_mentors:
mentor_id = mentor_ref.get("id")
if not mentor_id:
continue

try:
mentor = MentorProfile.objects.get(id=mentor_id)
except:
continue

# Count messages
sent_messages = [
msg for msg in messages if str(msg.sender_id) == str(mentor.id)
]
received_messages = [
msg for msg in messages if str(msg.recipient_id) == str(mentor.id)
]

# Determine activity status
total_sent = len(sent_messages)
total_received = len(received_messages)
has_messages = total_sent > 0 or total_received > 0

if has_messages:
is_active = "Yes"
activity_reason = f"Sent: {total_sent}, Received: {total_received} messages"
else:
is_active = "No"
activity_reason = "No messages sent or received"

# Format educations
educations = []
for edu in mentor.education:
educations.append(
"{0} from {1} ({2})".format(
edu.education_level if edu.education_level else "",
edu.school if edu.school else "",
edu.graduation_year if edu.graduation_year else "",
)
)

accts.append(
[
mentor.name,
mentor.email,
mentor.professional_title,
mentor.linkedin,
mentor.website,
"Yes" if mentor.image and mentor.image.url else "No",
mentor.image.url if mentor.image else "",
(
mentor.videos[0].url
if mentor.videos and len(mentor.videos) > 0
else ""
),
"Yes" if mentor.videos and len(mentor.videos) > 0 else "No",
"|".join(educations),
",".join(mentor.languages) if mentor.languages else "",
",".join(mentor.specializations) if mentor.specializations else "",
mentor.biography,
"Yes" if mentor.taking_appointments else "No",
"Yes" if mentor.offers_in_person else "No",
"Yes" if mentor.offers_group_appointments else "No",
"Yes" if mentor.text_notifications else "No",
"Yes" if mentor.email_notifications else "No",
total_received,
total_sent,
partner.organization if partner.organization else "",
is_active,
activity_reason,
]
)

return generate_file(
"partner_mentors", accts, get_mentor_columns_with_activity(), file_format
)


def download_partner_mentee_accounts(partner, messages, file_format="xlsx"):
"""Download mentees assigned to a partner with activity status."""
if not partner.assign_mentees:
return generate_file(
"partner_mentees", [], get_mentee_columns_with_activity(), file_format
)

accts = []

for mentee_ref in partner.assign_mentees:
mentee_id = mentee_ref.get("id")
if not mentee_id:
continue

try:
mentee = MenteeProfile.objects.get(id=mentee_id)
except:
continue

# Count messages
sent_messages = [
msg for msg in messages if str(msg.sender_id) == str(mentee.id)
]
received_messages = [
msg for msg in messages if str(msg.recipient_id) == str(mentee.id)
]

# Determine activity status
total_sent = len(sent_messages)
total_received = len(received_messages)
has_messages = total_sent > 0 or total_received > 0

if has_messages:
is_active = "Yes"
activity_reason = f"Sent: {total_sent}, Received: {total_received} messages"
else:
is_active = "No"
activity_reason = "No messages sent or received"

# Format educations
educations = []
for edu in mentee.education:
educations.append(
"{0} from {1} ({2})".format(
edu.education_level if edu.education_level else "",
edu.school if edu.school else "",
edu.graduation_year if edu.graduation_year else "",
)
)
if mentee.education_level and not educations:
educations.append(
EDUCATION_LEVEL.get(mentee.education_level, mentee.education_level)
)

accts.append(
[
mentee.name,
mentee.gender,
mentee.location,
mentee.age,
mentee.email,
mentee.phone_number,
mentee.image.url if mentee.image else "",
"|".join(educations),
",".join(mentee.languages) if mentee.languages else "",
"|".join(mentee.specializations) if mentee.specializations else "",
mentee.biography,
partner.organization if partner.organization else "",
"Yes" if mentee.image and mentee.image.url else "No",
"Yes" if mentee.video and mentee.video.url else "No",
1 if mentee.text_notifications else 0,
1 if mentee.email_notifications else 0,
1 if mentee.is_private else 0,
mentee.video.url if mentee.video else "",
(
",".join(mentee.favorite_mentors_ids)
if mentee.favorite_mentors_ids
else ""
),
total_sent,
total_received,
partner.organization if partner.organization else "",
is_active,
activity_reason,
]
)

return generate_file(
"partner_mentees", accts, get_mentee_columns_with_activity(), file_format
)


def get_mentor_columns_with_activity():
return [
"mentor Full Name",
"email",
"professional_title",
"linkedin",
"website",
"profile pic up",
"image url",
"video url",
"video(s) up",
"educations",
"languages",
"specializations",
"biography",
"taking_appointments",
"offers_in_person",
"offers_group_appointments",
"text_notifications",
"email_notifications",
"total_received_messages",
"total_sent_messages",
"Affiliated",
"is_active",
"activity_reason",
]


def get_mentee_columns_with_activity():
return [
"mentee name",
"gender",
"location",
"age",
"email",
"phone number",
"image url",
"educations",
"languages",
"Areas of interest",
"biography",
"Organization Affiliation",
"profile pic up",
"video(s) up",
"text_notifications",
"email_notifications",
"private account",
"video url",
"favorite_mentor_ids",
"total_sent_messages",
"total_received_messages",
"Affiliated",
"is_active",
"activity_reason",
]
3 changes: 3 additions & 0 deletions backend/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading