/python-challenge

Python Coding Challenge

Primary LanguagePython

PennyMac Coding Challenge

Welcome! We're thrilled that you have considered PennyMac for the next step in your journey to becoming a kick-ass developer.

As a candidate for a development position at PennyMac, were you to be hired, it is the most likely case that your first tasks will involve working with a service that is completely unfamiliar to you. In the first couple weeks, your success would hinge on your ability to sift through source code, learn the architecture, and seek knowledge transfer. For the sake of this coding challenge, assume that is the case: You have accepted a position on the team and are contributing to a service unfamiliar to you.

Background

The service you are contributing to generates reports for loans that are used by the analytics team downstream. Currently there are two reports generated by the service: the borrowers report and the residences report. Both reports are generated from raw loan data using mapping rules, which are located in the resources/ folder. These mapping rules simply map a source value to a target value using appropriate JSONPaths.

Consider the following rule as an example:

{
    "source": "$.applications[0].borrower.firstName",
    "target": "$.reports[?(@.title == 'Borrowers Report')].borrowers[0].first_name"
}

The service will find the value(s) located at

$.applications[0].borrower.firstName

in the source loan data, and insert those into

$.reports[?(@.title == 'Borrowers Report')].borrowers[0].first_name

within the outputted report.

This service will be hosted within an AWS Lambda, with its handler at handler.main (i.e. the main function within the module handler.py). The Lambda will be triggered by an AWS EventBridge event, where the Loan data is supplied through a JSON payload as its detail. The generated report will be the output of the handler.main method and through extension, the output for the service Lambda.

The Challenge

Your task is to complete the following tickets and ultimately have your changes be accepted by the team.

These tickets will include a Description of the feature, typically describing the intended goal or outcome; the Requirements which outline smaller features that constitute the larger outcome for the ticket; and finally Acceptance Criteria which are a rubric of sorts for accepting the ticket as a whole. Tickets may also include Technical Considerations that describe any potential hiccups technically that will make the ticket challenging or pose a risk to the service as a whole.

Tickets

[FTR] CC-01

Description

For the residences report, we would like to reduce the output to only those residences that are unique. That is, the residences report should only list residences once each if they share a street, city, state, and zip.

Requirements

  • The service should identify which residence records are similar before generating the residences report.

Acceptance Criteria

  • The service generates a residences report which resemebles the following structure

    {
        "reports": [
            {
                "title": "Residences Report",
                "residences": [
                    {
                        "street": "...",
                        "city": "...",
                        "state": "...",
                        "zip": "..."
                    },
                    ...
                ]
            }
        ]
    }

    where each record in the

    $.reports[?(@.title == 'Residences Report')].residences
    

    collection is unique.

Technical Considerations

  • Currently, the service simply passes the mailing addresses for each borrower into separate records within the residences report's residences collection. This is due to limitations in how the service generates reports, assuming each item in the source loan data is independent of each other- that is not the case here. To accomplish this ticket, we need to introduce logic that allows the service to groom all mapped values before generating the report.

[FTR] CC-02

Description

For the borrowers report, we would like to insert a flag which indicates if the borrower and coborrower for a given application live together.

Requirements

  • The service should identify for each application if the borrower and coborrower share an address.
  • The service should include a new boolean field shared_address in the borrower's report if they do share an address.

Acceptance Criteria

  • The service generates a borrowers report which resemebles the following structure

    {
        "reports": [
            {
                "title": "Borrowers Report",
                "borrowers": [
                    {
                        "first_name": "...",
                        "last_name": "..."
                    },
                    ...
                ],
                "shared_address": true | false
            }
        ]
    }

    with a boolean field

    $.reports[?(@.title == 'Borrowers Report')].shared_address
    

    that is True if and only if the borrower and coborrower have identical mailingAddress objects.

[BUG] CC-03 (Optional)

This ticket is optional and is not required for this Coding Challenge, although, it may be discussed conversationally in the interview.

Description

For loans that have multiple applications, only the first application's borrowers are appearing in the borrowers report.

Reproduction

  1. Generate loan data that contains multiple appications. For example:

    {
        "applications": [
            {
                "borrower": { "firstName": "JANE" },
                "coborrower": { "firstName": "JOHN" }
            },
            {
                "borrower": { "firstName": "JACK" },
                "coborrower": { "firstName": "JILL" }
            }
        ]
    }
  2. Execute the service using an appropriate EventBridge event with the loan data from (1) as its detail.

Intended Result

"reports": [
    {
        "title": "Borrowers Report",
        "borrowers": [
            {"first_name": "JANE"},
            {"first_name": "JOHN"},
            {"first_name": "JACK"},
            {"first_name": "JILL"}
        ]
    }
]

Actual Result

"reports": [
    {
        "title": "Borrowers Report",
        "borrowers": [
            {"first_name": "JANE"},
            {"first_name": "JOHN"}
        ]
    }
]

Guidance

The purpose of this coding challenge is to gauge how well you can navigate within a project that you did not design. Also, for the meta-gamers out there, at the end of the day this is still part of an interview, so keep this in mind as some features and complexity have been injected into the repo purposefully to challenge your abilities.

In this section we offer some guidance to help you with this challenge.

Determine the entry-point for the service

This service is not a single script- its a series of modules and packages that are used cohesively within an AWS Lambda. Start by locating the entry-point for the service, and take a moment to plot that through the source code you intend to modify or add to.

The included test suite could help you here...

Focus on the components associated with each ticket

This repository is created with the intention to look as close to a service we use in production today as possible- just at a much smaller scale. Still though, there is a lot to sift through. For each ticket, start by recognizing which documents and modules are directly related to that feature and start there.

Don't feel the need to understand all code front-to-back. Trust the provided documentation and treat methods and classes as blackboxes, where you only need to concern yourself with the inputs and outputs, and not the internal mechanisms.

We aren't testing you on your knowledge of the service, but your ability to contribute to it- so don't try to memorize everything going on mechanically.

Leverage the test suite that is provided

Tinker with the code, insert print statements, modify rule sources and targets- the options are endless. Look at the tests that are included in the tests/ folder and modify those as you see fit to familiarize yourself with how the code behaves.

Also, the test suite could give some valuable context as to how the service is used if you are having trouble wrapping your head around what the inputs and outputs look like.

Don't forget about the boring stuff

In internal code reviews, a decent portion of comments directed at new developers typically involve coding style, anti-patterns, and package structure. Make sure that your contributions are well-documented, and aren't so complex as to introduce bad patterns into the service.