Skip to content

Commit

Permalink
Feat: add regulator emails on projection creation (#345)
Browse files Browse the repository at this point in the history
* feat: add `MultipleEmailInput` component

* feat(project-creation): post regulator emails with project details  if the project requires regulators approval

* feat(task-description): update image uploadBox style

* feat: add `BindContentContainer` component

* feat: add `countries-list` package for listing all the countries

* feat(user-profile): show all the countries name on country dropdown with search functionality

* feat(project-approval-page): create UI

* feat: add route for project approval page

* Feat/project approval from regulator (#352)

* feat(task-description): update image uploadBox style

* feat: add `BindContentContainer` component

* feat: add `countries-list` package for listing all the countries

* feat(user-profile): show all the countries name on country dropdown with search functionality

* feat(project-approval-page): create UI

* feat: add route for project approval page

* feat: add regualtor approval and commect section with additon of role regulator

* feat: add regualtor approval and commect section with additon of role regulator

* feat: add regulator email template

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* feat: add project total area

* feat: add project total area

* fix: Pydantic class to validate the token as Base64

* fix: using uitls to send email

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Sujit <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* update: email template for local regulation authority committee

* commented some statements for project list in sql statements

* update: email subject

* fix: local regulation committee boolean selection

---------

Co-authored-by: Sujit <[email protected]>
Co-authored-by: Saurav Aryal <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Niraj Adhikari <[email protected]>
  • Loading branch information
5 people authored Nov 26, 2024
1 parent a686dec commit 5da527a
Show file tree
Hide file tree
Showing 28 changed files with 976 additions and 26 deletions.
16 changes: 16 additions & 0 deletions src/backend/app/db/db_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
ProjectVisibility,
UserRole,
State,
RegulatorApprovalStatus,
)
from sqlalchemy.orm import (
object_session,
Expand Down Expand Up @@ -137,11 +138,26 @@ class DbProject(Base):
requires_approval_from_manager_for_locking = cast(
bool, Column(Boolean, default=False)
)
requires_approval_from_regulator = cast(bool, Column(Boolean, default=False))
regulator_emails = cast(list, Column(ARRAY(String), nullable=True))
# PROJECT STATUS
status = cast(
ProjectStatus,
Column(Enum(ProjectStatus), default=ProjectStatus.DRAFT, nullable=False),
)
regulator_approval_status = cast(
RegulatorApprovalStatus, Column(Enum(RegulatorApprovalStatus), nullable=True)
)
regulator_comment = cast(str, Column(String, nullable=True))
commenting_regulator_id = cast(
str,
Column(
String,
ForeignKey("users.id", name="fk_projects_commenting_regulator_id"),
nullable=True,
),
)
commenting_regulator = relationship(DbUser, uselist=False, backref="regulator")
visibility = cast(
ProjectVisibility,
Column(
Expand Down
142 changes: 142 additions & 0 deletions src/backend/app/email_templates/regulator/approval_request.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Drone Tasking Manager Invite</title>
<link
href="https://fonts.cdnfonts.com/css/barlow-condensed"
rel="stylesheet"
/>
<style>
body {
font-family: "Barlow Condensed", sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: auto;
width: 65%;
}

.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;
}
.content {
padding: 25px;
}

.logo-section {
margin-bottom: 45px;
}

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

.button:hover {
text-decoration: underline;
}

.heading h1 {
font-weight: 400 !important;
font-size: 24px;
}

.footer {
background-color: #ffffff;
text-align: start;
padding: 20px;
color: #666666;
font-size: 14px;
border-radius: 0 0 10px 10px;
border-top: 1px solid #8d8d8d;
}
.footer .social-media a {
text-decoration: none;
margin-right: 10px;
}
</style>
</head>
<body>
<div class="email-container">
<div class="content">
<div class="logo-section">
<img
src="https://minio.dronetm.org/frontendstatic/assets/DTM-logo-black-35d6444f.svg"
alt="DTM-logo-black"
style="
max-width: 100%;
height: auto;
display: block;
margin: 0 auto;
"
/>
</div>

<div class="heading">
<h2>Project Review Request</h2>
</div>
<p>
Hello,
<br />

A new project, titled <strong>{{project_name}}</strong>, has been
created by <strong>{{creator_name}}</strong>. As the designated
authority for the local regulation committee, your review and approval
are required to enable drone operations for this project.

<br />
<br />

<span
>Please note that drone operators will only gain access to and be
able to view the project details upon your approval.</span
>
<br />
<br />

We kindly request you to review the project at your earliest
convenience to facilitate the next steps. <br />
Should you need any additional information or clarification, please do
not hesitate to contact us.

<br />
</p>
<a href="{{project_link}}" class="button">Review Project</a>
</div>
<div class="footer">
<!-- <div class="social-media">
<a href="">
<img src="./fb_logo.png" alt="">
</a>
<a href="">
<img src="./insta_logo.png" alt="">
</a>
</a>
<a href="">
<img src="./x_logo.png" alt="">
</a>
</a>
</div> -->
<p>Email sent from Drone Tasking Manager</p>
</div>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""add regulators approval and comment
Revision ID: 162edb7e3cc4
Revises: 4ea77c60b715
Create Date: 2024-11-22 04:21:04.322488
"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = "162edb7e3cc4"
down_revision: Union[str, None] = "4ea77c60b715"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

regulator_approval_status_enum = sa.Enum(
"PENDING", "APPROVED", "REJECTED", name="regulatorapprovalstatus"
)


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
regulator_approval_status_enum.create(op.get_bind())
op.add_column(
"projects",
sa.Column("requires_approval_from_regulator", sa.Boolean(), nullable=True),
)
op.execute("""
UPDATE projects
SET requires_approval_from_regulator = FALSE
WHERE requires_approval_from_regulator IS NULL
""")
op.add_column(
"projects", sa.Column("regulator_emails", sa.ARRAY(sa.String()), nullable=True)
)
op.add_column(
"projects",
sa.Column(
"regulator_approval_status",
sa.Enum("PENDING", "APPROVED", "REJECTED", name="regulatorapprovalstatus"),
nullable=True,
),
)
op.add_column(
"projects", sa.Column("regulator_comment", sa.String(), nullable=True)
)
op.add_column(
"projects", sa.Column("commenting_regulator_id", sa.String(), nullable=True)
)
op.create_foreign_key(
"fk_projects_commenting_regulator_id",
"projects",
"users",
["commenting_regulator_id"],
["id"],
)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(
"fk_projects_commenting_regulator_id", "projects", type_="foreignkey"
)
op.drop_column("projects", "commenting_regulator_id")
op.drop_column("projects", "regulator_comment")
op.drop_column("projects", "regulator_approval_status")
op.drop_column("projects", "regulator_emails")
op.drop_column("projects", "requires_approval_from_regulator")
# ### end Alembic commands ###
regulator_approval_status_enum.drop(op.get_bind())
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""add user role regulator
Revision ID: 3cd04bfdb1e4
Revises: 162edb7e3cc4
Create Date: 2024-11-22 04:29:30.071824
"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


new_userrole_enum = sa.Enum(
"PROJECT_CREATOR", "DRONE_PILOT", "REGULATOR", name="userrole"
)

old_userrole_enum = sa.Enum("PROJECT_CREATOR", "DRONE_PILOT", name="userrole")

# revision identifiers, used by Alembic.
revision: str = "3cd04bfdb1e4"
down_revision: Union[str, None] = "162edb7e3cc4"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.execute("ALTER TYPE userrole ADD VALUE 'REGULATOR'")
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.execute("ALTER TYPE userrole RENAME TO userrole_old")
op.execute("CREATE TYPE userrole AS ENUM('PROJECT_CREATOR', 'DRONE_PILOT')")
## remove row containing regulator
op.execute("""
UPDATE user_profile
SET role = array_remove(role, 'REGULATOR')
WHERE 'REGULATOR' = ANY(role)
""")

op.execute("""
ALTER TABLE user_profile
ALTER COLUMN role
TYPE userrole[]
USING role::text[]::userrole[]
""")

op.execute("DROP TYPE userrole_old")
# ### end Alembic commands ###
9 changes: 9 additions & 0 deletions src/backend/app/models/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ class ProjectStatus(IntEnum, Enum):
DRAFT = 2


class RegulatorApprovalStatus(IntEnum, Enum):
"""Enum to describe all possible state of a Project from Regulator"""

PENDING = 0
APPROVED = 1
REJECTED = 2


class ProjectVisibility(IntEnum, Enum):
"""Enum describing task splitting type."""

Expand Down Expand Up @@ -120,6 +128,7 @@ class DroneType(IntEnum):
class UserRole(IntEnum, Enum):
PROJECT_CREATOR = 1
DRONE_PILOT = 2
REGULATOR = 3


class State(int, Enum):
Expand Down
13 changes: 13 additions & 0 deletions src/backend/app/projects/project_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,16 @@ def get_project_info_from_s3(project_id: uuid.UUID, task_id: uuid.UUID):
except Exception as e:
log.exception(f"An error occurred while retrieving assets info: {e}")
raise HTTPException(status_code=500, detail=str(e))


async def check_regulator_project(db: Connection, project_id: str, email: str):
sql = """
SELECT id FROM projects WHERE
id = %(project_id)s
AND %(email)s = ANY(regulator_emails)
AND regulator_comment IS NULL
"""
async with db.cursor() as cur:
await cur.execute(sql, {"project_id": project_id, "email": email})
project = await cur.fetchone()
return bool(project)
Loading

0 comments on commit 5da527a

Please sign in to comment.