Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .coverage
Binary file not shown.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
**/__pycache__
*.pyc
venv
.DS_Store
.env
33 changes: 33 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.PHONY: test coverage clean run test-clean

PYTHON = python
COVERAGE = coverage

test-clean:
rm -f profiles/migrations/0002_copy_data_from_old_models.py
$(PYTHON) manage.py test lettings oc_lettings_site profiles

test:
$(PYTHON) manage.py test lettings oc_lettings_site profiles

test-coverage:
$(COVERAGE) run --source='.' manage.py test lettings oc_lettings_site profiles
$(COVERAGE) report
$(COVERAGE) html

run:
$(PYTHON) manage.py runserver

clean:
find . -type d -name "__pycache__" -exec rm -r {} +
find . -type f -name "*.pyc" -delete
find . -type f -name "*.pyo" -delete
find . -type f -name "*.pyd" -delete
find . -type f -name ".coverage" -delete
find . -type d -name "*.egg-info" -exec rm -r {} +
find . -type d -name "*.egg" -exec rm -r {} +
find . -type d -name "htmlcov" -exec rm -r {} +
find . -type d -name ".pytest_cache" -exec rm -r {} +
find . -type d -name ".coverage" -exec rm -r {} +

all: test-clean
Empty file added lettings/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions lettings/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.contrib import admin
from .models import Letting, Address

# Register your models here.
admin.site.register(Letting)
admin.site.register(Address)
6 changes: 6 additions & 0 deletions lettings/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class LettingsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'lettings'
36 changes: 36 additions & 0 deletions lettings/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Generated by Django 4.2.11 on 2025-05-16 11:59

import django.core.validators
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Address',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('number', models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(9999)])),
('street', models.CharField(max_length=64)),
('city', models.CharField(max_length=64)),
('state', models.CharField(max_length=2, validators=[django.core.validators.MinLengthValidator(2)])),
('zip_code', models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(99999)])),
('country_iso_code', models.CharField(max_length=3, validators=[django.core.validators.MinLengthValidator(3)])),
],
),
migrations.CreateModel(
name='Letting',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=256)),
('address', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='lettings.address')),
],
),
]
36 changes: 36 additions & 0 deletions lettings/migrations/0002_copy_data_from_old_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from django.db import migrations

def copy_lettings_and_addresses(apps, schema_editor):
OldAddress = apps.get_model('oc_lettings_site', 'Address')
OldLetting = apps.get_model('oc_lettings_site', 'Letting')
NewAddress = apps.get_model('lettings', 'Address')
NewLetting = apps.get_model('lettings', 'Letting')

old_to_new_address = {}
for old_addr in OldAddress.objects.all():
new_addr = NewAddress.objects.create(
number=old_addr.number,
street=old_addr.street,
city=old_addr.city,
state=old_addr.state,
zip_code=old_addr.zip_code,
country_iso_code=old_addr.country_iso_code,
)
old_to_new_address[old_addr.id] = new_addr

# Copier les lettings
for old_letting in OldLetting.objects.all():
NewLetting.objects.create(
title=old_letting.title,
address=old_to_new_address.get(old_letting.address_id)
)

class Migration(migrations.Migration):
dependencies = [
('lettings', '0001_initial'),
('oc_lettings_site', '__first__'),
]

operations = [
migrations.RunPython(copy_lettings_and_addresses),
]
Empty file added lettings/migrations/__init__.py
Empty file.
45 changes: 45 additions & 0 deletions lettings/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from django.db import models
from django.core.validators import MaxValueValidator, MinLengthValidator


class Address(models.Model):
"""
Model representing a physical address.
This model stores :
number, street, city, state, zip code, and country code.
"""
number = models.PositiveIntegerField(validators=[MaxValueValidator(9999)])
street = models.CharField(max_length=64)
city = models.CharField(max_length=64)
state = models.CharField(max_length=2, validators=[MinLengthValidator(2)])
zip_code = models.PositiveIntegerField(validators=[MaxValueValidator(99999)])
country_iso_code = models.CharField(max_length=3, validators=[MinLengthValidator(3)])

class Meta:
verbose_name_plural = "Addresses"

def __str__(self):
"""
Returns a string representation of the address.
Returns:
str: The address in the format 'number street'
"""
return f'{self.number} {self.street}'


class Letting(models.Model):
"""
Model representing a property letting.
This model stores title and associated addresses.
"""
title = models.CharField(max_length=256)
address = models.OneToOneField(Address, on_delete=models.CASCADE)

class Meta:
verbose_name_plural = "Lettings"

def __str__(self):
"""
Returns the title of the letting.
"""
return self.title
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ <h1 class="page-header-ui-title mb-3 display-6">Lettings</h1>
<ul class="list-group list-group-flush list-group-careers">
{% for letting in lettings_list %}
<li class="list-group-item">
<a href="{% url 'letting' letting_id=letting.id %}">{{ letting.title }}</a>
<a href="{% url 'lettings:letting' letting_id=letting.id %}">{{ letting.title }}</a>
</li>
{% endfor %}
</ul>
Expand All @@ -36,10 +36,10 @@ <h1 class="page-header-ui-title mb-3 display-6">Lettings</h1>
<a class="btn fw-500 ms-lg-4 btn-primary px-10" href="{% url 'index' %}">
Home
</a>
<a class="btn fw-500 ms-lg-4 btn-primary px-10" href="{% url 'profiles_index' %}">
<a class="btn fw-500 ms-lg-4 btn-primary px-10" href="{% url 'profiles:index' %}">
Profiles
</a>
</div>
</div>

{% endblock %}
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ <h1 class="page-header-ui-title mb-3 display-6">{{ title }}</h1>

<div class="container px-5 py-5 text-center">
<div class="justify-content-center">
<a class="btn fw-500 ms-lg-4 btn-primary px-10" href="{% url 'lettings_index' %}">
<a class="btn fw-500 ms-lg-4 btn-primary px-10" href="{% url 'lettings:index' %}">
<i class="ms-2" data-feather="arrow-right"></i>
Back
</a>
<a class="btn fw-500 ms-lg-4 btn-primary px-10" href="{% url 'index' %}">
Home
</a>
<a class="btn fw-500 ms-lg-4 btn-primary px-10" href="{% url 'profiles_index' %}">
<a class="btn fw-500 ms-lg-4 btn-primary px-10" href="{% url 'profiles:index' %}">
Profiles
</a>
</div>
</div>

{% endblock %}
{% endblock %}
46 changes: 46 additions & 0 deletions lettings/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from django.test import TestCase, Client
from django.urls import reverse
from .models import Letting, Address


class LettingViewsTest(TestCase):
def setUp(self):
"""Set up test data."""
self.client = Client()
self.address = Address.objects.create(
number=123,
street="Test Street",
city="Test City",
state="TS",
zip_code=12345,
country_iso_code="FR"
)
self.letting = Letting.objects.create(
title="Test Letting",
address=self.address
)

def test_index_view(self):
"""Test the index view."""
url = reverse('lettings:index')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'lettings/index.html')
self.assertIn('lettings_list', response.context)
self.assertEqual(len(response.context['lettings_list']), 1)
self.assertEqual(response.context['lettings_list'][0], self.letting)

def test_letting_view(self):
"""Test the letting detail view."""
url = reverse('lettings:letting', args=[self.letting.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'lettings/letting.html')
self.assertEqual(response.context['title'], self.letting.title)
self.assertEqual(response.context['address'], self.letting.address)

def test_letting_view_404(self):
"""Test the letting detail view with non-existent letting."""
url = reverse('lettings:letting', args=[999])
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
9 changes: 9 additions & 0 deletions lettings/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.urls import path
from . import views

app_name = 'lettings'

urlpatterns = [
path('', views.index, name='index'),
path('<int:letting_id>/', views.letting, name='letting'),
]
47 changes: 47 additions & 0 deletions lettings/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from django.shortcuts import render, get_object_or_404
from django.http import Http404
import logging
from .models import Letting

logger = logging.getLogger(__name__)

def index(request):
"""
View function for the lettings list page.
Args:
request: The HTTP request object
Returns:
template 'lettings/index.html' with a list of all lettings
"""
try:
lettings_list = Letting.objects.all()
logger.info(f"Retrieved {len(lettings_list)} lettings for index page")
return render(request, 'lettings/index.html', context={'lettings_list': lettings_list})
except Exception as e:
logger.error(f"Error retrieving lettings list: {str(e)}", exc_info=True)
raise

def letting(request, letting_id):
"""
View function for displaying a specific letting's details.
Args:
request: The HTTP request object
letting_id: The ID of the letting to display
Returns:
template 'lettings/letting.html' with the requested letting's details
Raises:
Http404: If the letting with the given ID doesn't exist
"""
try:
letting = get_object_or_404(Letting, id=letting_id)
logger.info(f"Retrieved letting {letting_id}: {letting.title}")
return render(request, 'lettings/letting.html', context={
'title': letting.title,
'address': letting.address,
})
except Http404:
logger.warning(f"Letting with id {letting_id} not found")
raise
except Exception as e:
logger.error(f"Error retrieving letting {letting_id}: {str(e)}", exc_info=True)
raise
Binary file modified oc-lettings-site.sqlite3
Binary file not shown.
11 changes: 1 addition & 10 deletions oc_lettings_site/admin.py
Original file line number Diff line number Diff line change
@@ -1,10 +1 @@
from django.contrib import admin

from .models import Letting
from .models import Address
from .models import Profile


admin.site.register(Letting)
admin.site.register(Address)
admin.site.register(Profile)
# Les modèles ont été déplacés dans les applications 'lettings' et 'profiles'.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 4.2.11 on 2025-05-16 12:00

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('oc_lettings_site', '0001_initial'),
]

operations = [
migrations.RemoveField(
model_name='letting',
name='address',
),
migrations.RemoveField(
model_name='profile',
name='user',
),
migrations.DeleteModel(
name='Address',
),
migrations.DeleteModel(
name='Letting',
),
migrations.DeleteModel(
name='Profile',
),
]
Loading