mirror of https://github.com/Bunsly/JobSpy
fixes
parent
7aa8632aa1
commit
a2f02d7efd
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 ? 🧐"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue