from serenitas.utils.exchange import ExchangeMessage, FileAttachment from serenitas.utils.env import DAILY_DIR import warnings import datetime from .misc import get_dir, _recipients, _cc_recipients import gpg from serenitas.ops.trade_dataclasses import BondDeal from serenitas.ops.funds import Service from typing import ClassVar from dataclasses import dataclass _sql = ( "INSERT INTO bond_csv_upload (allocationid, identifier, principal, interest) SELECT id, identifier, principal_payment, " "accrued_payment FROM bond_trades WHERE trade_date=%s AND account=%s AND tradeid IS NOT NULL ON CONFLICT DO NOTHING RETURNING allocationid;" ) _bond_query = "SELECT * FROM bond_trades WHERE id in %s;" def upload_to_custodian(account, trade_date, conn, upload, em): _service = {"BBH": "BRINKER", "UMB": "UMB"} _fund = {"BBH": "BRINKER", "UMB": "SERCGMAST"} custodian = Service[_service[account]] with conn.cursor() as c: c.execute( _sql, ( trade_date, "BAC" if account == "UMB" else account, ), ) tids = tuple(row.allocationid for row in c) if not tids: return c.execute(_bond_query, (tids,)) for row in c: trade = BondDeal.from_dict(**row._asdict(), scaled=True) match account: case "BBH": custodian.staging_queue.append(trade.to_bbh("NEW")) case "UMB": custodian.staging_queue.append(trade.to_umb("NEW")) buf, dest = custodian.build_buffer("bond") custodian.staging_queue.clear() conn.commit() if upload: em = ExchangeMessage() em.send_email( f"{account}: Bond Positions Uploaded for {trade_date}", "Hi, \nWe've just uploaded the positions via SFTP. File receipt attached to this email", _recipients.get(account, _cc_recipients[_fund[account]]), cc_recipients=_cc_recipients[_fund[account]], reply_to=_cc_recipients[_fund[account]], attach=(FileAttachment(name=dest.name, content=buf),), ) custodian.upload(buf, dest.name, confirm=account != "UMB") @dataclass class Custodian: date: datetime.date account: ClassVar[str] def __init_subclass__(cls, account): cls.account = account class NT(Custodian, account="NT"): @classmethod def download_reports(cls, date=datetime.date.today()): em = ExchangeMessage() for msg in em.get_msgs(path=["SeleneOps", "Passport"]): for attach in msg.attachments: message_time = attach.last_modified_time if attach.name == "Attachment1.pgp" and message_time.date() == date: dest = get_dir(message_time.date(), archived=False) dest.mkdir(exist_ok=True, parents=True) with attach.fp as fp: plaintext, result, verify_result = gpg.Context().decrypt( fp.read(), passphrase="Serenitas1" ) fname = ( "custodian_wires" if "custodian" in verify_result.file_name else "cash" ) dest = dest / f"{fname}_{message_time:%Y%m%d%H%M}.csv" with open(dest, "w") as csvFile: text = plaintext.decode("utf-8").replace("\t", ",") csvFile.write(text) class UMB(Custodian, account="UMB"): @classmethod def download_reports(cls, date=datetime.date.today()): em = ExchangeMessage() for msg in em.get_msgs(count=20, path=["NYops", "Powerstation"]): for attach in msg.attachments: timestamp = attach.last_modified_time if ( attach.name.startswith("cash_balances_umb") and timestamp.date() == date ): dest = get_dir(timestamp.date(), archived=False) dest.mkdir(exist_ok=True, parents=True) p = dest / f"umb_{timestamp:%Y%m%d%H%M}.xlsx" if not p.exists(): p.write_bytes(attach.content) class BNY(Custodian, account="BONY2"): @classmethod def download_reports(cls, date=datetime.date.today()): em = ExchangeMessage() bowdst_wire_recent = True for msg in em.get_msgs( 20, path=["BowdoinOps", "Reports"], subject__startswith="Document(s) from Reporting", ): if msg.sender == "notify@bnymellon.com": for attach in msg.attachments: fname = attach.name if fname.endswith("csv"): base_name = fname.removesuffix(".csv") file_type, date_part = base_name.split("_") match file_type: case ( "Asset Detail" | "Net Investment Earned Income by Security" | "Settled Cash Statement" ): date = datetime.datetime.strptime( date_part, "%d %b %Y" ).date() case "BowdstWires": try: date = datetime.datetime.strptime( date_part, "%Y%m%d%H%M%S" ).date() except ValueError: date = datetime.datetime.strptime( date_part, "%d %b %Y%H%M%S" ).date() case "Live-cash": date = datetime.datetime.strptime( date_part, "%Y%m%d%H%M%S" ).date() case _: warnings.warn(f"Unknown report type {file_type}") continue p = DAILY_DIR / str(date) / "Reports" / fname if not p.parent.exists(): p.parent.mkdir(parents=True, exist_ok=True) if not p.exists(): p.write_bytes(attach.content)