Skip to content

Commit

Permalink
Added processing to apply project credits, determine institution name…
Browse files Browse the repository at this point in the history
… for each PI, and exporting HU and BU invoices
  • Loading branch information
QuanMPhm committed Apr 3, 2024
1 parent 18c2770 commit a497271
Showing 1 changed file with 154 additions and 6 deletions.
160 changes: 154 additions & 6 deletions process_report/process_report.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,105 @@
import argparse
import os
import sys

import pandas


### Invoice field names
PROJECT_FIELD = 'Project - Allocation'
MANAGER_PI_FIELD = 'Manager (PI)'
INSTITUTION_FIELD = 'Institution'
COST_FIELD = 'Cost'
CREDIT_FIELD = 'Credit'
CREDIT_CODE_FIELD = 'Credit Code'
BALANCE_FIELD = 'Balance'
###


def apply_credits_0001(dataframe):
credit_code = "0001"
pass


def apply_credits_0002(dataframe):
"""Applies the New PI Credit. This credit function expects the
env var `C0002_OLD_PI` to be set, pointing to a txt file containing old PIs"""
credit_code = "0002"
credit_amount = 1000

old_pi_list = set()
try:
with open(os.getenv("C0002_OLD_PI")) as f:
for pi in f: old_pi_list.add(pi.strip())

except Exception:
print("Old PI file does not exist, or the C0002_OLD_PI env var is not set")
sys.exit(1)

pi_list = dataframe[MANAGER_PI_FIELD].unique()

for pi in pi_list:
if pi != pi: continue # NaN check
if pi in old_pi_list: continue # Is the PI an old PI?

pi_projects = dataframe[dataframe[MANAGER_PI_FIELD] == pi]
rem_credit = credit_amount
for i, row in pi_projects.iterrows():
project_cost = row[COST_FIELD]
if project_cost >= rem_credit:
dataframe.at[i, CREDIT_FIELD] = rem_credit
dataframe.at[i, CREDIT_CODE_FIELD] = credit_code
dataframe.at[i, BALANCE_FIELD] = row[COST_FIELD] - rem_credit
break
else:
dataframe.at[i, CREDIT_FIELD] = project_cost
dataframe.at[i, CREDIT_CODE_FIELD] = credit_code
dataframe.at[i, BALANCE_FIELD] = 0
rem_credit -= project_cost

return dataframe


applied_credits = [apply_credits_0002]


def get_institution_from_pi(pi_uname):

institute_map = {
"northeastern.edu" : "Northeastern University",
"bu.edu" : "Boston University",
"bentley.edu" : "Bentley",
"uri.edu" : "University of Rhode Island",
"redhat.com" : "Red Hat",
"childrens.harvard.edu" : "Boston Childrens Hospital",
"mclean.harvard.edu" : "McLean Hospital",
"meei.harvard.edu" : "Massachusetts Eye & Ear",
"dfci.harvard.edu" : "Dana-Farber Cancer Institute",
"bwh.harvard.edu" : "Brigham and Women's Hospital",
"bidmc.harvard.edu" : "Beth Israel Deaconess Medical Center",
"harvard.edu" : "Harvard University",
"wpi.edu" : "Worcester Polytechnic Institute",
"mit.edu" : "Massachusetts Institute of Technology",
"umass.edu" : "University of Massachusetts Amherst",
"uml.edu" : "University of Massachusetts Lowell",
"codeforboston.org" : "Code For Boston",
"mmsh" : "Harvard University",
"gstuart" : "University of Massachusetts Amherst",
"rudolph" : "Boston Childrens Hospital",
"robbaron" : "Boston University",
"kmdalton" : "Harvard University",
"mzink" : "University of Massachusetts Amherst",
"yale.edu" : "Yale University",
"francesco.pontiggia" : "Harvard University",
}

for name, institute in institute_map.items():
if name in pi_uname: return institute

print(f"PI name {pi_uname} does not match any institution!")
return ""


def main():
"""Remove non-billable PIs and projects"""

Expand Down Expand Up @@ -41,6 +137,18 @@ def main():
default="pi_invoices",
help="Name of output folder containing pi-specific invoice csvs"
)
parser.add_argument(
"--HU-only",
required=False,
default="HU_only.csv",
help="Name of output csv for HU invoices"
)
parser.add_argument(
"--HU-BU",
required=False,
default="HU_BU.csv",
help="Name of output csv for HU and BU invoices"
)
args = parser.parse_args()
merged_dataframe = merge_csv(args.csv_files)

Expand All @@ -60,9 +168,13 @@ def main():

projects = list(set(projects + timed_projects_list))

merged_dataframe = add_credits(merged_dataframe)
merged_dataframe = add_institution(merged_dataframe)
billable_projects = remove_non_billables(merged_dataframe, pi, projects, args.output_file)
remove_billables(merged_dataframe, pi, projects, "non_billable.csv")
export_pi_billables(billable_projects, args.output_folder)
export_HU_only(billable_projects, args.HU_only)
export_HU_BU(billable_projects, args.HU_BU)


def merge_csv(files):
Expand Down Expand Up @@ -102,7 +214,7 @@ def timed_projects(timed_projects_file, invoice_date):

def remove_non_billables(dataframe, pi, projects, output_file):
"""Removes projects and PIs that should not be billed from the dataframe"""
filtered_dataframe = dataframe[~dataframe['Manager (PI)'].isin(pi) & ~dataframe['Project - Allocation'].isin(projects)]
filtered_dataframe = dataframe[~dataframe[MANAGER_PI_FIELD].isin(pi) & ~dataframe[PROJECT_FIELD].isin(projects)]
filtered_dataframe.to_csv(output_file, index=False)
return filtered_dataframe

Expand All @@ -112,21 +224,57 @@ def remove_billables(dataframe, pi, projects, output_file):
So this *keeps* the projects/pis that should not be billed.
"""
filtered_dataframe = dataframe[dataframe['Manager (PI)'].isin(pi) | dataframe['Project - Allocation'].isin(projects)]
filtered_dataframe = dataframe[dataframe[MANAGER_PI_FIELD].isin(pi) | dataframe[PROJECT_FIELD].isin(projects)]
filtered_dataframe.to_csv(output_file, index=False)


def export_pi_billables(dataframe: pandas.DataFrame, output_folder):
if not os.path.exists(output_folder):
os.mkdir(output_folder)

invoice_month = dataframe['Invoice Month'].iat[0]
pi_list = dataframe['Manager (PI)'].unique()
pi_list = dataframe[MANAGER_PI_FIELD].unique()

for pi in pi_list:
pi_projects = dataframe[dataframe['Manager (PI)'] == pi]
pi_instituition = pi_projects['Institution'].iat[0]
if pi != pi: continue
pi_projects = dataframe[dataframe[MANAGER_PI_FIELD] == pi]
pi_instituition = pi_projects[INSTITUTION_FIELD].iat[0]
pi_projects.to_csv(output_folder + f"/{pi_instituition}_{pi}_{invoice_month}.csv")



def add_credits(dataframe : pandas.DataFrame):
"""Adds credits to PIs depending on different criterions"""
dataframe.insert(dataframe.columns.get_loc(COST_FIELD) + 1, CREDIT_FIELD, 0.0)
dataframe.insert(dataframe.columns.get_loc(CREDIT_FIELD) + 1, CREDIT_CODE_FIELD, None)
dataframe.insert(dataframe.columns.get_loc(CREDIT_CODE_FIELD) + 1, BALANCE_FIELD, 0.0)

# Apply credits
for credit_func in applied_credits:
dataframe = credit_func(dataframe)

return dataframe


def add_institution(dataframe: pandas.DataFrame):
"""Determine the PI's institution name, logging any PI whose institution cannot be determined"""
for i, row in dataframe.iterrows():
pi_name = row[MANAGER_PI_FIELD]
if pi_name != pi_name: print(f"Project {row[PROJECT_FIELD]} has no PI") # Nan check
else: dataframe.at[i, INSTITUTION_FIELD] = get_institution_from_pi(pi_name)

return dataframe


def export_HU_only(dataframe, output_file):
HU_projects = dataframe[dataframe[INSTITUTION_FIELD] == 'Harvard University']
HU_projects.to_csv(output_file)


def export_HU_BU(dataframe, output_file):
HU_BU_projects = dataframe[(dataframe[INSTITUTION_FIELD] == 'Harvard University') |
(dataframe[INSTITUTION_FIELD] == 'Boston University')]
HU_BU_projects.to_csv(output_file)


if __name__ == "__main__":
main()

0 comments on commit a497271

Please sign in to comment.