def test_reminder_message_is_not_created_if_member_has_pending_labaccess_days( self): member = self.db.create_member() self.db.create_span(type=Span.LABACCESS, enddate=self.date(LABACCESS_REMINDER_DAYS_BEFORE)) p0_count = 1 expected_sum = self.p0_price * p0_count cart = [ { "id": self.p0_id, "count": p0_count }, ] transaction = create_transaction(member_id=member.member_id, purchase=dict( cart=cart, expected_sum=expected_sum), stripe_reference_id="not_used") transaction.status = Transaction.COMPLETED db_session.add(transaction) db_session.flush() self.send_labaccess() self.assertEqual(0, db_session.query(Message).count())
def register_permissions(permissions): for permission in permissions: try: db_session.add(Permission(permission=permission)) db_session.commit() except IntegrityError: db_session.rollback()
def answer_question(question_id): data = request.json option_id = int(data["option_id"]) option = db_session \ .query(QuizQuestionOption) \ .join(QuizQuestionOption.question) \ .filter((QuizQuestion.id == question_id) & (QuizQuestionOption.id == option_id) & (QuizQuestionOption.deleted_at == None)) \ .one_or_none() if option == None: return ( 400, f"Option id {option_id} is not an option for question id {question_id}" ) db_session.add( QuizAnswer(question_id=question_id, option_id=option_id, member_id=g.user_id, correct=option.correct)) db_session.flush() question = db_session.query(QuizQuestion).get(question_id) json = quiz_question_entity.to_obj(question) json["options"] = [] for option in question.options: option = quiz_question_option_entity.to_obj(option) json["options"].append(option) return json
def create_action_required_response(transaction, payment_intent): """ The payment_intent requires customer action to be confirmed. Create response to client""" try: db_session.add( StripePending(transaction_id=transaction.id, stripe_token=payment_intent.id)) if payment_intent.next_action.type == PaymentIntentNextActionType.USE_STRIPE_SDK: return dict(type=PaymentIntentNextActionType.USE_STRIPE_SDK, client_secret=payment_intent.client_secret) elif payment_intent.next_action.type == PaymentIntentNextActionType.REDIRECT_TO_URL: raise InternalServerError( log= f"unexpected next_action type, {payment_intent.next_action.type}" ) else: raise PaymentFailed( log= f"unknown next_action type, {payment_intent.next_action.type}") except Exception: # Fail transaction on all known and unknown errors to be safe, we won't charge a failed transaction. commit_fail_transaction(transaction) logger.info( f"failing transaction {transaction.id}, due to error when processing 3ds card" ) raise
def send_message(template: MessageTemplate, member, db_session=None, render_template=None, **kwargs): if render_template is None: from flask import render_template subject = render_template( f"{template.value}.subject.html", public_url=get_public_url, member=member, **kwargs, ) body = render_template( f"{template.value}.body.html", public_url=get_public_url, member=member, **kwargs, ) if not db_session: from service.db import db_session db_session.add( Message( subject=subject, body=body, member_id=member.member_id, recipient=member.email, status=Message.QUEUED, template=template.value, ))
def create_product_action(self, **kwargs): if self.product: kwargs.setdefault('product_id', self.product.id) obj = self.obj.create_product_action(**kwargs) self.action = ProductAction(**obj) db_session.add(self.action) db_session.flush() return self.action
def create_permission(self, **kwargs): obj = dict( permission=random_str(), ) obj.update(kwargs) self.permission = Permission(**obj) db_session.add(self.permission) db_session.commit() return self.permission
def complete_transaction(transaction): assert transaction.status == Transaction.PENDING transaction.status = Transaction.COMPLETED db_session.add(transaction) db_session.flush() logger.info(f"completing transaction {transaction.id}, payment confirmed" f", sending email receipt to member {transaction.member_id}") send_receipt_email(transaction)
def get_or_create(model, defaults=None, **kwargs): entity = db_session.query(model).filter_by(**kwargs).first() if entity: return entity entity = model(**{**kwargs, **defaults}) db_session.add(entity) db_session.flush() return entity
def create_product(self, **kwargs): if self.category: kwargs.setdefault('category_id', self.category.id) obj = self.obj.create_product(**kwargs) self.product = Product(**obj) db_session.add(self.product) db_session.flush() return self.product
def create_key(self, **kwargs): if 'member' in kwargs: member = kwargs.pop('member') else: member = self.member obj = self.obj.create_key(**kwargs) self.key = Key(**obj, member=member) db_session.add(self.key) db_session.commit() return self.key
def create_box(self, **kwargs): obj = dict( member_id=self.member.member_id, box_label_id=randint(1e9, 9e9), session_token=random_str(), ) obj.update(kwargs) self.box = Box(**obj) db_session.add(self.box) db_session.commit() return self.box
def create_span(self, **kwargs): if 'member' in kwargs: member = kwargs.pop('member') else: member = self.member obj = self.obj.create_span(**kwargs) self.span = Span(**obj, member=member) db_session.add(self.span) db_session.commit() return self.span
def create_password_reset_token(self, member=None, **kwargs): member = member or self.member obj = dict( member_id=member.member_id, token=random_str(), ) obj.update(**kwargs) self.password_reset_token = PasswordResetToken(**obj) db_session.add(self.password_reset_token) db_session.commit() return self.password_reset_token
def create_access_token(self, **kwargs): obj = dict( user_id=TEST_SERVICE_USER_ID, access_token=random_str(), browser=f'a-browser-{random_str()}', ip=f'{randint(0, 255)}.{randint(0, 255)}.{randint(0, 255)}.{randint(0, 255)}', expires=self.test.datetime(days=1), ) obj.update(kwargs) self.access_token = AccessToken(**obj) db_session.add(self.access_token) db_session.commit() return self.access_token
def roll_service_token(user_id): try: access_token = db_session.query(AccessToken).filter_by( user_id=user_id).one() access_token.access_token = generate_token() db_session.add(access_token) except NoResultFound: raise NotFound() except MultipleResultsFound as e: raise Exception( f"Found multiple of service token id {user_id}, this is a bug." ) from e
def request_password_reset(user_identification): member = get_member_by_user_identification(user_identification) token = generate_token() db_session.add(PasswordResetToken(member_id=member.member_id, token=token)) db_session.flush() send_message( MessageTemplate.PASSWORD_RESET, member, url=config.get_admin_url( f"/password-reset?reset_token={quote_plus(token)}"), )
def _create_internal(self, data, commit=True): """ Internal create to make it easier for subclasses to manipulated data before create. """ input_data = self.to_model(data) self.validate_all(input_data) if not input_data: raise UnprocessableEntity("Can not create using empty data.") entity = self.model(**input_data) db_session.add(entity) if commit: db_session.commit() else: db_session.flush() # Flush to get id of created entity. return entity
def create_message(self, member=None, **kwargs): member = member or self.member obj = dict( member=member, subject=random_str(), body=self.fake.bs(), recipient=member.email if member else self.fake.email(), status=Message.QUEUED, ) obj.update(**kwargs) self.message = Message(**obj) db_session.add(self.message) db_session.commit() return self.member
def send_messages(key, domain, sender, to_override, limit): query = db_session.query(Message) query = query.filter(Message.status == Message.QUEUED) query = query.limit(limit) for message in query: to = message.recipient msg = f"sending {message.id} to {to}" if to_override: msg += f" (overriding to {to_override})" to = to_override msg += f": {message.subject}" logger.info(msg) response = requests.post( f"https://api.mailgun.net/v3/{domain}/messages", auth=('api', key), data={ 'from': sender, 'to': to, 'subject': message.subject, 'html': message.body, }) if response.ok: message.status = Message.SENT message.sent_at = datetime.utcnow() db_session.add(message) db_session.commit() else: message.status = Message.FAILED db_session.add(message) db_session.commit() logger.error( f"failed to send {message.id} to {to}: {response.content.decode('utf-8')}" )
def create_access_token(ip, browser, user_id, valid_duration: Optional[timedelta] = None): assert user_id > 0 access_token = AccessToken( user_id=user_id, access_token=generate_token(), browser=browser, ip=ip, expires=datetime.utcnow() + (timedelta(minutes=15) if valid_duration is None else valid_duration), lifetime=int(timedelta(days=14).total_seconds()), ) db_session.add(access_token) return dict(access_token=access_token.access_token, expires=access_token.expires.isoformat())
def add_membership_days(member_id=None, span_type=None, days=None, creation_reason=None, default_start_date=None): assert days >= 0 old_span = db_session.query(Span).filter_by( creation_reason=creation_reason).first() if old_span: if days == (old_span.enddate - old_span.startdate).days and span_type == old_span.type: # Duplicate add days can happend because the code that handles the transactions is not yet done in a db # transaction, there are also an external script for handling puchases in ticktail that can create # dupllicates. return get_membership_summary(member_id) raise UnprocessableEntity(f"Duplicate entry.", fields='creation_reason', what=NOT_UNIQUE) if not default_start_date: default_start_date = date.today() last_end, = db_session.query(func.max(Span.enddate)).filter( Span.member_id == member_id, Span.type == span_type, Span.deleted_at.is_(None)).first() if not last_end or last_end < default_start_date: last_end = default_start_date end = last_end + timedelta(days=days) span = Span(member_id=member_id, startdate=last_end, enddate=end, type=span_type, creation_reason=creation_reason) db_session.add(span) db_session.flush() return get_membership_summary(member_id)
def box_terminator_validate(member_number=None, box_label_id=None, session_token=None): query = get_box_query() query = query.filter(Box.box_label_id == box_label_id) try: box = query.one() except NoResultFound: try: member = db_session.query(Member).filter( Member.member_number == member_number).one() except NoResultFound: raise NotFound() box = Box(member_id=member.member_id, box_label_id=box_label_id) box.last_check_at = datetime.utcnow() box.session_token = session_token db_session.add(box) db_session.flush() return get_box_info(box)
def password_reset(reset_token, unhashed_password): try: password_reset_token = db_session.query(PasswordResetToken).filter_by( token=reset_token).one() except NoResultFound: return dict( error_message= "Could not find password reset token, try to request a new reset link." ) except MultipleResultsFound: raise InternalServerError( log=f"Multiple tokens {reset_token} found, this is a bug.") if datetime.utcnow() - password_reset_token.created_at > timedelta( minutes=10): return dict(error_message="Reset link expired, try to request a new.") try: hashed_password = check_and_hash_password(unhashed_password) except ValueError as e: return dict(error_message=str(e)) try: member = db_session.query(Member).get(password_reset_token.member_id) except NoResultFound: raise InternalServerError( log= f"No member with id {password_reset_token.member_id} found, this is a bug." ) member.password = hashed_password db_session.add(member) return {}
def commit_transaction_to_db(member_id=None, total_amount=None, contents=None, stripe_card_source_id=None, activates_member=False): """ Save as new transaction with transaction content in db and return it transaction. """ transaction = Transaction(member_id=member_id, amount=total_amount, status=Transaction.PENDING) db_session.add(transaction) db_session.flush() for content in contents: content.transaction_id = transaction.id db_session.add(content) db_session.flush() db_session.execute( """ INSERT INTO webshop_transaction_actions (content_id, action_type, value, status) SELECT :content_id AS content_id, action_type, SUM(:count * value) AS value, :pending AS status FROM webshop_product_actions WHERE product_id=:product_id AND deleted_at IS NULL GROUP BY action_type """, { 'content_id': content.id, 'count': content.count, 'pending': TransactionAction.PENDING, 'product_id': content.product_id }) if activates_member: # Mark this transaction as one that is for registering a member. db_session.add(PendingRegistration(transaction_id=transaction.id)) db_session.add( StripePending(transaction_id=transaction.id, stripe_token=stripe_card_source_id)) return transaction
def create_member(self, **kwargs): obj = self.obj.create_member(**kwargs) self.member = Member(**obj, member_number=self.get_member_number()) db_session.add(self.member) db_session.commit() return self.member
def commit_fail_transaction(transaction): transaction.status = Transaction.FAILED db_session.add(transaction) db_session.commit()
def activate_member(member): logger.info(f"activating member {member.member_id}") member.deleted_at = None db_session.add(member) db_session.flush() send_new_member_email(member)
def complete_pending_action(action): action.status = TransactionAction.COMPLETED action.completed_at = datetime.utcnow() db_session.add(action) db_session.flush()
def create_group(self, **kwargs): obj = self.obj.create_group(**kwargs) self.group = Group(**obj) db_session.add(self.group) db_session.commit() return self.group