class UserRequestService: def __init__(self, config): self.config = config self.email_service = EmailService(config) @validate_input({"subject_id": UUID_RULE}) def get_requests(self, subject_id): with session_scope() as session: if not session.query(User).get(subject_id).is_committee: raise InvisibleUnauthorizedException("Not committee") buy_requests = ( session.query(UserRequest, User) .join(User, User.id == UserRequest.user_id) .filter( UserRequest.is_buy == True, UserRequest.closed_by_user_id == None ) .all() ) sell_requests = ( session.query(UserRequest, User) .join(User, User.id == UserRequest.user_id) .filter( UserRequest.is_buy == False, UserRequest.closed_by_user_id == None ) .all() ) return { "buyers": [ { **r[0].asdict(), **{ k: v for k, v in r[1].asdict().items() if k not in ["id", "created_at", "updated_at"] }, } for r in buy_requests ], "sellers": [ { **r[0].asdict(), **{ k: v for k, v in r[1].asdict().items() if k not in ["id", "created_at", "updated_at"] }, } for r in sell_requests ], } @validate_input({"request_id": UUID_RULE, "subject_id": UUID_RULE}) def approve_request(self, request_id, subject_id): with session_scope() as session: if not session.query(User).get(subject_id).is_committee: raise InvisibleUnauthorizedException("Not committee") request = session.query(UserRequest).get(request_id) request.closed_by_user_id = subject_id user = session.query(User).get(request.user_id) if request.is_buy: user.can_buy = True self.email_service.send_email( emails=[user.email], template="approved_buyer" ) else: user.can_sell = True self.email_service.send_email( emails=[user.email], template="approved_seller" ) @validate_input({"request_id": UUID_RULE, "subject_id": UUID_RULE}) def reject_request(self, request_id, subject_id): with session_scope() as session: if not session.query(User).get(subject_id).is_committee: raise InvisibleUnauthorizedException("Not committee") request = session.query(UserRequest).get(request_id) request.closed_by_user_id = subject_id user = session.query(User).get(request.user_id) email_template = "rejected_buyer" if request.is_buy else "rejected_seller" self.email_service.send_email(emails=[user.email], template=email_template)
def __init__(self, config): self.config = config self.email_service = EmailService(config)
class MatchService: def __init__(self, config): self.config = config self.email_service = EmailService(config) def run_matches(self): round_id = RoundService(self.config).get_active()["id"] buy_orders, sell_orders, banned_pairs = self._get_matching_params(round_id) match_results = match_buyers_and_sellers(buy_orders, sell_orders, banned_pairs) buy_order_to_buyer_dict = { order["id"]: order["user_id"] for order in buy_orders } sell_order_to_seller_dict = { order["id"]: order["user_id"] for order in sell_orders } self._add_db_objects( round_id, match_results, sell_order_to_seller_dict, buy_order_to_buyer_dict ) self._send_emails(buy_orders, sell_orders, match_results) def _get_matching_params(self, round_id): with session_scope() as session: buy_orders = [ b.asdict() for b in session.query(BuyOrder) .join(User, User.id == BuyOrder.user_id) .filter(BuyOrder.round_id == round_id, User.can_buy) .all() ] sell_orders = [ s.asdict() for s in session.query(SellOrder) .join(User, User.id == SellOrder.user_id) .filter(SellOrder.round_id == round_id, User.can_sell) .all() ] banned_pairs = [ (bp.buyer_id, bp.seller_id) for bp in session.query(BannedPair).all() ] return buy_orders, self._double_sell_orders(sell_orders), banned_pairs def _double_sell_orders(self, sell_orders): seller_counts = defaultdict(lambda: 0) for sell_order in sell_orders: seller_counts[sell_order["user_id"]] += 1 new_sell_orders = [] for sell_order in sell_orders: new_sell_orders.append(sell_order) if seller_counts[sell_order["user_id"]] == 1: new_sell_orders.append(sell_order) return new_sell_orders def _add_db_objects( self, round_id, match_results, sell_order_to_seller_dict, buy_order_to_buyer_dict, ): with session_scope() as session: for buy_order_id, sell_order_id in match_results: match = Match(buy_order_id=buy_order_id, sell_order_id=sell_order_id) chat_room = ChatRoom( seller_id=sell_order_to_seller_dict[sell_order_id], buyer_id=buy_order_to_buyer_dict[buy_order_id], ) session.add_all([match, chat_room]) session.query(Round).get(round_id).is_concluded = True def _send_emails(self, buy_orders, sell_orders, match_results): matched_uuids = set() for buy_order_uuid, sell_order_uuid in match_results: matched_uuids.add(buy_order_uuid) matched_uuids.add(sell_order_uuid) all_user_ids = set() matched_user_ids = set() for buy_order in buy_orders: all_user_ids.add(buy_order["user_id"]) if buy_order["id"] in matched_uuids: matched_user_ids.add(buy_order["user_id"]) for sell_order in sell_orders: all_user_ids.add(sell_order["user_id"]) if sell_order["id"] in matched_uuids: matched_user_ids.add(sell_order["user_id"]) with session_scope() as session: matched_emails = [ user.email for user in session.query(User) .filter(User.id.in_(matched_user_ids)) .all() ] self.email_service.send_email( matched_emails, template="match_done_has_match" ) unmatched_emails = [ user.email for user in session.query(User) .filter(User.id.in_(all_user_ids - matched_user_ids)) .all() ] self.email_service.send_email( unmatched_emails, template="match_done_no_match" )
class UserService: def __init__(self, config): self.config = config self.email_service = EmailService(config) def create_if_not_exists( self, email, display_image_url, full_name, provider_user_id, is_buy, auth_token ): with session_scope() as session: user = ( session.query(User) .filter_by(provider_user_id=provider_user_id) .one_or_none() ) if user is None: user = User( email=email, full_name=full_name, display_image_url=display_image_url, provider="linkedin", can_buy=False, can_sell=False, provider_user_id=provider_user_id, auth_token=auth_token, ) session.add(user) session.flush() req = UserRequest(user_id=str(user.id), is_buy=is_buy) session.add(req) email_template = "register_buyer" if is_buy else "register_seller" self.email_service.send_email(emails=[email], template=email_template) committee_emails = [ u.email for u in session.query(User).filter_by(is_committee=True).all() ] self.email_service.send_email( emails=committee_emails, template="new_user_review" ) else: user.email = email user.full_name = full_name user.display_image_url = display_image_url user.auth_token = auth_token session.commit() return user.asdict() def get_user_by_linkedin_id(self, provider_user_id): with session_scope() as session: user = ( session.query(User) .filter_by(provider_user_id=provider_user_id) .one_or_none() ) if user is None: raise ResourceNotFoundException() user_dict = user.asdict() return user_dict
class RoundService: def __init__(self, config): self.config = config self.email_service = EmailService(config) def get_all(self): with session_scope() as session: return [r.asdict() for r in session.query(Round).all()] def get_active(self): with session_scope() as session: active_round = ( session.query(Round) .filter(Round.end_time >= datetime.now(), Round.is_concluded == False) .one_or_none() ) return active_round and active_round.asdict() def should_round_start(self): with session_scope() as session: unique_sellers = ( session.query(SellOrder.user_id) .filter_by(round_id=None) .distinct() .count() ) if ( unique_sellers >= self.config["ACQUITY_ROUND_START_NUMBER_OF_SELLERS_CUTOFF"] ): return True total_shares = ( session.query(func.sum(SellOrder.number_of_shares)) .filter_by(round_id=None) .scalar() or 0 ) return ( total_shares >= self.config["ACQUITY_ROUND_START_TOTAL_SELL_SHARES_CUTOFF"] ) def create_new_round_and_set_orders(self, scheduler): with session_scope() as session: end_time = datetime.now(timezone.utc) + self.config["ACQUITY_ROUND_LENGTH"] new_round = Round(end_time=end_time, is_concluded=False) session.add(new_round) session.flush() for sell_order in session.query(SellOrder).filter_by(round_id=None): sell_order.round_id = str(new_round.id) for buy_order in session.query(BuyOrder).filter_by(round_id=None): buy_order.round_id = str(new_round.id) emails = [user.email for user in session.query(User).all()] self.email_service.send_email(emails, template="round_opened") if scheduler is not None: scheduler.add_job( MatchService(self.config).run_matches, "date", run_date=end_time ) @validate_input({"security_id": UUID_RULE}) def get_previous_round_statistics(self, security_id): return None
class BuyOrderService: def __init__(self, config): self.config = config self.email_service = EmailService(config) @validate_input(CREATE_BUY_ORDER_SCHEMA) def create_order(self, user_id, number_of_shares, price, security_id): with session_scope() as session: user = session.query(User).get(user_id) if user is None: raise ResourceNotFoundException() if user.asdict()["can_buy"] == "NO": raise UnauthorizedException("User cannot place buy orders.") buy_order_count = session.query(BuyOrder).filter_by(user_id=user_id).count() if buy_order_count >= self.config["ACQUITY_BUY_ORDER_PER_ROUND_LIMIT"]: raise UnauthorizedException("Limit of buy orders reached.") active_round = RoundService(self.config).get_active() buy_order = BuyOrder( user_id=user_id, number_of_shares=number_of_shares, price=price, security_id=security_id, round_id=(active_round and active_round["id"]), ) session.add(buy_order) session.commit() self.email_service.send_email( emails=[user.email], template="create_buy_order" ) return buy_order.asdict() @validate_input({"user_id": UUID_RULE}) def get_orders_by_user(self, user_id): with session_scope() as session: buy_orders = session.query(BuyOrder).filter_by(user_id=user_id).all() return [buy_order.asdict() for buy_order in buy_orders] @validate_input({"id": UUID_RULE, "user_id": UUID_RULE}) def get_order_by_id(self, id, user_id): with session_scope() as session: order = session.query(BuyOrder).get(id) if order is None: raise ResourceNotFoundException() if order.user_id != user_id: raise ResourceNotOwnedException() return order.asdict() @validate_input(EDIT_ORDER_SCHEMA) def edit_order(self, id, subject_id, new_number_of_shares=None, new_price=None): with session_scope() as session: buy_order = session.query(BuyOrder).get(id) if buy_order is None: raise ResourceNotFoundException() if buy_order.user_id != subject_id: raise ResourceNotOwnedException("You need to own this order.") if new_number_of_shares is not None: buy_order.number_of_shares = new_number_of_shares if new_price is not None: buy_order.price = new_price session.commit() user = session.query(User).get(buy_order.user_id) self.email_service.send_email( emails=[user.email], template="edit_buy_order" ) return buy_order.asdict() @validate_input(DELETE_ORDER_SCHEMA) def delete_order(self, id, subject_id): with session_scope() as session: buy_order = session.query(BuyOrder).get(id) if buy_order is None: raise ResourceNotFoundException() if buy_order.user_id != subject_id: raise ResourceNotOwnedException("You need to own this order.") session.delete(buy_order) return {}
class SellOrderService: def __init__(self, config): self.config = config self.email_service = EmailService(config) @validate_input(CREATE_SELL_ORDER_SCHEMA) def create_order(self, user_id, number_of_shares, price, security_id, scheduler): with session_scope() as session: user = session.query(User).get(user_id) if user is None: raise ResourceNotFoundException() if user.asdict()["can_sell"] == "NO": raise UnauthorizedException("User cannot place sell orders.") sell_order_count = ( session.query(SellOrder).filter_by(user_id=user_id).count() ) if sell_order_count >= self.config["ACQUITY_SELL_ORDER_PER_ROUND_LIMIT"]: raise UnauthorizedException("Limit of sell orders reached.") sell_order = SellOrder( user_id=user_id, number_of_shares=number_of_shares, price=price, security_id=security_id, ) active_round = RoundService(self.config).get_active() if active_round is None: session.add(sell_order) session.commit() if RoundService(self.config).should_round_start(): RoundService(self.config).create_new_round_and_set_orders(scheduler) else: sell_order.round_id = active_round["id"] session.add(sell_order) session.commit() self.email_service.send_email( emails=[user.email], template="create_sell_order" ) return sell_order.asdict() @validate_input({"user_id": UUID_RULE}) def get_orders_by_user(self, user_id): with session_scope() as session: sell_orders = session.query(SellOrder).filter_by(user_id=user_id).all() return [sell_order.asdict() for sell_order in sell_orders] @validate_input({"id": UUID_RULE, "user_id": UUID_RULE}) def get_order_by_id(self, id, user_id): with session_scope() as session: order = session.query(SellOrder).get(id) if order is None: raise ResourceNotFoundException() if order.user_id != user_id: raise ResourceNotOwnedException() return order.asdict() @validate_input(EDIT_ORDER_SCHEMA) def edit_order(self, id, subject_id, new_number_of_shares=None, new_price=None): with session_scope() as session: sell_order = session.query(SellOrder).get(id) if sell_order is None: raise ResourceNotFoundException() if sell_order.user_id != subject_id: raise ResourceNotOwnedException("You need to own this order.") if new_number_of_shares is not None: sell_order.number_of_shares = new_number_of_shares if new_price is not None: sell_order.price = new_price session.commit() user = session.query(User).get(sell_order.user_id) self.email_service.send_email( emails=[user.email], template="edit_sell_order" ) return sell_order.asdict() @validate_input(DELETE_ORDER_SCHEMA) def delete_order(self, id, subject_id): with session_scope() as session: sell_order = session.query(SellOrder).get(id) if sell_order is None: raise ResourceNotFoundException() if sell_order.user_id != subject_id: raise ResourceNotOwnedException("You need to own this order.") session.delete(sell_order) return {}
class ChatService: def __init__(self, config): self.config = config self.email_service = EmailService(config=config) @validate_input(GET_CHATS_BY_USER_ID_SCHEMA) def get_chats_by_user_id(self, user_id, as_buyer, as_seller): roles = [] if as_buyer: roles.append("BUYER") if as_seller: roles.append("SELLER") with session_scope() as session: user = session.query(User).get(user_id) if (as_buyer and (not user.can_buy)) or (as_seller and (not user.can_sell)): raise UnauthorizedException("Too much permissions requested.") chat_room_queries = (session.query( ChatRoom, UserChatRoomAssociation, Match, BuyOrder, SellOrder).join(Match, ChatRoom.match_id == Match.id).join( UserChatRoomAssociation, UserChatRoomAssociation.chat_room_id == ChatRoom.id, ).join(BuyOrder, Match.buy_order_id == BuyOrder.id).join( SellOrder, Match.sell_order_id == SellOrder.id).filter( UserChatRoomAssociation.user_id == user_id).filter( UserChatRoomAssociation.role.in_(roles)).all()) chats = session.query(Chat).all() offers = session.query(Offer).all() offer_responses = session.query(OfferResponse).all() whitelist_chat_rooms = None if not as_seller: whitelist_chat_rooms = set( str(r[0].id) for r in session.query(ChatRoom, Chat). join(Chat, ChatRoom.id == Chat.chat_room_id).all()) | set( str(r[0].id) for r in session.query(ChatRoom, Offer).join( Offer, ChatRoom.id == Offer.chat_room_id).all()) res = {} for chat_room, assoc, match, buy_order, sell_order in chat_room_queries: chat_room_id = str(chat_room.id) if (chat_room_id in res) or ( (whitelist_chat_rooms is not None) and (chat_room_id not in whitelist_chat_rooms)): continue chat_room_repr = ChatRoomService( self.config)._serialize_chat_room(chat_room, user_id) res[chat_room_id] = chat_room_repr res[chat_room_id]["buy_order"] = buy_order.asdict() res[chat_room_id]["sell_order"] = (sell_order.asdict() if as_seller else None) res[chat_room_id]["chats"] = [] res[chat_room_id]["latest_offer"] = None for chat in chats: if chat.chat_room_id in res: res[chat.chat_room_id]["chats"].append({ "type": "chat", **chat.asdict() }) offer_d = {} for offer in offers: if offer.chat_room_id in res: offer_d[str(offer.id)] = offer res[offer.chat_room_id]["chats"].append({ "type": "offer", **offer.asdict() }) if offer.offer_status != "REJECTED": res[offer.chat_room_id]["latest_offer"] = offer.asdict( ) for offer_resp in offer_responses: offer = offer_d.get(offer_resp.offer_id) if offer is None: continue if offer.offer_status == "CANCELED": author_id = offer.author_id else: author_id = ChatRoomService._get_other_party_id( chat_room_id=offer.chat_room_id, user_id=offer.author_id) res[offer.chat_room_id]["chats"].append( OfferService._serialize_chat_offer( offer=offer.asdict(), is_deal_closed=chat_room.is_deal_closed, offer_response=offer_resp.asdict(), author_id=author_id, )) for v in res.values(): v["chats"].sort(key=lambda x: x["created_at"]) archived_room_ids = set(q[1].chat_room_id for q in chat_room_queries if q[1].is_archived) unarchived_res = {} archived_res = {} for chat_room_id, room in res.items(): if chat_room_id in archived_room_ids: archived_res[chat_room_id] = room else: unarchived_res[chat_room_id] = room return {"archived": archived_res, "unarchived": unarchived_res} @validate_input(CREATE_NEW_MESSAGE_SCHEMA) def create_new_message(self, chat_room_id, message, author_id): with session_scope() as session: chat_room = session.query(ChatRoom).get(chat_room_id) if chat_room is None: raise ResourceNotFoundException("Chat room not found") if ChatRoomService.is_disbanded(chat_room): raise ResourceNotFoundException("Chat room is disbanded") if (session.query(UserChatRoomAssociation).filter_by( user_id=author_id, chat_room_id=chat_room_id).count() == 0): raise ResourceNotOwnedException( "User is not in this chat room") first_chat = (session.query(Chat).filter_by( chat_room_id=chat_room_id).count() == 0) message = Chat( chat_room_id=str(chat_room_id), message=message, author_id=str(author_id), ) session.add(message) session.flush() chat_room.updated_at = message.created_at if first_chat: other_party_id = ChatRoomService._get_other_party_id( chat_room_id=str(chat_room.id), user_id=author_id) other_party_email = session.query(User).get( other_party_id).email self.email_service.send_email(emails=[other_party_email], template="new_chat_message") return {"type": "chat", **message.asdict()}
from src.parser import Parser from src.email_service import EmailService if __name__ == '__main__': data = Parser.run() EmailService.send_mail(data)