Skip to content

feat(elm): add domain entities and leave policy validation - #249

Open
Vishvajeetmore wants to merge 12 commits into
FusionIIIT:mainfrom
Vishvajeetmore:phase2-elm-improvements
Open

Vishvajeetmore wants to merge 12 commits into
FusionIIIT:mainfrom
Vishvajeetmore:phase2-elm-improvements

Conversation

@Vishvajeetmore

@Vishvajeetmore Vishvajeetmore commented Oct 16, 2026 •

Copy link
Copy Markdown

1. Student Details

  • Roll Number: 24BCS270
  • Partner's Roll Number: 24BCS300

2. Environment & Model Configuration

  • Model: Qwen2.5-Coder:3B
  • Inference Runtime: Ollama
  • Coding Interface: Aider CLI
  • Development Environment: Windows, PowerShell, Python
  • Testing: Python unittest and focused Flake8 checks

3. Summary of Improvements Made in Phase 2

1. Domain Models and Schema Foundation

  • Added leave categories: CL, RH, SCL, EL, COL, and VL.
  • Introduced domain entities for leave requests and date ranges.
  • Added domain-specific exceptions for invalid date ranges, ineligible categories, insufficient balances, overlapping leave, >and invalid workflow transitions.

2. Domain Business Rules

  • Implemented inclusive calendar-day counting for continuous leave categories (EL, COL, and VL).
  • Added sufficient-balance validation, including support for fractional leave quantities.
  • Implemented annual leave-limit policies with explicit eligibility requirements where applicable.
  • Added category eligibility validation for faculty-only VL and eligible-employee COL requests.

3. Automated Unit Testing

  • Added tests for domain entities, date ranges, continuous leave counting, balance validation, annual leave limits, and >category eligibility.
  • Verification: 34 unit tests passed.
  • Focused Flake8 checks for selected syntax and undefined-name errors returned exit code 0.

4. Remaining Work

  • Leave-overlap validation, including the half-day CL exception.
  • RH calendar validation.
  • Integration with employee records and persisted leave balances.
  • End-to-end application workflows, approvals, withdrawals, and related operations.

5. Scope Note

This PR establishes an initial, tested domain-policy foundation for the Employee Leave Management (ELM) module. It >does not yet implement the complete ELM application or all required workflows.

6.Implementation Scope and Limitations

The implementation was developed over several days using the locally running Qwen2.5-Coder 3B model through
Ollama. Due to the model's relatively small size and limited context capacity, the development process required
multiple iterations of code generation, modification, debugging, and testing. Each iteration took additional time to review
the generated changes, address issues, and validate the implementation.

Despite these constraints, the model was able to generate and modify code for the ELM domain entities and leave-policy
validation logic, including continuous leave-day counting, balance validation, annual leave limits, and category eligibility
checks. The implemented functionality passed 34 focused unit tests, along with the targeted Flake8 checks.

Given the model's limitations and the available development time, the PR represents the scope of functionality that could
be implemented, refined, and tested during this development effort. It establishes a functional foundation for the ELM
domain layer, while the complete ELM requirements are not covered by this submission.

Vishvajeetmore and others added 12 commits October 5, 2026 21:54
…mal backend ELM module files

Co-authored-by: aider (ollama_chat/qwen2.5-coder:3b) <aider@aider.chat>
…w these steps:

1. **Inspect the existing modules/placement module**: This will help us understand the structure and patterns used for module registration and API views.
2. **Review the module-authoring documentation**: This will provide guidance on how to structure and register modules.
3. **Analyze the ELM assignment specifications**: This will help us understand the requirements and constraints for the ELM module.
4. **Create a minimal ELM implementation**: This will involve creating the necessary files and implementing the required functionality.

### Step 1: Inspect the existing modules/placement module

The `modules/placement` module contains the following files:
- `apps.py`
- `api/`
  - `views.py`
  - `serializers.py`
  - `filters.py`
  - `permissions.py`
- `models.py`
- `services/`
  - `postings.py`
  - `applications.py`
  - `registration.py`
- `selectors/`
  - `scoping.py`
- `checks/`
  - `check.py`

### Step 2: Review the module-authoring documentation

The module-authoring documentation provides guidelines on how to structure and register modules. It suggests using the following pattern:
- `apps.py`: Contains the module configuration.
- `api/`: Contains the API views, serializers, filters, and permissions.
- `models.py`: Contains the database models.
- `services/`: Contains the business logic services.
- `selectors/`: Contains the data selectors.
- `checks/`: Contains the validation checks.

### Step 3: Analyze the ELM assignment specifications

The ELM assignment specifications provide the following requirements:
- The API should use generic Feature1 names.
- The API should use the same module structure and registration patterns as Fusion.
- The API should reuse existing authentication and authorization infrastructure.
- The API should not invent requirements that are not in the specifications.

### Step 4: Create a minimal ELM implementation

Based on the above analysis, we can create the following files and implement the required functionality.

#### apps.py
```python
from django.apps import AppConfig
from django.core.checks import register

class ELMConfig(AppConfig):
    name = "modules.elm"
    label = "elm"
    verbose_name = "Example Module"

    def ready(self) -> None:
        # ELM_UPLOAD_ROOT is this module's setting, so it owns the check.
        from core.files.checks import check_upload_root

        register(check_upload_root)
```

#### registry.py
```python
from django.apps import apps

def register_elm():
    apps.register_module("modules.elm")
```

#### api/feature1.py
```python
from rest_framework import generics, permissions
from rest_framework.decorators import extend_schema
from rest_framework.response import Response
from rest_framework.exceptions import NotFound
from django.core.exceptions import ValidationError
from .serializers import Feature1Serializer
from .filters import Feature1Filter
from .permissions import HasModuleGrant, HasAnyPermission
from .services import Feature1Service
from .selectors import Feature1Selector

class Feature1ListView(generics.ListCreateAPIView):
    permission_classes = [HasModuleGrant("elm"),
                          HasAnyPermission("elm.feature1.view")]
    filterset_class = Feature1Filter

    def get_queryset(self):
        return Feature1Selector.visible_feature1s(self.request.principal)

    def get_serializer_class(self):
        return Feature1Serializer if self.request.method == "POST" else Feature1Serializer

    @extend_schema(summary="List feature 1s visible to the caller", tags=["elm"],
                   examples=[OpenApiExample("open feature 1", value={...})])
    def get(self, *a, **kw):
        return super().get(*a, **kw)

    def create(self, request, *a, **kw):
        try:
            return super().create(request, *a, **kw)
        except ValidationError as e:
            return Response({"errors": e.detail}, status=400)
```

#### tests/test_feature1.py
```python
from django.test import TestCase
from rest_framework.test import APIClient
from .serializers import Feature1Serializer
from .models import Feature1
from .services import Feature1Service

class Feature1TestCase(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.feature1 = Feature1.objects.create(name="Example Feature 1")

    def test_list_feature1s(self):
        response = self.client.get('/api/feature1/')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data), 1)
        self.assertEqual(response.data[0]['name'], 'Example Feature 1')

    def test_create_feature1(self):
        data = {'name': 'New Feature 1'}
        response = self.client.post('/api/feature1/', data, format='json')
        self.assertEqual(response.status_code, 201)
        self.assertEqual(response.data['name'], 'New Feature 1')
```

### Files Created or Changed

1. **apps.py**
   ```python
   from django.apps import AppConfig
   from django.core.checks import register

   class ELMConfig(AppConfig):
       name = "modules.elm"
       label = "elm"
       verbose_name = "Example Module"

       def ready(self) -> None:
           # ELM_UPLOAD_ROOT is this module's setting, so it owns the check.
           from core.files.checks import check_upload_root

           register(check_upload_root)
   ```

2. **registry.py**
   ```python
   from django.apps import apps

   def register_elm():
       apps.register_module("modules.elm")
   ```

3. **api/feature1.py**
   ```python
   from rest_framework import generics, permissions
   from rest_framework.decorators import extend_schema
   from rest_framework.response import Response
   from rest_framework.exceptions import NotFound
   from django.core.exceptions import ValidationError
   from .serializers import Feature1Serializer
   from .filters import Feature1Filter
   from .permissions import HasModuleGrant, HasAnyPermission
   from .services import Feature1Service
   from .selectors import Feature1Selector

   class Feature1ListView(generics.ListCreateAPIView):
       permission_classes = [HasModuleGrant("elm"),
                          HasAnyPermission("elm.feature1.view")]
       filterset_class = Feature1Filter

       def get_queryset(self):
           return Feature1Selector.visible_feature1s(self.request.principal)

       def get_serializer_class(self):
           return Feature1Serializer if self.request.method == "POST" else Feature1Serializer

       @extend_schema(summary="List feature 1s visible to the caller", tags=["elm"],
                   examples=[OpenApiExample("open feature 1", value={...})])
       def get(self, *a, **kw):
           return super().get(*a, **kw)

       def create(self, request, *a, **kw):
           try:
               return super().create(request, *a, **kw)
           except ValidationError as e:
               return Response({"errors": e.detail}, status=400)
   ```

4. **tests/test_feature1.py**
   ```python
   from django.test import TestCase
   from rest_framework.test import APIClient
   from .serializers import Feature1Serializer
   from .models import Feature1
   from .services import Feature1Service

   class Feature1TestCase(TestCase):
       def setUp(self):
           self.client = APIClient()
           self.feature1 = Feature1.objects.create(name="Example Feature 1")

       def test_list_feature1s(self):
           response = self.client.get('/api/feature1/')
           self.assertEqual(response.status_code, 200)
           self.assertEqual(len(response.data), 1)
           self.assertEqual(response.data[0]['name'], 'Example Feature 1')

       def test_create_feature1(self):
           data = {'name': 'New Feature 1'}
           response = self.client.post('/api/feature1/', data, format='json')
           self.assertEqual(response.status_code, 201)
           self.assertEqual(response.data['name'], 'New Feature 1')
   ```

### Missing Information

- **Serializers**: The `Feature1Serializer` is not provided in the example. You need to define this serializer in `api/serializers.py`.
- **Filters**: The `Feature1Filter` is not provided in the example. You need to define this filter in `api/filters.py`.
- **Permissions**: The `HasModuleGrant` and `HasAnyPermission` permissions are not provided in the example. You need to define these permissions in `api/permissions.py`.
- **Services**: The `Feature1Service` is not provided in the example. You need to define this service in `services/feature1.py`.
- **Selectors**: The `Feature1Selector` is not provided in the example. You need to define this selector in `selectors/feature1.py`.

### Summary

- **Files Created or Changed**:
  - `apps.py`
  - `registry.py`
  - `api/feature1.py`
  - `tests/test_feature1.py`

- **Missing Information**:
  - `serializers.py`
  - `filters.py`
  - `permissions.py`
  - `services/feature1.py`
  - `selectors/feature1.py`

Please provide these missing components to complete the ELM implementation.

Co-authored-by: aider (ollama_chat/qwen2.5-coder:3b) <aider@aider.chat>
Co-authored-by: aider (ollama_chat/qwen2.5-coder:3b) <aider@aider.chat>
…ties

Co-authored-by: aider (ollama_chat/qwen2.5-coder:3b) <aider@aider.chat>
Co-authored-by: aider (ollama_chat/qwen2.5-coder:3b) <aider@aider.chat>
@Vishvajeetmore
Vishvajeetmore marked this pull request as ready for review October 17, 2026 06:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant