Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
bc9eae5
Profiles migration done
Apr 14, 2025
8e2a0f5
lettings migrations done.
Apr 14, 2025
31ededa
Refactoring urls/views/templates.
Apr 14, 2025
df8cc09
Merge pull request #1 from mumz0/Improve-modular-architecture
mumz0 Apr 14, 2025
f2fe88b
Solved project issues and initialise Sentry.
Apr 20, 2025
aa2f836
Merge pull request #2 from mumz0/solve-project-issues
mumz0 Apr 20, 2025
cd179a2
Create ci.yml
mumz0 Apr 25, 2025
0dceaec
Merge pull request #3 from mumz0/mumz0-patch-1
mumz0 Apr 25, 2025
f9245d3
Indentation fixed.
Apr 25, 2025
fcdf67e
Merge pull request #4 from mumz0/fix/ci_yml
mumz0 Apr 25, 2025
557dd1d
Trigger CI
Apr 25, 2025
932b8db
Create django.yml
mumz0 Apr 25, 2025
e48ed0c
fix django.yml file.
Apr 25, 2025
501458d
fix django.yml file.
Apr 25, 2025
01f2975
Fix secret in django.yml file.
Apr 25, 2025
b9b71eb
Trigger CI
Apr 25, 2025
2194bac
Add secret for tests.
Apr 25, 2025
04817e1
fix yml.
Apr 25, 2025
e47a789
fix yml.
Apr 25, 2025
2a38a41
fix yml.
Apr 25, 2025
78495a0
Add dockerfile
Apr 25, 2025
313f8b2
Merge pull request #5 from mumz0/feature/add-dockerfile
mumz0 Apr 25, 2025
50c3a6f
Rename Dokerfile to Dockerfile
mumz0 Apr 25, 2025
6ce8412
Merge pull request #6 from mumz0/mumz0-patch-2
mumz0 Apr 25, 2025
8cad1f3
Update django.yml
mumz0 Apr 25, 2025
077afb7
Merge pull request #7 from mumz0/mumz0-patch-3
mumz0 Apr 25, 2025
f6cdfb3
Update Dockerfile
mumz0 Apr 25, 2025
e54887b
Merge pull request #8 from mumz0/mumz0-patch-4
mumz0 Apr 25, 2025
ceb0cee
Update django.yml
mumz0 Apr 25, 2025
f481db8
Merge pull request #9 from mumz0/mumz0-patch-5
mumz0 Apr 25, 2025
2f86221
Add connection and push to dockerhub.
Apr 26, 2025
a5be09d
Merge pull request #10 from mumz0/feature/push-dockerhub
mumz0 Apr 26, 2025
dfd3f36
setup for prod.
Apr 26, 2025
268e948
setup for prod.
Apr 26, 2025
43ec435
Merge pull request #11 from mumz0/feature/enable-render
mumz0 Apr 26, 2025
b8bfc52
test with new allowed host.
Apr 26, 2025
05973ad
Merge pull request #12 from mumz0/fix/allowed-hosts-issue
mumz0 Apr 26, 2025
7ac7954
Welcome message has been changed to verify if the render deployment s…
Apr 26, 2025
223301d
Welcome message back to origin.
Apr 26, 2025
9de5d4b
Add success condition and coverage execution when run tests.
Apr 26, 2025
0765a62
Fix coverage issue on ci workflow.
Apr 26, 2025
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.
15 changes: 15 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[run]
source =
lettings
profiles
oc_lettings_site

omit =
*/tests/*
*/tests.py
*/migrations/*
*/apps.py
manage.py
oc_lettings_site/wsgi.py
oc_lettings_site/asgi.py
*/__init__.py
77 changes: 77 additions & 0 deletions .github/workflows/django.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Django CI

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:
build:
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: [3.12.3]
env:
SECRET_KEY: ${{ secrets.SECRET_KEY }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
DJANGO_SETTINGS_MODULE: oc_lettings_site.settings

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}

- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Lint with flake8
if: success()
run: flake8

- name: Run tests with coverage
if: success()
run: |
pytest --cov=.
coverage html
coverage report --fail-under=80

- name: Archive code coverage results
if: success() || failure()
uses: actions/upload-artifact@v3
with:
name: code-coverage-report
path: htmlcov/

- name: Collect static files
if: success()
run: python manage.py collectstatic --noinput

- name: Build Docker image
if: success()
run: >-
docker build
--build-arg SECRET_KEY=${{ secrets.SECRET_KEY }}
--build-arg SENTRY_DSN=${{ secrets.SENTRY_DSN }}
-t oc-lettings-site .

- name: Login to Docker Hub
if: success()
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Tag Docker image
if: success()
run: docker tag oc-lettings-site ${{ secrets.DOCKERHUB_USERNAME }}/oc-lettings-site:latest

- name: Push Docker image
if: success()
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/oc-lettings-site:latest
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
**/__pycache__
*.pyc
venv
.env
htmlcov
pytest_cache
.coverage
21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM python:3.12.3-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

ENV DJANGO_SETTINGS_MODULE=oc_lettings_site.settings
ENV PYTHONUNBUFFERED=1

ARG SECRET_KEY
ARG SENTRY_DSN

ENV SECRET_KEY=${SECRET_KEY}
ENV SENTRY_DSN=${SENTRY_DSN}

RUN python manage.py collectstatic --noinput

CMD ["gunicorn", "oc_lettings_site.wsgi:application", "--bind", "0.0.0.0:8000"]
Empty file added lettings/__init__.py
Empty file.
12 changes: 12 additions & 0 deletions lettings/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""
Admin configuration for lettings app.

This module registers the Letting and Address models with the Django admin interface
to allow management of lettings and addresses through the administration panel.
"""

from django.contrib import admin
from .models import Letting, Address

admin.site.register(Letting)
admin.site.register(Address)
19 changes: 19 additions & 0 deletions lettings/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
Module for the lettings application configuration.

This module defines the configuration class for the lettings app.
"""

from django.apps import AppConfig


class LettingsConfig(AppConfig):
"""
Configuration class for the lettings application.
This class is used to set up the application and its settings.
It inherits from Django's AppConfig base class.

:param AppConfig: Django's application configuration base class.
:type AppConfig: django.apps.AppConfig
"""
name = 'lettings'
41 changes: 41 additions & 0 deletions lettings/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Generated by Django 3.0 on 2025-04-14 12:57

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


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.CreateModel(
name='Address',
fields=[
('id', models.AutoField(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.AutoField(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')),
],
),
],
database_operations=[],
),
]
Empty file added lettings/migrations/__init__.py
Empty file.
66 changes: 66 additions & 0 deletions lettings/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""
Module for lettings models.

This module defines the Address and Letting models used to represent lettings data.
"""

from django.db import models
from django.core.validators import MaxValueValidator, MinLengthValidator


class Address(models.Model):
"""
Represents a physical address.

This model stores address details such as number, street,
city, state, zip code, and country ISO code.

:param models: Django's ORM models module.
:type models: django.db.models
:return: An instance of Address representing a physical address.
:rtype: Address
"""
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)])

def __str__(self):
"""
Return a string representation of the address.

:return: A string combining the number and street.
:rtype: str
"""
return f'{self.number} {self.street}'

class Meta:
"""
This class defines the verbose name and plural form for the Address model.
"""
verbose_name_plural = "Addresses"


class Letting(models.Model):
"""
Represents a letting.

This model stores information about a letting, including its title and the associated address.

:param models: Django's ORM models module.
:type models: django.db.models
:return: An instance of Letting representing a letting record.
:rtype: Letting
"""
title = models.CharField(max_length=256)
address = models.OneToOneField(Address, on_delete=models.CASCADE)

def __str__(self):
"""Return the title of the letting.

:return: The title of the letting.
:rtype: str
"""
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,7 +36,7 @@ <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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ <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>
Expand Down
59 changes: 59 additions & 0 deletions lettings/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""
Unit tests for the lettings app.

This module contains test cases for models, views and urls of the lettings application.
"""

from django.test import TestCase
from django.urls import reverse
from .models import Address, Letting


class TestLettings(TestCase):
"""Test class for lettings app."""

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

def test_address_str_method(self):
"""Test the string representation of Address model."""
assert str(self.address) == "123 Test Street"

def test_letting_str_method(self):
"""Test the string representation of Letting model."""
assert str(self.letting) == "Test Letting"

def test_lettings_index_view(self):
"""Test the index view of lettings app."""
url = reverse('lettings:index')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn("Lettings", str(response.content))
self.assertEqual(response.templates[0].name, 'lettings/index.html')

def test_letting_detail_view(self):
"""Test the detail view of a letting."""
url = reverse('lettings:letting', kwargs={'letting_id': self.letting.id})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn(self.letting.title, str(response.content))
self.assertEqual(response.templates[0].name, 'lettings/letting.html')

def test_letting_detail_view_404(self):
"""Test the detail view with invalid letting id."""
url = reverse('lettings:letting', kwargs={'letting_id': 999})
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
self.assertTemplateNotUsed(response, 'lettings/letting.html')
15 changes: 15 additions & 0 deletions lettings/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""
Module for the URL configuration of the lettings application.

This module maps URL patterns to their corresponding view functions.
"""

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'),
]
Loading