The live link can be found here.
This project was created as part of the Full Stack Software Development course offered by Code Institute.
- Dublin Inn Real Estate
- Table of Contents
- Briefing
- User Experience | UX
- User Interface | UI
- Features
- Information Architecture
- Technologies Used
- Testing
- Deployment
- Credits
- Acknowledgements
- Disclaimer
- Author
Dublin Inn Real Estate is a specialist rental company across Ireland. Based in Dublin, the company has a simple website that receives around 2,000 simultaneous hits per day.
In just over 1 year of the website, the marketing team of Dublin Inn Real Estate noticed that the volume of access to the website was falling, contributing with a significant portion of the company's revenue.
Based on this information, the team of analysts decided to investigate the reason for the decrease in access and found that the site had become obsolete and the system no longer supported the number of daily accesses, which required a redesign of the entire web application. Therefore, the marketing team launched a proposal to innovate the company's visual identity.
Concerned about this metric falling, the CEO of Dublin Inn Real Estate hired me to develop a new web application for the company.
At the end of the work, it is expected that a new application will be delivered, with a new identity, new functionalities, reliable and scalable, which supports more simultaneous accesses than the old website.
The company's business team gave me a report on the features that the CEO wants:
- A website whose purpose of the company is immediately understood by the user.
- Have a clear information on what the site is about and what it provides
- Have an easy navigation that is consistent throughout the website
- Consistent layout without any confusing elements
- Accessibility considerations are taken throughout the site
- A form where the user can filter the search by properties, passing information such as location, property type and price range (This functionality does not exist in the current application).
- A functionality where the user can schedule a visit to one or more properties and a dashboard where he can view, edit and/or delete a visit (This functionality does not exist in the current application).
The application's administrative functions are intended to manage what each user can do within the system. Permissions such as adding, editing or removing a property, for example, should under no circumstances be given to the user of the application. Such functionalities must be assigned exclusively to the administrator.
In the system, there will be the following user functionalities:
- Admin: has permission in all areas of the system.
- Users: can edit your own profile like change profile picture or change password. The user can search for properties by location, type of property (apartment, house, studio, etc.), price range, in addition to being able to schedule visits, edit date and time of visit (according to availability) and you can also cancel a certain scheduled visit.
- Admin:
- will be able to manage users.
- Add/Edit/Remove properties.
- View all screens users can view as well.
- Users:
- can log in and out of the application.
- manage his/her own profile.
- form to search for properties.
- Provide access to high standard properties for visitors to browse
- Give potential clients a testimonial from previous clients about their satisfaction using the company's services.
- Allow visitors the option to book a property visit directly from the site.
- Allow the visitor to be able to search for a property according to its location, type of property (Studio, Apartment or House) and price.
- Allow the visitor to be able to create an account and manage their own content in the site.
- As a Site User I can register an account so that I can favourite the properties I like and book a visit.
- As a Site User I can filter a property according to its price so that I can choose the one that best suits my budget.
- As a Site User I can filter a property according to its type so that I can choose the one that fits on my needs.
- As a Site User I can filter a property according to its location so that I can choose the one that are of interest to me.
- As a Site User I can click on a property so that I can read the full content.
- As a Site User I can schedule a day and time so that I can visit the property when it's convenient for me.
- As a Site User I can see all visits scheduled as well as modify or cancel so that I can manage my scheduled visits.
- As a Site User, I expect to have a button so that I can sign up for the site using my Facebook or Gmail accounts.
- As a Site Admin I want to be able to add a new property easily.
- As a Site Admin I want to be able to see all existing properties in a simple and easy manner.
- As a Site Admin I want to be able to manage the existing properties such as edit price or delete easily.
- As a Developer I want to ensure that all application features work as they were implemented to work.
- As a Developer I want to ensure an authenticated user can access all required information correctly.
- As a Developer I want to work together with the administrator of the site for improvements for the user of the same.
As a primary font, I have chosen to use Poppins as it has a geometric in style, clean, and includes 18 different font weights, from thin to black. Also, Poppins is great for both headlines and paragraph copy to improve readability and style.
As a secondary font I have chosen to use Fira Sans to compliment the site content and allow for the extra content to stand out from the rest of the site content. Furthermore, the Fira font family also aim to cover the legibility needs for a large range of handsets varying in screen quality and rendering.
For the Logo I have chosen to use Gorditas because it is a fun and funky display slab serif typeface family, with heart details, which fit perfectly into the design thought for the project.
Talking to the marketing team at Dublin Inn Real Estate, they informed me that the CEO would like the site to have references to the colors of the Irish flag.
With this information I have chosen to use the following colours to provide a great contrast and to help the content stand out.
The wireframes were created in Figma which can be explored in details by clicking on the image below π
Right on the home page, the user will come across a form where he can search for properties according to location (area), type of property (studio, apartment or house) and price range (min and max).
If the user hits directly on the 'search' button, without adding anything to the search, all properties registered in the system will be returned.
Still on the home page, the user has the presentation of the properties that are highlighted on the site. However, there is a 'View all' link just above, in the right corner, and when clicking the user will be directed to the properties listing page.
In the list of properties, there is a summary about the property, such as the name of the property, monthly price and some characteristics such as the number of bedrooms, bathrooms, etc.
The user can click on 'See more' to access the specific content of that property. A more detailed page will be presented showing more information about the property, in addition to a map with the location. It is worth mentioning that in this project, the map is not dynamic, that is, no API was consumed to return the real location since the property address does not represent an existing address.
So, the user should see a page like that.
As part of the request made by the CEO of Dublin Inn Real Estate, the site will have the option for the user to schedule a visit of a specific property. With that in mind, a 'Schedule your visit' button was developed, which is just below the property description.
Once this is done, the user will be directed to the scheduling form page, which will be as shown in the image below.
Finally, all the schedules that that user has will be displayed. The user can still cancel the appointment if he is unable to attend the scheduled date and time.
The configuration file is one of the most important elements in any Django project. Knowing this, I decided to divide the configuration files structure into four files, one of them being the base file, which will contain everything that is needed in any project environment, be it production, development or testing. The other three files are for development, testing and finally production environments.
For that, create a folder called settings
inside the main project folder. Inside it, add the development.py
, testing.py
, production.py
and __init__.py
files. After that, take the base settings.py
file (which is in the root of the main project folder) and move it into the settings folder.
We will do this so that within the settings.py
file there are only elements that will be used in any of the environments, be it production, testing or development. However, whatever we need to create differently for each environment, we will put it in the files we just created.
For this project, both the development and test environments use the sqlite
database locally, while the production environment uses the Postgresql
database. That way, development and testing data doesn't mix with the data that will be in production. To do this kind of separation, we must place the database settings in their respective files.
See below how the project structure should look π
Now, we will need to open each file and add some settings.
from .settings import *
DEBUG = True
SECRET_KEY = os.environ.get("SECRET_KEY_DEVELOPMENT")
ALLOWED_HOSTS = ["localhost", "127.0.0.1"]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
from .settings import *
DEBUG = True
SECRET_KEY = os.environ.get("SECRET_KEY_TESTING")
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
import os
import dj_database_url
from .settings import *
if os.path.exists("env.py"):
import env
DEBUG = False
SECRET_KEY = os.environ.get("SECRET_KEY_PRODUCTION")
ALLOWED_HOSTS = [os.environ.get("HEROKU_HOST")]
DATABASES = {'default': dj_database_url.parse(os.environ.get("HEROKU_DB"))}
Remembering that it is good programming practices not to leave SECRET_KEYS visible as this will result in the vulnerability of the application. Therefore, generate your own secret keys and configure them through environment variables.
You can use Python os
library to set your environment variables and then import them into the files, as I exemplify below:
import os
# SECRET KEYS
os.environ.setdefault("SECRET_KEY_DEVELOPMENT", "YOUR SECRET KEY")
os.environ.setdefault("SECRET_KEY_PRODUCTION", "YOUR SECRET KEY")
os.environ.setdefault("SECRET_KEY_TESTING", "YOUR SECRET KEY")
Once that's done, don't forget to remove the database settings and secret key from the settings.py
file.
It is worth mentioning that with this change in the project structure, you will need to change some more settings, so that the application works correctly. By default, in the settings.py
file, the BASE_DIR
variable looks like this: BASE_DIR = Path(__file__).resolve().parent.parent
. You will need to add a new .parent
as there has been a subdivision of the main project folder. So the BASE_DIR
variable should look like this: BASE_DIR = Path(__file__).resolve().parent.parent.parent
.
Also, in the wsgi.py
file you will need to notice that by default, the environment variable setting will look like this:
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
"dublinrealestate.settings")
application = get_wsgi_application()
Since three independent environments were created, we need to tell Django which environment we are going to work with. In this way, it is necessary to add, right after settings
, the environment name.
If working in the development environment, add .development
. If working in the testing environment, add .testing
and so on.
The same thing should apply to the manage.py
file which by default appears as
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE',
'dublinrealestate.settings')
# The rest of the code was hidden to optimize the page. Do not remove anything after this line.
...
If working in the development environment, add .development
after settings
. If working in the testing environment, add .testing
and so on.
Be sure to change it each time you are working in a different environment.
With the project configured correctly, when running the application, you will see something similar to this.
Title | Key In Database | Form Validation | Data Type |
---|---|---|---|
id | id | No Validation | Primary Key |
Username | username | max_length 20 | CharField |
First Name | first_name | max_lenght 20 | CharField |
Last Name | last_name | max_lenght 20 | CharField |
E-mail Address | Must contain @ & .com etc | ||
Password | password | max length 50 | CharField |
Once a user registers in the system, a profile is automatically created for that user.
Title | Key In Database | Form Validation | Data Type |
---|---|---|---|
Id | id | No Validation | Primary Key |
user | user | max length 50 | Foreign Key |
Role | role | choices=ROLE_CHOICE*, default=2 | IntegerField |
Date of Birthday | date_of_birthday | default=None, null=True, blank=True | DateTimeField |
Created at | created_at | auto_now_add=True | DateTimeField |
Updated at | updated_at | auto_now_add=True | DateTimeField |
Token | token | max_length=255 | CharField |
Avatar | avatar | default='placeholder' | ImageField |
Favourites | favourites | blank=True | ManyToManyField |
- ROLE_CHOICE = ( (1, 'Admin'), (2, 'Client'), (3, 'Agent') )
Title | Key In Database | Form Validation | Data Type |
---|---|---|---|
Id | id | No Validation | Primary Key |
Property Name | property_name | max_length=50, null=False, unique=True | CharField |
Slug | slug | max_length=100, null=False, unique=True | SlugField |
Property Address | property_address | max_length=255 | CharField |
Neighborhood | neighborhood | null=True | Foreign Key |
District | district | No Validation | Foreign Key |
Image | image | blank=True | ManyToManyField |
Property Category | property_category | choices=PROPERTY_CATEGORY*, default=2 | IntegerField |
Property Type | property_type | choices=PROPERTY_TYPE*, default=1 | IntegerField |
Property Price | property_price | No Validation | IntegerField |
Bedrooms | bedrooms | No Validation | IntegerField |
Bathrooms | bathrooms | No Validation | IntegerField |
Metreage | metreage | No Validation | FloatField |
Viewing Date | viewing_date | No Validation | ManyToManyField |
Viewing Time | viewing_time | No Validation | ManyToManyField |
Created at | created_at | auto_now_add=True | DateTimeField |
Updated at | updated_at | auto_now_add=True | DateTimeField |
-
PROPERTY_CATEGORY = ( (1, 'Sale'), (2, 'Rent') )
-
PROPERTY_TYPE = ( (1, 'Studio'), (2, 'Apartment'), (3, 'House') )
Title | Key in Database | Form Validation | Data Type |
---|---|---|---|
Id | id | No Validation | Primary Key |
Name | name | max length 50 | CharField |
Created at | created_at | auto_now_add=True | DateTimeField |
Updated at | updated_at | auto_now_add=True | DateTimeField |
Title | Key in Database | Form Validation | Data Type |
---|---|---|---|
Id | id | No Validation | Primary Key |
Name | name | max length 50 | CharField |
County | county | on_delete=models.SET_NULL | Foreign Key |
Created at | created_at | auto_now_add=True | DateTimeField |
Updated at | updated_at | auto_now_add=True | DateTimeField |
Title | Key in Database | Form Validation | Data Type |
---|---|---|---|
Id | id | No Validation | Primary Key |
Name | name | max length 50 | CharField |
District | district | on_delete=models.SET_NULL | Foreign Key |
Created at | created_at | auto_now_add=True | DateTimeField |
Updated at | updated_at | auto_now_add=True | DateTimeField |
Title | Key In Database | Form Validation | Data Type |
---|---|---|---|
Id | id | No Validation | Primary Key |
Property | property | on_delete=models.SET_NULL | Foreign Key |
User | user | on_delete=models.SET_NULL | Foreign Key |
Day for viewing | day_for_viewing | null=False, max_length=20 | CharField |
Time for viewing | time_for_viewing | No Validation | TimeField |
Status | status | choices=VIEWING_STATUS*, default=1 | IntegerField |
Created at | created_at | auto_now_add=True | DateTimeField |
Updated at | updated_at | auto_now_add=True | DateTimeField |
- VIEWING_STATUS = ( (1, 'Scheduled'), (2, 'Cancelled') )
Title | Key in Database | Form Validation | Data Type |
---|---|---|---|
Id | id | No Validation | Primary Key |
Day for viewing | day_for_viewing | null=False, max_length=20 | CharField |
Created at | created_at | auto_now_add=True | DateTimeField |
Updated at | updated_at | auto_now_add=True | DateTimeField |
Title | Key in Database | Form Validation | Data Type |
---|---|---|---|
Id | id | No Validation | Primary Key |
Time for viewing | time_for_viewing | No Validation | TimeField |
Created at | created_at | auto_now_add=True | DateTimeField |
Updated at | updated_at | auto_now_add=True | DateTimeField |
-
HTML5, or Hyper Text Markup Language: Used to construct the page within this app -
https://developer.mozilla.org/en-US/docs/Web/HTML -
CSS3, or Cascading Style Sheets: Used to style the various elements on the app's pages via coloring, fonts, spacing, etc. - https://www.w3.org/Style/CSS/Overview.en.html
-
Javascript: Used to create iterations across the page. - https://www.javascript.com/
-
Python: Used to develop all application logic. (https://www.python.org/)
-
Am I Responsive? : was used in order to see responsive design throughout the process and to generate mockup imagery to be used.
-
Responsive Design Tester : was used to see the responsivity throughout an array of devices (screen sizes) and resolutions.
-
Squoosh : was used to reduce the size of the images throughout the page.
Some manual tests were performed to ensure the correct functioning of the application.
-
Implementation π¨: When I had set up the data.json and loaded into the database I could then view all available properties. That way, I could ensure all ones were loaded as expected and that properties information was visible when selected.
-
Test π: To test this, I went through each property and loaded the its information page, then looked at changing the url to ensure each item was loading correctly
-
Result π: All properties loaded as expected to the main store page. When amending the url all properties again loaded as expected. However, if I tried to access an property slug that didn't exist I was presented with a 404 page.
-
Verdict β : This test passed and no amendments were required.
-
Implementation π¨: As the user can only schedule for visiting a property, I wanted to ensure that the user must be logged in to complete the appointment.
-
Test π: To test this I tried to schedule a visit to the property without being logged in. By clicking the 'schedule your visit' button, I was moved directly to the login page. Also, I tried to access the scheduling page directly from the URL and in the same way I was redirected to the login page.
-
Result π: The login page was displayed right after I tried to schedule a visit without being properly logged in.
-
Verdict β : This test completed as expected without bugs.
-
Implementation π¨: To test the responsiveness of the site, the page was loaded on local mobile devices to check design choices.
-
Test π: This test was performed by loading the site and navigating through each page. In addition, the part where the user can schedule a visit to a property was tested.
-
Result π: Each page loaded and displayed correctly with the exception of an issue found in the responsive menu button. When clicking on the button, the user will see the menu opening on the side of the screen. However, when scrolling down the page, the button to close the menu will be at the top of the screen, thus forcing the user to scroll up and then close the menu.
-
Verdict β: This test was classed as a fail.
-
Implementation π¨: To make sure that the user can navigate the site easily, I have tested the navigation of all items, links, buttons, etc., in order to ensure that the user does not get lost within the application sessions.
-
Test π: This test was performed in a systematic way, so that each page was navigated, clicked on all the links, in order to ensure that the user knew how to "find himself" within the site.
-
Result π: Every page and link was checked and each provided a positive result, in no time was the user sent to an unexpected destination.
-
Verdict β : This test completed as expected without bugs.
- Problem π: Account system failed to register user.
- Causeπ : As the sign up was intended to work without having to verify an e-mail address, when sign-up the process failed due to no e-mail setting being available.
- Resolutionβ : Added ACCOUNT_EMAIL_VERIFICATION = 'none' to settings.py to stop an e-mail verification trying to be sent.
The only known thing, but that is still present on the site, is that when the user searches for a property, passing only the location parameter, after the 'Dublin 7' area, the result presented does not correspond with the chosen localization. For example, if the user selects the search for Dublin 18, properties from other regions will appear. A different solution would need to be found on the backend to ensure this doesn't happen. I couldn't find a solution before the submission date of this project.
By forking the GitHub Repository we make a copy of the original repository on our GitHub account to view and/or make changes without affecting the original repository by using the following steps...
-
Log in to GitHub and locate the GitHub Repository
-
In the Repository header (not at the top of the page), find a "Code" drop-down button. By clicking this button, you will find some options to clone the project repository. If you have your SSH key configured, choose to select the 'SSH' option and then click on the button right after the url. This button will copy the url and you will paste it in your terminal. If you have not configured your SSH key, you can choose to use the HTTPS protocol. In the same way as was done in the SSH option, when selecting 'HTTPS' you must click on the button right after the url to copy and then paste it into your terminal.
https://github.com/emidiovaleretto/django-dublin-inn-real-estate.git
-
You should now have a copy of the original repository in your GitHub account.
-
Ideally you will want to work within a virtual environment to allow all packages to be kept within the project, this can be installed using the following command (please note some IDE's require pip3 instead of pip, please check with the documentation for your chosen IDE). To create a virtual environment, run the command
The installation of a virtualenv is done using pip, Python's package manager. It is with it that we install, remove and update packages in our projects. One note is that PIP is already installed when we are using IDE's like VSCode or PyCharm for our Python projects. So, just run the command below to install the virtualenv package on our computer:
pip install virtualenv
Once this is done, the package will be installed and ready to be used. Now you can create and manage your virtual environments.
The process of creating a virtualenv is quite simple and can be done using a single command, as seen below:
virtualenv your_virtualenv_name
Hint: I usually choose to name my virtual environments after the project name, rather than just writing 'venv', for example, the project is called MyBlogProject
, so the name of the virtual environment would be something like myblogenv
. If you need to return to a certain project after a while, you'll easily find the respective environment for that project. But that is totally up to you.
After creating a virtualenv, it's needed to activate it so that you can install the necessary packages for the project. To do this, run the following command:
source your_virtualenv_name/bin/activate (Linux ou macOS)
your_virtualenv_name/Scripts/Activate (Windows)
- Once that's done, you need to install the project's dependencies. To do this, just run the following command:
pip3 install -r requirements.txt
- Next you need to create a new file within the root directory called
env.py
. This file will contain all your secret keys, public keys, production database settings etc. Everything you think should not be exposed, you should put within this file.
So add the following lines to configure the environmental variables.
import os
# SECRET KEYS
os.environ.setdefault("SECRET_KEY_DEVELOPMENT", "YOUR SECRET KEY")
os.environ.setdefault("SECRET_KEY_PRODUCTION", "YOUR SECRET KEY")
os.environ.setdefault("SECRET_KEY_TESTING", "YOUR SECRET KEY")
# DATABASE URL
os.environ.setdefault("DATABASE_URL", "YOUR DATABASE URL")
- To set up your database you will first need to run the following command:
python3 manage.py migrate
- Then you need to create a superuser. This will allow you to access the application's admin panel. To do so, run the following command in your terminal and fill in the required information as prompted.
python3 manage.py createsuperuser
- From there you need to delete any objects from the database that are not in the fixture. To do this, run the following commands:
python3 manage.py shell
A terminal screen should appear. In the terminal, paste the following command:
from django.contrib.contenttypes.models import ContentType
ContentType.objects.all().delete()
- Now you should be able to run the server using the following command:
python3 manage.py runserver
If everything has been correctly configure you should not get a message giving you a link to your locally hosted site usually at http://127.0.0.1:8000
- Finally, stop the server by pressing CTRL + C (or cmd + C on Mac) and run the following command to populate the database.
python3 manage.py loaddata data.json
After running this command, all information contained in the data.json
file will be saved in the database. Once that's done, run the python3 manage.py runserver
command again and you should be able to see the application working.
To set up heroku you must:
- If your requirements.txt file has not changed you can skip this step. Otherwise, in your terminal type 'pip freeze > requirements.txt' then save and push the changes.
- Go to Heroku.com and sign in to your account or create a free one.
- From the heroku dashboard click the 'Create new app' button.
- Name the app something unique and choose what region you are in then click 'Create app'.
- In the resources section, in the add-ons field, type
Heroku Postgreslq
and select the free cost option. - Now, go to the settings tab and find the Config Vars section. Click 'Reveal Config Vars'.
In the settings tab, select Reveal Config Vars and copy the pre populated DATABASE_URL
into your settings.py
file in your project in the Config Vars in Heroku you will need to populate with the following keys:
Key | Value |
---|---|
SECRET_KEY_PRODUCTION | [Your Secret Key] |
DEBUG_PRODUCTION | False |
DATABASE_URL | [Your DATABASE URL] |
HEROKU_HOST | [Your Heroku Host] |
- Then head over to the deploy section by clicking deploy from the nav bar at the top of the page.
- From the 'Deployment method' section select GitHub and click 'Connect to GitHub'.
- Enter the repository name as it is in GitHub and click 'search'.
- Click the 'connect' button next to the repository to link it to heroku.
- To deploy, scroll down and click the 'Deploy Branch' button.
- Heroku will notify you that the app was successfully deployed with a button to view the app.
- If you want to rebuild your app automatically you can also select the 'Enable Automatic Deploys' button which will then rebuild the app every time you push any changes.
- Pexels - All images were downloaded from the website.
- Table of contents
- Code Institute
I would like to take the opportunity to thank:
- To God first, to my family, friends and colleagues for their advice, support and help with testing.
- To my mentors Felipe Alarcon & Richard Wells for their feedback, advices, support and, above all, for their patience.
- All Code Institute Tutors and Community on Slack for the peer reviews and advice.
*Disclaimer: The following Context is completely fictional, the company, the context, the CEO, the business questions exist only in my imagination.
**For educational purposes only.