def campaign(request):
    """
    View for the campaign service page.
    """

    # Overrride requests Session authentication handling
    class NoRebuildAuthSession(requests.Session):
        def rebuild_auth(self, prepared_request, response):
            """
            No code here means requests will always preserve the Authorization
            header when redirected.
            Be careful not to leak your credentials to untrusted hosts!
            """

    url = "https://api.givebutter.com/v1/transactions/"
    headers = {"Authorization": f"Bearer {settings.GIVEBUTTER_API_KEY}"}
    response = None
    # Create custom requests session
    session = NoRebuildAuthSession()

    # Make GET request to server, timeout in seconds
    try:
        r = session.get(url, headers=headers, timeout=0.75)
        if r.status_code == 200:
            response = r.json()
        else:
            logger.error(f"ERROR in request: {r.status_code}")
    except requests.exceptions.Timeout:
        logger.warning("Connection to GiveButter API Timed out")
    except requests.ConnectionError:
        logger.warning("Connection to GiveButter API could not be resolved")
    except requests.exceptions.RequestException:
        logger.error(
            "An unknown issue occurred while trying to retrieve GiveButter Donor List"
        )

    # Grab context object to use later
    ctx = _get_context("Campaign")

    # Check for successful response, if so - filter, sort, and format data
    if response and "data" in response:
        response = response["data"]  # Pull data from GET response object
        logger.debug(f"GiveButter API Response: {response}")

        # Filter by only successful transactions, then sort by amount descending
        successful_txs = [tx for tx in response if tx["status"] == "succeeded"]
        sorted_txs = sorted(successful_txs, key=lambda tx: tx["amount"], reverse=True)

        # Clean data to a list of dictionaries & remove unnecessary data
        transactions = [
            {
                "name": tx["giving_space"]["name"],
                "amount": tx["giving_space"]["amount"],
                "message": tx["giving_space"]["message"],
            }
            for tx in sorted_txs[:20]
        ]

        # Attach transaction dictionary & length to context object
        ctx["transactions"] = transactions
        ctx["num_txs"] = len(successful_txs)

    return render(
        request,
        "public/campaign.html",
        ctx,
    )