async def create_tito_registration(secrets, event, registration, square_data): log = logging.getLogger(__name__) json_result = await put_tito_generic(secrets, event, 'registrations', json={'registration': registration}) query = Fields('registration') find = query.find(json_result) if len(find) > 0: find = find[0] registration_results = find.value asyncio.create_task( write_tito_registration({ **registration_results, **json_result }, event)) registration_slug = registration_results['slug'] asyncio.create_task( put_tito_generic( secrets, event, f"registrations/{registration_slug}/confirmations")) asyncio.create_task( update_tito_tickets(secrets, event, json_result, square_data)) else: logger.log_struct({ 'msg': "no registrations found", **json_result }, severity='DEBUG') return json_result
def get_enrollments_errors(response: dict) -> dict: # $.enrollments.importSummaries[*][?(@.status='ERROR')].description jsonpath_expr = Child( Where( Child(Child(Fields("enrollments"), Fields("importSummaries")), Slice()), Cmp(Fields("status"), eq, "ERROR")), Fields("description")) matches = jsonpath_expr.find(response) return {str(match.full_path): match.value for match in matches}
def get_entity_errors(response: dict) -> dict: # $[?(@.status='ERROR')].description jsonpath_expr = Child(Where(Root(), Cmp(Fields("status"), eq, "ERROR")), Fields("description")) # We write the JSONPath expression programmatically because # currently jsonpath-ng does not support parsing comparison # expressions like ``[?(@.status='ERROR')]`` matches = jsonpath_expr.find(response) return {str(match.full_path): match.value for match in matches}
def test_data_model_field(self) -> None: data_model_body = 'def get_source_ip(event):\n\treturn "source_ip"' data_model_mappings = [{'name': 'destination_ip', 'path': 'dst_ip'}, {'name': 'source_ip', 'method': 'get_source_ip'}] data_model = DataModel({'id': 'data.model.id', 'body': data_model_body, 'versionId': 'version', 'mappings': data_model_mappings}) self.assertEqual('data.model.id', data_model.data_model_id) self.assertEqual(data_model_body, data_model.body) self.assertEqual('version', data_model.version) expected_path_value = Fields('dst_ip') self.assertEqual(expected_path_value, data_model.paths['destination_ip'])
def get_property_map(case_config): """ Returns a map of case properties to OpenMRS patient properties and attributes, and a value source dict to deserialize them. """ property_map = {} for person_prop, value_source_dict in case_config['person_properties'].items(): if 'case_property' in value_source_dict: jsonpath = parse_jsonpath('person.' + person_prop) property_map[value_source_dict['case_property']] = (jsonpath, value_source_dict) for attr_type_uuid, value_source_dict in case_config['person_attributes'].items(): # jsonpath-ng offers programmatic JSONPath expressions. For details on how to create JSONPath # expressions programmatically see the # `jsonpath-ng documentation <https://github.com/h2non/jsonpath-ng#programmatic-jsonpath>`__ # # The `Where` JSONPath expression "*jsonpath1* `where` *jsonpath2*" returns nodes matching *jsonpath1* # where a child matches *jsonpath2*. `Cmp` does a comparison in *jsonpath2*. It accepts a # comparison operator and a value. The JSONPath expression for matching simple attribute values is:: # # (person.attributes[*] where attributeType.uuid eq attr_type_uuid).value # # This extracts the person attribute values where their attribute type UUIDs match those configured in # case_config['person_attributes']. # # Person attributes with Concept values have UUIDs. The following JSONPath uses Union to match both simple # values and Concept values. if 'case_property' in value_source_dict: jsonpath = Union( # Simple values: Return value if it has no children. # (person.attributes[*] where attributeType.uuid eq attr_type_uuid).(value where not *) Child( Where( Child(Child(Fields('person'), Fields('attributes')), Slice()), Cmp(Child(Fields('attributeType'), Fields('uuid')), eq, attr_type_uuid) ), WhereNot(Fields('value'), Fields('*')) ), # Concept values: Return value.uuid if value.uuid exists: # (person.attributes[*] where attributeType.uuid eq attr_type_uuid).value.uuid Child( Where( Child(Child(Fields('person'), Fields('attributes')), Slice()), Cmp(Child(Fields('attributeType'), Fields('uuid')), eq, attr_type_uuid) ), Child(Fields('value'), Fields('uuid')) ) ) property_map[value_source_dict['case_property']] = (jsonpath, value_source_dict) for name_prop, value_source_dict in case_config['person_preferred_name'].items(): if 'case_property' in value_source_dict: jsonpath = parse_jsonpath('person.preferredName.' + name_prop) property_map[value_source_dict['case_property']] = (jsonpath, value_source_dict) for addr_prop, value_source_dict in case_config['person_preferred_address'].items(): if 'case_property' in value_source_dict: jsonpath = parse_jsonpath('person.preferredAddress.' + addr_prop) property_map[value_source_dict['case_property']] = (jsonpath, value_source_dict) for id_type_uuid, value_source_dict in case_config['patient_identifiers'].items(): if 'case_property' in value_source_dict: if id_type_uuid == 'uuid': jsonpath = parse_jsonpath('uuid') else: # The JSONPath expression below is the equivalent of:: # # (identifiers[*] where identifierType.uuid eq id_type_uuid).identifier # # Similar to `person_attributes` above, this will extract the person identifier values where # their identifier type UUIDs match those configured in case_config['patient_identifiers'] jsonpath = Child( Where( Child(Fields('identifiers'), Slice()), Cmp(Child(Fields('identifierType'), Fields('uuid')), eq, id_type_uuid) ), Fields('identifier') ) property_map[value_source_dict['case_property']] = (jsonpath, value_source_dict) return property_map
async def sync_event(secrets, event): j = await read_registrations() st = storage.get_storage() tito_registrations = j[st.col0]['tito'][st.col1][event][st.col2] square_registrations = j[st.col0]['square'][st.col1][event][st.col2] # TODO: filter out test or production tito entries # square order_id is used as the source in tito to prevent duplicates query = Slice().child(Fields('source')) find = query.find(tito_registrations) tito_sources = {m.value for m in find} square_by_date = sorted(square_registrations, key=lambda reg: isoparse(reg['closed_at'])) order_from_square_tito_add = [] order_from_square = [] order_dates = {} releases, rel_ids = await get_tito_release_names(secrets, event) for order in square_by_date: order_id = order['order_id'] items = [] tito = {'discount_code': ''} query = parse("$..note") match = query.find(order) note = [m.value for m in match] cust = order.get('customer', None) order_date = order['closed_at'] email = '' name = '' if cust: email = cust.get('email_address', '') name = cust.get('given_name', '') + ' ' + cust.get( 'family_name', '') tito['name'] = name tito['email'] = email tito['source'] = order_id tito['line_items'] = [] for line_item in order['line_items']: item_name = line_item['name'] if 'variation_name' in line_item: item_name = item_name + " - " + line_item['variation_name'] quantity = int(line_item['quantity']) tito_name = await square_ticket_tito_name(secrets, event, item_name) if not tito_name: continue if 'dealer' in item_name.lower(): # two badger per dealer space quantity *= 2 item = {'release_id': rel_ids[tito_name], 'quantity': quantity} tito['line_items'].append(item) for _ in range(int(quantity)): items.append(tito_name) if not tito['name']: tito['name'] = ' '.join(note) # if there are no line_items, then its not a membership sale if tito['line_items'] and not order_id in tito_sources: order_from_square_tito_add.append(tito) tito = tito.copy() tito['order_date'] = order_date tito['note'] = note order_from_square.append(tito) order_dates[order_id] = order_date square_map = square_registration_order_map(square_registrations) with concurrent.futures.ThreadPoolExecutor(max_workers=2) as pool: futures = pool.map(create_tito_registration, [secrets for _ in order_from_square_tito_add], [event for _ in order_from_square_tito_add], order_from_square_tito_add, [ square_map[item['source']] for item in order_from_square_tito_add ]) registration_creation_results = await asyncio.gather(*futures) new_tito_registrations = [ r['registration'] for r in registration_creation_results if 'registration' in r ] # merge tito native and from square orders, sort by date made # sort tito by # 'completed_at': '2019-12-22T22:16:16.000-08:00', # filter Source: None for reg in [*tito_registrations, *new_tito_registrations]: source = reg.get('source', None) if source is None: reg['order_date'] = reg['completed_at'] else: # order originated in square if source in order_dates: reg['order_date'] = order_dates[source] reg['square_data'] = square_map[source] else: logger.log_struct( { 'msg': 'Could find order_id in square data. Deletion?', 'order_id': source }, severity='warning') sorted_by_date = sorted([*tito_registrations, *new_tito_registrations], key=lambda item: isoparse(item['order_date'])) # add badge numbers badge_number = 2 # 1 is reserved tasks = [] for registration in sorted_by_date: membership_count = len([ 0 for ticket in registration['tickets'] if is_membership_ticket(ticket['release_title']) ]) tasks.append( asyncio.create_task( update_tito_tickets(secrets, event, registration, registration.get('square_data', {}), badge_number=badge_number))) badge_number = badge_number + membership_count # wait for everything to complete before sync is done updates = await asyncio.gather(*tasks) logger.log_struct({ 'event': event, 'count.square': len(square_registrations), 'count.tito': len(tito_registrations), 'count.tito.added': len(order_from_square_tito_add), 'sorted': [order['name'] for order in sorted_by_date], 'updates': updates, 'registrations': len(sorted_by_date) }) return (event, order_from_square_tito_add)
), ( [("b", 2, True), ("a", 1, False), ("b", 1, True)], lambda x: x[2], [[("a", 1, False)], [("b", 2, True), ("b", 1, True)]], ), ], ) def test_group_by(iterable, key, expected): assert list(group_by(iterable, key)) == expected @pytest.mark.parametrize( "pointer, expected", [ (Child(Fields("a"), Fields("b")), ("a", "b")), (Child(Root(), Fields("a")), ("a", )), (Child(Fields("a"), Child(Fields("b"), Fields("c"))), ("a", "b", "c")), ], ) def test__pointer_to_tuple(pointer, expected): assert _pointer_to_tuple(pointer) == expected @pytest.mark.parametrize( "text, expected", [ ("$.a[:]", ("a", )), ("$.a[*].b", ("a", "b")), ("$['a'][*]", ("a", )), ("$['a'][*]['b']", ("a", "b")),
async def get_membership_items(secrets, client): membership_item_names = {} locations = set() result = client.catalog.search_catalog_objects( body={ "include_related_objects": True, "object_types": ["ITEM"], "query": { "prefix_query": { "attribute_name": "name", "attribute_prefix": "f" } }, "limit": 100 }) if result.is_success(): json_result = result.body dats = parse("objects[*]").find(json_result) for dat in dats: item_id = [f.value for f in Fields('id').find(dat.value)][0] item_name = [ f.value for f in parse('item_data.name').find(dat.value) ][0] item_loc = [ f.value for f in Fields('present_at_location_ids').find(dat.value) ] if item_loc: item_loc = item_loc[0] membership_item_names[item_id] = item_name locations.update(item_loc) vdats = parse('item_data.variations[*]').find(dat.value) for vdat in vdats: item_id = [f.value for f in Fields('id').find(vdat.value)][0] var_item_name = [ f.value for f in parse('item_variation_data.name').find(vdat.value) ][0] item_loc = [ f.value for f in Fields('present_at_location_ids').find(vdat.value) ] if item_loc: item_loc = item_loc[0] composit_name = f"{item_name} - {var_item_name}" membership_item_names[item_id] = composit_name locations.update(item_loc) elif result.is_error(): print(result.errors) logger.log_struct( { "membership_item_names": membership_item_names, "item locations": list(locations) }, severity='DEBUG') return membership_item_names, locations