def __init__(cls, what, bases=None, dict_=None): super().__init__(what, bases, dict_) if hasattr(cls, 'public_interface'): # create a class with a specific set of fields class SpecificObjectFieldGroup(ObjectFieldGroup): def __init__(self, fields): super().__init__(fields, cls) # field group for the constructor parameters cls.constructor_parameters = ArgsFieldGroup(cls.public_interface) # field group for the public properties cls.properties = SpecificObjectFieldGroup(cls.public_interface) # field group for private members cls.internal = cls.constructor_parameters.derive( prefix_name_with_underscore, SpecificObjectFieldGroup) # mapping from constructor params to private members cls.constructor_to_internal = Mapping(cls.constructor_parameters, cls.internal) # mapping from public properties to private members cls.properties_to_internal = Mapping(cls.properties, cls.internal) # dynamically create properties for property_field, internal in cls.properties_to_internal: property_field.create_property_on_class(cls, internal.name)
def households_from_gsheet(session, extract_from_parish_list): household_gsheet = get_gsheet_fields( Household, { 'reference no': 'id', 'address1': 'ADDRESS1', 'address2': 'ADDRESS2', 'address3': 'ADDRESS3', 'county': 'County', 'eircode': 'EIRCODE', 'telephone': 'landline', }) household_mapping = Mapping(household_gsheet, Household.constructor_parameters, field_casts={ 'address1': strip_commas_and_spaces, 'address2': strip_commas_and_spaces, 'address3': strip_commas_and_spaces, 'county': strip_commas_and_spaces, 'eircode': strip_commas_and_spaces, }) household_rows = extract_from_parish_list( 'households', 'A1', ('id', 'ADDRESS1', 'ADDRESS2', 'ADDRESS3', 'County', 'EIRCODE', 'landline')) households = list( model_instances(household_rows, household_mapping, Household)) session.add_all(households)
def nominal_accounts_from_gsheet(session, extract_from_detailed_ledger): nominal_account_gsheet = get_gsheet_fields( NominalAccount, { 'code': 'Code', 'description': 'Description', 'SOFA heading': 'SOFA heading', 'category': 'Category', 'sub category': 'Sub-category', }) nominal_account_gsheet['SOFA heading'] = StringField('SOFA heading') nominal_account_gsheet['Category'] = StringField('Category') nominal_account_gsheet['Sub-category'] = StringField('Sub-category') field_casts = { 'SOFA heading': conform_sofa_heading, 'Category': conform_category, 'Sub-category': conform_sub_category, } nominal_account_mapping = Mapping(nominal_account_gsheet, NominalAccount.constructor_parameters, field_casts=field_casts) nominal_accounts = extract_from_detailed_ledger( 'RCB Nominal Accounts', 'A1', ('Code', 'Description', 'SOFA heading', 'Category', 'Sub-category')) load_class(session, nominal_accounts, nominal_account_mapping, NominalAccount)
def __init__(self, item_instance_class, account_collection): self._item_instance_class = item_instance_class self._account_collection = account_collection csv_field_account = StringField('account') csv_field_date = StringField('date') csv_field_details = StringField('details') csv_field_currency = StringField('currency') csv_field_debit = StringField('debit') csv_field_credit = StringField('credit') csv_field_balance = StringField('balance') csv_field_detail_override = ComputedStringField( 'detail_override', lambda fg, i: None) csv_field_designated_balance = ComputedStringField( 'designated_balance', lambda fg, i: StatementItemDesignatedBalance.No) statement_item_csv_fields = ListFieldGroup( (csv_field_account, UnusedField('_unused_'), csv_field_date, UnusedField('_unused_'), csv_field_details, csv_field_currency, csv_field_debit, csv_field_credit, csv_field_balance, csv_field_detail_override, csv_field_designated_balance)) field_mappings = tuple( zip((csv_field_account, csv_field_date, csv_field_details, csv_field_currency, csv_field_debit, csv_field_credit, csv_field_balance, csv_field_detail_override, csv_field_designated_balance), item_instance_class.constructor_parameters)) self.csv_to_constructor = Mapping( statement_item_csv_fields, item_instance_class.constructor_parameters, field_mappings, field_casts=dict(date=cast_dmy_date_from_string))
def funds_from_gsheet(session, extract_from_detailed_ledger): fund_gsheet = get_gsheet_fields( Fund, { 'name': 'fund', 'restriction': 'type', 'is parish fund': 'parish fund', 'is realised': 'realised', 'account': 'bank account id' }) fund_gsheet['restriction'] = StringField('restriction') fund_gsheet['parish fund'] = StringField('parish fund') fund_gsheet['realised'] = StringField('realised') fund_gsheet['bank account id'] = StringField('bank account id') field_casts = { 'type': conform_fund_restriction, 'parish fund': conform_yes_no, 'realised': conform_yes_no, 'bank account id': AccountQuery(session).instance_finder('reference_no', int) } fund_mapping = Mapping(fund_gsheet, Fund.constructor_parameters, field_casts=field_casts) funds = extract_from_detailed_ledger( 'funds', 'A11', ('fund', 'type', 'parish fund', 'realised', 'bank account id')) load_class(session, funds, fund_mapping, Fund)
def _statement_items_for_gsheet(statement_items): gsheet_fields = _statement_item_gsheet_export_fields() gsheet_field_names = [field.name for field in gsheet_fields] gsheet_field_group = TupleFieldGroup(gsheet_fields) internal_to_gsheet = Mapping(StatementItem.internal, gsheet_field_group) statement_items_for_gsheet = (internal_to_gsheet.cast_from(statement_item) for statement_item in statement_items) return gsheet_field_names, statement_items_for_gsheet
def subjects_from_gsheet(session, extract_from_detailed_ledger): subject_gsheet = get_gsheet_fields(Subject, { 'name': 'subject', }) subject_mapping = Mapping(subject_gsheet, Subject.constructor_parameters) subjects = extract_from_detailed_ledger( 'report lookups', 'A3', ('subject', 'select vestry summary', 'easter vestry summary')) load_class(session, subjects, subject_mapping, Subject)
def accounts_from_gsheet(session, extract_from_detailed_ledger): account_gsheet = get_gsheet_fields(Account, {'reference no': 'id'}) account_gsheet['status'] = StringField('status') account_mapping = Mapping(account_gsheet, Account.constructor_parameters, field_casts=field_casts) accounts = extract_from_detailed_ledger( 'bank accounts', 'A1', ('id', 'purpose', 'status', 'name', 'institution', 'sort code', 'account no', 'BIC', 'IBAN')) load_class(session, accounts, account_mapping, Account)
def statement_item_csv(statement_items, csv_file): csv_fields = _statement_item_export_fields() csv_field_names = [field.name for field in csv_fields] csv_field_group = DictFieldGroup(csv_fields) internal_to_csv = Mapping(StatementItem.internal, csv_field_group) csv_writer = DictWriter(csv_file, csv_field_names, dialect=excel_tab) csv_writer.writeheader() for statement_item in statement_items: csv_writer.writerow(internal_to_csv.cast_from(statement_item)) return csv_file
def tax_rebates_from_gsheet(session, extract_from_detailed_ledger): gs_field_parishioner_id = IntField('id') gs_field_status = StringField('status') gs_field_2015_rebate = StringField('2015 rebate') gs_field_2016_rebate = StringField('2016 rebate') gs_field_2017_rebate = StringField('2017 rebate') gs_field_2018_rebate = StringField('2018 rebate') tax_rebate_gsheet = ListFieldGroup( ( gs_field_parishioner_id, UnusedField('household id'), UnusedField('new pps'), gs_field_status, gs_field_2015_rebate, gs_field_2016_rebate, gs_field_2017_rebate, gs_field_2018_rebate, ) ) field_mappings = tuple(zip( ( gs_field_parishioner_id, gs_field_status, gs_field_2015_rebate, gs_field_2016_rebate, gs_field_2017_rebate, gs_field_2018_rebate, ), TaxRebate.constructor_parameters )) field_casts = { 'id': PersonQuery(session).instance_finder('parishioner_reference_no', int), } tax_rebate_mapping = Mapping(tax_rebate_gsheet, TaxRebate.constructor_parameters, field_mappings, field_casts) tax_rebates = extract_from_detailed_ledger( 'tax rebate responses', 'A1', ('id', 'household id', 'new pps', 'status', '2015 rebate', '2016 rebate', '2017 rebate', '2018 rebate') ) load_class(session, tax_rebates, tax_rebate_mapping, TaxRebate)
def statement_item_from_gsheet(session, extract_from_detailed_ledger): statement_item_gsheet = get_gsheet_fields(StatementItem, None) field_casts = { 'account': AccountQuery(session).instance_finder('account_no', None), 'date': cast_dmy_date_from_string, 'debit': strip_commas, 'credit': strip_commas, 'balance': ignore_na, 'designated balance': cast_designated_balance, } statement_item_mapping = Mapping(statement_item_gsheet, StatementItem.constructor_parameters, field_casts=field_casts) statement_items = extract_from_detailed_ledger( 'bank statements', 'A1', ('account', 'date', 'details', 'currency', 'debit', 'credit', 'balance', 'detail override', 'designated balance')) load_class(session, statement_items, statement_item_mapping, StatementItem)
def tax_rebate_submissions_from_gsheet(session, extract_from_tax_rebates): gs_field_status = StringField('status') gs_field_year = IntField('year') gs_field_cal_rebate = StringField('calculated rebate') gs_field_filing_date = StringField('filing date') gs_field_est_rebate = StringField('estimated rebate from CDS1') gs_field_notice_no = StringField('notice number') gs_field_notes = StringField('notes') tax_rebates_gsheet = ListFieldGroup( (gs_field_status, gs_field_year, UnusedField('parishoner count'), UnusedField('donor count'), UnusedField('donations'), gs_field_cal_rebate, gs_field_filing_date, gs_field_est_rebate, gs_field_notice_no, gs_field_notes)) field_mappings = tuple( zip((gs_field_status, gs_field_year, gs_field_cal_rebate, gs_field_filing_date, gs_field_est_rebate, gs_field_notice_no, gs_field_notes), TaxRebateSubmission.constructor_parameters)) field_casts = { 'status': lambda v, _: SubmissionStatus.Posted if v == 'posted' else SubmissionStatus.Revoked, 'calculated rebate': strip_commas, 'filing date': cast_dmy_date_from_string, 'estimated rebate from CDS1': strip_commas } tax_rebates_mapping = Mapping(tax_rebates_gsheet, TaxRebateSubmission.constructor_parameters, field_mappings, field_casts) tax_rebate_submissions = extract_from_tax_rebates( 'records', 'B1', ('status', 'year', 'parishoner count', 'donor count', 'donations', 'calculated rebate', 'filing date', 'estimated rebate from CDS1', 'notice number', 'notes')) load_class(session, tax_rebate_submissions, tax_rebates_mapping, TaxRebateSubmission)
def envelopes_from_gsheet(session, extract_from_detailed_ledger): gs_field_year = IntField('year') gs_field_counterparty = IntField('counterpartyid') gs_field_parishioner = IntField('parishionerid') gs_field_envelope_number = IntField('envelope number') envelope_gsheet = ListFieldGroup(( gs_field_year, gs_field_counterparty, gs_field_parishioner, UnusedField('household id'), gs_field_envelope_number, )) field_mappings = tuple( zip(( gs_field_year, gs_field_counterparty, gs_field_parishioner, gs_field_envelope_number, ), Envelope.constructor_parameters)) field_casts = { 'counterpartyid': CounterpartyQuery(session).instance_finder('reference_no', int), 'parishionerid': PersonQuery(session).instance_finder('parishioner_reference_no', int), } envelope_mapping = Mapping(envelope_gsheet, Envelope.constructor_parameters, field_mappings, field_casts) envelopes = extract_from_detailed_ledger( 'FWE records', 'A1', ('year', 'counterpartyid', 'parishionerid', 'household id', 'envelope number')) load_class(session, envelopes, envelope_mapping, Envelope)
def counterparty_from_gsheet(session, extract_from_detailed_ledger): gs_field_id = IntField('id') gs_field_bank_text = StringField('bank text') gs_field_person = IntField('parishoner id') gs_field_organisation = IntField('household id') gs_field_name_override = StringField('name override') gs_field_method = StringField('method') gs_field_so_card = StringField('SO card?') gs_field_by_email = StringField('by email') gs_field_notes = StringField('notes') counterparty_gsheet = ListFieldGroup(( gs_field_id, gs_field_bank_text, gs_field_person, gs_field_organisation, UnusedField('main contact'), UnusedField('.'), gs_field_name_override, UnusedField('name'), UnusedField('_'), UnusedField('reverse lookup parishoner id'), UnusedField('reverse lookup cp id'), UnusedField('__'), UnusedField('___'), UnusedField('____'), UnusedField('_____'), UnusedField('______'), gs_field_method, gs_field_so_card, gs_field_by_email, gs_field_notes, )) field_mappings = tuple( zip(( gs_field_id, gs_field_bank_text, gs_field_person, gs_field_organisation, gs_field_name_override, gs_field_method, gs_field_so_card, gs_field_by_email, gs_field_notes, ), Counterparty.constructor_parameters)) field_casts = { 'parishoner id': PersonQuery(session).instance_finder('parishioner_reference_no', int), 'household id': OrganisationQuery(session).instance_finder('reference_no', int), 'SO card?': cast_yes_no, 'by email': cast_yes_no, } counterparty_mapping = Mapping(counterparty_gsheet, Counterparty.constructor_parameters, field_mappings, field_casts) counterparties = extract_from_detailed_ledger( 'counterparties', 'A1', ('id', 'bank text', 'parishoner id', 'household id', 'main contact', '.', 'name override', 'name', '_', 'reverse lookup parishoner id', 'reverse lookup cp id', '__', '___', '____', '_____', '______', 'method', 'SO card?', 'by email', 'notes')) load_class(session, counterparties, counterparty_mapping, Counterparty)
def parishioners_from_gsheet(session, extract_from_parish_list): gs_field_id = IntField('id') gs_field_surname = StringField('SURNAME') gs_field_first_name = StringField('FIRST_NAME') gs_field_title = StringField('TITLE') gs_field_status = StringField('STATUS') gs_field_main_contact = StringField('main_contact') gs_field_household_ref_no = IntField('household_id') gs_field_mobile = StringField('mobile') gs_field_other_personal = StringField('other personal') gs_field_email = StringField('email') gs_field_gdpr_response = StringField('gdpr response?') gs_field_by_email = StringField('email?') gs_field_by_phone = StringField('phone?') gs_field_by_post = StringField('post?') gs_field_news = StringField('news?') gs_field_finance = StringField('finance?') parishioner_gsheet = ListFieldGroup(( gs_field_id, gs_field_surname, gs_field_first_name, gs_field_title, gs_field_status, gs_field_main_contact, gs_field_household_ref_no, UnusedField('ADDRESS1'), UnusedField('ADDRESS2'), UnusedField('ADDRESS3'), UnusedField('County'), UnusedField('EIRCODE'), UnusedField('TELEPHONE'), gs_field_mobile, gs_field_other_personal, gs_field_email, gs_field_gdpr_response, gs_field_by_email, gs_field_by_phone, gs_field_by_post, gs_field_news, gs_field_finance, )) field_mappings = tuple( zip(( gs_field_id, gs_field_surname, gs_field_first_name, gs_field_title, gs_field_status, gs_field_main_contact, gs_field_household_ref_no, gs_field_mobile, gs_field_other_personal, gs_field_email, gs_field_gdpr_response, gs_field_by_email, gs_field_by_phone, gs_field_by_post, gs_field_news, gs_field_finance, ), Parishioner.constructor_parameters)) parishioner_mapping = Mapping(parishioner_gsheet, Parishioner.constructor_parameters, field_mappings) parishioner_rows = extract_from_parish_list( 'parishioners', 'A1', ('id', 'SURNAME', 'FIRST_NAME', 'TITLE', 'STATUS', 'main contact?', 'household id', 'ADDRESS1', 'ADDRESS2', 'ADDRESS3', 'County', 'EIRCODE', 'landline', 'mobile', 'other personal', 'email', 'gdpr response?', 'email?', 'phone?', 'post?', 'news?', 'finance?')) parishioners = list( model_instances(parishioner_rows, parishioner_mapping, Parishioner)) session.add_all(parishioners)
def transactions_from_gsheet(session, extract_from_detailed_ledger): gs_field_reference_no = StringField('id') gs_field_public_code = StringField('reference') gs_field_year = StringField('year') gs_field_month = StringField('month') gs_field_day = StringField('day') gs_field_counterparty = StringField('counterparty_id') gs_field_payment_method = StringField('payment_method') gs_field_description = StringField('description') gs_field_amount = StringField('amount') gs_field_subject = StringField('subject') gs_field_income_expenditure = StringField('income_expenditure') gs_field_FY = StringField('FY') gs_field_fund = StringField('fund') gs_field_comments = StringField('comments') transaction_gsheet = ListFieldGroup(( gs_field_reference_no, gs_field_fund, gs_field_public_code, UnusedField('bank account'), UnusedField('compositeId'), gs_field_year, gs_field_month, gs_field_day, gs_field_counterparty, UnusedField('counterparty name'), UnusedField('household_id'), gs_field_payment_method, gs_field_description, gs_field_amount, gs_field_subject, gs_field_income_expenditure, gs_field_FY, UnusedField('sign'), UnusedField('net'), UnusedField('from bank statement'), UnusedField('reconciles'), UnusedField('bank stmt year'), UnusedField('year reconciles?'), UnusedField('monthText'), UnusedField('quarter'), UnusedField('subjectSummary'), UnusedField('fund type'), gs_field_comments, )) field_casts = { 'counterparty_id': CounterpartyQuery(session).instance_finder('reference_no', int), 'payment_method': cast_payment_method, 'amount': strip_commas, 'subject': SubjectQuery(session).instance_finder('name', None), 'income_expenditure': cast_income_expenditure, 'fund': FundQuery(session).instance_finder('name', None), } field_mappings = tuple( zip(( gs_field_reference_no, gs_field_public_code, gs_field_year, gs_field_month, gs_field_day, gs_field_counterparty, gs_field_payment_method, gs_field_description, gs_field_amount, gs_field_subject, gs_field_income_expenditure, gs_field_FY, gs_field_fund, gs_field_comments, ), Transaction.constructor_parameters)) transaction_mapping = Mapping(transaction_gsheet, Transaction.constructor_parameters, field_mappings, field_casts) transactions = extract_from_detailed_ledger( 'transactions', 'A1', ('id', 'fund', 'reference', 'bank account', 'compositeId', 'year', 'month', 'day', 'counterparty_id', 'counterparty_name', 'household_id', 'payment_method', 'description', 'amount', 'subject', 'income/expenditure', 'FY', 'sign', 'net', 'from bank statement', 'reconciles', 'bank stmt year', 'year reconciles?', 'monthText', 'quarter', 'subjectSummary', 'fund type', 'comments')) load_class(session, transactions, transaction_mapping, Transaction)
} def conform_value(value, _): return ACCOUNT_STATUS_MAP.get(value.lower(), AccountStatus.Active) field_casts = dict(status=conform_value) account_csv_fields = Account.constructor_parameters.derive( replace_underscore_with_space, DictFieldGroup) account_csv_fields['reference no'].name = 'id' account_csv_fields['status'] = StringField('status') csv_to_constructor = Mapping(account_csv_fields, Account.constructor_parameters, field_casts=field_casts) def accounts_from_csv(account_csv): items = [] for row in DictReader(account_csv): account_args = csv_to_constructor.cast_from(row) items.append(Account(**account_args)) collection = AccountCollection(items) return collection def accounts_from_gsheet(session, extract_from_detailed_ledger): account_gsheet = get_gsheet_fields(Account, {'reference no': 'id'})
def node_connection_field(model_class, query_class, node_class, description): """ Make a ConnectionField for a model class :param query_class: query class for model :param node_class: ObjectType :param description: string describing the collection :return: ConnectionField """ entity_name = node_class.__name__ class NodeConnection(object): @with_session def resolve_total_count(self, args, context, info, session): return len(query_class(session)) @with_session def resolve_filtered_count(self, args, context, info, session): return context['count'] @with_session def resolve_page_info(self, args, context, info, session): return context['pageInfo'] connection_class = type( '{}Connection'.format(entity_name), # inherit class methods from NodeConnection and other behaviour from # graphene.relay.Connection (NodeConnection, Connection), # equivalent to # total_count = graphene.Int() # filtered_count = graphene.Int() # class Meta: # node = node_class { 'total_count': graphene.Int(), 'filtered_count': graphene.Int(), 'Meta': type('Meta', (object, ), { 'node': node_class, }) }) untyped_filter_field_group = model_class.properties.derive( field_group_class=PartialDictFieldGroup) typed_filter_field_group = model_class.internal.derive( field_group_class=PartialDictFieldGroup) filter_casts = _filter_casts(model_class, typed_filter_field_group) filter_to_internal = Mapping(untyped_filter_field_group, typed_filter_field_group, field_casts=filter_casts) camel_case_field_group = model_class.properties.derive( snake_to_camel_case, PartialDictFieldGroup) order_by_field_group = model_class.internal.derive(make_boolean, PartialDictFieldGroup) order_by_to_mapped = Mapping(camel_case_field_group, order_by_field_group) @with_session def resolver(self, args, context, info, session): query = query_class(session) filters = args.get('filters') if filters: typed = filter_to_internal.cast_from(filters) criteria = tuple(query.criteria_from_dict(typed)) query.filter(*criteria) filtered_count = len(query) order_by = args.get('orderBy') if order_by: order_by_values = OrderedDict(_parse_order_by(order_by)) mapped = order_by_to_mapped.cast_from(order_by_values) # TODO only add this sort by id if there is no other sort by a unique field # required in order have stable sorting and paging when sorting by a non-unique field mapped['_id'] = True criteria = tuple(query.sort_criteria_from_dict(mapped)) query.order_by(*criteria) offset = 0 after = args.get('after') if after: offset = int(Node.from_global_id(after)[1]) query.offset(offset) offset += 1 args.pop('after') first = args.get('first') if first: limit = int(first) query.limit(limit) instances = list(query.collection()) context['count'] = filtered_count context['pageInfo'] = PageInfo( start_cursor=offset_to_cursor(query.start_index), end_cursor=offset_to_cursor(query.end_index), has_previous_page=False if query.start_index is None else query.start_index > 0, has_next_page=False if query.end_index is None else query.end_index < filtered_count - 1) return instances # equivalent to # class SomeFilterInput(graphene.InputObjectType): # refNo = graphene.Argument(graphene.Int) # ... filter_input = type('{}FilterInput'.format(entity_name), (graphene.InputObjectType, ), get_filter_fields(model_class)) connection_field = ConnectionField( connection_class, resolver=resolver, description=description, filters=graphene.Argument(filter_input), orderBy=graphene.Argument(graphene.String), ) return connection_field