Skip to content

Commit 3c6e1ef

Browse files
committed
feat: shortlist notifier
1 parent 3c15d4e commit 3c6e1ef

File tree

5 files changed

+191
-41
lines changed

5 files changed

+191
-41
lines changed

mftp/company.py

+9
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ def filter(companies, filter):
3030
for company in companies:
3131
if filter_func(company):
3232
filtered.append(company)
33+
logging.info(
34+
f" {company['Name']} | {company['Role']} | {company['CTC']} | {company['End_Date']} | {company['Interview_Date']}"
35+
)
3336

3437
return filtered
3538

@@ -102,11 +105,17 @@ def get_new_and_modified_companies(fetched, stored, unique_key="Job_Description"
102105
if key not in stored_dict:
103106
# New entry
104107
new_entries.append(fetched_entry)
108+
logging.info(
109+
f" [NEW COMPANY]: {fetched_entry['Name']} | {fetched_entry['Role']} | {fetched_entry['CTC']} | {fetched_entry['End_Date']} | {fetched_entry['Interview_Date']}"
110+
)
105111
else:
106112
# Compare the values of the fetched entry with the stored entry
107113
stored_entry = stored_dict[key]
108114
if any(fetched_entry[k] != stored_entry.get(k) for k in fetched_entry):
109115
updated_entries.append(fetched_entry)
116+
logging.info(
117+
f" [MODIFIED COMPANY]: {fetched_entry['Name']} | {fetched_entry['Role']} | {fetched_entry['CTC']} | {fetched_entry['End_Date']} | {fetched_entry['Interview_Date']}"
118+
)
110119

111120
return new_entries, updated_entries
112121

mftp/env.example.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
HOSTER_EMAIL = ["hoster@kgpian.iitkgp.ac.in", "emergency.hoster@kgpian.iitkgp.ac.in"]
2323
HOSTER_NAME = "Arpit Bhardwaj"
2424
HOSTER_ROLL = ROLL_NUMBER
25-
HOSTER_INTERESTED_ROLLS = [HOSTER_ROLL, "XXYYXXXXX", "NNAANNNNN"]
25+
HOSTER_INTERESTED_ROLLS = ["XXYYXXXXX", "NNAANNNNN"]
2626

2727
# SHORTLIST CONFIG (MUST)
2828
## Maps roll number to student names

mftp/mail.py

+136-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,144 @@
55
from email.mime.text import MIMEText
66
from email.mime.base import MIMEBase
77
from email.mime.multipart import MIMEMultipart
8-
from env import FROM_EMAIL, FROM_EMAIL_PASS, BCC_EMAIL_S, HOSTER_EMAIL
8+
from env import FROM_EMAIL, FROM_EMAIL_PASS, BCC_EMAIL_S, HOSTER_EMAIL, HOSTER_INTERESTED_ROLLS, ROLL_MAIL, ROLL_NAME
99

1010

11-
def format_shortlist():
12-
pass
11+
def send_shortlists(mails, gmail_api, smtp):
12+
print('[SENDING SHORTLIST UPDATES]', flush=True)
13+
14+
if gmail_api:
15+
import base64
16+
17+
try:
18+
service = generate_send_service()
19+
except Exception as e:
20+
logging.error(f" Failed to generate GMAIL API creds ~ {str(e)}")
21+
return
22+
23+
for mail in mails:
24+
try:
25+
response = service.users().messages().send(
26+
userId="me",
27+
body={"raw": base64.urlsafe_b64encode(mail.as_bytes()).decode()}
28+
).execute()
29+
except Exception as e:
30+
logging.error(f" Failed to send request to GMAIL API : {mail['Subject']} -x-> ({mail['Bcc']}) ~ {str(e)}")
31+
break
32+
33+
if 'id' in response:
34+
logging.info(f" [MAIL SENT] ~ {mail['Subject']} -> ({mail['Bcc']})")
35+
else:
36+
logging.error(f" Failed to Send Mail : {mail['Subject']} -x-> ({mail['Bcc']}) ~ {response}")
37+
break
38+
elif smtp:
39+
import ssl
40+
import smtplib
41+
context = ssl.create_default_context()
42+
43+
logging.info(" [Connecting to smtp.google.com] ...")
44+
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
45+
logging.info(" [Connected!]")
46+
try:
47+
server.login(FROM_EMAIL, FROM_EMAIL_PASS)
48+
logging.info(" [Logged In!]")
49+
except Exception as e:
50+
logging.error(f" Failed to log in ~ {str(e)}")
51+
return
52+
53+
for mail in mails:
54+
try:
55+
server.sendmail(mail["From"], mail["Bcc"].split(', '), mail.as_string())
56+
logging.info(f" [MAIL SENT] ~ {mail['Subject']} -> ({mail['Bcc']})")
57+
except smtplib.SMTPException as e:
58+
logging.error(f" Failed to Send Mail : {mail['Subject']} -x-> ({mail['Bcc']}) ~ {str(e)}")
59+
break
60+
61+
62+
def format_shortlists(shortlists):
63+
print('[FORMATTING SHORTLIST UPDATES]', flush=True)
64+
65+
table_body = """
66+
<html>
67+
<body>
68+
<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);">
69+
<div align="center">
70+
<table style="width: 100%; border-collapse: collapse;">
71+
<thead>
72+
<tr style="background-color: #f2f2f2;">
73+
{columns}
74+
</tr>
75+
</thead>
76+
<tbody>
77+
{rows}
78+
</tbody>
79+
</table>
80+
</div>
81+
</div>
82+
</body>
83+
</html>
84+
"""
85+
86+
host_interested_message = MIMEMultipart()
87+
host_interested_message["Subject"] = "People you are interested in are shortlisted!"
88+
host_interested_message["From"] = f'MFTP < {FROM_EMAIL} >'
89+
host_interested_message["Bcc"] = ", ".join(HOSTER_EMAIL)
90+
91+
host_interested_cols = """
92+
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Name (count)</th>
93+
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Company (notice_id)</th>
94+
"""
95+
96+
def generate_row(company_shortlist):
97+
return f"""
98+
<tr>
99+
<td style="border: 1px solid #ddd; padding: 8px;">{company_shortlist['company']} (#{company_shortlist['id']})</td>
100+
<td style="border: 1px solid #ddd; padding: 8px;">{company_shortlist['count']}</td>
101+
</tr>
102+
"""
103+
104+
def generate_host_interested_row(name, company_shortlist):
105+
return f"""
106+
<tr>
107+
<td style="border: 1px solid #ddd; padding: 8px;">{name} ({company_shortlist['count']})</td>
108+
<td style="border: 1px solid #ddd; padding: 8px;">{company_shortlist['company']} (#{company_shortlist['id']})</td>
109+
</tr>
110+
"""
111+
112+
host_interested_rows_list = []
113+
114+
mails = []
115+
for roll, shortlist in shortlists.items():
116+
if roll in HOSTER_INTERESTED_ROLLS:
117+
name = ROLL_NAME.get(roll)
118+
host_interested_rows_list += [generate_host_interested_row(name, company_shortlist) for company_shortlist in shortlist]
119+
120+
student_mails = ROLL_MAIL.get(roll)
121+
if student_mails is None:
122+
continue
123+
124+
message = MIMEMultipart()
125+
message["Subject"] = "You have been shortlisted!"
126+
message["From"] = f'MFTP < {FROM_EMAIL} >'
127+
message["Bcc"] = ", ".join(student_mails)
128+
129+
rows = ''.join(generate_row(company_shortlist) for company_shortlist in shortlist)
130+
columns = """
131+
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Company (notice_id)</th>
132+
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Count</th>
133+
"""
134+
135+
shortlist_table = table_body.format(columns=columns, rows=rows)
136+
message.attach(MIMEText(shortlist_table, "html"))
137+
138+
mails.append(message)
139+
140+
host_interested_rows = ''.join(host_interested_rows_list)
141+
host_interested_shortlist_table = table_body.format(columns=host_interested_cols, rows=host_interested_rows)
142+
host_interested_message.attach(MIMEText(host_interested_shortlist_table, "html"))
143+
mails.append(host_interested_message)
144+
145+
return mails
13146

14147

15148
def send_companies(mail, gmail_api, smtp):

mftp/mftp.py

+3-20
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import company
88
import shortlist
99

10-
import logging
1110
import requests
1211
import argparse
1312
from datetime import datetime
@@ -65,28 +64,10 @@
6564
if args.gmail_api or args.smtp:
6665
_, new, modified = company.fetch(session, headers, ssoToken)
6766

68-
if new:
69-
print("[NEW COMPANIES]", flush=True)
70-
for com in new:
71-
logging.info(
72-
f" {com['Name']} | {com['Role']} | {com['CTC']} | {com['End_Date']} | {com['Interview_Date']}"
73-
)
74-
if modified:
75-
print("[MODIFIED COMPANIES]", flush=True)
76-
for com in modified:
77-
logging.info(
78-
f" {com['Name']} | {com['Role']} | {com['CTC']} | {com['End_Date']} | {com['Interview_Date']}"
79-
)
80-
8167
filtered = []
8268
if new + modified:
8369
filtered = company.filter(new + modified, "OPEN_N")
8470
if filtered:
85-
for com in filtered:
86-
logging.info(
87-
f" {com['Name']} | {com['Role']} | {com['CTC']} | {com['End_Date']} | {com['Interview_Date']}"
88-
)
89-
9071
latest_ssoToken = session.cookies.get("ssoToken")
9172
mail_subject = "APPLY NOW! New companies opened"
9273
companies_mail = mail.format_companies(
@@ -111,7 +92,9 @@
11192
else:
11293
shortlists = shortlist.search(notices)
11394
if shortlists:
114-
pass
95+
shortlists_mails = mail.format_shortlists(shortlists)
96+
if shortlists_mails:
97+
mail.send_shortlists(shortlists_mails, args.gmail_api, args.ntfy)
11598
mails = mail.format_notices(notices)
11699
if mails:
117100
mail.send_notices(mails, args.smtp, args.gmail_api, notice_db)

mftp/shortlist.py

+42-17
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,87 @@
22
import io
33
import PyPDF2
44
import logging
5+
from env import ROLL_NAME
56
from collections import defaultdict
6-
from env import HOSTER_INTERESTED_ROLLS, ROLL_MAIL, ROLL_NAME
77

88

99
def search(notices):
10-
print('[SEARCHING SHORTLISTS]', flush=True)
10+
notice_wise_shortlists = search_notice_wise_shortlists(notices)
11+
if notice_wise_shortlists:
12+
student_wise_shortlists = calc_student_wise_shortlists(notice_wise_shortlists)
13+
return student_wise_shortlists
14+
else:
15+
print("[NO NEW SHORTLISTS]", flush=True)
16+
return defaultdict(list)
1117

12-
shortlists = []
18+
19+
def calc_student_wise_shortlists(notice_wise_shortlists):
20+
print("[PARSING STUDENT WISE SHORTLISTS]", flush=True)
21+
22+
student_wise_shortlists = defaultdict(list)
23+
24+
for notice_shortlist in notice_wise_shortlists:
25+
for roll, shortlists in notice_shortlist.items():
26+
student = student_wise_shortlists[roll]
27+
student.append(shortlists)
28+
29+
for roll, shortlists in student_wise_shortlists.items():
30+
shortlists_str = " | ".join([
31+
f"{shortlist['company']} (#{shortlist['id']}) [{shortlist['count']}]"
32+
for shortlist in shortlists
33+
])
34+
name = ROLL_NAME.get(roll)
35+
logging.info(f" [{name} ({roll})]: {shortlists_str}")
36+
37+
return student_wise_shortlists
38+
39+
40+
def search_notice_wise_shortlists(notices):
41+
print("[SEARCHING NOTICE WISE SHORTLISTS]", flush=True)
42+
43+
notice_wise_shortlists = []
1344
for notice in notices:
1445
# Handling Shortists in Body
1546
try:
1647
body_shortlists = search_body(notice)
1748
if body_shortlists:
18-
shortlists.append(body_shortlists)
49+
notice_wise_shortlists.append(body_shortlists)
1950
except Exception as e:
2051
logging.error(f" Failed to parse shortlists from notice body ~ {str(e)}")
2152
continue
2253

2354
# Handling Shortlist in attachment
2455
try:
25-
if 'Attachment' in notice:
56+
if "Attachment" in notice:
2657
attachment_shortlists = search_attachment(notice)
2758
if attachment_shortlists:
28-
shortlists.append(attachment_shortlists)
59+
notice_wise_shortlists.append(attachment_shortlists)
2960
except Exception as e:
3061
logging.error(f" Failed to parse shortlists from attachment ~ {str(e)}")
3162
continue
3263

33-
return shortlists
64+
return notice_wise_shortlists
3465

3566

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

41-
for roll in HOSTER_INTERESTED_ROLLS:
72+
for roll in ROLL_NAME.keys():
4273
count = body.count(roll)
4374
if count > 0:
4475
id_ = notice["UID"].split("_")[0]
4576
company = notice["Company"]
4677
name = ROLL_NAME.get(roll)
47-
mails = ROLL_MAIL.get(roll)
4878

4979
shortlists[roll] = {
5080
"id": id_,
5181
"company": company,
52-
"name": name,
5382
"count": count,
54-
"mails": mails,
5583
}
5684
logging.info(
57-
f" [NOTICEBODY] {name} ({count}) -> {company} (#{id_})"
85+
f" [NOTICEBODY] {name} ({roll}) [{count}] -> {company} (#{id_})"
5886
)
5987

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

67-
for roll in HOSTER_INTERESTED_ROLLS:
95+
for roll in ROLL_NAME.keys():
6896
count = parse_pdf_bytes(attachment, roll)
6997
if count > 0:
7098
id_ = notice["UID"].split("_")[0]
7199
company = notice["Company"]
72100
name = ROLL_NAME.get(roll)
73-
mails = ROLL_MAIL.get(roll)
74101

75102
shortlists[roll] = {
76103
"id": id_,
77104
"company": company,
78-
"name": name,
79105
"count": count,
80-
"mails": mails,
81106
}
82107
logging.info(
83-
f" [ATTACHMENT] {name} ({count}) -> {company} (#{id_})"
108+
f" [ATTACHMENT] {name} ({roll}) [{count}] -> {company} (#{id_})"
84109
)
85110

86111
return shortlists

0 commit comments

Comments
 (0)