Skip to content

Commit

Permalink
* Send message to contributors and background task.
Browse files Browse the repository at this point in the history
* Image upload in comment section.
* Last updated utc serialization in my tasks of my contributions
  • Loading branch information
prabinoid committed Nov 20, 2024
1 parent a5b30c3 commit 0fc773f
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 52 deletions.
48 changes: 30 additions & 18 deletions backend/api/projects/actions.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import threading

from databases import Database
from fastapi import APIRouter, Body, Depends, Request
from fastapi import APIRouter, Body, Depends, Request, BackgroundTasks
from fastapi.responses import JSONResponse
from loguru import logger
from shapely import GEOSException
from shapely.errors import TopologicalError

from backend.db import get_db
from backend.db import get_db, db_connection
from backend.models.dtos.grid_dto import GridDTO
from backend.models.dtos.message_dto import MessageDTO
from backend.models.dtos.user_dto import AuthUserDTO
Expand Down Expand Up @@ -101,10 +99,10 @@ async def post(
@router.post("/{project_id}/actions/message-contributors/")
async def post(
request: Request,
background_tasks: BackgroundTasks,
project_id: int,
user: AuthUserDTO = Depends(login_required),
db: Database = Depends(get_db),
message_dto: MessageDTO = Body(...),
):
"""
Send message to all contributors of a project
Expand Down Expand Up @@ -151,27 +149,41 @@ async def post(
description: Internal Server Error
"""
try:
message_dto.from_user_id = user.id
except ValueError as e:
request_json = await request.json()
request_json["from_user_id"] = user.id
message_dto = MessageDTO(**request_json)
except Exception as e:
logger.error(f"Error validating request: {str(e)}")
return {
"Error": "Unable to send message to mappers",
"SubCode": "InvalidData",
}, 400

if not ProjectAdminService.is_user_action_permitted_on_project(user.id, project_id):
return JSONResponse(
content={
"Error": "Unable to send message to contributors",
"SubCode": "InvalidData",
},
status_code=400,
)
if not await ProjectAdminService.is_user_action_permitted_on_project(
user.id, project_id, db
):
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 JSONResponse(content={"Success": "Messages started"}, status_code=200)
try:
background_tasks.add_task(
MessageService.send_message_to_all_contributors,
project_id,
message_dto,
db_connection.database,
)
return JSONResponse(content={"Success": "Messages started"}, status_code=200)
except Exception as e:
logger.error(f"Error starting background task: {str(e)}")
return JSONResponse(
content={"Error": "Failed to send messages"}, status_code=500
)


@router.post("/{project_id}/actions/feature/")
Expand Down
11 changes: 5 additions & 6 deletions backend/api/system/image_upload.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from fastapi import APIRouter, Depends, Request, Body
from fastapi.responses import JSONResponse
import json

import requests
from fastapi import APIRouter, Body, Depends, Request
from fastapi.responses import JSONResponse

from backend.config import settings
from backend.db import get_db
from backend.models.dtos.user_dto import AuthUserDTO
from backend.config import settings
from backend.services.users.authentication_service import login_required

router = APIRouter(
Expand All @@ -16,9 +17,7 @@
)


# class SystemImageUploadRestAPI(Resource):
# @token_auth.login_required
@router.post("/image-upload")
@router.post("/image-upload/")
async def post(
request: Request,
user: AuthUserDTO = Depends(login_required),
Expand Down
7 changes: 4 additions & 3 deletions backend/api/users/tasks.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from databases import Database
from datetime import datetime

from databases import Database
from fastapi import APIRouter, Depends, Request
from fastapi.responses import JSONResponse

from backend.services.users.user_service import UserService
from backend.db import get_db
from backend.models.dtos.user_dto import AuthUserDTO
from backend.services.users.authentication_service import login_required
from backend.services.users.user_service import UserService

router = APIRouter(
prefix="/users",
Expand Down Expand Up @@ -115,7 +116,7 @@ async def get(
sort_by=sort_by,
db=db,
)
return tasks.model_dump(by_alias=True)
return tasks
except ValueError:
print("InvalidDateRange- Date range can not be bigger than 1 year")
return JSONResponse(
Expand Down
9 changes: 6 additions & 3 deletions backend/models/dtos/mapping_dto.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from datetime import datetime
from backend.models.postgis.statuses import TaskStatus
from typing import List, Optional

from pydantic import BaseModel, Field, ValidationError, validator

from backend.models.dtos.mapping_issues_dto import TaskMappingIssueDTO
from backend.models.dtos.task_annotation_dto import TaskAnnotationDTO
from pydantic import BaseModel, Field, ValidationError, validator
from typing import List, Optional
from backend.models.postgis.statuses import TaskStatus


def is_valid_mapped_status(value):
Expand Down Expand Up @@ -107,6 +109,7 @@ class TaskDTO(BaseModel):

class Config:
populate_by_name = True
json_encoders = {datetime: lambda v: v.isoformat() + "Z" if v else None}


class TaskDTOs(BaseModel):
Expand Down
5 changes: 4 additions & 1 deletion backend/models/postgis/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -1626,7 +1626,10 @@ async def task_as_dto(
task_dto.lock_holder = user.username if user else None
task_dto.task_history = task_history
task_dto.last_updated = last_updated if last_updated else None
task_dto.auto_unlock_seconds = await Task.auto_unlock_delta()
unlock_delta = await Task.auto_unlock_delta()
task_dto.auto_unlock_seconds = (
unlock_delta.total_seconds() if unlock_delta else None
)
task_dto.comments_number = comments if isinstance(comments, int) else None
return task_dto

Expand Down
31 changes: 12 additions & 19 deletions backend/services/messaging/message_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,40 +129,33 @@ async def send_message_after_validation(

@staticmethod
async def send_message_to_all_contributors(
project_id: int, message_dto: MessageDTO, db: Database
project_id: int, message_dto: MessageDTO, database: Database
):
"""Sends supplied message to all contributors on specified project. Message all contributors can take
over a minute to run, so this method is expected to be called on its own thread
"""
# TODO: Background task.
app = (
create_app()
) # Because message-all run on background thread it needs it's own app context

with app.app_context():
contributors = await Message.get_all_contributors(project_id, db)
project = await Project.get(project_id, db)
project_name = await ProjectInfo.get_dto_for_locale(
db, project_id, project.default_locale
).name
async with database.connection() as conn:
contributors = await Message.get_all_contributors(project_id, conn)
project = await Project.get(project_id, conn)
project_info = await ProjectInfo.get_dto_for_locale(
conn, project_id, project.default_locale
)
message_dto.message = "A message from {} managers:<br/><br/>{}".format(
MessageService.get_project_link(
project_id, project_name, highlight=True
project_id, project_info.name, highlight=True
),
markdown(message_dto.message, output_format="html"),
)

messages = []
for contributor in contributors:
message = Message.from_dto(contributor.id, message_dto)
message = Message.from_dto(contributor, message_dto)
message.message_type = MessageType.BROADCAST.value
message.project_id = project_id
user = await UserService.get_user_by_id(contributor.id, db)
user = await UserService.get_user_by_id(contributor, conn)
messages.append(
dict(message=message, user=user, project_name=project_name)
dict(message=message, user=user, project_name=project_info.name)
)

await MessageService._push_messages(messages, db)
await MessageService._push_messages(messages, conn)

@staticmethod
async def _push_messages(messages: list, db: Database):
Expand Down
2 changes: 0 additions & 2 deletions backend/services/validator_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,6 @@ async def revert_user_tasks(revert_dto: RevertUserTasksDTO, db: Database):
"project_id": revert_dto.project_id,
"task_status": TaskStatus[revert_dto.action].value,
}

if TaskStatus[revert_dto.action].value == TaskStatus.BADIMAGERY.value:
query += " AND mapped_by = :user_id"
values["user_id"] = revert_dto.user_id
Expand All @@ -571,7 +570,6 @@ async def revert_user_tasks(revert_dto: RevertUserTasksDTO, db: Database):
values["user_id"] = revert_dto.user_id

tasks_to_revert = await db.fetch_all(query=query, values=values)

for task in tasks_to_revert:
await MappingService.undo_mapping(
revert_dto.project_id,
Expand Down

0 comments on commit 0fc773f

Please sign in to comment.