From a2f02d7efd17739931a06b2a1c4ea5cc8ae75509 Mon Sep 17 00:00:00 2001 From: Yariv Menachem <yariv220895@gmail.com> Date: Sun, 5 Jan 2025 17:12:25 +0200 Subject: [PATCH] fixes --- src/model/Position.py | 2 +- src/model/User.py | 2 +- src/model/codec/position_codec.py | 4 +- src/model/user_repository.py | 70 ++++++++++++------- .../start_handler_constats.py | 28 ++++---- .../telegram_start_handler.py | 21 ++++-- 6 files changed, 80 insertions(+), 47 deletions(-) diff --git a/src/model/Position.py b/src/model/Position.py index 4b22f78..0464607 100644 --- a/src/model/Position.py +++ b/src/model/Position.py @@ -1,7 +1,7 @@ from enum import Enum -class Position(Enum): +class Position(str, Enum): PRODUCT_MANAGEMENT = "Product Management" DATA_ANALYST = "Data Analyst" DATA_SCIENCE = "Data Science, ML & Algorithms" diff --git a/src/model/User.py b/src/model/User.py index a437dbe..72ebfad 100644 --- a/src/model/User.py +++ b/src/model/User.py @@ -9,4 +9,4 @@ class User(BaseModel): full_name: str username: str chat_id: Union[int, str] = None - field: Optional[Position] = Position.SOFTWARE_ENGINEERING + field: Optional[Position] = None diff --git a/src/model/codec/position_codec.py b/src/model/codec/position_codec.py index e23360c..95bbddc 100644 --- a/src/model/codec/position_codec.py +++ b/src/model/codec/position_codec.py @@ -8,10 +8,10 @@ class PositionCodec(TypeCodec): bson_type = str def transform_python(self, value): - return value.value + return value.name def transform_bson(self, value): return Position(value) -position_codec = PositionCodec() +# position_codec = PositionCodec() diff --git a/src/model/user_repository.py b/src/model/user_repository.py index 6a5d060..6003825 100644 --- a/src/model/user_repository.py +++ b/src/model/user_repository.py @@ -1,33 +1,22 @@ from typing import Optional -from bson.codec_options import TypeRegistry, CodecOptions +from cachebox import LRUCache from dotenv import load_dotenv from pymongo import UpdateOne from jobspy import create_logger from .User import User -from .codec.position_codec import position_codec from .monogo_db import mongo_client load_dotenv() class UserRepository: - _instance = None - - def __new__(cls): - - if cls._instance is not None: - return cls._instance - - self = super().__new__(cls) - cls._instance = self - self.logger = create_logger("UserRepository") - type_registry = TypeRegistry([position_codec]) - codec_options = CodecOptions(type_registry=type_registry) - self.collection = mongo_client.get_collection('user', codec_options=codec_options) - self.collection.create_index('username', unique=True) - return cls._instance + def __init__(self): + self._cache = LRUCache(50) + self._logger = create_logger("UserRepository") + self._collection = mongo_client.get_collection('user') + self._collection.create_index('username', unique=True) def find_by_id(self, user_id: str) -> Optional[User]: """ @@ -39,8 +28,41 @@ class UserRepository: Returns: The user document if found, otherwise None. """ - result = self.collection.find_one({"id": user_id}) - return User(**result) + user = None + cached_user = self._cache[user_id] + if cached_user: + return User(**cached_user) + + result = self._collection.find_one({"id": user_id}) + + if result: + user = User(**result) + self._cache[user_id] = user + + return user + + def find_by_username(self, username: str) -> Optional[User]: + """ + Finds a user document in the collection by its username. + + Args: + username: The username of the user to find. + + Returns: + The user document if found, otherwise None. + """ + user = None + cached_user = self._cache.get(username) + if cached_user: + return cached_user + + self._logger.info("Find user by username") + result = self._collection.find_one({"username": username}) + if result: + user = User(**result) + self._cache[username] = user + + return user def update(self, user: User) -> bool: """ @@ -52,7 +74,7 @@ class UserRepository: Returns: True if the update was successful, False otherwise. """ - result = self.collection.update_one({"id": user.id}, {"$set": user.model_dump()}) + result = self._collection.update_one({"id": user.id}, {"$set": user.model_dump()}) return result.modified_count > 0 def insert_user(self, user: User): @@ -65,8 +87,8 @@ class UserRepository: Raises: Exception: If an error occurs during insertion. """ - self.collection.insert_one(user.model_dump()) - self.logger.info(f"Inserted new user with username {user.username}.") + self._collection.insert_one(user.model_dump()) + self._logger.info(f"Inserted new user with username {user.username}.") def insert_many_if_not_found(self, users: list[User]) -> tuple[list[User], list[User]]: """ @@ -89,8 +111,8 @@ class UserRepository: if operations: # Execute all operations in bulk - result = self.collection.bulk_write(operations) - self.logger.info(f"Matched: {result.matched_count}, Upserts: { + result = self._collection.bulk_write(operations) + self._logger.info(f"Matched: {result.matched_count}, Upserts: { result.upserted_count}, Modified: {result.modified_count}") # Get the newly inserted users (those that were upserted) diff --git a/src/telegram_handler/start_handler_constats.py b/src/telegram_handler/start_handler_constats.py index ff3f607..ee70ea1 100644 --- a/src/telegram_handler/start_handler_constats.py +++ b/src/telegram_handler/start_handler_constats.py @@ -7,23 +7,27 @@ POSITION_MESSAGE: str = "What kind of position are you looking for? ✨\n" \ POSITION_NOT_FOUND: str = "I couldn't find any positions matching your request. 😕\n" \ "Please try again" +multi_value_message: str = "📌 You can enter multiple tags separated by commas." + LOCATION_MESSAGE: str = "Where are you hoping to find a position? 🌎\n" \ - "(e.g., Rishon Lezion, New York City, San Francisco)\n" \ - "You can enter multiple tags separated by commas. 🔍" + "(e.g., Rishon Lezion, New York City, San Francisco)\n\n" + multi_value_message + EXPERIENCE_MESSAGE: str = "How many years of professional experience do you have in this field? 💼\n" + FILTER_TILE_MESSAGE: str = "To help me narrow down your search, tell me about any relevant tags or keywords.\n" \ - "For example: 'remote', 'entry-level', 'python', 'machine learning', 'QA'.\n" \ - "You can enter multiple tags separated by commas. 🔍" + "For example: 'remote', 'entry-level', 'python', 'machine learning', 'QA'.\n\n" + multi_value_message + THANK_YOU_MESSAGE: str = "Thank you for chatting with Professor Bot!\n\n" \ - "I can help you find jobs on LinkedIn, Glassdoor, and more." \ - "To search for jobs on a specific site, simply send the site name:\n" \ - "/linkedin\n" \ - "/glassdoor\n" \ - "/google\n" \ - "Or, use the command /find to search across all supported job boards for a broader search.\n" \ - "Let me know how I can assist you further! 😊" + "I can help you find jobs on LinkedIn, Glassdoor, and more." + +SEARCH_MESSAGE: str = "To search for jobs on a specific site, simply send the site name:\n" \ + "/linkedin\n" \ + "/glassdoor\n" \ + "/google\n\n" \ + "Or, use the command /find to search across all supported job boards for a broader search.\n\n" \ + "Let me know how I can assist you further! 😊" BYE_MESSAGE: str = "Have a great day!✨\n" \ "I hope to assist you with your job search in the future.😊" -VERIFY_MESSAGE:str = "Did you choose: %s ? 🧐" +VERIFY_MESSAGE: str = "Did you choose: %s ? 🧐" diff --git a/src/telegram_handler/telegram_start_handler.py b/src/telegram_handler/telegram_start_handler.py index c2daced..37f76ce 100644 --- a/src/telegram_handler/telegram_start_handler.py +++ b/src/telegram_handler/telegram_start_handler.py @@ -8,9 +8,11 @@ from telegram.ext import ( from jobspy.scrapers.utils import create_logger from model.Position import Position from model.User import User +from model.user_repository import user_repository from telegram_bot import TelegramBot from telegram_handler.start_handler_constats import START_MESSAGE, POSITION_MESSAGE, POSITION_NOT_FOUND, \ - LOCATION_MESSAGE, EXPERIENCE_MESSAGE, FILTER_TILE_MESSAGE, THANK_YOU_MESSAGE, BYE_MESSAGE, VERIFY_MESSAGE + LOCATION_MESSAGE, EXPERIENCE_MESSAGE, FILTER_TILE_MESSAGE, THANK_YOU_MESSAGE, BYE_MESSAGE, VERIFY_MESSAGE, \ + SEARCH_MESSAGE from telegram_handler.telegram_handler import TelegramHandler @@ -36,8 +38,11 @@ class TelegramStartHandler(TelegramHandler): async def start(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: """Starts the conversation and asks the user about their position.""" chat: Chat = update.message.chat - user = User(full_name=chat.full_name, username=chat.username, chat_id=chat.id) - # user_repository.insert_user(user) + user = user_repository.find_by_username(chat.username) + if not user: + user = User(full_name=chat.full_name, username=chat.username, chat_id=chat.id) + user_repository.insert_user(user) + await update.message.reply_text(START_MESSAGE) buttons = [[KeyboardButton(position.value)] for position in Position] @@ -54,10 +59,10 @@ class TelegramStartHandler(TelegramHandler): """Stores the selected position and asks for a locations.""" user = update.message.from_user self.logger.info("Position of %s: %s", user.first_name, update.message.text) - position = next((p for p in Position if p.name == update.message.text), None) + position = next((p for p in Position if p.value == update.message.text), None) if not position: await update.message.reply_text(POSITION_NOT_FOUND) - buttons = [[KeyboardButton(position.name)] for position in Position] + buttons = [[KeyboardButton(position.value)] for position in Position] reply_markup = ReplyKeyboardMarkup(buttons, one_time_keyboard=True, input_field_placeholder=Flow.POSITION.name) await update.message.reply_text( @@ -70,13 +75,13 @@ class TelegramStartHandler(TelegramHandler): return Flow.ADDRESS.value - async def address(self, update: Update) -> int: + async def address(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: """Asks for a location.""" user = update.message.from_user self.cities = update.message.text.split(",") reply_markup = ReplyKeyboardMarkup([[KeyboardButton("Yes"), KeyboardButton("No")]], one_time_keyboard=True, input_field_placeholder=Flow.VERIFY_ADDRESS.name) - await update.message.reply_text(VERIFY_MESSAGE % self.filters, reply_markup=reply_markup) + await update.message.reply_text(VERIFY_MESSAGE % self.cities, reply_markup=reply_markup) return Flow.VERIFY_ADDRESS.value @@ -119,6 +124,7 @@ class TelegramStartHandler(TelegramHandler): return Flow.FILTERS.value await update.message.reply_text(THANK_YOU_MESSAGE) + await update.message.reply_text(SEARCH_MESSAGE) return ConversationHandler.END @@ -127,6 +133,7 @@ class TelegramStartHandler(TelegramHandler): user = update.message.from_user self.logger.info("User %s did not send a filters.", user.first_name) await update.message.reply_text(THANK_YOU_MESSAGE) + await update.message.reply_text(SEARCH_MESSAGE) return ConversationHandler.END