-
Notifications
You must be signed in to change notification settings - Fork 0
[US-9.1 / OBT-216] Include team_size in project data #95
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
base: main
Are you sure you want to change the base?
Changes from all commits
fd8c434
4db31f5
7c1e04a
7a498f2
46221ce
9770654
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| from sqlalchemy import distinct, func, select | ||
| from sqlalchemy.ext.asyncio import AsyncSession | ||
|
|
||
| from app.db.models.project import ProjectUserAccess | ||
|
|
||
|
|
||
| async def count_project_team_sizes(db: AsyncSession, project_ids: list[str]) -> dict[str, int]: | ||
| if not project_ids: | ||
| return {} | ||
| stmt = ( | ||
| select( | ||
| ProjectUserAccess.project_id, | ||
| func.count(distinct(ProjectUserAccess.user_id)), | ||
| ) | ||
| .where(ProjectUserAccess.project_id.in_(project_ids)) | ||
| .group_by(ProjectUserAccess.project_id) | ||
| ) | ||
| result = await db.execute(stmt) | ||
| counts: dict[str, int] = {row[0]: row[1] for row in result.all()} | ||
| return {project_id: counts.get(project_id, 0) for project_id in project_ids} |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,18 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from sqlalchemy.ext.asyncio import AsyncSession | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.db.models.project import Project | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.models.project import ProjectResponse | ||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.services.project.count_project_team_sizes import count_project_team_sizes | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| async def serialize_projects(db: AsyncSession, projects: list[Project]) -> list[ProjectResponse]: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| team_sizes = await count_project_team_sizes(db, [p.id for p in projects]) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return [ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ProjectResponse.model_validate(p).model_copy(update={"team_size": team_sizes.get(p.id, 0)}) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| for p in projects | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| async def serialize_project(db: AsyncSession, project: Project) -> ProjectResponse: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| (response,) = await serialize_projects(db, [project]) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return response | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+8
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win Add concise docstrings to the exported service functions.
As per coding guidelines, Suggested documentation async def serialize_projects(db: AsyncSession, projects: list[Project]) -> list[ProjectResponse]:
+ """Serialize projects and attach distinct direct-access team sizes."""
team_sizes = await count_project_team_sizes(db, [p.id for p in projects])
async def serialize_project(db: AsyncSession, project: Project) -> ProjectResponse:
+ """Serialize one project and attach its distinct direct-access team size."""
(response,) = await serialize_projects(db, [project])📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Coding guidelines |
||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import pytest | ||
|
|
||
| from app.services import project_service | ||
| from tests.baker import ( | ||
| make_language, | ||
| make_project, | ||
| make_project_user_access, | ||
| make_user, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_count_team_sizes_counts_distinct_users(db_session) -> None: | ||
| lang = await make_language(db_session, code="tsa") | ||
| project = await make_project(db_session, language_id=lang.id) | ||
| u1 = await make_user(db_session, email="[email protected]") | ||
| u2 = await make_user(db_session, email="[email protected]") | ||
| await make_project_user_access(db_session, project.id, u1.id) | ||
| await make_project_user_access(db_session, project.id, u2.id) | ||
|
|
||
| sizes = await project_service.count_project_team_sizes(db_session, [project.id]) | ||
| assert sizes == {project.id: 2} | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_count_team_sizes_zero_for_project_without_access(db_session) -> None: | ||
| lang = await make_language(db_session, code="tsb") | ||
| project = await make_project(db_session, language_id=lang.id) | ||
|
|
||
| sizes = await project_service.count_project_team_sizes(db_session, [project.id]) | ||
| assert sizes == {project.id: 0} | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_count_team_sizes_isolates_projects(db_session) -> None: | ||
| lang = await make_language(db_session, code="tsc") | ||
| p1 = await make_project(db_session, language_id=lang.id, name="P1") | ||
| p2 = await make_project(db_session, language_id=lang.id, name="P2") | ||
| u1 = await make_user(db_session, email="[email protected]") | ||
| u2 = await make_user(db_session, email="[email protected]") | ||
| await make_project_user_access(db_session, p1.id, u1.id) | ||
| await make_project_user_access(db_session, p1.id, u2.id) | ||
| await make_project_user_access(db_session, p2.id, u1.id) | ||
|
|
||
| sizes = await project_service.count_project_team_sizes(db_session, [p1.id, p2.id]) | ||
| assert sizes == {p1.id: 2, p2.id: 1} | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_count_team_sizes_empty_input(db_session) -> None: | ||
| assert await project_service.count_project_team_sizes(db_session, []) == {} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All the tests are on |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Every other file here has one public function named after the file (
create_project.py,count_project_team_sizes.py, and so on). This one has two and the name matches neither. Please verify that on the folder and if it is really the majority, split it and adapt the__init__.