# Projects module

### 🧭 My Projects View

#### 🔧 Workflow

**GET Request (`/my-projects`)**

Returns all active projects the current user is assigned to.

```python
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.

```python
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**

```python
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.

```python
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.

```python
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.

```python
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.

```python
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.

```python
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.

```python
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.

```python
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.

```python
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:

    ```python
    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:

    ```python
    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:

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

**8.All POST requests contain CSRF protection**
