def it_gets_admin_users(self, db, db_session, sample_user): user = create_user(email='*****@*****.**', name='Sam Black', access_area='admin') admin_users = dao_get_admin_users() assert len(admin_users) == 1 assert admin_users == [user] assert admin_users[0].is_admin()
def it_creates_admin_user_if_email_is_admin_in_env_var(self, mocker, client, db_session): data = {'email': '*****@*****.**', 'name': 'Admin User', 'access_area': ',email,'} response = client.post( url_for('user.create_user'), data=json.dumps(data), headers=[('Content-Type', 'application/json'), create_authorization_header()] ) assert response.status_code == 201 json_resp = json.loads(response.get_data(as_text=True)) assert json_resp['access_area'] == USER_ADMIN assert dao_get_admin_users()[0].email == data['email']
def send_message(): data = request.get_json(force=True) current_app.logger.info('send_message: %r', data) validate(data, post_send_message_schema) emails_to = [user.email for user in dao_get_admin_users()] status_code = send_smtp_email(emails_to, 'Web message: {}'.format(data['reason']), data['message'], from_email=data['email'], from_name=data['name']) return jsonify({ 'message': 'Your message was sent' if status_code == 200 else 'An error occurred sending your message' })
def send_event_email_reminder(): current_app.logger.info('Task send_event_email_reminder received: {}') for event in dao_get_future_events(): if event.event_state != APPROVED: continue event.event_dates.sort(key=lambda k: k.event_datetime) time_to_send = (event.event_dates[0].event_datetime - timedelta(weeks=2)) < datetime.today() if time_to_send and not dao_get_email_by_event_id(event.id): subject = f"Event: {event.title} email reminder" message = f"Please <a href='{current_app.config['FRONTEND_ADMIN_URL']}/emails'>"\ f"login</a> to create an email for {event.title}" message_html = get_email_html(BASIC, message=message) for user in dao_get_admin_users(): status_code = send_smtp_email(user.email, subject, message_html) if status_code != 200: current_app.logger.error( f"Problem sending reminder email {subject} for {user.id}, status code: {status_code}")
def update_event(event_id): data = request.get_json(force=True) current_app.logger.info('Update event: {}'.format(data)) validate(data, post_update_event_schema) try: event = dao_get_event_by_id(event_id) except NoResultFound: raise InvalidRequest('event not found: {}'.format(event_id), 400) errs = [] event_dates = [] event_data = {} if data.get('event_state') == REJECTED: new_rejects = [ r for r in data.get('reject_reasons') if not r.get('id') ] if not new_rejects: raise InvalidRequest('rejected event requires new reject reason', 400) elif data.get('event_state') == APPROVED: if data.get('reject_reasons'): rejects = [ r for r in data.get('reject_reasons') if not r.get('resolved') ] if rejects: raise InvalidRequest( 'approved event should not have any reject reasons', 400) data_event_dates = data.get('event_dates') if data_event_dates: serialized_event_dates = event.serialize_event_dates() data_event_dates__dates = [e['event_date'] for e in data_event_dates] serialized_event_dates__dates = [ e['event_datetime'] for e in serialized_event_dates ] diff_add = set(data_event_dates__dates).difference( serialized_event_dates__dates) intersect = set(data_event_dates__dates).intersection( serialized_event_dates__dates) dates_to_add = [ e for e in data_event_dates if e['event_date'] in diff_add ] dates_to_update = [ e for e in data_event_dates if e['event_date'] in intersect ] for _date in dates_to_add: speakers = [] for s in _date.get('speakers', []): speaker = dao_get_speaker_by_id(s['speaker_id']) speakers.append(speaker) e = EventDate(event_id=event_id, event_datetime=_date['event_date'], end_time=_date.get('end_time'), speakers=speakers) current_app.logger.info('Adding event date: {}'.format( _date['event_date'])) dao_create_event_date(e) if _date['event_date'] not in [ _e.event_datetime for _e in event_dates ]: event_dates.append(e) for _date in sorted(dates_to_update, key=lambda k: k['event_date']): speakers = [] for s in _date['speakers']: speaker = dao_get_speaker_by_id(s['speaker_id']) speakers.append(speaker) db_event_date = [ e for e in event.event_dates if e.event_datetime.strftime( '%Y-%m-%d %H:%M') == _date['event_date'] ][0] db_event_date.speakers = speakers if _date['event_date'] not in [ _e.event_datetime for _e in event_dates ]: event_dates.append(db_event_date) if data.get('reject_reasons'): for reject_reason in data.get('reject_reasons'): if reject_reason.get('id'): reject_data = { 'reason': reject_reason['reason'], 'resolved': reject_reason.get('resolved') or False } dao_update_reject_reason(reject_reason.get('id'), **reject_data) else: rr = RejectReason(event_id=event_id, reason=reject_reason['reason'], resolved=reject_reason.get('resolved') or False, created_by=reject_reason.get('created_by')) dao_create_reject_reason(rr) event_data = {} for k in data.keys(): if hasattr(Event, k) and k not in ['reject_reasons']: event_data[k] = data[k] if event_dates: event_data['event_dates'] = event_dates elif data_event_dates == []: raise InvalidRequest('{} needs an event date'.format(event_id), 400) if event_data.get('fee') and event_data.get('fee') > 0: update_data = { 'fee': event_data.get('fee'), 'conc_fee': event_data.get('conc_fee'), 'multi_day_fee': event_data.get('multi_day_fee') or 0, 'multi_day_conc_fee': event_data.get('multi_day_conc_fee') or 0, 'event_type_id': event_data.get('event_type_id'), } db_data = { 'fee': event.fee, 'conc_fee': event.conc_fee, 'multi_day_fee': event.multi_day_fee, 'multi_day_conc_fee': event.multi_day_conc_fee, 'event_type_id': str(event.event_type.id), } if update_data != db_data: if data.get('event_state') in [READY, APPROVED]: paypal_tasks.create_update_paypal_button_task.apply_async( (str(event_id), )) res = dao_update_event(event_id, **event_data) if res: image_data = data.get('image_data') image_filename = data.get('image_filename') if current_app.config['STORAGE'].startswith('None'): current_app.logger.warn('Storage not setup') else: storage = Storage(current_app.config['STORAGE']) if image_data: event_year = str( event.event_dates[0].event_datetime).split('-')[0] target_image_filename = '{}/{}'.format(event_year, str(event_id)) if data.get('event_state') != APPROVED: target_image_filename += '-temp' storage.upload_blob_from_base64string( image_filename, target_image_filename, base64.b64decode(image_data)) unix_time = time.time() image_filename = '{}?{}'.format(target_image_filename, unix_time) elif image_filename: image_filename_without_cache_buster = image_filename.split( '?')[0] if not storage.blob_exists( image_filename_without_cache_buster): raise InvalidRequest( '{} does not exist'.format( image_filename_without_cache_buster), 400) if image_filename: if data.get('event_state') == APPROVED: if '-temp' in image_filename: q_pos = image_filename.index('-temp?') image_filename = image_filename[0:q_pos] storage.rename_image(image_filename + '-temp', image_filename) else: current_app.logger.warn( f"No temp file to rename: {image_filename}") event.image_filename = image_filename dao_update_event(event.id, image_filename=image_filename) json_event = event.serialize() if data.get('event_state') == READY: emails_to = [admin.email for admin in dao_get_admin_users()] message = 'Please review this event for publishing <a href="{}">{}</a>'.format( '{}/events/{}'.format(current_app.config['FRONTEND_ADMIN_URL'], event_id), event.title) status_code = send_smtp_email( emails_to, '{} is ready for review'.format(event.title), message) if status_code != 200: errs.append(f"Problem sending admin email {status_code}") elif data.get('event_state') == REJECTED: emails_to = [user.email for user in dao_get_users()] message = '<div>Please correct this event <a href="{}">{}</a></div>'.format( '{}/events/{}'.format(current_app.config['FRONTEND_ADMIN_URL'], event_id), event.title) message += '<ol>' for reject_reason in [ rr for rr in json_event.get('reject_reasons') if not rr.get('resolved') ]: message += '<li>{}</li>'.format(reject_reason['reason']) message += '</ol>' status_code = send_smtp_email( emails_to, '{} event needs to be corrected'.format(event.title), message) if status_code != 200: errs.append(f"Problem sending smtp emails: {status_code}") json_event['errors'] = errs return jsonify(json_event), 200 raise InvalidRequest('{} did not update event'.format(event_id), 400)
def paypal_ipn(params=None, allow_emails=True, replace_order=False): message = '' bypass_verify = False if not params: params = request.form.to_dict(flat=False) else: bypass_verify = True current_app.logger.info('IPN params: %r', params) def get_data(params): data = {} for key in params.keys(): if isinstance(params[key], list): data[key] = params[key][0] else: data[key] = params[key] return data if bypass_verify or (current_app.config['TEST_VERIFY'] and current_app.config['ENVIRONMENT'] != 'live'): v_response = 'VERIFIED' current_app.logger.info('Test paypal verify') else: VERIFY_URL = current_app.config['PAYPAL_VERIFY_URL'] params['cmd'] = '_notify-validate' headers = { 'content-type': 'application/x-www-form-urlencoded', 'user-agent': 'Python-IPN-Verification-Script' } current_app.logger.info("params: %r", params) # debug r = requests.post(VERIFY_URL, params=params, headers=headers, verify=True) r.raise_for_status() v_response = r.text if v_response == 'VERIFIED': current_app.logger.info('VERIFIED: %s', params['txn_id']) # Check return message and take action as needed if v_response == 'VERIFIED': status = product_message = delivery_message = error_message = '' diff = 0.0 data = get_data(params) order_data, tickets, events, products, delivery_zones, errors = parse_ipn( data, replace_order) if 'payment already made' in (','.join(errors)): current_app.logger.info("Transaction payment already made %r", data['txn_id']) return "Duplicate transaction %s" % {data['txn_id']} order_data['params'] = json.dumps(params) if 'is_donation' in order_data.keys( ) and order_data['is_donation'] == "Donation": order_data['is_donation'] = True order_data['linked_txn_id'] = None order = Order(**order_data) dao_create_record(order) if order_data['payment_status'] != 'Completed': err_msg = f"Payment not Completed: {order_data['payment_status']}" errors = [err_msg] else: message = f"<p>Thank you for your order ({order.id})</p>" if order_data['txn_type'] == 'web_accept' and order_data[ 'linked_txn_id']: linked_order = dao_get_order_with_txn_id( order_data['linked_txn_id']) diff = linked_order.delivery_balance - Decimal( order_data['payment_total']) if diff == 0: status = statuses.DELIVERY_EXTRA_PAID elif diff > 0: status = statuses.DELIVERY_EXTRA current_app.logger.warning( 'Delivery balance not paid in full') elif diff < 0: status = statuses.DELIVERY_REFUND current_app.logger.warning('Delivery balance overpaid') dao_update_record(Order, linked_order.id, delivery_status=status, payment_total=linked_order.payment_total + Decimal(order_data['payment_total']), delivery_balance=abs(diff)) order_data['delivery_status'] = status order_data['delivery_zone'] = linked_order.delivery_zone order_data['delivery_balance'] = abs(diff) _payment_total = _get_nice_cost(order.payment_total) _diff = _get_nice_cost(diff) if status == statuses.DELIVERY_EXTRA_PAID: message += f"<div>Outstanding payment for order ({order_data['linked_txn_id']}) of £" \ f"{_payment_total} for delivery to {order_data['delivery_zone']} has been paid.</div>" elif status == statuses.DELIVERY_EXTRA: message += f"<div>Outstanding payment for order ({order_data['linked_txn_id']}) of £" \ f"{_payment_total} for delivery to {order_data['delivery_zone']} has been " \ f"partially paid.</div><div>Not enough delivery paid, £{_diff} due.</div>" elif status == statuses.DELIVERY_REFUND: message += f"<p>You have overpaid for delivery on order ({order_data['linked_txn_id']}) " \ f"by £{_diff}, please send a message to website admin if there is " \ "no refund within 5 working days.</p>" else: if products: for product in products: if product['type'] == BOOK: book_to_order = BookToOrder( book_id=product['book_id'], order_id=order.id, quantity=product['quantity']) dao_create_book_to_order(book_to_order) product_message += ( f'<tr><td>{product["title"]}</td><td> x {product["quantity"]}</td>' f'<td> = {_get_nice_cost(product["price"] * product["quantity"])}</td></tr>' ) product_message = f'<table>{product_message}</table>' address_delivery_zone = None if 'address_country_code' not in order_data: delivery_message = "No address supplied. " status = "missing_address" else: address_delivery_zone = get_delivery_zone( order_data['address_country_code']) admin_message = "" total_cost = 0 for dz in delivery_zones: _d = [ _dz for _dz in DELIVERY_ZONES if _dz['name'] == dz ] if _d: d = _d[0] total_cost += d['price'] _price = _get_nice_cost(d['price']) admin_message += f"<tr><td>{d['name']}</td><td>{_price}</td></tr>" else: errors.append(f'Delivery zone: {dz} not found') admin_message += f"<tr><td>{dz}</td><td>Not found</td></tr>" _total_cost = _get_nice_cost(total_cost) _price = _get_nice_cost(address_delivery_zone['price']) diff = total_cost - address_delivery_zone['price'] if diff != 0: admin_message = f"<p>Order delivery zones: <table>{admin_message}" \ f"</table>Total: £{_total_cost}</p>" admin_message += "<p>Expected delivery zone: " \ f"{address_delivery_zone['name']} - £{_price}</p>" order_data['delivery_balance'] = _get_nice_cost( diff) if diff > 0: status = "refund" delivery_message = f"Refund of £{order_data['delivery_balance']} " \ "due as wrong delivery fee paid" elif diff < 0: _diff = _get_nice_cost(diff) status = "extra" delivery_message = "{}, £{} due. ".format( "No delivery fee paid" if total_cost == 0 else "Not enough delivery paid", order_data['delivery_balance']) admin_message = f"Transaction ID: {order.txn_id}<br>Order ID: {order.id}" \ f"<br>{delivery_message}.{admin_message}" for user in dao_get_admin_users(): send_smtp_email(user.email, f'New Acropolis {status}', admin_message) else: status = statuses.DELIVERY_PAID dao_update_record( Order, order.id, delivery_status=status, delivery_zone=address_delivery_zone['name'] if address_delivery_zone else None, delivery_balance=str(abs(diff))) if delivery_message: order_data['delivery_status'] = status if status == 'refund': delivery_message = f"<p>{delivery_message}, please send a message " \ "to website admin if there is no refund within 5 working days.</p>" else: order_data['delivery_zone'] = address_delivery_zone['name']\ if address_delivery_zone else None else: product_message = ( f'{product_message}<br><div>Delivery to: {order_data["address_street"]},' f'{order_data["address_city"]}, ' f'{order_data["address_postal_code"]}, {order_data["address_country"]}</div>' ) if tickets: for i, _ticket in enumerate(tickets): _ticket['order_id'] = order.id ticket = Ticket(**_ticket) dao_create_record(ticket) tickets[i]['ticket_id'] = ticket.id tickets[i]['title'] = ticket.event.title storage = Storage(current_app.config['STORAGE']) for ticket in tickets: link_to_post = '{}{}'.format( current_app.config['API_BASE_URL'], url_for('.use_ticket', ticket_id=ticket['ticket_id'])) img = pyqrcode.create(link_to_post) buffer = io.BytesIO() img.png(buffer, scale=2) img_b64 = base64.b64encode(buffer.getvalue()) target_image_filename = '{}/{}'.format( 'qr_codes', str(ticket['ticket_id'])) storage.upload_blob_from_base64string( 'qr.code', target_image_filename, img_b64) message += '<div><span><img src="{}/{}"></span>'.format( current_app.config['IMAGES_URL'], target_image_filename) event_date = dao_get_event_date_by_id( ticket['eventdate_id']) minutes = ':%M' if event_date.event_datetime.minute > 0 else '' message += "<div>{} on {}</div></div>".format( ticket['title'], event_date.event_datetime.strftime( '%-d %b at %-I{}%p'.format(minutes))) if event_date.event.remote_access: message += f"<br><div>Meeting id: {event_date.event.remote_access}" if event_date.event.remote_pw: message += f", Password: {event_date.event.remote_pw}" message += "</div>" message += f"<div><a href='https://zoom.us/j/{event_date.event.remote_access}'>"\ "Join zoom event</a></div>" if errors: error_message = '' for error in errors: error_message += f"<div>{error}</div>" order.errors.append(OrderError(error=error)) error_message = f"<p>Errors in order: {error_message}</p>" if status in [ statuses.DELIVERY_EXTRA, statuses.DELIVERY_MISSING_ADDRESS, statuses.DELIVERY_NOT_PAID ]: _delivery_zone_balance = '' if 'delivery_balance' in order_data: _delivery_balance = _get_nice_cost( order_data['delivery_balance']) _delivery_zone_balance = f"/{order_data['delivery_zone']}/{_delivery_balance}"\ if order_data['delivery_zone'] else '' delivery_message = ( f"<p>{delivery_message}Please " f"<a href='{current_app.config['FRONTEND_URL']}/order/{order_data['delivery_status']}/" f"{order_data['txn_id']}{_delivery_zone_balance}'>complete</a> " "your order.</p>") if allow_emails: send_email( order.email_address, 'New Acropolis Order', message + product_message + delivery_message + error_message) else: if v_response == 'INVALID': current_app.logger.info('INVALID %r', params['txn_id']) else: current_app.logger.info('UNKNOWN response %r', params['txn_id']) data = get_data(params) data[ 'txn_id'] = f"XX-{v_response}_{int(datetime.utcnow().timestamp())}-{data['txn_id']}" order_data, tickets, events, products, delivery_zones, errors = parse_ipn( data) order_data['params'] = json.dumps(params) order = Order(**order_data) order.errors.append(OrderError(error=f"{v_response} verification")) dao_create_record(order) return 'Paypal IPN'
def it_does_not_get_admin_user_if_no_admin(self, db, db_session, sample_user): admin_user = dao_get_admin_users() assert not admin_user