Skip to content

Commit

Permalink
Merge pull request #95 from hotosm/feat/requested-tasks
Browse files Browse the repository at this point in the history
Feat/requested tasks
  • Loading branch information
nrjadkry authored Jul 25, 2024
2 parents b6781b3 + d5004f5 commit 245d383
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 6 deletions.
140 changes: 140 additions & 0 deletions src/backend/app/email_templates/mapping_approved_or_rejected.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ email_subject }}</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}

.email-container {
max-width: 600px;
background-color: #ffffff;
border-radius: 10px;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);
overflow: hidden;
margin: 20px;
}

.header {
background-color: #d73f3f;
color: #ffffff;
text-align: center;
padding: 25px;
border-radius: 10px 10px 0 0;
}

.header h1 {
margin: 0;
font-size: 26px;
}

.content {
padding: 25px;
}

.content p {
font-size: 16px;
line-height: 1.5;
color: #333333;
}

.task {
margin: 20px 0;
background-color: #f9f9f9;
padding: 20px;
border: 1px solid #eeeeee;
border-radius: 8px;
}

.task h2 {
margin: 0 0 10px;
font-size: 20px;
color: #d73f3f;
}

.task p {
margin: 5px 0;
color: #555555;
}

.task-details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}

.task-details p {
margin: 0;
font-size: 16px;
color: #555555;
}

.task-button {
display: inline-block;
margin-top: 20px;
padding: 12px 24px;
background-color: #d73f3f;
color: #ffffff;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s ease;
}

.task-button:hover {
background-color: #a33030;
}

.footer {
background-color: #f4f4f4;
text-align: center;
padding: 20px;
color: #666666;
font-size: 14px;
border-radius: 0 0 10px 10px;
}
body {
margin: auto;
width: 65%;
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<h1>{{ email_subject }}</h1>
</div>
<div class="content">
<p>Dear {{ drone_operator_name }},</p>
<p>
{{ email_body }}
</p>
<p>Please find below the details of the {{ task_status }} task:</p>
<div class="task">
<h2>{{ task_status|capitalize }} Task Details</h2>
<div class="task-details">
<p><strong>Project:</strong> {{ project_name }}</p>
<p><strong>Task ID:</strong> {{ task_id }}</p>
<p><strong>Description:</strong> {{ description }}</p>
</div>
{% if task_status == 'approved' %}
<a href="https://dronetm-dev.naxa.com.np" class="task-button">Start Mapping</a>
{% endif %}
</div>
</div>
<div class="footer">
<p>Thank you for your support,</p>
<p>The HOTOSM Team</p>
</div>
</div>
</body>
</html>
6 changes: 3 additions & 3 deletions src/backend/app/email_templates/mapping_requests.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ <h1>Drone Tasking Manager Invite</h1>
<div class="task">
<h2>Mapping Task Details</h2>
<div class="task-details">
<p><strong>Drone Operator:</strong> {{drone_operator_name}} </p>
<p><strong>Task ID:</strong> {{task_id}} </p>
<p><strong>Project:</strong>{{project_name}}</p>
<p>
<strong>Description:</strong> Drone Tasking Manager Project
Description
</p>
<strong>Description:</strong> {{description}}</p>
</div>
<a href="https://dronetm-dev.naxa.com.np" class="task-button"
>Start Mapping</a
Expand Down
18 changes: 18 additions & 0 deletions src/backend/app/tasks/task_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,21 @@ async def get_requested_user_id(
if result is None:
raise ValueError("No user requested for mapping")
return result["user_id"]


async def get_project_task_by_id(db: Database, project_id: uuid.UUID, user_id):
"""Get all tasks associated with a specific project by project_id."""
raw_sql = """
SELECT t.id AS task_id, te.event_id, te.user_id, te.comment, te.state, te.created_at
FROM tasks t
LEFT JOIN task_events te ON t.id = te.task_id
WHERE t.project_id = :project_id
AND te.user_id = :user_id
ORDER BY t.project_task_index;
"""
db_tasks = await db.fetch_all(
raw_sql, {"project_id": project_id, "user_id": user_id}
)

return db_tasks
80 changes: 77 additions & 3 deletions src/backend/app/tasks/task_routes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import uuid
from fastapi import APIRouter, BackgroundTasks, Depends
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from app.config import settings
from app.models.enums import EventType, State
from app.tasks import task_schemas, task_crud
Expand Down Expand Up @@ -60,6 +60,7 @@ async def new_event(
"drone_operator_name": user_data.name,
"task_id": task_id,
"project_name": project.name,
"description": project.description,
},
)
background_tasks.add_task(
Expand All @@ -69,11 +70,40 @@ async def new_event(
html_content,
)
return data

case EventType.MAP:
# TODO: send notification here after this function
project = await get_project_by_id(db, project_id)
if user_id != project.author_id:
raise HTTPException(
status_code=403,
detail="Only the project creator can approve the mapping.",
)

requested_user_id = await task_crud.get_requested_user_id(
db, project_id, task_id
)
drone_operator = await get_user_by_id(db, requested_user_id)
html_content = render_email_template(
template_name="mapping_approved_or_rejected.html",
context={
"email_subject": "Mapping Request Approved",
"email_body": "We are pleased to inform you that your mapping request has been approved. Your contribution is invaluable to our efforts in improving humanitarian responses worldwide.",
"task_status": "approved",
"name": user_data.name,
"drone_operator_name": drone_operator.name,
"task_id": task_id,
"project_name": project.name,
"description": project.description,
},
)

background_tasks.add_task(
send_notification_email,
drone_operator.email_address,
"Task is approved",
html_content,
)

return await task_crud.update_task_state(
db,
project_id,
Expand All @@ -83,11 +113,40 @@ async def new_event(
State.REQUEST_FOR_MAPPING,
State.LOCKED_FOR_MAPPING,
)

case EventType.REJECTED:
# TODO: send notification here after this function
project = await get_project_by_id(db, project_id)
if user_id != project.author_id:
raise HTTPException(
status_code=403,
detail="Only the project creator can approve the mapping.",
)

requested_user_id = await task_crud.get_requested_user_id(
db, project_id, task_id
)
drone_operator = await get_user_by_id(db, requested_user_id)
html_content = render_email_template(
template_name="mapping_approved_or_rejected.html",
context={
"email_subject": "Mapping Request Rejected",
"email_body": "We are sorry to inform you that your mapping request has been rejected.",
"task_status": "rejected",
"name": user_data.name,
"drone_operator_name": drone_operator.name,
"task_id": task_id,
"project_name": project.name,
"description": project.description,
},
)

background_tasks.add_task(
send_notification_email,
drone_operator.email_address,
"Task is Rejected",
html_content,
)

return await task_crud.update_task_state(
db,
project_id,
Expand Down Expand Up @@ -140,3 +199,18 @@ async def new_event(
)

return True


@router.get("/requested_tasks/{project_id}/pending")
async def get_pending_tasks(
project_id: uuid.UUID,
user_data: AuthUser = Depends(login_required),
db: Database = Depends(database.encode_db),
):
"""Get a list of pending tasks for a specific project and user."""
user_id = user_data.id

pending_tasks = await task_crud.get_project_task_by_id(db, project_id, user_id)
if pending_tasks is None:
raise HTTPException(status_code=404, detail="Project not found")
return pending_tasks

0 comments on commit 245d383

Please sign in to comment.