Skip to content

Commit

Permalink
Merge pull request #100 from hotosm/feat/add-project-slug
Browse files Browse the repository at this point in the history
Feat/add project slug
  • Loading branch information
nrjadkry authored Jul 29, 2024
2 parents e987c77 + e15a26d commit 4f95a66
Show file tree
Hide file tree
Showing 7 changed files with 886 additions and 1,130 deletions.
2 changes: 1 addition & 1 deletion src/backend/app/db/db_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
)
from sqlalchemy.orm import declarative_base
from sqlalchemy.dialects.postgresql import UUID

from geoalchemy2 import Geometry, WKBElement
from app.models.enums import (
TaskStatus,
Expand Down Expand Up @@ -83,6 +82,7 @@ class DbProject(Base):

id = cast(str, Column(UUID(as_uuid=True), primary_key=True))
name = cast(str, Column(String))
slug = cast(str, Column(String, unique=True, index=True, nullable=False))
short_description = cast(str, Column(String))
description = cast(str, Column(String))
per_task_instructions = cast(str, Column(String))
Expand Down
31 changes: 31 additions & 0 deletions src/backend/app/migrations/versions/87b6f9d734e8_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
Revision ID: 87b6f9d734e8
Revises: 9d01411fd221
Create Date: 2024-07-26 05:18:16.267171
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision: str = "87b6f9d734e8"
down_revision: Union[str, None] = "9d01411fd221"
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.add_column("projects", sa.Column("slug", sa.String(), nullable=False))
op.create_index(op.f("ix_projects_slug"), "projects", ["slug"], unique=True)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_projects_slug"), table_name="projects")
op.drop_column("projects", "slug")
# ### end Alembic commands ###
8 changes: 6 additions & 2 deletions src/backend/app/projects/project_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from fastapi.concurrency import run_in_threadpool
from databases import Database
from app.models.enums import ProjectStatus
from app.utils import generate_slug


async def create_project_with_project_info(
Expand All @@ -19,9 +20,10 @@ async def create_project_with_project_info(
_id = uuid.uuid4()
query = """
INSERT INTO projects (
id, author_id, name, description, per_task_instructions, status, visibility, outline, no_fly_zones, dem_url, output_orthophoto_url, output_pointcloud_url, output_raw_url, task_split_dimension, deadline_at, created_at)
id, slug, author_id, name, description, per_task_instructions, status, visibility, outline, no_fly_zones, dem_url, output_orthophoto_url, output_pointcloud_url, output_raw_url, task_split_dimension, deadline_at, created_at)
VALUES (
:id,
:slug,
:author_id,
:name,
:description,
Expand All @@ -45,6 +47,7 @@ async def create_project_with_project_info(
query,
values={
"id": _id,
"slug": generate_slug(project_metadata.name),
"author_id": author_id,
"name": project_metadata.name,
"description": project_metadata.description,
Expand Down Expand Up @@ -83,6 +86,7 @@ async def get_project_info_by_id(db: Database, project_id: uuid.UUID):
query = """
SELECT
projects.id,
projects.slug,
projects.name,
projects.description,
projects.per_task_instructions,
Expand All @@ -109,7 +113,7 @@ async def get_projects(
):
"""Get all projects."""
raw_sql = """
SELECT id, name, description, per_task_instructions, outline
SELECT id, slug, name, description, per_task_instructions, outline
FROM projects
ORDER BY created_at DESC
OFFSET :skip
Expand Down
2 changes: 1 addition & 1 deletion src/backend/app/projects/project_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from app.models.enums import ProjectVisibility, State
from shapely import wkb
from datetime import date

from app.utils import (
geojson_to_geometry,
multipolygon_to_polygon,
Expand Down Expand Up @@ -106,6 +105,7 @@ class ProjectOut(BaseModel):
"""Base project model."""

id: uuid.UUID
slug: Optional[str] = None
name: str
description: str
per_task_instructions: Optional[str] = None
Expand Down
31 changes: 30 additions & 1 deletion src/backend/app/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from jinja2 import Template
from pathlib import Path
from dataclasses import dataclass

from slugify import slugify
from email.mime.text import MIMEText
from email.utils import formataddr
from aiosmtplib import send as send_email
Expand Down Expand Up @@ -383,3 +383,32 @@ def test_email(email_to: str, subject: str = "Test email") -> None:
send_notification_email(
email_to=email_to, subject=subject, html_content=html_content
)


def generate_slug(name: str) -> str:
"""
Generate a unique slug based on the provided name.
The slug is created by converting the given name into a URL-friendly format and appending
the current date and time to ensure uniqueness. The date and time are formatted as
"ddmmyyyyHHMM" to create a timestamp.
Args:
name (str): The name from which the slug will be generated.
Returns:
str: The generated slug, which includes the URL-friendly version of the name and
a timestamp. If an error occurs during the generation, an empty string is returned.
Raises:
Exception: If an error occurs during the slug generation process.
"""
try:
slug = slugify(name)
now = datetime.now()
date_time_str = now.strftime("%d%m%Y%H%M")
slug_with_date = f"{slug}-{date_time_str}"
return slug_with_date
except Exception as e:
print(f"An error occurred while generating the slug: {e}")
return ""
Loading

0 comments on commit 4f95a66

Please sign in to comment.