Skip to content

Commit

Permalink
Convert Reusable Code to Widgets & Refactor Event/Organizer Models (#237
Browse files Browse the repository at this point in the history
)

* Convert event editor user lookup to a reusable widget
This converts the event editor user lookup form field to a widget so it
can be used throughout the application.

Additional Changes to Be Made: Create a new model to represent public
users/info so the form field can be used for ANY user.

* Create new `PublicUser` model
This commit creates a PublicUser model to represent public information
about a user. This is surfaced on the frontend as "PublicProfile" to
match referencing the User as "Profile".

This PublicUser model replaces the EventOrganizer and EventMember models.

* Remove attendees from Event model
This commit removes the list of attendees from the Event model to avoid
exposing the attendee information when retrieving events. This list is
never actually used, so it does not impact the code.

* Create user chip list widget
This commit turns the user chip list on the event details card into a
widget so it can be reused to display user information. It also adds
an "enableMailTo" field so users may be listed without allowing other
users to email them.
  • Loading branch information
jadekeegan authored Jan 5, 2024
1 parent 1f2da54 commit debb6a1
Show file tree
Hide file tree
Showing 24 changed files with 301 additions and 262 deletions.
8 changes: 4 additions & 4 deletions backend/api/events/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from fastapi import APIRouter, Depends, HTTPException
from datetime import datetime, timedelta
from typing import Sequence
from backend.models.event_member import EventMember
from backend.models.public_user import PublicUser
from backend.models.pagination import Paginated, PaginationParams

from backend.services.organization import OrganizationService
Expand Down Expand Up @@ -284,7 +284,7 @@ def register_for_event(
subject: User = Depends(registered_user),
event_service: EventService = Depends(),
user_service: UserService = Depends(),
) -> EventMember:
) -> PublicUser:
"""
Register a user event based on the event ID.
Expand Down Expand Up @@ -315,7 +315,7 @@ def get_event_registration_of_user(
event_id: int,
subject: User = Depends(registered_user),
event_service: EventService = Depends(),
) -> EventMember:
) -> PublicUser:
"""
Check the registration status of a user for an event, raise ResourceNotFound if unregistered.
Expand All @@ -337,7 +337,7 @@ def get_event_registrations(
event_id: int,
subject: User = Depends(registered_user),
event_service: EventService = Depends(),
) -> Sequence[EventMember]:
) -> Sequence[PublicUser]:
"""
Get the registrations of an event.
Expand Down
4 changes: 1 addition & 3 deletions backend/entities/event_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def to_model(self, subject: User | None = None) -> Event:

# Hide organizer info for unauthenticated users
organizers = [
registration.to_flat_organizer_model()
registration.to_flat_model()
for registration in self.registrations
if registration.registration_type == RegistrationType.ORGANIZER
]
Expand All @@ -134,7 +134,6 @@ def to_model(self, subject: User | None = None) -> Event:
organization_id=self.organization_id,
registration_count=len(attendees),
is_attendee=is_attendee,
attendees=attendees,
is_organizer=is_organizer,
organizers=organizers,
)
Expand All @@ -160,7 +159,6 @@ def to_details_model(self, subject: User | None = None) -> EventDetails:
organization_id=self.organization_id,
organization=self.organization.to_model(),
is_attendee=event.is_attendee,
attendees=event.attendees,
is_organizer=event.is_organizer,
organizers=event.organizers,
)
39 changes: 15 additions & 24 deletions backend/entities/event_registration_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship

from backend.models.event_member import EventOrganizer
from backend.entities.event_entity import EventEntity
from backend.entities.user_entity import UserEntity

from ..models import RegistrationType, EventMember
from ..models import RegistrationType
from ..models.public_user import PublicUser
from .entity_base import EntityBase
from typing import Self
from ..models.event_registration import EventRegistration, NewEventRegistration
Expand Down Expand Up @@ -58,8 +60,8 @@ def from_model(cls, model: EventRegistration) -> Self:
return cls(
event_id=model.event_id,
user_id=model.user_id,
event=model.event,
user=model.user,
event=EventEntity.from_model(model.event),
user=UserEntity.from_model(model.user),
registration_type=model.registration_type,
)

Expand All @@ -79,7 +81,7 @@ def from_new_model(cls, model: NewEventRegistration) -> Self:
registration_type=model.registration_type,
)

def to__model(self) -> EventRegistration:
def to_model(self) -> EventRegistration:
"""
Converts an `EventRegistrationEntity` into an `EventRegistration` model object
to store registration information.
Expand All @@ -89,36 +91,25 @@ def to__model(self) -> EventRegistration:
"""
return EventRegistration(
event_id=self.event_id,
event=self.event,
event=self.event.to_model(),
user_id=self.user_id,
user=self.user,
user=self.user.to_model(),
registration_type=self.registration_type,
)

def to_flat_model(self) -> EventMember:
def to_flat_model(self) -> PublicUser:
"""
Converts an `EventRegistrationEntity` into an `EventMember` model object
to store user ID.
Converts an `EventRegistrationEntity` into an `PublicUser` model object
to store public user information.
Returns:
EventMember: `EventMember` object from the entity
PublicUser: `PublicUser` object from the entity
"""
return EventMember(id=self.user_id, registration_type=self.registration_type)

def to_flat_organizer_model(self) -> EventMember:
"""
Converts an `EventRegistrationEntity` into an `EventMember` model object
to store user ID.
Returns:
EventMember: `EventMember` object from the entity
"""
return EventOrganizer(
return PublicUser(
id=self.user_id,
registration_type=self.registration_type,
first_name=self.user.first_name,
last_name=self.user.last_name,
pronouns=self.user.pronouns,
email=self.user.email,
github_avatar=self.user.github_avatar
github_avatar=self.user.github_avatar,
)
2 changes: 1 addition & 1 deletion backend/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .role_details import RoleDetails
from .organization import Organization
from .event import Event
from .event_member import EventMember
from .public_user import PublicUser
from .event_details import EventDetails
from .room import Room
from .room_details import RoomDetails
Expand Down
5 changes: 2 additions & 3 deletions backend/models/event.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pydantic import BaseModel
from datetime import datetime

from .event_member import EventMember, EventOrganizer
from .public_user import PublicUser

__authors__ = ["Ajay Gandecha", "Jade Keegan", "Brianna Ta", "Audrey Toney"]
__copyright__ = "Copyright 2023"
Expand All @@ -23,7 +23,7 @@ class DraftEvent(BaseModel):
public: bool
registration_limit: int
organization_id: int
organizers: list[EventOrganizer] = []
organizers: list[PublicUser] = []


class Event(DraftEvent):
Expand All @@ -37,5 +37,4 @@ class Event(DraftEvent):
id: int
registration_count: int = 0
is_attendee: bool = False
attendees: list[EventMember] = []
is_organizer: bool = False
36 changes: 0 additions & 36 deletions backend/models/event_member.py

This file was deleted.

24 changes: 24 additions & 0 deletions backend/models/public_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from pydantic import BaseModel
from .registration_type import RegistrationType


__authors__ = ["Ajay Gandecha"]
__copyright__ = "Copyright 2023"
__license__ = "MIT"


class PublicUser(BaseModel):
"""
Pydantic model to represent public information about users to avoid
exposing sensitive information about them.
This model is based on the `UserEntity` model, which defines the shape
of the `User` database in the PostgreSQL database
"""

id: int | None
first_name: str
last_name: str
pronouns: str
email: str
github_avatar: str | None = None
31 changes: 17 additions & 14 deletions backend/services/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
from sqlalchemy import func, select, or_
from sqlalchemy.orm import Session, aliased
from backend.entities.user_entity import UserEntity
from backend.models.event_member import EventMember
from backend.models.event_registration import EventRegistration
from ..models.public_user import PublicUser
from backend.models.organization_details import OrganizationDetails
from backend.models.pagination import Paginated, PaginationParams
from backend.models.registration_type import RegistrationType
Expand Down Expand Up @@ -296,7 +297,7 @@ def delete(self, subject: User, id: int) -> None:

def get_registration(
self, subject: User, attendee: User, event: EventDetails
) -> EventMember | None:
) -> EventRegistration | None:
"""
Get a registration of an attendee for an Event.
Expand All @@ -306,7 +307,7 @@ def get_registration(
event: EventDetails of the event seeking registration for
Returns:
EventMember or None if no registration found
PublicUser or None if no registration found
Raises:
UserPermissionException if subject does not have permission
Expand All @@ -329,13 +330,13 @@ def get_registration(

# Return EventRegistration model or None
if event_registration_entity is not None:
return event_registration_entity.to_flat_model()
return event_registration_entity.to_model()
else:
return None

def get_registrations_of_event(
self, subject: User, event: EventDetails
) -> list[EventMember]:
) -> list[PublicUser]:
"""
List the registrations of an event.
Expand All @@ -348,7 +349,7 @@ def get_registrations_of_event(
event: The event whose registrations are being queried.
Returns:
list[EventMember]
list[PublicUser]
Raises:
UserPermissionException if user is not an event organizer or admin.
Expand All @@ -370,7 +371,7 @@ def get_registrations_of_event(

def set_event_organizer(
self, subject: User, user_id: int, event: EventDetails
) -> EventMember:
) -> PublicUser:
"""
Set the organizer of an event.
Expand All @@ -379,7 +380,7 @@ def set_event_organizer(
event: The EventDetails being registered for
Returns:
EventMember
PublicUser
"""

Expand All @@ -400,11 +401,11 @@ def set_event_organizer(
self._session.commit()

# Return registration
return event_registration_entity.to_flat_organizer_model()
return event_registration_entity.to_flat_model()

def register(
self, subject: User, attendee: User, event: EventDetails
) -> EventMember:
) -> PublicUser:
"""
Register a user for an event.
Expand All @@ -414,7 +415,7 @@ def register(
event: The EventDetails being registered for
Returns:
EventMember
PublicUser
Raises:
UserPermissionException if subject does not have permission to register user
Expand All @@ -440,7 +441,9 @@ def register(
# Permission to manage / read registration is enforced in EventService#get_registration
existing_registration = self.get_registration(subject, attendee, event)
if existing_registration:
return existing_registration
return EventRegistrationEntity.from_model(
existing_registration
).to_flat_model()

# Add new object to table and commit changes
event_registration_entity = EventRegistrationEntity(
Expand Down Expand Up @@ -492,7 +495,7 @@ def unregister(self, subject: User, attendee: User, event: EventDetails) -> None

def get_registrations_of_user(
self, subject: User, user: User, time_range: TimeRange
) -> Sequence[EventMember]:
) -> Sequence[PublicUser]:
"""
Get a user's registrations to events falling within a given time range.
Expand All @@ -502,7 +505,7 @@ def get_registrations_of_user(
time_range: The period over which to search for event registrations.
Returns:
Sequence[EventMember] event registrations
Sequence[PublicUser] event registrations
Raises:
UserPermissionException when the user is requesting the registrations
Expand Down
3 changes: 0 additions & 3 deletions backend/test/services/event/event_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import pytest
from unittest.mock import create_autospec
from backend.models.pagination import PaginationParams
from backend.models.registration_type import RegistrationType

from backend.services.exceptions import (
EventRegistrationException,
Expand Down Expand Up @@ -114,7 +113,6 @@ def test_create_event_as_root(event_svc_integration: EventService):
assert created_event.organizers[0].id == root.id
assert created_event.is_organizer == True

assert len(created_event.attendees) == 0
assert created_event.is_attendee == False


Expand Down Expand Up @@ -270,7 +268,6 @@ def test_register_for_event_as_user(event_svc_integration: EventService):
event_details = event_svc_integration.get_by_id(event_one.id, root) # type: ignore
created_registration = event_svc_integration.register(root, root, event_details) # type: ignore
assert created_registration is not None
assert created_registration.registration_type == RegistrationType.ATTENDEE


def test_register_for_event_as_user_twice(event_svc_integration: EventService):
Expand Down
Loading

0 comments on commit debb6a1

Please sign in to comment.