Projects module

Manages all project-related operations, including creation, editing, viewing, member management, and change logging. Access is session- and role-controlled.

🧭 My Projects View

🔧 Workflow

GET Request (/my-projects)

Returns all active projects the current user is assigned to.

projects = request.dbsession.query(Project)\
    .join(ProjectsUser)\
    .filter(ProjectsUser.user_id == request.session.get('user_id'), Project.active == True)\
    .all()

➕ Create Project

🔧 Workflow

GET Request (/create-project)

Renders the project creation form. Restricted to admin users.

POST Request (/create-project)

Processes form input for project creation. Validates form data, creates the project, assigns the creator as project manager, and logs the creation.

new_project = Project(name=name, description=description, creation_datetime=datetime.now())
request.dbsession.add(new_project)
request.dbsession.flush()
request.dbsession.add(ProjectsUser(project_id=new_project.id, user_id=user_id, role="project_manager"))

📝 Activity Log

ActivityLog(
    user_id=request.session['user_id'],
    project_id=new_project.id,
    action='project_added',
    changes=f"{new_project.__repr__()}"
)

📂 Project Detail View

🔧 Workflow

GET Request (/project/{id})

Displays a single project view. Includes all tasks grouped by status and the list of members with their roles.

tasks_by_status = {
    status: request.dbsession.query(Task)
        .filter_by(project_id=project_id, status=status, active=True)
        .order_by(Task.due_date)
        .all()
    for status in ['assigned', 'in_progress', 'under_review', 'completed']
}

Access is validated via the ProjectsUser relationship.


✏️ Edit Project

🔧 Workflow

POST Request (/edit-project/{id})

Admins can update the project’s name and description. Changes are compared against current values and logged individually.

if project.name != new_name:
    ActivityLog(action='project_edited_title', changes='old: ... new: ...')

🗑️ Delete Project

🔧 Workflow

GET Request (/delete-project/{id})

Performs a soft delete by setting project.active = False. Only admins can delete. Change is logged.

project.active = False
ActivityLog(action='project_removed', changes='Project: XYZ set to inactive')

👥 Project Members

🔧 Search Users

GET Request (/search-users)

Performs a username search. If project_id is passed, filters out users already in the project.

query = query.filter(~User.id.in_(subquery))

➕ Add Member

POST Request (/add-member/{id})

Adds a user to the project with role "member". Admin-only. Logs the operation.

ProjectsUser(project_id=..., user_id=..., role="member")
ActivityLog(action='project_added_user', changes='User X added to Project Y')

➖ Remove Member

POST Request (/remove-member/{id})

Removes a user from the project. Admin-only. Logs the action.

request.dbsession.delete(relation)
ActivityLog(action='project_removed_user', changes='User removed from Project')

🔄 Update Task Status

🔧 Workflow

POST Request (/update-task-status)

Expects JSON data. Changes the status of a task. Does not reload the page.

task.status = new_status

🗂️ Kanban Partial

🔧 Workflow

GET Request (/kanban-partial/{id})

Returns an HTML fragment with grouped tasks by status. Used for async kanban updates.

tasks_by_status = {
    status: request.dbsession.query(Task)
        .filter_by(project_id=project_id, status=status)
        .order_by(Task.due_date)
        .all()
}

✅ Projects Module Validations

1. Authentication & Permissions

  • Session Validation

    • All routes require an active, valid session via @verify_session.

    • Ensures that only authenticated users can interact with the project system.

  • Admin Permission Checks

    • Project creation, deletion, editing, and member management require "admin" permission.

    • Enforced via Pyramid’s @view_config(permission="admin").


2. Access Control

  • Membership Validation

    • For views like /project/{id}, the system checks if the requesting user is a member of the project:

      project = request.dbsession.query(Project).join(ProjectsUser)...filter(ProjectsUser.user_id == session_user)
    • Prevents non-members from accessing project details.


3. Project Data Validation

  • Name and Description Non-Empty Check

    • On project creation and editing, the form input is validated to ensure:

      • name is not empty or whitespace.

      • description is provided.

      • Usually checked server-side before committing changes.

  • Edit Check (Change Detection)

    • Edit operations log only actual changes. Old vs. new values are compared before saving and logging.


4. Project Deletion Protection

  • Soft Delete

    • Deletion only sets project.active = False.

    • Prevents data loss and allows historical audits.


5. Member Management Validation

  • Duplicate Member Check

    • Before adding a user, the system checks that the user is not already assigned to the project:

      exists = request.dbsession.query(ProjectsUser)...filter_by(user_id=user_id, project_id=project_id).first()
  • Valid User ID

    • When adding or removing users, the user ID is validated:

      • Must exist.

      • Must not conflict with existing records or permissions.


6. Task Status Update Validations

  • Valid Task ID and Project Match

    • When updating task status:

      • Ensures that the task exists.

      • The task belongs to the project the user has access to.

      • Prevents tampering via direct API calls.

  • Valid Status Enum

    • Only allows updates to known statuses:

      • 'assigned', 'in_progress', 'under_review', 'completed'.

      • Invalid statuses are rejected.


7. User Search Filtering

  • Duplicate Filter in Search

    • When using /search-users?project_id=XYZ, it excludes users already assigned to the project to prevent duplicates:

      query = query.filter(~User.id.in_(subquery))

8.All POST requests contain CSRF protection

Last updated