Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesign the Profile Page, Add Public Profiles #547

Merged
merged 10 commits into from
Jul 23, 2024
6 changes: 6 additions & 0 deletions backend/api/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ def update_profile(
email=profile.email,
pronouns=profile.pronouns,
accepted_community_agreement=profile.accepted_community_agreement,
bio=profile.bio,
linkedin=profile.linkedin,
website=profile.website,
)
user = user_svc.create(user, user)
else:
Expand All @@ -75,6 +78,9 @@ def update_profile(
user.pronouns = profile.pronouns
user.onyen = onyen
user.accepted_community_agreement = profile.accepted_community_agreement
user.bio = profile.bio
user.linkedin = profile.linkedin
user.website = profile.website
user = user_svc.update(user, user)

user_details = user_svc.get(user.pid)
Expand Down
10 changes: 10 additions & 0 deletions backend/api/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,13 @@ def search(
):
"""Search for users based on a query string which matches against name, onyen, and email address."""
return user_svc.search(subject, q)


@api.get("/{onyen}", tags=["Users"])
def get_by_onyen(
onyen: str,
subject: User = Depends(registered_user),
user_svc: UserService = Depends(),
):
"""Search for one user by their onyen"""
return user_svc.get_by_onyen(subject, onyen)
5 changes: 5 additions & 0 deletions backend/entities/event_registration_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,14 @@ def to_flat_model(self) -> PublicUser:
"""
return PublicUser(
id=self.user_id,
onyen=self.user.onyen,
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=self.user.github,
bio=self.user.bio,
linkedin=self.user.linkedin,
website=self.user.website,
)
20 changes: 20 additions & 0 deletions backend/entities/user_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ class UserEntity(EntityBase):
accepted_community_agreement: Mapped[bool] = mapped_column(
Boolean, nullable=False, default=False
)
# Bio of the user
bio: Mapped[str | None] = mapped_column(String(), nullable=True)
# LinkedIn URL for the user
linkedin: Mapped[str | None] = mapped_column(String(), nullable=True)
# Website of the user
website: Mapped[str | None] = mapped_column(String(), nullable=True)

# All of the roles for the given user.
# NOTE: This field establishes a many-to-many relationship between the users and roles table.
Expand Down Expand Up @@ -92,6 +98,9 @@ def from_model(cls, model: User) -> Self:
github_id=model.github_id,
github_avatar=model.github_avatar,
accepted_community_agreement=model.accepted_community_agreement,
bio=model.bio,
linkedin=model.linkedin,
website=model.website,
)

def to_model(self) -> User:
Expand All @@ -113,6 +122,9 @@ def to_model(self) -> User:
github_avatar=self.github_avatar,
pronouns=self.pronouns,
accepted_community_agreement=self.accepted_community_agreement,
bio=self.bio,
linkedin=self.linkedin,
website=self.website,
)

def update(self, model: User) -> None:
Expand All @@ -133,13 +145,21 @@ def update(self, model: User) -> None:
self.github_id = model.github_id or None
self.github_avatar = model.github_avatar or ""
self.accepted_community_agreement = model.accepted_community_agreement
self.bio = model.bio
self.linkedin = model.linkedin
self.website = model.website

def to_public_model(self) -> PublicUser:
return PublicUser(
id=self.id,
onyen=self.onyen,
first_name=self.first_name,
last_name=self.last_name,
pronouns=self.pronouns,
email=self.email,
github_avatar=self.github_avatar,
github=self.github,
bio=self.bio,
linkedin=self.linkedin,
website=self.website,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Migration for 484-profile-page-m3

Revision ID: 3da4a0776787
Revises: 909fe23fc93f
Create Date: 2024-07-23 18:32:25.724472
"""

from alembic import op
import sqlalchemy as sa


revision = "3da4a0776787"
down_revision = "909fe23fc93f"
branch_labels = None
depends_on = None


def upgrade() -> None:
op.add_column(
"user", sa.Column("bio", sa.String(), nullable=False, server_default="")
)
op.add_column(
"user", sa.Column("linkedin", sa.String(), nullable=False, server_default="")
)
op.add_column(
"user", sa.Column("website", sa.String(), nullable=False, server_default="")
)


def downgrade() -> None:
op.drop_column("user", "website")
op.drop_column("user", "linkedin")
op.drop_column("user", "bio")
5 changes: 5 additions & 0 deletions backend/models/public_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ class PublicUser(BaseModel):
"""

id: int | None
onyen: str
first_name: str
last_name: str
pronouns: str
email: str
github_avatar: str | None = None
github: str | None = None
bio: str | None = None
linkedin: str | None = None
website: str | None = None
6 changes: 6 additions & 0 deletions backend/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class User(UserIdentity, BaseModel):
github_id: int | None = None
github_avatar: str | None = None
accepted_community_agreement: bool = False
bio: str | None = None
linkedin: str | None = None
website: str | None = None


class NewUser(User, BaseModel):
Expand Down Expand Up @@ -63,3 +66,6 @@ class ProfileForm(BaseModel):
pronouns: str
email: str
accepted_community_agreement: bool = False
bio: str | None = None
linkedin: str | None = None
website: str | None = None
23 changes: 21 additions & 2 deletions backend/services/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from sqlalchemy import select, or_, func, cast, String
from sqlalchemy.orm import Session
from ..database import db_session
from ..models import User, UserDetails, Paginated, PaginationParams
from ..models import User, UserDetails, Paginated, PaginationParams, PublicUser
from ..entities import UserEntity
from .exceptions import ResourceNotFoundException
from .permission import PermissionService
Expand Down Expand Up @@ -68,6 +68,25 @@ def get_by_id(self, id: int) -> User:

return user_entity.to_model()

def get_by_onyen(self, subject: User, onyen: str) -> PublicUser:
"""Get a User by their onyen.

Args:
onyen: The onyen of the user.

Returns:
PublicUser

Raises:
ResourceNotFoundException if the User ID is not found
"""
user_query = select(UserEntity).where(UserEntity.onyen == onyen)
user_entity = self._session.scalars(user_query).one_or_none()
if user_entity is None:
raise ResourceNotFoundException(f"User with {id} not found")

return user_entity.to_public_model()

def search(self, _subject: User, query: str) -> list[User]:
"""Search for users by their name, onyen, email.

Expand All @@ -84,7 +103,7 @@ def search(self, _subject: User, query: str) -> list[User]:
UserEntity.last_name.ilike(f"%{query}%"),
UserEntity.onyen.ilike(f"%{query}%"),
UserEntity.email.ilike(f"%{query}%"),
cast(UserEntity.pid, String).ilike(f"%{query}%")
cast(UserEntity.pid, String).ilike(f"%{query}%"),
)
statement = statement.where(criteria).limit(10)
entities = self._session.execute(statement).scalars()
Expand Down
24 changes: 24 additions & 0 deletions backend/test/services/event/event_test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,13 @@
organizers=[
PublicUser(
id=root.id,
onyen=root.onyen,
first_name=root.first_name,
last_name=root.last_name,
pronouns=root.pronouns,
email=root.email,
linkedin=root.linkedin,
website=root.website,
)
],
)
Expand Down Expand Up @@ -98,10 +101,13 @@
organizers=[
PublicUser(
id=user.id,
onyen=user.onyen,
first_name=user.first_name,
last_name=user.last_name,
pronouns=user.pronouns,
email=user.email,
linkedin=user.linkedin,
website=user.website,
),
],
)
Expand All @@ -118,17 +124,23 @@
organizers=[
PublicUser(
id=user.id,
onyen=user.onyen,
first_name=user.first_name,
last_name=user.last_name,
pronouns=user.pronouns,
email=user.email,
linkedin=user.linkedin,
website=user.website,
),
PublicUser(
id=ambassador.id,
onyen=ambassador.onyen,
first_name=ambassador.first_name,
last_name=ambassador.last_name,
pronouns=ambassador.pronouns,
email=ambassador.email,
linkedin=ambassador.linkedin,
website=ambassador.website,
),
],
)
Expand Down Expand Up @@ -156,24 +168,33 @@
organizers=[
PublicUser(
id=user.id,
onyen=user.onyen,
first_name=user.first_name,
last_name=user.last_name,
pronouns=user.pronouns,
email=user.email,
linkedin=user.linkedin,
website=user.website,
),
PublicUser(
id=ambassador.id,
onyen=user.onyen,
first_name=ambassador.first_name,
last_name=ambassador.last_name,
pronouns=ambassador.pronouns,
email=ambassador.email,
linkedin=ambassador.linkedin,
website=ambassador.website,
),
PublicUser(
id=root.id,
onyen=user.onyen,
first_name=root.first_name,
last_name=root.last_name,
pronouns=root.pronouns,
email=root.email,
linkedin=root.linkedin,
website=root.website,
),
],
)
Expand All @@ -190,10 +211,13 @@
organizers=[
PublicUser(
id=user.id,
onyen=user.onyen,
first_name=user.first_name,
last_name=user.last_name,
pronouns=user.pronouns,
email=user.email,
linkedin=user.linkedin,
website=user.website,
),
],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,16 @@ export class SectionEditorComponent {
.map((staff) => {
return {
id: staff.user_id!,
onyen: '',
first_name: staff.first_name,
last_name: staff.last_name,
pronouns: '',
email: '',
github_avatar: ''
github_avatar: '',
github: '',
bio: '',
linkedin: '',
website: ''
};
}) ?? [];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,23 @@
<mat-card-content>
<mat-chip-set>
<!-- Organization chip -->
<mat-chip
<mat-chip-row
id="organization-chip"
[routerLink]="'/organizations/' + event.organization_slug">
<img
class="profile-icon"
matChipAvatar
[src]="event.organization_icon" />
{{ event.organization_name }}
</mat-chip>
</mat-chip-row>

@if (event.organizers.length > 0) {
<mat-divider class="vertical-divider" [vertical]="true" />
}

<!-- Organizer Chips -->
@for (organizer of event.organizers; track organizer.id) {
<mat-chip>
<mat-chip-row [routerLink]="'/profile/' + organizer.onyen">
@if (organizer.github_avatar && organizer.github_avatar !== '') {
<img
class="profile-icon"
Expand All @@ -56,7 +56,7 @@
}

{{ organizer.first_name }} {{ organizer.last_name }}
</mat-chip>
</mat-chip-row>
}
</mat-chip-set>
<p class="description">
Expand Down
Loading