The aim of this task is to create design, develop and test a website for the Fitzroy Catholic Bookshop.
The user can search for specific items based on the publisher, title, category, and price range. The web project has been designed using xhtml thus making it compatible with almost all broswers regardless of the platform or device. CSS and bootstrap have been used for styling and conditional styling while python/django has been used to cater for server side requirement.
The images used in the web project have been downloaded from www.userlogos.org (e.g https://www.userlogos.org/files/logos/euphonicnight/Lib1.png) and usage of the images has been acknowledged wherever they have been used. Images of my own were also uploaded to https://image.ibb.co/, thus the <img> tags reference to that site in most cases.
“Fitzroy Catholic Bookshop” has been used as the company title through out the website.
So as to know the currently active page, the following peace of code has be used.
<meta charset=”utf-8″> {% if title %} <!– if the var title has not null, append the (Current page) title to out Main Title –> <title>Fitzroy Catholic Bookshop | {{title}}</title> {% else %} <title> Fitzroy Catholic Bookshop </title> |
When the one browses to a page lets say the search results page, a “Home | Search Results” will be displayed on top right side of that page.
The Look and Feal has been kept quite simple with the main content area background and Pale yellow background for footers.
The functionality of the website has been tested and works as expected. A test script (tests.py) has been includes. It contains dummy data and aims to assert that the results are as expected given predetermine queries depicting real user actions.
admin.py
from django.contrib import admin from .models import Image,Product #Register the models. admin.site.register(Image) admin.site.register(Product) |
forms.py
from django import forms from . models import Product CATEGORY_CHOICES= [ (‘book’, ‘Book’), (‘audio book’, ‘Audio Books’), (‘video’, ‘Video’), (‘music’, ‘Music’), ] PUBLISHER_CHOICES= [ (‘franciscan media’, ‘Franciscan Media’), (‘Penguins’, ‘Penguins’), (‘st pauls publication’, ‘St Pauls Publication’), (‘marvels’, ‘Marvels’), (‘harper collins’, ‘Harper Collins’), ] class ProductForm(forms.Form): ”’ New Products form ”’ title = forms.CharField(label=’Title’,max_length = 140) isbn = forms.CharField(label=’ISBN’,max_length = 64) category = forms.CharField(label=’Category’, widget=forms.Select(choices=CATEGORY_CHOICES)) author = forms.CharField(label=’Author’,max_length = 64) publisher = forms.CharField(label=’Publisher’, widget=forms.Select(choices=PUBLISHER_CHOICES)) price = forms.IntegerField(label=’price’) quantity = forms.CharField(label=’Quantity’,max_length = 64) image = forms.ImageField(label=’Image’,max_length = 64) class SearchForm(forms.Form): ”’ Create the search form ”’ min_price = forms.IntegerField(label=’Min price’) max_price = forms.IntegerField(label=’Max price’) category = forms.CharField(label=’Category’, widget=forms.Select(choices=CATEGORY_CHOICES)) publisher = forms.CharField(label=’Publisher’, widget=forms.Select(choices=PUBLISHER_CHOICES)) |
apps.py
from django.apps import AppConfig #Name of the Web Application class FitzroybookshopConfig(AppConfig): name = ‘fitzroybookshop’ |
models.py
from django.db import models class Product(models.Model): ”’ Products object ”’ title = models.CharField(max_length=140) isbn = models.CharField(max_length=64) category = models.CharField(max_length=64) author = models.CharField(max_length=64) publisher = models.CharField(max_length=64) price = models.IntegerField( null = True) quantity = models.IntegerField(null = True) image = models.ImageField(upload_to = ‘images/’ ,null = True) def __str__(self): return self.title def save_prod(self): self.save() def delete_prod(self): self.delete() @classmethod def find_prod(cls,name): found_prods = cls.objects.filter(username__icontains = name).all() return found_prods @classmethod def search_prod(cls,min_price,max_price,category,publisher): ”’ return a list of objects which satisfy user query ”’ list_out =[] requested_products = cls.objects.filter(category = category,publisher = publisher).all() for item in requested_products: if item.price>= min_price and item.price <= max_price: list_out.append(item) return list_out class Image(models.Model): ”’ image objects ”’ name = models.CharField(max_length= 30) image = models.ImageField(upload_to = ‘images/’ ,null = True) |
from django.shortcuts import render from django.shortcuts import render,redirect from . models import Product from . forms import ProductForm,SearchForm def home(request): return render(request, ‘home.html’) def maintenance(request): form = ProductForm() if request.method == ‘POST’: form = ProductForm(request.POST,request.FILES) if form.is_valid(): title = form.cleaned_data[‘title’] isbn = form.cleaned_data[‘isbn’] category = form.cleaned_data[‘category’] author = form.cleaned_data[‘author’] publisher = form.cleaned_data[‘publisher’] price = form.cleaned_data[‘price’] quantity = form.cleaned_data[‘quantity’] image = form.cleaned_data[‘image’] prod = Product(title = title, isbn =isbn ,category =category,author =author ,publisher = publisher ,price = price,quantity = quantity ,image = image) prod.save() form = ProductForm() else: form = ProductForm() return render(request, ‘maintenance.html’,{“form”:form}) def search(request): form = SearchForm() if request.method == ‘POST’: form = SearchForm(request.POST,request.FILES) # Ensure the data meets the criteria set in forms.py if form.is_valid(): min_price = form.cleaned_data[‘min_price’] max_price = form.cleaned_data[‘max_price’] publisher = form.cleaned_data[‘publisher’] category = form.cleaned_data[‘category’] requested_prods = Product.search_prod(min_price,max_price,category,publisher) return render(request, ‘results.html’,{“form”:form,”requested_prods”:requested_prods}) else: form = ProductForm() return render(request, ‘search.html’,{“form”:form}) def map(request): title = ‘Site Map’ return render(request, ‘map.html’,{“title”:title}) |
tests.py
from django.test import TestCase from . models import Product class ProductTestClass(TestCase): ”’ Test the Product model ”’ def setUp(self): ”’ Setup function for the tests ”’ self.prod = Product(title = ‘test title1’,isbn = ‘ISBN 0000′,category =’Book’,author = ‘test author1’,publisher = ‘test publisher1′,price = 90,quantity = 10,image =’images/img.png’) def test_instance(self): ”’ Test instanciation of products ”’ self.assertTrue(isinstance(self.prod,Product)) def test_save_prod(self): ”’ Test the save function of model image ”’ self.prod.save_prod() all_prods= Product.objects.all() self.assertTrue(len(all_prods)>0) def test_delete_prod(self): ”’ Test the delete_prod function ”’ self.prod.save_prod() new_prod = Product(title = ‘test title2’,isbn = ‘ISBN 1111′,category =’Video’,author = ‘test artist’,publisher = ‘test publisher2′,price = 10,quantity = 10,image =’images/vid.mpeg’) new_prod.save_prod() self.prod.delete_prod() all_prods = Product.objects.all() self.assertTrue(len(all_prods)==1) def test_search_prod(self): ”’ Test the search_prod function ”’ self.prod.save_prod() new_prod = Product(title = ‘test title2’,isbn = ‘ISBN 1111′,category =’Book’,author = ‘test artist’,publisher = ‘test publisher1′,price = 90,quantity = 10,image =’images/vid.mpeg’) new_prod.save_prod() all_prods = Product.search_prod(80,100,’Book’,’test publisher1′) self.assertTrue(len(all_prods) ==2) |
urls.py
from django.conf.urls import url from . import views from django.conf import settings from django.conf.urls.static import static # Allow the app to make use of the static paths from django.conf import settings from django.conf.urls.static import static #Define the Links to various pages on the site urlpatterns=[ url(‘^$’,views.home,name = ‘Home’), url(‘^search/’,views.search,name = ‘Search’), url(‘^maintenance/’,views.maintenance,name = ‘Maintenance’), url(‘^site/map’,views.map,name = ‘Site_Map’), ] if settings.DEBUG: urlpatterns+= static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT) |
base.html
{% load bootstrap3 %} {% load staticfiles %} <!DOCTYPE html> <html> <head> <meta charset=”utf-8″> {% if title %} <title>Fitzroy Catholic Bookshop | {{title}}</title> {% else %} <title> Fitzroy Catholic Bookshop </title> {% endif %} {% block styles %} {% bootstrap_css %} <link rel=”stylesheet” href=”{% static ‘css/base.css’ %}”> {% endblock %} <!– UIkit CSS –> <link rel=”stylesheet” href=”https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.42/css/uikit.min.css” /> <!– UIkit JS –> <script data-src=”https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.42/js/uikit.min.js”></script> <script data-src=”https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.42/js/uikit-icons.min.js”></script> </head> <body> {% include “navbar.html” %} {% block content %} {%endblock%} {% block scripts %} <script data-src=”https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js”></script> {% bootstrap_javascript %} {% endblock %} </body> |
home.html
{% load staticfiles%} {% load bootstrap3 %} {% load staticfiles %} <!DOCTYPE html> <html> <head> <meta charset=”utf-8″> {% if title %} <!– if the var title has not null, append the (Current page) title to out Main Title –> <title>Fitzroy Catholic Bookshop | {{title}}</title> {% else %} <title> Fitzroy Catholic Bookshop </title> {% endif %} {% block styles %} {% bootstrap_css %} <link rel=”stylesheet” href=”{% static ‘css/base.css’ %}”> {% endblock %} <!– UIkit CSS –> <link rel=”stylesheet” href=”https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.42/css/uikit.min.css” /> <!– UIkit JS –> <script data-src=”https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.42/js/uikit.min.js”></script> <script data-src=”https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.42/js/uikit-icons.min.js”></script> </head> <body > <!– Add navbar.html to this template –> {% include “navbar.html” %} {% block content %} <!– Landing Section –> <div class=”container” > <h5 style=”text-align:left;”><span id=”bread_title” >Navigation : </span> <span id=”bread”>Home -></span></h5> <hr> <div class=”col-md-12″ id=”call_to_action” > <div class=”row”> <div class=”col-md-6″> <h4 style=”text-align:left;color:#5e12aa;font-weight:bolder;”>About Us.</h4> <div id=”description” style=”border-radius:5%; padding:20px;margin:30px;background-color: #ffffff; opacity: .4;color:black;”> <h3 style=”color:black;margin:30px;”> Fitzroy Catholic Bookshop is a one-stop place for all Best Sellers. </h3> <h4>Discover them all at the comfort of your home!</h4> </div> </div> </div> <div class=”row”> <div class=”col-md-4″></div> <div class=”col-md-4″ style=”background-color:ffffff;text-align:center;”> <h5 style=”color:#5e12aa;font-weight:bolder;font-size:30px;” >Search For A Product </h5> <button class=”btn btn-primary” style=”margin-bottom:20px;padding-left:50px;padding-right:50px;”>Search</button> </div> <div class=”col-md-4″></div> </div> </div> <!– Contact us section –> <div class=”col-md-12″ style=”background-color:#fffba9;”> <div class=”col-md-6″> <h3 style=”margin-top:40px; text-align:center;font-weight:bolder;color:#5e12aa;”>Contact Us</h3> <!– description for contact us –> <h4 style=”margin-top:50px; text-align:center;font-weight:bolder;”>Your needs and user experience is at heart. We will give you feedback in less than 48 hours</h4> </div> <div class=”col-md-6″> <div class=”panel” style=”border-color:#3c11cb; padding:20px; margin:30px;”> <!– the contact us form –> <form> <div class=”form-group”> <label >Name</label> <input type=”text” class=”form-control” placeholder=”Name”> <label >Email address</label> <input type=”email” class=”form-control” aria-describedby=”emailHelp” placeholder=”Email”> <label >Message</label> <input type=”textfield” class=”form-control” placeholder=”Message”> <small id=”emailHelp” class=”form-text text-muted”>Your Email is never shared with anyone else..</small> </div> <button type=”submit” class=”btn btn-primary”>Submit</button> </form> <!– End of contact us –> </div> </div> </div> <!– Sitemap section: for fitzroy library –> <div class=”col-md-12″> <div class=”col-md-6″> <img style=”width:600px; margin-top:30px;margin-bottom:30px;” data-src=”data:image/jpeg;base64,/9j/4A…b/lYujjqCm/57koLidGj8KjQ/uV7w/wCpa/8ASJcED//Z”/> </div> <div class=”col-md-6″> <h3 style=”margin-top:40px;font-weight:bolder;color:#5e12aa;”>Locate Us</h3> <h4>Use the sitemap to the left to locate Us.</h4> </div> </div> <!– Footer section–> <div class=”col-md-12″ style=”background-color:#fffba9;”> <!– Social media–> <div class=”col-md-12″ style=”background:#fffba9;padding-top:20px;border-color:#5e12aa;”> <div class=”col-md-2″> <h4 style=”height:150px;width:150px;”>Facebook<img data-src=”data:image/png;base64,iVB…YII=”/> </h4> </div> <div class=”col-md-2″> <p style=”margin-top:30px;”>Join Us On Facebook to stay up to date with out new arrivals </div> <div class=”col-md-2″> <h4 style=”height:150px;width:150px;”>Twitter <img data-src=”https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS_Cl7FZnFqvBrKZWB4uiK1I2ckryTAb_3FgWtGBzBMHwZLuzOh”/></h4> </div> <div class=”col-md-2″> <p style=”margin-top:30px;”> Join Us on Twitter get instant updates answers to your queries </div> <div class=”col-md-2″> <h4 style=”height:150px;width:150px;”>Google + <img data-src=”data:image/jpeg;base64,/9j/4AAQSkZJ.QP/Z”/></h4> </div> <div class=”col-md-2″> <p style=”margin-top:30px;”>Our Community on Google + is waiting for you. Join Us.</p> </div> </div> <div class=”col-md-12″ style=”margin-top:30px; margin-bottom:30px;”> <div class=”col-md-4″></div> <div class=”col-md-4″> <p> Are you stranded on how to find what you are looking for? Whenever you have forgotten the name of what you are looking for, or when spoiled of choice when it comes to choosing what to buy, our community on social media will be more than glad to help you out. Join Us today and get all the benefits at no extra cost. </p> </div> <div class=”col-md-4″></div> </div> </div> <!– End Of Footer Section –> </div> {%endblock%} {% block scripts %} <script data-src=”https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js”></script> {% bootstrap_javascript %} {% endblock %} </body> |
maintenance.html
{% extends ‘base.html’%}{% load staticfiles%}{% load bootstrap3 %} {% block content %} {{block.super}} <h5 style=”text-align:right;”><span id=”bread_title” >Navigation : </span> <span id=”bread”>Home | Maintenance</span></h5> <hr> <div class=”container”> <div class=”col-xs-0 col-sm-0 col-md-5 col-lg-5 col-xl-5″> <h3 style=”margin-top:50px;text-align:left;color:#5e12aa;font-weight:bolder;”>Adding Items To Catalogue</h3> <p style=”margin-top:50px;”>Fill in the form with the item’s information to add it to the bookshop’s database. Please ensure that prices are numeric value and not words. </p> </div> <div class=”col-xs-12 col-sm-12 col-md-7 col-lg-7 col-xl-7″> <form method=”post” enctype=”multipart/form-data”> {% csrf_token %} {% bootstrap_form form%} <input type=”submit” class=”btn btn-primary btn-sm” value=”Add to Catalogue” style=”margin-bottom:20px;”> <input type=”hidden” name=”next” value=”{{next}}”> </form> </div> |
map.html
{% extends ‘base.html’%}{% load staticfiles%}{% load bootstrap3 %} {% block content %} {{block.super}} <h5 style=”text-align:right;”><span id=”bread_title” >Navigation : </span> <span id=”bread”>Home | Site Map</span></h5> <hr> <div class=”container”> <div class=”col-xs-0 col-sm-0 col-md-3 col-lg-3 col-xl-3″ style=”background-color:#fffba9;”></div> <div class=”col-xs-12 col-sm-12 col-md-6 col-lg-6 col-xl-6″ style=”background-color:#fffba9;”> <div > <img style=”width:500px;height:300px;” data-src=”https://image.ibb.co/ge1RT8/map.png” alt=”Site_Map” border=”0″> </div> </div> <div class=”col-xs-0 col-sm-0 col-md-3 col-lg-3 col-xl-3″ style=”background-color:#fffba9;”></div> |
navba.html
{% load bootstrap3 %}{% load static %} <!DOCTYPE html> <html lang=”en”> <head> <meta charset=”utf-8″> <meta name=”viewport” content=”width=device-width, initial-scale=1″> <link rel=”stylesheet” href=”path/to/font-awesome/css/font-awesome.min.css”> <link rel=”stylesheet” href=”https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css”> <link rel=”stylesheet” href=”{% static ‘css/navbar.css’ %}”> </head> <body> <nav id=”nav_body” class=”navbar navbar-inverse” id=”nav_body” > <div class=”col-md-5″ > <image data-src=”https://www.userlogos.org/files/logos/euphonicnight/Lib1.png” id =”logo_image” style=”width:300px;height:150px;”/> </div> <div class=”col-md-7″> <h1 id =”title”>Fitzroy Catholic Bookshop</h1> <h5 id=”menu_bar” style=”text-align:center;”> <a href=”{%url ‘Home’%}”>Home</a> | <a href=”{%url ‘Site_Map’%}”>Site Map</a> | <a href=”{%url ‘Search’%}”>Search</a> | <a href=”{%url ‘Maintenance’%}”>Maintenance</a></h5> </div> </nav> </div> </body> </html> |
results.html
<!– inherit from base template –> {% extends ‘base.html’%}{% load staticfiles%}{% load bootstrap3 %} {% block content %} {{block.super}} <h5 style=”text-align:center;”><span id=”bread_title” >Navigation : </span> <span id=”bread”>Home | Search Results</span></h5> <hr> <div class=”container”> <div class=”col-xs-12 col-sm-12 col-md-12 col-lg-12 col-xl-12″> <div class=”panel” style=”padding:20px;”> <!– Return Results of items matching user query –> {% if requested_prods %} <h3>{{ requested_prods|length }} products found that match your search</h3> {% else %} <h3> Oops! No items match your search</h3> {% endif %} <!– Loop though the result and displays them within the template –> {% for item in requested_prods %} <div class=”panel” style=”background-color:#fffba9; padding:20px;”> <h4 style=”margin:10px;”>{{item.title}}</h4> <img data-src=”{{item.image.url}}” class=”img-responsive” alt=’Image’> <p>Author: {{item.author}} Publisher: {{item.publisher}} Price: {{item.quantity}}</p></image> </div> {% endfor %} </div> </div> |
search.html
<!– inherit from the base template –> {% extends ‘base.html’%} {% load staticfiles%}{% load bootstrap3 %} <!– the body contained within the block content tags –> {% block content %} {{block.super}} <h5 style=”text-align:right;”><span id=”bread_title” >Navigation : </span> <span id=”bread”>Home -> Search</span></h5> <hr> <div class=”container” > <div class=col-md-12 style=”padding:20px;”> <div class=”col-xs-12 col-sm-12 col-md-12 col-lg-5 col-xl-5″> <h3 style=”margin-top:20px;text-align:center;color:#5e12aa;font-weight:bolder;”>How to Search</h3> <p style=”margin-top:30px;font-weight:bolder;”>In order to find your favourite book use the filters to the left to find a book that matches your preferences”</p> </div> <!– Search form section –> <div class=”col-xs-12 col-sm-12 col-md-7 col-lg-7 col-xl-7″> <form method=”post” enctype=”multipart/form-data”> {% csrf_token %} {% bootstrap_form form%} <input type=”submit” class=”btn btn-primary btn-sm” value=”Search”> <input type=”hidden” name=”next” value=”{{next}}”> </form> </div> </div> |
base.css
h1 { color: green; } #welcome_message { margin-left: 30px; } #call_to_action{ background:url(‘https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRuWSLq8CS5FqjofyPNn_xXZoUNhi_MKnn2owjo3OuGB5ZiM8HCvw’); background-repeat: no-repeat; background-size: cover; } #description{ background: rgba(46, 51, 56, 0.2)!important; } #description { margin:50px; } |
#menu_bar{ text-align: right; color:red; } #title { color: #5e12aa; font-weight: bolder; margin: 20px; font-size:50px; } #nav_body{ background-color:#f2f2f2; border:#f2f2f2; padding: 10px; } #logo_image{ width: 350px; height:200px; } #bread_title{ color:#6fa3f7; font-weight: bolder; margin-left:60px; } #bread{ color:#f7956f; font-weight: bolder; } |