Skip to content

Commit

Permalink
feat: implement user invitation functionality for projects via OSM
Browse files Browse the repository at this point in the history
  • Loading branch information
Anuj-Gupta4 committed Dec 23, 2024
1 parent 7f012c6 commit 90b77e3
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 3 deletions.
17 changes: 17 additions & 0 deletions src/backend/app/auth/providers/osm.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,20 @@ def send_osm_message(
else:
msg = "Sending message via OSM failed"
log.error(f"{msg}: {response.text}")


async def check_osm_user(osm_username: str):
"""Check if the user is an OSM user based on their username."""
osm_url = f"https://www.openstreetmap.org/user/{osm_username}/"

user_exists = False
try:
response = requests.get(osm_url)
if response.status_code == 200:
user_exists = True
except Exception as e:
log.exception(
f"Failed to check if user exists on OSM. Error: {e}",
stack_info=True,
)
return user_exists
54 changes: 51 additions & 3 deletions src/backend/app/projects/project_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,39 @@
import uuid
from io import BytesIO
from pathlib import Path
from textwrap import dedent
from traceback import format_exc
from typing import Optional, Union

import geojson
import geojson_pydantic
import requests
from asgiref.sync import async_to_sync
from fastapi import HTTPException
from fastapi import (
HTTPException,
Request,
)
from loguru import logger as log
from osm_fieldwork.basemapper import create_basemap_file
from osm_login_python.core import Auth
from osm_rawdata.postgres import PostgresClient
from psycopg import Connection
from psycopg.rows import class_row

from app.auth.providers.osm import get_osm_token, send_osm_message
from app.central import central_crud, central_schemas
from app.config import settings
from app.db.enums import BackgroundTaskStatus, HTTPStatus, XLSFormType
from app.db.models import DbBackgroundTask, DbBasemap, DbProject, DbTask
from app.db.enums import (
BackgroundTaskStatus,
HTTPStatus,
XLSFormType,
)
from app.db.models import (
DbBackgroundTask,
DbBasemap,
DbProject,
DbTask,
)
from app.db.postgis_utils import (
check_crs,
featcol_keep_single_geom_type,
Expand Down Expand Up @@ -932,3 +947,36 @@ async def get_project_users_plus_contributions(db: Connection, project_id: int):
) as cur:
await cur.execute(query, {"project_id": project_id})
return await cur.fetchall()


async def send_invitation_message(
request: Request,
project: DbProject,
invitee_username: str,
osm_auth: Auth,
):
"""Send an invitation message to a user to join a project."""
log.info(f"Sending invitation message to osm user ({invitee_username}).")

osm_token = get_osm_token(request, osm_auth)

project_url = f"{settings.FMTM_DOMAIN}/project/{project.id}"
if not project_url.startswith("http"):
project_url = f"http://{project_url}"

message_content = dedent(f"""
You have been invited to join the project **{project.name}**.
Please click this link:
[Project]({project_url})
Thank you for being a part of our platform!
""")

send_osm_message(
osm_token=osm_token,
osm_username=invitee_username,
title=f"You have been invited to join the project {project.name}",
body=message_content,
)
log.info(f"Invitation message sent to osm user ({invitee_username}).")
31 changes: 31 additions & 0 deletions src/backend/app/projects/project_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
Form,
HTTPException,
Query,
Request,
Response,
UploadFile,
)
Expand All @@ -47,6 +48,7 @@

from app.auth.auth_deps import login_required, mapper_login_required
from app.auth.auth_schemas import AuthUser, OrgUserDict, ProjectUserDict
from app.auth.providers.osm import check_osm_user, init_osm_auth
from app.auth.roles import mapper, org_admin, project_manager
from app.central import central_crud, central_deps, central_schemas
from app.config import settings
Expand Down Expand Up @@ -743,6 +745,35 @@ async def add_new_project_manager(
return Response(status_code=HTTPStatus.OK)


@router.post("/invite-new-user")
async def invite_new_user(
request: Request,
background_tasks: BackgroundTasks,
project: Annotated[DbProject, Depends(project_deps.get_project)],
invitee_username: str,
osm_auth=Depends(init_osm_auth),
):
"""Add a new project manager.
The logged in user must be either the admin of the organisation or a super admin.
"""
user_exists = await check_osm_user(invitee_username)

if not user_exists:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="User does not exist on Open Street Map",
)
background_tasks.add_task(
project_crud.send_invitation_message,
request=request,
project=project,
invitee_username=invitee_username,
osm_auth=osm_auth,
)
return Response(status_code=HTTPStatus.OK)


@router.post("/update-form")
async def update_project_form(
xlsform: Annotated[BytesIO, Depends(central_deps.read_xlsform)],
Expand Down

0 comments on commit 90b77e3

Please sign in to comment.