Skip to content

Commit

Permalink
feat: shortlist notifier
Browse files Browse the repository at this point in the history
  • Loading branch information
proffapt committed Nov 30, 2024
1 parent 3c15d4e commit 3c6e1ef
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 41 deletions.
9 changes: 9 additions & 0 deletions mftp/company.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ def filter(companies, filter):
for company in companies:
if filter_func(company):
filtered.append(company)
logging.info(
f" {company['Name']} | {company['Role']} | {company['CTC']} | {company['End_Date']} | {company['Interview_Date']}"
)

return filtered

Expand Down Expand Up @@ -102,11 +105,17 @@ def get_new_and_modified_companies(fetched, stored, unique_key="Job_Description"
if key not in stored_dict:
# New entry
new_entries.append(fetched_entry)
logging.info(
f" [NEW COMPANY]: {fetched_entry['Name']} | {fetched_entry['Role']} | {fetched_entry['CTC']} | {fetched_entry['End_Date']} | {fetched_entry['Interview_Date']}"
)
else:
# Compare the values of the fetched entry with the stored entry
stored_entry = stored_dict[key]
if any(fetched_entry[k] != stored_entry.get(k) for k in fetched_entry):
updated_entries.append(fetched_entry)
logging.info(
f" [MODIFIED COMPANY]: {fetched_entry['Name']} | {fetched_entry['Role']} | {fetched_entry['CTC']} | {fetched_entry['End_Date']} | {fetched_entry['Interview_Date']}"
)

return new_entries, updated_entries

Expand Down
2 changes: 1 addition & 1 deletion mftp/env.example.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
HOSTER_EMAIL = ["hoster@kgpian.iitkgp.ac.in", "emergency.hoster@kgpian.iitkgp.ac.in"]
HOSTER_NAME = "Arpit Bhardwaj"
HOSTER_ROLL = ROLL_NUMBER
HOSTER_INTERESTED_ROLLS = [HOSTER_ROLL, "XXYYXXXXX", "NNAANNNNN"]
HOSTER_INTERESTED_ROLLS = ["XXYYXXXXX", "NNAANNNNN"]

# SHORTLIST CONFIG (MUST)
## Maps roll number to student names
Expand Down
139 changes: 136 additions & 3 deletions mftp/mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,144 @@
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from env import FROM_EMAIL, FROM_EMAIL_PASS, BCC_EMAIL_S, HOSTER_EMAIL
from env import FROM_EMAIL, FROM_EMAIL_PASS, BCC_EMAIL_S, HOSTER_EMAIL, HOSTER_INTERESTED_ROLLS, ROLL_MAIL, ROLL_NAME


def format_shortlist():
pass
def send_shortlists(mails, gmail_api, smtp):
print('[SENDING SHORTLIST UPDATES]', flush=True)

if gmail_api:
import base64

try:
service = generate_send_service()
except Exception as e:
logging.error(f" Failed to generate GMAIL API creds ~ {str(e)}")
return

for mail in mails:
try:
response = service.users().messages().send(
userId="me",
body={"raw": base64.urlsafe_b64encode(mail.as_bytes()).decode()}
).execute()
except Exception as e:
logging.error(f" Failed to send request to GMAIL API : {mail['Subject']} -x-> ({mail['Bcc']}) ~ {str(e)}")
break

if 'id' in response:
logging.info(f" [MAIL SENT] ~ {mail['Subject']} -> ({mail['Bcc']})")
else:
logging.error(f" Failed to Send Mail : {mail['Subject']} -x-> ({mail['Bcc']}) ~ {response}")
break
elif smtp:
import ssl
import smtplib
context = ssl.create_default_context()

logging.info(" [Connecting to smtp.google.com] ...")
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
logging.info(" [Connected!]")
try:
server.login(FROM_EMAIL, FROM_EMAIL_PASS)
logging.info(" [Logged In!]")
except Exception as e:
logging.error(f" Failed to log in ~ {str(e)}")
return

for mail in mails:
try:
server.sendmail(mail["From"], mail["Bcc"].split(', '), mail.as_string())
logging.info(f" [MAIL SENT] ~ {mail['Subject']} -> ({mail['Bcc']})")
except smtplib.SMTPException as e:
logging.error(f" Failed to Send Mail : {mail['Subject']} -x-> ({mail['Bcc']}) ~ {str(e)}")
break


def format_shortlists(shortlists):
print('[FORMATTING SHORTLIST UPDATES]', flush=True)

table_body = """
<html>
<body>
<div style="font-family: Arial, sans-serif; width: 90%; margin: 0 auto; border: 1px solid #333; padding: 20px; margin-bottom: 20px; border-radius: 10px; box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);">
<div align="center">
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="background-color: #f2f2f2;">
{columns}
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
</div>
</body>
</html>
"""

host_interested_message = MIMEMultipart()
host_interested_message["Subject"] = "People you are interested in are shortlisted!"
host_interested_message["From"] = f'MFTP < {FROM_EMAIL} >'
host_interested_message["Bcc"] = ", ".join(HOSTER_EMAIL)

host_interested_cols = """
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Name (count)</th>
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Company (notice_id)</th>
"""

def generate_row(company_shortlist):
return f"""
<tr>
<td style="border: 1px solid #ddd; padding: 8px;">{company_shortlist['company']} (#{company_shortlist['id']})</td>
<td style="border: 1px solid #ddd; padding: 8px;">{company_shortlist['count']}</td>
</tr>
"""

def generate_host_interested_row(name, company_shortlist):
return f"""
<tr>
<td style="border: 1px solid #ddd; padding: 8px;">{name} ({company_shortlist['count']})</td>
<td style="border: 1px solid #ddd; padding: 8px;">{company_shortlist['company']} (#{company_shortlist['id']})</td>
</tr>
"""

host_interested_rows_list = []

mails = []
for roll, shortlist in shortlists.items():
if roll in HOSTER_INTERESTED_ROLLS:
name = ROLL_NAME.get(roll)
host_interested_rows_list += [generate_host_interested_row(name, company_shortlist) for company_shortlist in shortlist]

student_mails = ROLL_MAIL.get(roll)
if student_mails is None:
continue

message = MIMEMultipart()
message["Subject"] = "You have been shortlisted!"
message["From"] = f'MFTP < {FROM_EMAIL} >'
message["Bcc"] = ", ".join(student_mails)

rows = ''.join(generate_row(company_shortlist) for company_shortlist in shortlist)
columns = """
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Company (notice_id)</th>
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Count</th>
"""

shortlist_table = table_body.format(columns=columns, rows=rows)
message.attach(MIMEText(shortlist_table, "html"))

mails.append(message)

host_interested_rows = ''.join(host_interested_rows_list)
host_interested_shortlist_table = table_body.format(columns=host_interested_cols, rows=host_interested_rows)
host_interested_message.attach(MIMEText(host_interested_shortlist_table, "html"))
mails.append(host_interested_message)

return mails


def send_companies(mail, gmail_api, smtp):
Expand Down
23 changes: 3 additions & 20 deletions mftp/mftp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import company
import shortlist

import logging
import requests
import argparse
from datetime import datetime
Expand Down Expand Up @@ -65,28 +64,10 @@
if args.gmail_api or args.smtp:
_, new, modified = company.fetch(session, headers, ssoToken)

if new:
print("[NEW COMPANIES]", flush=True)
for com in new:
logging.info(
f" {com['Name']} | {com['Role']} | {com['CTC']} | {com['End_Date']} | {com['Interview_Date']}"
)
if modified:
print("[MODIFIED COMPANIES]", flush=True)
for com in modified:
logging.info(
f" {com['Name']} | {com['Role']} | {com['CTC']} | {com['End_Date']} | {com['Interview_Date']}"
)

filtered = []
if new + modified:
filtered = company.filter(new + modified, "OPEN_N")
if filtered:
for com in filtered:
logging.info(
f" {com['Name']} | {com['Role']} | {com['CTC']} | {com['End_Date']} | {com['Interview_Date']}"
)

latest_ssoToken = session.cookies.get("ssoToken")
mail_subject = "APPLY NOW! New companies opened"
companies_mail = mail.format_companies(
Expand All @@ -111,7 +92,9 @@
else:
shortlists = shortlist.search(notices)
if shortlists:
pass
shortlists_mails = mail.format_shortlists(shortlists)
if shortlists_mails:
mail.send_shortlists(shortlists_mails, args.gmail_api, args.ntfy)
mails = mail.format_notices(notices)
if mails:
mail.send_notices(mails, args.smtp, args.gmail_api, notice_db)
Expand Down
59 changes: 42 additions & 17 deletions mftp/shortlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,87 @@
import io
import PyPDF2
import logging
from env import ROLL_NAME
from collections import defaultdict
from env import HOSTER_INTERESTED_ROLLS, ROLL_MAIL, ROLL_NAME


def search(notices):
print('[SEARCHING SHORTLISTS]', flush=True)
notice_wise_shortlists = search_notice_wise_shortlists(notices)
if notice_wise_shortlists:
student_wise_shortlists = calc_student_wise_shortlists(notice_wise_shortlists)
return student_wise_shortlists
else:
print("[NO NEW SHORTLISTS]", flush=True)
return defaultdict(list)

shortlists = []

def calc_student_wise_shortlists(notice_wise_shortlists):
print("[PARSING STUDENT WISE SHORTLISTS]", flush=True)

student_wise_shortlists = defaultdict(list)

for notice_shortlist in notice_wise_shortlists:
for roll, shortlists in notice_shortlist.items():
student = student_wise_shortlists[roll]
student.append(shortlists)

for roll, shortlists in student_wise_shortlists.items():
shortlists_str = " | ".join([
f"{shortlist['company']} (#{shortlist['id']}) [{shortlist['count']}]"
for shortlist in shortlists
])
name = ROLL_NAME.get(roll)
logging.info(f" [{name} ({roll})]: {shortlists_str}")

return student_wise_shortlists


def search_notice_wise_shortlists(notices):
print("[SEARCHING NOTICE WISE SHORTLISTS]", flush=True)

notice_wise_shortlists = []
for notice in notices:
# Handling Shortists in Body
try:
body_shortlists = search_body(notice)
if body_shortlists:
shortlists.append(body_shortlists)
notice_wise_shortlists.append(body_shortlists)
except Exception as e:
logging.error(f" Failed to parse shortlists from notice body ~ {str(e)}")
continue

# Handling Shortlist in attachment
try:
if 'Attachment' in notice:
if "Attachment" in notice:
attachment_shortlists = search_attachment(notice)
if attachment_shortlists:
shortlists.append(attachment_shortlists)
notice_wise_shortlists.append(attachment_shortlists)
except Exception as e:
logging.error(f" Failed to parse shortlists from attachment ~ {str(e)}")
continue

return shortlists
return notice_wise_shortlists


def search_body(notice):
shortlists = defaultdict(dict)
body_data = notice["BodyData"]
body = body_data.decode_contents(formatter="html")

for roll in HOSTER_INTERESTED_ROLLS:
for roll in ROLL_NAME.keys():
count = body.count(roll)
if count > 0:
id_ = notice["UID"].split("_")[0]
company = notice["Company"]
name = ROLL_NAME.get(roll)
mails = ROLL_MAIL.get(roll)

shortlists[roll] = {
"id": id_,
"company": company,
"name": name,
"count": count,
"mails": mails,
}
logging.info(
f" [NOTICEBODY] {name} ({count}) -> {company} (#{id_})"
f" [NOTICEBODY] {name} ({roll}) [{count}] -> {company} (#{id_})"
)

return shortlists
Expand All @@ -64,23 +92,20 @@ def search_attachment(notice):
shortlists = defaultdict(dict)
attachment = notice["Attachment"]

for roll in HOSTER_INTERESTED_ROLLS:
for roll in ROLL_NAME.keys():
count = parse_pdf_bytes(attachment, roll)
if count > 0:
id_ = notice["UID"].split("_")[0]
company = notice["Company"]
name = ROLL_NAME.get(roll)
mails = ROLL_MAIL.get(roll)

shortlists[roll] = {
"id": id_,
"company": company,
"name": name,
"count": count,
"mails": mails,
}
logging.info(
f" [ATTACHMENT] {name} ({count}) -> {company} (#{id_})"
f" [ATTACHMENT] {name} ({roll}) [{count}] -> {company} (#{id_})"
)

return shortlists
Expand Down

0 comments on commit 3c6e1ef

Please sign in to comment.