/reporters/evangelist.py
https://gitlab.com/made-financial/made-financial-scripts · Python · 114 lines · 73 code · 20 blank · 21 comment · 21 complexity · f8dccbd60c5790dea3570b8c1e166680 MD5 · raw file
- # Reports how many memberships each member in a list has paid.
- # This is used to prepare a report for our evangelist/outreach contractor.
- #
- # Supported options:
- # - ids: output data for members in this comma-separated list
- # Supported formats:
- # - text: textual report to send by email
- import sys
- from collections import namedtuple
- import pandas as pd
- from .common import get_refunded
- sys.path.append("../data/plugins")
- from categorize.entities import Entities
- entities = Entities("../data/")
- sources = dict(PP="Paypal", BK="Bank", CA="Cash", CB="Bitcoin")
- Payment = namedtuple('Payment', 'month payee amount source id')
- Summary = namedtuple('Summary', 'payments total by_source by_level')
- def evangelist(movements, options, format):
- items = []
- refunded = get_refunded(movements)
- ids = options.get('ids').split(",")
- print(ids)
- for m in movements:
- # All movmements should have an id, if not discard them for now.
- if not 'movement_id' in m.meta:
- continue
- id = m.meta['movement_id']
- # Discard anything that has zero postings in Income:Membership
- payments = [p for p in m.postings if p.account == "Income:Membership"]
- if len(payments) == 0:
- continue
- # If a payment has been refunded, exclude it
- if id in refunded:
- continue
- for p in payments:
- # We need to find out which month this payment is for.
- # There are three possibilities:
- # - pay only for current month: no payment_period meta
- # - pay for another month: movement has payment_period meta
- # - pay for various months: each posting has a payment_period meta
- period = p.meta.get('payment_period', None)
- if period is None:
- period = m.meta.get('payment_period', None)
- if period is None:
- period = "{}-{:0>2}".format(m.date.year, m.date.month)
- # We also want to know who the payment is from.
- # In most cases the parent transaction's payee is what we want.
- # However in some cases a single transaction pays for various
- # members, identified by their entity_id.
- payee = "<< Unknown >>"
- member_id = p.meta.get('entity_id', None)
- if member_id is None:
- member_id = m.meta.get('entity_id', None)
- if member_id is not None and member_id != 'unknown':
- member = entities.members.get(int(member_id), None)
- if member is not None:
- payee = member['fullname']
- else:
- if m.payee is not None:
- payee = m.payee
- # Exclude this payment if member does not match the filter.
- if member_id not in ids:
- continue
- item = dict(id = m.meta['movement_id'],
- month = period,
- amount = round(p.units.number * -1),
- payee = payee,
- source = m.meta['movement_id'][0:2])
- items.append(item)
- if len(items) == 0:
- print("No records for any of the members in the list.")
- return
- paid = pd.DataFrame().from_records(items).groupby('payee')
- # output the data
- as_is = lambda v: str(v)
- format = lambda f: lambda v: f.format(v)
- import calendar
- valid = []
- invalid = []
- for payee, items in paid:
- header = "{}: {} payments of {} EUR".format(payee, items['amount'].count(),
- items['amount'].sum() / items['amount'].count())
- format_month = lambda m: calendar.month_abbr[int(m.split('-')[1])]
- mnts = [format_month(m) for m in items['month'].apply(lambda i: i).values]
- m = "{} : {}".format(header, ",".join(mnts))
- if len(mnts) < 3:
- invalid.append(m)
- else:
- valid.append(m)
- print("== Valid")
- print("\n".join(valid))
- print()
- print("== Not Valid")
- print("\n".join(invalid))