From 907a616ccc8e90356b70e6b96c930995e25be256 Mon Sep 17 00:00:00 2001
From: prabinoid <38830224+prabinoid@users.noreply.github.com>
Date: Fri, 15 Nov 2024 17:53:12 +0545
Subject: [PATCH] fix: Project search and deletion.
---
backend/api/projects/actions.py | 13 ++++++-----
backend/api/teams/actions.py | 25 ++++++++++++++++++----
backend/models/dtos/message_dto.py | 19 ++--------------
backend/models/postgis/project.py | 2 ++
backend/models/postgis/team.py | 16 --------------
backend/services/project_search_service.py | 14 ++++++++----
backend/services/team_service.py | 16 ++++++++------
7 files changed, 52 insertions(+), 53 deletions(-)
diff --git a/backend/api/projects/actions.py b/backend/api/projects/actions.py
index 31309a4c03..0809e4a01b 100644
--- a/backend/api/projects/actions.py
+++ b/backend/api/projects/actions.py
@@ -160,15 +160,18 @@ async def post(
}, 400
if not ProjectAdminService.is_user_action_permitted_on_project(user.id, project_id):
- return {
- "Error": "User is not a manager of the project",
- "SubCode": "UserPermissionError",
- }, 403
+ return JSONResponse(
+ content={
+ "Error": "User is not a manager of the project",
+ "SubCode": "UserPermissionError",
+ },
+ status_code=403,
+ )
threading.Thread(
target=MessageService.send_message_to_all_contributors,
args=(project_id, message_dto),
).start()
- return {"Success": "Messages started"}, 200
+ return JSONResponse(content={"Success": "Messages started"}, status_code=200)
@router.post("/{project_id}/actions/feature/")
diff --git a/backend/api/teams/actions.py b/backend/api/teams/actions.py
index 2913a507c5..d83260ee7e 100644
--- a/backend/api/teams/actions.py
+++ b/backend/api/teams/actions.py
@@ -314,6 +314,19 @@ async def post(
)
+import asyncio
+
+
+# Function to run async code in a thread
+def run_asyncio_in_thread(func, *args, **kwargs):
+ # Create a new event loop for the thread
+ loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(loop)
+ # Create a new database connection (to be used in this thread)
+ db = get_db()
+ loop.run_until_complete(func(*args, db=db, **kwargs))
+
+
@router.post("/{team_id}/actions/message-members/")
async def post(
request: Request,
@@ -321,7 +334,6 @@ async def post(
user: AuthUserDTO = Depends(login_required),
db: Database = Depends(get_db),
team_id: int = None,
- message_dto: MessageDTO = Body(...),
):
"""
Message all team members
@@ -368,9 +380,11 @@ async def post(
description: Internal Server Error
"""
try:
+ request_json = await request.json()
+ request_json["from_user_id"] = user.id
+ message_dto = MessageDTO(**request_json)
# Validate if team is present
team = await TeamService.get_team_by_id(team_id, db)
-
is_manager = await TeamService.is_user_team_manager(team_id, user.id, db)
if not is_manager:
raise ValueError
@@ -397,10 +411,13 @@ async def post(
)
try:
+ # Start a new thread for sending messages
+ # Use threading to run the async function in a separate thread
# threading.Thread(
- # target=TeamService.send_message_to_all_team_members,
- # args=(team_id, team.name, message_dto, db),
+ # target=run_asyncio_in_thread,
+ # args=(TeamService.send_message_to_all_team_members, team_id, team.name, message_dto, user.id)
# ).start()
+
background_tasks.add_task(
TeamService.send_message_to_all_team_members,
team_id,
diff --git a/backend/models/dtos/message_dto.py b/backend/models/dtos/message_dto.py
index f6534c032c..e4525d6a77 100644
--- a/backend/models/dtos/message_dto.py
+++ b/backend/models/dtos/message_dto.py
@@ -54,8 +54,8 @@ class ChatMessageDTO(BaseModel):
timestamp: datetime
username: str
- # class Config:
- # populate_by_name = True
+ class Config:
+ populate_by_name = True
# json_encoders = {
# datetime: lambda v: v.isoformat() + "Z" if v else None
@@ -76,21 +76,6 @@ class Config:
json_encoders = {datetime: lambda v: v.isoformat() + "Z" if v else None}
- # def dict(self, *args, **kwargs):
- # """
- # Override the dict method to exclude `user_id` and `project_id`
- # from the dictionary representation.
- # """
- # exclude_fields = {"user_id", "project_id"}
- # # Generate the dict as usual, excluding the fields
- # return super().dict(*args, **kwargs, exclude=exclude_fields)
-
- # def dict(self, **kwargs):
- # data = super().dict(**kwargs)
- # if self.timestamp:
- # data["timestamp"] = self.timestamp.isoformat() + "Z"
- # return data
-
class ProjectChatDTO(BaseModel):
"""DTO describing all chat messages on one project"""
diff --git a/backend/models/postgis/project.py b/backend/models/postgis/project.py
index 156ea40898..4ec7ec7e61 100644
--- a/backend/models/postgis/project.py
+++ b/backend/models/postgis/project.py
@@ -825,6 +825,8 @@ async def delete(self, db: Database):
"tasks",
"project_info",
"project_chat",
+ "project_partnerships_history",
+ "project_partnerships",
]
# Start a transaction to ensure atomic deletion
diff --git a/backend/models/postgis/team.py b/backend/models/postgis/team.py
index dff93b9c5b..ce660c34d8 100644
--- a/backend/models/postgis/team.py
+++ b/backend/models/postgis/team.py
@@ -121,22 +121,6 @@ class Team(Base):
# organisation = relationship(Organisation, backref="teams", lazy="joined")
organisation = relationship(Organisation, backref="teams")
- # async def create(self, db: Database):
- # """Creates and saves the current model to the DB"""
- # print(self.members)
-
- # team = await db.execute(
- # insert(Team.__table__).values(
- # organisation_id=self.organisation_id,
- # name=self.name,
- # logo=self.logo,
- # description=self.description,
- # join_method=self.join_method,
- # visibility=self.visibility,
- # )
- # )
- # return team if team else None
-
async def create(self, db: Database):
"""Creates and saves the current model to the DB, including members if they exist."""
diff --git a/backend/services/project_search_service.py b/backend/services/project_search_service.py
index 153fcacba6..ec0eb66b98 100644
--- a/backend/services/project_search_service.py
+++ b/backend/services/project_search_service.py
@@ -265,15 +265,21 @@ async def _filter_projects(search_dto: ProjectSearchDTO, user, db: Database):
search_text = "".join(
char for char in search_dto.text_search if char not in "@|&!><\\():"
)
- or_search = " | ".join([x for x in search_text.split(" ") if x])
+ tsquery_search = " & ".join([x for x in search_text.split(" ") if x])
+ ilike_search = f"%{search_text}%"
+
subquery_filters.append(
- "text_searchable @@ to_tsquery('english', :text_search) OR name ILIKE :text_search"
+ """
+ text_searchable @@ to_tsquery('english', :tsquery_search)
+ OR name ILIKE :text_search
+ """
)
- params["text_search"] = or_search
+ params["tsquery_search"] = tsquery_search
+ params["text_search"] = ilike_search
filters.append(
"""
- p.id IN (
+ p.id = ANY(
SELECT project_id
FROM project_info
WHERE {}
diff --git a/backend/services/team_service.py b/backend/services/team_service.py
index cfd01ece95..b4801fa4c9 100644
--- a/backend/services/team_service.py
+++ b/backend/services/team_service.py
@@ -631,11 +631,15 @@ async def _get_team_members(team_id: int, db: Database):
@staticmethod
async def _get_active_team_members(team_id: int, db: Database):
- query = """
- SELECT * FROM team_members
- WHERE team_id = :team_id AND active = TRUE
- """
- return await db.fetch_all(query, values={"team_id": team_id})
+ try:
+ query = """
+ SELECT * FROM team_members
+ WHERE team_id = :team_id AND active = TRUE
+ """
+ return await db.fetch_all(query, values={"team_id": team_id})
+ except Exception as e:
+ print(f"Error executing query: {str(e)}")
+ raise
@staticmethod
async def activate_team_member(team_id: int, user_id: int, db: Database):
@@ -791,7 +795,6 @@ async def send_message_to_all_team_members(
team_members = await TeamService._get_active_team_members(team_id, db)
user = await UserService.get_user_by_id(user_id, db)
sender = user.username
-
message_dto.message = (
"A message from {}, manager of {} team:
{}".format(
MessageService.get_user_profile_link(sender),
@@ -799,7 +802,6 @@ async def send_message_to_all_team_members(
markdown(message_dto.message, output_format="html"),
)
)
-
messages = []
for team_member in team_members:
if team_member.user_id != user_id: