alexkisielewicz / photo-adventures

Photo Adventures Website - Portfolio Project #4 (Full-Stack Toolkit) for Diploma in Full Stack Software Development at Code Institute.
https://photo-adventures.herokuapp.com/
3 stars 1 forks source link
cloudinary css3 django-project full-stack-web-development html5 javascript python

Photo Adventures Website

Full-Stack Project (HTML5 CSS3, Bootstrap, Django, Python, JavaScript, jQuery, PostgreSQL, Cloudinary)


Developer: Aleksander Kisielewicz

View live website here :computer:

Program mockup

Photo Adventures Website was created as Portfolio Project #4 (Full-Stack Toolkit) for Diploma in Full Stack Software Development at Code Institute.

Project purpose was to build a full-stack site using agile methodology to plan and design web application using MVC framework and related contemporary technologies. Appplication offers users full CRUD (create, read, update, delete) functionality.

Application offers such functionalities as:

Table of content

Project

Strategy/Scope

I chose to develop an web application that can be used in real life. My main focus was always on providing an excellent user experience, which is why I decided to create a web application that is both practical and engaging. Photo Adventures is a user-friendly blog-style website where users can share their photo adventures by creating visually appealing blog posts that include both images and text. Application should have clean and intuitive user interface and offer easy access and navigation to all functionalities. Application should also be responsive on devices of all screen sizes.

The website's target audience is anyone who is passionate about photography and enjoys sharing their experiences and stories through pictures. It is inclusive of both amateur and professional photographers who are looking for a platform to showcase their work and connect with like-minded individuals. Whether it's capturing stunning landscapes, wildlife images, or simply capturing moments of everyday life, Photo Adventures Website welcomes anyone who wants to share their love of photography with the world.

To achieve the strategy goals I implemented following features:

Agile Methodology for project planning

I used agile methodology for the first time when planning full-stack django website with a focus on delivering the basic functionalities. I prioritized features by labeling them as "must-have" or "could-have" and moved some less critical ones to future development. To guide my development process, I created user stories for both the admin user and regular visitors. These stories helped to define the features and functionalities that were most important to project's target audience.

As a solo developer who was learning a lot during development, I faced challenges in estimating the time required for each task and only had a basic concept of what I would create. Therefore, I kept things simple and focused on achievable goals. As the project grew, I was able to add more advanced features and group user stories into milestones. However, I could not find the "epics" feature in GitHub Projects, only milestones (it provides only milestones and issues). Epics are supposed to be larger in scope than milestones, representing a significant amount of work. Milestones, on the other hand, are meant to mark significant points in time in terms of project completion. In this document, I added epics, but on the project board, I used only milestones to stay in order with GitHub's features.

To keep track of progress, I used a kanban board divided into following sections: "to do", "in progress" "done", "future enhancements" and "bugs" that allowed to visualize all tasks and prioritize next steps.

By using agile methodology, I was able to stay organized and focused on delivering the most important features, while also allowing flexibility for future development. This experience gave me valuable insight and lessons that I can apply to future projects.

agile

agile-boards

Epic Milestone User stories
Epic 1: Basic Site Functionality Milestone 1: Authentication #2, #4
Milestone 2: View and select posts #1, #5, #17
Milestone 3: Post CRUD operations #3, #10, #12, #15, #26
Milestone 4: Commenting and Liking #7, #8, #9, #11, #23, #30
Epic 2: User Management Milestone 1: User profile and dashboard #20, #21, #25
Milestone 2: Contact form and spam prevention #16, #33
Milestone 3: Validation #39, #40
Milestone 4: Error handling and feedback #31, #38
Epic 3: Content Management Milestone 1: Draft posts and featured posts #10, #41
Milestone 2: Categories and tags #13, #14, #28, #29, #34, #35
Epic 4: Social media Integration Milestone 1: Share posts on social media #37
Milestone 2: Popular posts and likes display #6, #19, #41

agile-milestones

Site owner goals

External user's goal

Wireframes

Wireframes - PDF File

Index - main page

index-top index-down

Blog

blog

Single full post

full-post

About

about

Contact

contact

All accounts pages

accounts

User Experience (UX)

Colour Scheme

Colour palette was selected using coolors.co generator.

I created CSS classes and assigned them roles (examples):

Colour Scheme

Typography

The Merriweather font is the main font used throughout the whole website with Sans Serif as the fallback. Merriweather is a clean, modern looking and well known font. It is sourced from Google fonts and it's linked to css document via @import method.

typography-merriweather

The Gloock is a contemporary high-contrast serif typeface intended for display use. It draws inspiration from newspaper's headlines but with a contemporary approach. I used this font to use for headings and post titles. Serif font is set as fallback.

typography-gloock

Lobster font is used as Photo Adventures text logo, cursive font is set as fallback.

typography-lobster

Logic and features

Data model ad database structure

Project uses cloud-based PostgreSQL database provided by ElephantSQL as a service. ElephantSQL is known for its ease of use, reliability, and is a popular choice for Django projects that requires PostgreSQL database. It offers web interface with console for SQL queries. Database URL including API key is stored as enviromental variable in heroku.

To generate model diagrams I used django-extensions with python library pygraphviz. They show relationship between models.

All django apps including generic/default apps:

all-model

Custom app "blog" with models Post, Comment and TaggedPost.

db-model

Model Description
Post This model represents a blog post and contains fields for the post's title, author, category, excerpt, featured image, location, content, creation and update times, status, and likes. It also has a slug field to generate unique URLs for each post and uses the TaggableManager (taggit library) to add tags.
TaggedPost This model is used to associate tags with posts and is created automatically by the TaggableManager (taggit library).
Comment This model represents a comment on a blog post and contains fields for the post it belongs to, the commenter's name and email, the comment's body, and creation time. It also has an approved field that can be used to moderate comments by admin.

Features

Navbar and main menu

Bootstrap navbar component was used to create navigation bar. It is always visible and stays fixed at the top of the screen. Navbar consists of image logo, text logo and links to main areas of the site (home, blog, about and contact pages). There is also place for Sign up and Sign in buttons which are displayed to unauthenticated user.

navbar-default

Nabar displayed to authenticated user includes user name, gravatar and button link leading to user dashboard.

navbar-loggedin-user

Navbar displayed to staff/admin user includes button with link leading to admin panel (accounts).

navbar-admin

Mobile navbar for medium screen devices with hamburger menu button and collapsible menu.

navbar-mobile1

Mobile navbar for small screen devices, without image logo to save screen space.

navbar-mobile2

Footer

The footer consists of 3 columns. The first column contains copyright info and address. The second one has quick navigation links for easy navigation. The third one includes site logo and social media links with icons, allowing visitors to connect with the brand on popular social media platforms.

On small devices all elements are centered in one column that takes all width of the screen.

footer

Home page - carousel

User is welcomed with image slideshow with pictures related to such categories as Landscape, Nature, Architecture and Travel. Bootstrap carousel component was used to showcase different types of pictures and provide users with a visually engaging and dynamic experience. Slideshow has automatic transition but users can take control of the slides using "previous" and "next" buttons. Each slide picture has header and short slogan description located in overlay. Carousel takes all viewport width and height on big screens and is reduced on small devices that are usually used in portrait mode.

index-carousel

Hero section is designed to catch visitor's attention and provide them basic introduction to site content and purpose. Hero section consists of two columns, one with text and second with image. Unauthorised users can see "Sign up" and "Visit blog" call-to-action buttons. Authenticated user can see "Add post" and "Visit blog" buttons instead.

Home page - hero

index-hero

Below hero section there are animated counters that present "page in numbers" statistics. They show total number of posts, comments, reactions and registered users.

Home page - counters

index-counters

Most liked posts is a section that presents three posts with highest numer of "likes". Posts are displayed as bootstrap cards, they include links to full articles.

Home page - most liked posts

index-most-liked

Blog

Blog page is a list of all posts. Each post is presented as bootstrap card that includes featured image (or default placeholder if picture is not provided by user), post title, brief excerpt. In addition to this elements user can also find post details such as its location, author, creation date and number of likes and comments. The posts are paginated by 5 articles per page.

blog

It is possible to browse posts by tags.

blog

Full Post

When viewing a post in full, users will see either a featured image or a placeholder if the author hasn't provided one. As on the blog page, the post title and other details are displayed beneath the image. On the left side of the picture, an overlay shows the location information. In the top right corner, users will find a like button. Only authenticated users can like posts, and if the user is also the author of the post, they will see an edit button below the like button. In the bottom right corner of the picture, there are social media sharing buttons, allowing users to easily share the post on Facebook or Twitter.

In the full post view, users can also see post tags, which can be clicked to bring the user to a list of related posts. This feature helps readers discover other content on the same topic and explore related ideas.

Beneath the main content of the article, you'll find a comment section. On the left-hand side of the page, all comments are displayed, along with the author's Gravatar, username, and date of the comment. For users who do not have a Gravatar account, a placeholder image will be displayed instead. This ensures that all comments are visually consistent. To post a comment, users must be authenticated and can use the form provided in the right-hand column. A link to guidelines and posting rules is also included to ensure that all comments are respectful and related to topic.

full_post1

full_post2

full_post3

full_post3

About Page

The About page is divided into three paragraphs. The first paragraph introduces who we are as a group, while the second highlights passion for photography. The third paragraph outlines the mission and what we hope to achieve. Images are included within the text to visually enhance the content.

about1

At the bottom of the page, a bootstrap accordion houses a FAQ section. The answers to common questions are provided in a concise format, making it easy for users to quickly find the information they need.

about2

Contact Page

The Contact page is designed with a two-column layout, featuring the contact form on the left and the location, address, and Google Maps on the right. Users can use the contact form to get in touch with the site admin directly. To facilitate sending emails through client-side technologies, the Email.js library has been integrated. The form allows users to fill in their name, email address, and message, and authenticated users can have their name and email fields pre-filled for convenience.

To prevent spam, a Google reCAPTCHA v2 widget has been implemented.

On the right-hand side, the location, address, and Google Maps provide users with a visual representation of where we are located and make it easy for them to find us.

contact

Admin Panel

The admin panel provides access to various details related to blog posts, including the post title, slug, author, category, tags, status, and creation date. These details make it easy for the admin to manage and organize posts effectively. There is also existing "featured" field in the model, this functionality was left as potential future enhancement and has been described in project's user stories.

In addition, actions have been registered that allow the admin to select multiple posts and change their status in one click. This feature saves time and helps to manage larger number of posts.

admin1

Also a list of filters have been registered in the admin view, admin can sort and display posts by author, status, etc.

admi2

User Dashboard

The page displays a dashboard for the user with basic statistics about their blog posts and gives user the ability to manage them (full CRUD functionality). The user's gravatar is displayed in the top left of the page, along with a welcome message that greets the user by name. In this section user can also find "Add post" button to click and start writing new post. The form of keypoints inform users that they can manage their content from the dashboard and can view their post history and monitor engagement such as likes and comments.

Three sections below show all posts written by user in a form of bootstrap cards. They are divideded into tabs: drafts, awaiting moderation and published posts. The sections stay hidden until there is related content to be presented. Each card can be clicked and overlay contains relevant control buttons allowing viewing, editing or deleteing posts.

dashboard1

dashboard2

dashboard3

dashboard4

Form of defensive development, post deletion confirmation screen.

dashboard5

Accounts Templates

All accounts subpages are based on one template designed for this purpose. It's a narrow container with header image and form or other relevant information.

User can sign up using registration form. Google reCaptcha v2 widget was implemented to ensure that only human users can register and to protect site against spam and bots. Users who have already registered can click a link at the bottom of the page to sign in.

accounts1

accounts2

Account verification setting has been set to "mandatory". Gmail SMTP service has been configured to provide email backend service for this functionality. Upon successful registration, user receives e-mail verification message.

accounts3

User is asked to click the link to activate the account.

accounts4

The link (if valid) brings user to confirmation page. On successful confirmation user is automatically signed in and redirected to home page.

accounts5

If the link is invalid or the token has expired, the user will be prompted to log in with their email and password to receive a new confirmation email.

accounts6

If a user who already has an account attempts to sign up again using the same email, they will receive a notification email.

accounts7

Returning registered user can sign in.

accounts8

Forgotten password can be reset.

accounts9

accounts10

Message that user receives if requested password reset. Again, if link/token is valid user can proceed with password change, if not, will see "bad token" error page and can repeat the process.

accounts11

accounts12

On successful password change user is asked to go to sign in page to continue.

accounts13

Add/Edit Post - form validation

Users can access the "Add Post" page from the user dashboard. The form on the page is used for submitting a new post. Basic validation has been implemented into the form using JavaScript.

The form fields reflect the model post fields: Title, Slug, Category, Tags, Excerpt, Image, Location, Content, and Status. All fields are required; however if users don't upload an image, a placeholder will be present in the template.

Slug field is being filled automatically (and converted to dash-separated lowercase words) when user type in title field, user still needs to interact with this field in order to make it valid. Event listener "input" is attached.

add-post1

Fields that are not validated with JavaScript:

Field Allowed input Matching charset, regular expression Length (min, max)
Title Alphanumeric, space, coma /^[a-zA-Z0-9 ,]+$/ 5 - 50
Slug Lowercase alphanumeric, dash /^[a-z0-9]+(?:-[a-z0-9]+)*$/ 5 - 50
Category (select) One option from dropdown menu Any option (no default is selected) N/A
Tags Comma-separated words with alpha char, space /^([a-zA-Z]{3,}\s,\s)*[a-zA-Z]{3,}$/ 3 - 60, the shortest tag - 3
Excerpt Alphanumeric, space, special any 10 - 200
Location Alpha, space, coma /^[a-zA-Z ,]+$/ 4 - 40
Status (radio checkbox) Any option from two available Valid if any is checked N/A

The validation functions have been connected to the field inputs using "input" event listeners. The script checks if the form is valid and then uses the Bootstrap classes "is-valid" or "is-invalid" to highlight the field in green or red. This gives the user feedback about whether the input is correct or incorrect. When all fields become valid and highlighted with green, the "submit" button becomes active and form can be send. Submit button is disabled by default when page is loaded. The same script and mechanism works on "Edit page", user need to interact with the fields, make them valid in order to be able to save changes.

add-post2 add-post3

Messages

Django messages are implemented in the website's app. These messages are displayed using the Bootstrap toast component on several occasions when feedback is needed, such as signing in/out, commenting, saving drafts, and editing changes to posts.

messages

Technology

Languages used

Software used

Python libraries/modules

Testing

Manual testing

Details of manual testing can be found in TESTING.md file.

Bugs/known issues

Minor syntax and spelling errors were eliminated during development, below is a list of registered issues.

Issue Problem Solution
#22 Bootstrap cards for posts without uploaded images are not displayed correctly If/else statement added in the template to diplay placeholder image if featured image is not available
#27 When the user sends the form with a new post or edits an existing post draft, the tag field stays empty after saving. The database is not being updated as expected. Save many to many method was added within if statement
#36 Error 404 - not found occurs when sending a comment on full_post.html. Queryset was changed to filted only published posts
#43 2 template syntax errors found using django-extensions validate_templates. One in account/email.html and one in account/verified_email_required.html Misspelled "load" in templates/accounts/email.html was corrected, head_title block was removed from accounts/verified_email_required.html (screenshot added to issue)
#44 During the tests, I found out that Google Maps iframe element (third party code) on contact.html page generates a console errors (screenshot added to issue) It appears that the errors are linked to the use of ad blockers in the browser. After testing with filters turned off and in private mode, I was able to browse without encountering any errors.

Deployment

App was deployed to heroku for the first time when django installation was completed to make sure that everything is working correctly.

Database (ElephangSQL)

  1. Navitate to ElephantSQL website, log in to your account
  2. In top-right corner click on green button "Create New Instance".
  3. Enter database name, leave plan field as is, optionaly enter tags.
  4. Select region, click on "Review" and then on "Create instance".
  5. Go to your dashboard, find newly created database instance, click on it.
  6. Copy URL starting with "postgress://"
  7. Paste this URL into env.py file as DATABASE_URL value and save the file.

    os.environ["DATABASE_URL"] = "postgres://yourLinkFromDatabaseDashboard"

Cloudinary

  1. Navigate to https://cloudinary.com/ and log in to your account.
  2. Navigate to dashboard/console https://console.cloudinary.com/console/ and copy API Enviroment variable starting with "cloudinary://".
  3. Paste copied url into env.py file as CLOUDINARY_URL value and save the file.
os.environ["CLOUDINARY_URL"] = "cloudinary://yourLinkFromCloudinaryDashboard"

Django secret key

In order to protect django app secret key it was set as anviroment variable and stored in env.py. Please change your password accordingly.

os.environ["SECRET_KEY"] = "yourSecretKey"

Gmail SMTP

Warning: Gitpod doesn't allow to use smtp service, emails won't be send if you run http server on Gitpod. If debug mode is switched to True, emails will be visible in the console. Emails will be sent correctly on deployed project assuming all enviromental variables are configured correctly.

In order to have google smtp service running in local env, make sure you add following variables to env.py:

import os

os.environ["EMAIL_HOST_USER"] = "youremail@gmail.com"
os.environ["EMAIL_HOST_PASSWORD"] = "yourpassword"
os.environ["DEFAULT_FROM_EMAIL"] = "Photo Adventures"

Email password needs to be generated in order to use gmail with third party apps. Go to https://myaccount.google.com/ navigate to security section to generate password. If you need more help on that, please follow this tutorial.

envvars

GitHub and Gitpod

Note: Repository was created using Code Institute template: https://github.com/Code-Institute-Org/gitpod-full-template

  1. Login to Github and navigate to repository: https://github.com/alexkisielewicz/photo-adventures

  2. Click on "Fork button" in upper-right corner and create a new form in your own account.

  3. Open your repository in local IDE or using Gitpod. Preferred way is to used Chrome Gitpod Extension. When you install extension, green "Gitpod" button appears in your repository. Click on it to cread new workspace.

  4. Go to workspace terminal and install all requirements using command: "pip install -r requirements.txt". All te packages will be installed. requirements.txt content:

    asgiref==3.6.0
    cloudinary==1.32.0
    crispy-bootstrap5==0.7
    dj-database-url==0.5.0
    dj3-cloudinary-storage==0.0.6
    Django==3.2.18
    django-allauth==0.52.0
    django-crispy-forms==2.0
    django-social-share==2.3.0
    django-summernote==0.8.20.0
    django-taggit==3.1.0
    gunicorn==20.1.0
    oauthlib==3.2.2
    psycopg2==2.9.5
    PyJWT==2.6.0
    python3-openid==3.2.0
    pytz==2022.7.1
    requests-oauthlib==1.3.1
    sqlparse==0.4.3
  5. Local env.py file should be configured as on example below:

    import os
    
    # Env vars
    os.environ["DATABASE_URL"] = "postgres://yourLinkCopiedFromElephantSQLDashboard"
    os.environ["SECRET_KEY"] = "YourSecretKey"
    os.environ["CLOUDINARY_URL"] = "cloudinary://yourLinkCopiedFromCloudinaryDashboard"
    
    # Gmail vars
    os.environ["EMAIL_HOST_USER"] = "youremail@gmail.com"
    os.environ["EMAIL_HOST_PASSWORD"] = "passwordObtainedFromGoogleAccount"
    os.environ["DEFAULT_FROM_EMAIL"] = "Photo Adventures"
  6. In order to save django changes in database migration needs to be made.

  7. Use terminal commands:

    python3 manage.py makemigrations
    python3 manage.py migrate
  8. Create superuser to access admin area using terminal command (email is optional, password won't be visible when typing, confirm password twice):

    python3 manage.py createsuperuser
  9. App can be run in gitpod enviroment using terminal command:

    python3 manage.py runserver
  10. Go to Heroku and follow further instructions below.

Heroku

  1. Navigate to https://heroku.com/ login to your account and open dashboard. Click button "New" and select "Create new app" button.

  2. Enter app name, I used "photo-adventures", chose your region and click on "Create app" button.

  3. Click on newly created app and go to "Deploy" tab and then to "Deployment method" section. Authorize and connect your GitHub account, then find and select your repository.

  4. Go to the "Settings" tab, click on "Reveal Config Vars" and add the following keys and values (all values should be strings without any quotation marks):

    NOTE: DISABLE_COLLECTSTATIC variable should be set to "1" for initial deployment. Before final deployment it should be removed.

    Key Value
    CLOUDINARY_URL cloudinary url beginning with cloudinary://
    DATABASE_URL postgress url beginning with postgress://
    DEFAULT_FROM_EMAIL Photo Adventures
    DISABLE_COLLECTSTATIC 1
    EMAIL_HOST_PASSWORD YourPassword obtained from Google account
    EMAIL_HOST_USER youremailaccount@gmail.com
    PORT 8000
    SECRET_KEY YourSecretKey, the same as in env.py

    envvars

  5. Return to your Gitpod workspace and navigate to the file photoadventures/settings.py. Change allowed hosts including the name of the app that you created in previous steps. In my case, it was 'photo-adventures.herokuapp.com'. Save the file.

    hosts

  6. Procfile required to run project on Heroku was already created but if you change your app's name please make sure that this change is reflected in Procfile. It can be found in your project's main directory. In my case Procfile looks as below:

    web: gunicorn photoadventures.wsgi
  7. After adding enviromental variables and editing Procfile project is ready for deployment. In Heroku app's dashboard navigate to "Deploy" tab, scroll down to "Manual deploy" section. Select main branch from dropdown menu and click on "Deploy Branch".

  8. Step required for final deployment: Navigate again to app's settings, reveal config vars and delete DISABLE_COLLECTSTATIC entry if it was set before.

  9. After built is done, you should be able to see the button with the link leading to deployed app. In my case http://photo-adventures.herokuapp.com.

Possible future development

If I had more time or decide to develop app further I would add/improve following functionalities that I moved to Future enhancements column on project board:

Credits

Code

Media

Learning resources

Acknowledgements

Disclaimer