Skip to content

bravoalpha79/MothRadar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MothRadar

Build Status

MothRadar is a ticket tracker for a fictional application called UnicornAttractor. MothRadar enables the users of the UnicornAttractor to raise tickets on issues encountered during the app use, to browse through existing tickets, and to comment on them. It also provides the users with the ability to upvote existing tickets, thus helping the UnicornAttractor app owner to prioritise fixes among existing bugs.

"MothRadar never loses track of any bug that enters its scope."

UX

MothRadar is intended primarily for the users of the UnicornAttractor app. It also aims to facilitate the owner's maintenance work on the app by providing user feedback in the form of tickets, comments and upvotes.

Visitors (unregistered users and users not logged-in) have only the option to browse, search and filter existing tickets and view individual ticket details (read-only access).

Registered and logged-in users have access to the full set of features of the MothRadar app: new ticket creation, commenting on existing tickets, and upvoting tickets.

Tickets can be raised as one of two types: Bug (for UnicornAttractor app issues) or Feature (for suggestions on app improvement). The structure and handling of either ticket type in MothRadar is identical, with the exception of the Upvote feature which is free for Bug tickets but requires payment for Feature tickets.

User Stories

Based on the above general requirements, the following user stories have been identified:

As a user:

  1. I want to be provided with some general information on the MothRadar app's available features and how to use them.
  2. I want to be able to create a new ticket.
  3. I want to be able to edit/amend a ticket that I have created.
  4. I want to be able to browse existing tickets.
  5. I want to be able to search existing tickets by keyword(s) to narrow my browsing scope.
  6. I want to be able to view the most upvoted tickets.
  7. I want to be able to filter my tickets only.
  8. I want to be able to filter by Bugs only or Features only.
  9. I want to be able to view all details of a selected ticket, including date created, author, upvotes count, and existing comments on the ticket.
  10. I want to be able to comment on an existing ticket in order to provide more details if I have them.
  11. I want to be able to edit my comment(s).
  12. I want to be able to delete my comment(s).
  13. I want to be able to upvote a ticket I find relevant for me, so I can get it fixed sooner.
  14. I want to be able to view my account details.
  15. I want to be able to edit my account details.
  16. I want to be able to change my password.
  17. I want to be able to log out so I can protect my account when I am finished working with the app.
  18. I want to be able to reset my password via email if I forget it.

As app owner:

  1. (19) I want to have a user login functionality.
  2. (20) I want only logged-in users to be able to make changes to the existing ticket data (ticket details, comments and upvotes).
  3. (21) I want a logged-in user to be able to edit ticket details only for tickets they have created, and only if the ticket is still in its initial status.
  4. (22) I want not logged-in users to be able to browse-only the site.
  5. (23) I want to provide a visitor with the option to register and create a user account.
  6. (24) I want to have a payment process for logged-in users upvoting Feature tickets.

UI structure

Essential elements

Due to time constraints, it has been decided that, with regards to comments, in this version of the app only creation of comments will be enabled. Editing and deleting of existing comments (by their creator only) - user stories 11 and 12 - will need to be implemented in a future version.

The following UI elements are essential for the implementation of the user stories:

  • navigation bar/menu,
  • landing page,
  • ticket creation form,
  • ticket search form,
  • ticket filters/views:
    • most upvoted,
    • created by user,
    • Bugs only,
    • Features only,
  • new comment form,
  • upvote button and counter,
  • logout button,
  • user login form,
  • user registration form,
  • payment form.

The full stack of the app is built using the Django framework.

Most of the needed forms are obtained either directly from Django default forms (User) or by defining Model forms (Ticket and Upvote) and rely on Django form validation. Crispy Forms are used for rendering of all Django-based forms.

The two exceptions to this are:

  • the Ticket Search form, which uses a GET method and therefore does not require validation, and
  • the comments "form", which uses JavaScript processing (including an Ajax call to backend) and is therefore not defined as an HTML form element but just as an HTML text area with a button to trigger the JavaScript script.

Visual layout

Bootstrap styling has been used as much as possible to assure full responsivity of the site.

A top-fixed Bootstrap navbar with the essential links is present at all times. At screen sizes below 992px this is reduced to just the Logo and the "burger icon" which opens a "sidenav" with the links. On screen sizes below 992px all content is displayed in single-column layout (screen-wide), while on large screens (992px and above) it is dependent on the actual page displayed. Four wireframe.cc wireframes (for desktop-size display) were produced during the prototyping phase of the project:

The only major departures from the initial design is in that the sidebar has been placed to the left of the main content (as opposed to the right in the wireframes) and that it has been removed on detail views as not needed.
Also, the "by status" filter has been replaced by a "my tickets" filter.

Font and colour scheme

The "Open Sans" font from Google Fonts was chosen for its professional appearance and readability.

The colour scheme was chosen to mimic the "dark mode" settings of Windows, IDEs, Stack Overflow, Slack etc.
Thus the basic background colour is a very dark (near-black) grey (#222121) with lighter or darker grey elements on top of it, whereas the main font colour is a very light grey (#b9b7b7) - pure white and off-white have not been used in order to avoid too strong a contrast.
The only elements departing from this general scheme are buttons and badges (styled with Bootstrap default colour classes), alerts (default Bootstrap colour classes with occasional custom adjustments), links/anchors (default blue colour) and ticket type and status tags (custom colours). These details were chosen in order to add clear highlight to elements that point to a certain functionality, or to subtly highlight specific ticket data.

Features

Existing features

Navbar

The navbar is implemented as a Bootstrap top-fixed navbar with responsive layout (breakpoint 992px). The contents (links) in the navbar change dynamically depending on whether the user is logged in or not.

Landing page

The landing page is designed in the form of interactive cards which, when clicked, expand to reveal the details of certain features of the application.

Ticket list view

The default ticket list view displays the list of all tickets in the database sorted by date and time created (newest first).

Ticket list view filters/sorting

Four sort/filter options are available to all users: sort by most recent (default view), sort by most upvoted, filter Bug tickets only, and filter Feature tickets only.
For logged-in users, a third filter is available to display only the tickets created by that user, the total number of those tickets being indicated in the sidebar.

The current selected view/filter is clearly indicated by the UI by colour-highlighting the corresponding view's link/button in the sidebar.

Pagination (5 tickets per page) is implemented in all list views.

Ticket list search

The search function is implemented as a simple search box with a button to trigger the search. The search is performed on top of any current view filters, so if e.g. the user has selected the "Features only" filter, the search will be performed among Feature tickets only.

Note: the current search functionality uses a simple __icontains on the Ticket model's description field. The plan is to implement a more complex search algorithm, and include both the title and the description fields, in a future version of the app.

Ticket detail view

The ticket detail view provides any user with all details of the ticket (author, creation date, title, description, ticket type and ticket status), plus the upvotes count and the complete list of comments for the ticket.
For logged-in users, this is expanded by:

  • a button to upvote the ticket (if not already upvoted),
  • a button to edit the ticket (if raised by the current user and if in "OPENED" i.e. initial status),
  • a text area and a button to post comments.

Upvoting system

Ticket upvoting is login-dependent. Any ticket can be upvoted only once by an individual user.

Bug ticket upvoting

Bug ticket upvoting is free of charge.
The status of the upvote button provides clear feedback on the availability of the upvote function for the current user (greyed out if the ticket is already upvoted by the user, and completely absent if the user is not logged in).
A click on the available Upvote button provides the user with success feedback, greys out the button, and updates the upvote count.

Feature ticket upvoting

The UI for the initiation of Ticket upvoting is near-identical to the one described above, with two differences:

  • a lock icon is displayed on the Upvote button in Feature tickets, to indicate a reserved feature, and
  • click on the available Upvote button triggers a confirmation modal which informs the user that it is a paid service (including the price) and provides the user with the option to cancel the action or to continue.

Confirmation of the modal redirects the user to a Stripe Card Payment processing form. Only credit card details (card number, CVV, expiry month and expiry year) are required to complete payment, the user is asked for no personal information.

The user is informed about the payment status on every step of the process, either by an error text next to the payment button (in case of invalid CVV or expiry date/month) or by a closable Bootstrap alert at the top of the screen.

User handling

User handling relies completely on Django authorisation and forms to create and update user data and to authorise users. Thus the user has the option to register, log in, log out, edit password, edit profile data, and reset password via email.

Defensive design

Defensive design principles have been implemented wherever possible, namely by restricting the ability to add to the database (ticket creation, ticket updating, adding of comments, upvoting) to logged-in users only and by restricting the edit possibilities only to the data "owned" (authored) by the current logged-in user.
Wherever possible, the restrictions have been implemented both at the frontend (absence of access to buttons/views) and at the backend (login_required view decoration, filtering querysets by author).

Features left to implement

  1. Add a better search algorithm (ability to search by complex phrases, as well as to search on both ticket title and description).

  2. For the logged-in user, add the ability to edit and delete own comments.

  3. Add additional ticket statuses (e.g. "Analysed" - to indicate that the ticket has been attended to i.e. it is no longer in initial state - and "Closed", to indicate that either the fix has been fully validated or that a ticket has been resolved in some other way).

  4. Add an algorithm to prevent the user from accidentally creating a duplicated ticket by going back to a previously created ticket's form (time constraints prevented this from being implemented in the current version).

Database structure

On top of the Django User model, which has not been modified for this project, three more models have been created:

  1. Ticket - with User as Foreign Key (author).

    idtitledescriptionticket_typestatusauthor_iddate_created
    2Some issue 1Some description of issue 1 editedFEATURESOLVED32020-05-02
    4Some issue 2Description of Issue 2 in a few words...BUGINPROG32020-05-02
  2. Comment - with User and Ticket as Foreign Keys (author and rel_ticket).

    idtextcreatedrel_ticket_idauthor_id
    1Here is my very fist comment to a ticket.2020-05-04 11:47:20.17183121
    2One more comment to this issue2020-05-04 11:47:38.59162323
  3. Upvote - with User and Ticket as Foreign Keys (upvoter and ticket).

    idticket_idupvoter_id
    46231
    47211

All Foreign Keys have been set with on_delete=models.CASCADE.

Each of the four models is handled by a separate app - users, tickets, comments and upvotes.

Deployment

The deployed app can be found here: MothRadar

The application has been deployed to Heroku using the following procedure:

  1. On Heroku, create a new app mothradar-ba79.
  2. In the Heroku App Dashboard, under the Resources tab, add Heroku Postgres (select the "Hobby Dev - Free" option).
  3. In the project workspace's virtual environment, use pip install to install dj-database-url, whitenoise and gunicorn (psycopg2 has already been installed).
  4. Run pip freeze --local > requirements.txt to update the requirements file.
  5. From Heroku App Config Vars (Settings tab), copy the DATABASE_URL.
    In env.py, add the DATABASE_URL, a DEVELOPMENT environment variable with the value of "1", and a LOCALHOST variable`.
  6. In settings.py, import the DEVELOPMENT variable and set DEBUG dependent on the value of the DEVELOPMENT variable:
if os.environ.get("DEVELOPMENT"):
    development = True
else:
    development = False
DEBUG = development
  1. In settings.py, import dj_database_url. Under DATABASES, comment out the Sqlite database and add the Postgres database:
DATABASES = {"default": dj_database_url.parse(os.environ.get("DATABASE_URL"))}
  1. Run python manage.py makemigrations and then python manage.py migrate.

  2. In settings.py, under DATABASES, uncomment the Sqlite database and set the database selection (Sqlite and Postgres) depending on the DATABASE_URL variable:

if os.environ.get("DATABASE_URL"):
    DATABASES = {"default": dj_database_url.parse(os.environ.get("DATABASE_URL"))}
else:
    DATABASES = {
        "default": {
            "ENGINE": "django.db.backends.sqlite3",
            "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
        }
    }
  1. In settings.py, under MIDDLEWARE, add whitenoise.middleware.WhiteNoiseMiddleware.
    At the bottom of the file, add:
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
  1. In the root of the project workspace, create a Procfile (capital P!) with the following content: web: gunicorn mothradar.wsgi:application
    Save the file.

  2. Still in the root, create a folder named "static".

  3. From env.py, copy the following environment variables and their values (without quotes!) into Heroku App Config Vars:

SECRET_KEY
EMAIL_ADDRESS
EMAIL_PASSWORD
STRIPE_PUBLISHABLE
STRIPE_SECRET

Add a DISABLE_COLLECTSTATIC Config Var and set its value to 1.

  1. In settings.py, get the LOCALHOST environment variable as localhost.
    Under ALLOWED_HOSTS, add mothradar-ba79.herokuapp.com and localhost.

  2. In Terminal, run python manage.py collectstatic.

  3. Commit and push all changes to GitHub master.

  4. In Heroku App DashBoard, under the Deploy tab, select GitHub as Deployment method. In the search box, type the name of the GitHub repo (MothRadar) and click "Connect".
    Under Manual deploy, make sure that the selected branch is master, and click Deploy Branch.

  5. The app is now deployed on Heroku.

Local development

Prerequisites:

  • IDE of your choice with:
  • created Gmail account with two-factor authentication enabled, and
  • created Stripe free account.

Note: the Terminal commands in the following steps assume a Windows environment and a Command Prompt shell. If you are using a different operating system and/or shell, the commands will need to be adapted to your environment.

If you want to work on this project locally, follow these steps:

  1. Clone or download the MothRadar GitHub repository into your local IDE.

  2. To install the project's dependencies, it is recommended to create a virtual environment to prevent the dependencies from being installed globally on your system.
    To create a virtual environment for your project, in the Terminal, in the project's root directory, enter:

    python -m venv venv

    and then activate the created virtual environment with

    venv\Scripts\activate

  3. Upgrade pip if needed:

    python -m pip install --upgrade pip

  4. Install the project dependencies using the following command:

    pip install -r requirements.txt

  5. In the root directory of the project (where the manage.py file is located), create a file named env.py.

    Remember to check immediately that the env.py file is listed in your .gitignore file to prevent your sensitive data from being committed and pushed to GitHub.

    Inside the env.py file, enter the following commands and variables:

    Note: all variable values must be in quotes.

     import os
    
     os.environ["DEVELOPMENT"] = "1"
     os.environ["LOCALHOST"] = "127.0.0.1"
    
     os.environ["SECRET_KEY"] =      # your secret key
     os.environ["EMAIL_ADDRESS"] =   # your Gmail email address 
     os.environ["EMAIL_PASSWORD"] =  # your Gmail two-factor authentication app password
    
     os.environ["STRIPE_PUBLISHABLE"] = # your Stripe Publishable Key 
     os.environ["STRIPE_SECRET"] = # your Stripe Secret Key

    Note: setting the "DEVELOPMENT" variable serves to set DEBUG=True during development. If you want DEBUG=False, simply omit the "DEVELOPMENT" variable definition.

    Save the env.py file.

  6. In the Terminal, run

    python manage.py makemigrations

    to create the migrations for your Django database, and then

    python manage.py migrate

    to apply the migrations to the database.

  7. In the Terminal, run

    python manage.py createsuperuser

    When prompted, enter a username, email, password, and repeat password, to complete superuser creation.

The application can now be run locally using the following command:

python manage.py runserver


Testing

The testing process and results are detailed in a separate document.

Testing summary:

A total of seven issues have been identified. Six of them have been fixed completely, and for one (Issue #1) a workaround solution has been implemented.
The HTML code errors raised during code validation caused no issues in the functioning of the app.

The application is considered ready for production.

Technologies Used

Languages:

  • HTML for page structure and content;
  • CSS for content styling;
  • JavaScript for HTML DOM manipulation, Ajax server requests and Stripe payment processing;
  • Python 3 for application logic;

Framework:

  • Django framework (v3.0.5) for application backend, development database provision (SQLite3), routing and template manipulation;

Libraries:

  • Bootstrap was used for responsive design, styling, navigation bar, buttons, alerts and modal implementation;
  • jQuery for easier DOM manipulation and for Stripe payment;
  • Crispy Forms to facilitate HTML rendering of Django forms;
  • Fonts were obtained from Google Fonts;
  • Icons were obtained from FontAwesome;

Development:

  • Visual Studio Code was used as the IDE for development and Git version control;
  • GitHub was used for source code storage;
  • Google Chrome Developer Tools were used for development and testing, debugging and as a styling aid;
  • Travis CI for continuous integration;

Code validation tools:

Deployment:

  • WhiteNoise to facilitate Django static files serving on Heroku;
  • Heroku for application online deployment and production database provision (PostgreSQL);

Utilities:

External (third-party) services:

  • Gmail for sending of Password Reset messages to users;
  • Stripe v2 was used for credit card payment processing.

Credits

Code

HTML

  1. The solution to highlight the currently active link in the sidebar within the ticket_list.html template ({% if path == ... %}) was found on Stack Overflow.
  2. The custom script to pass the Stripe Publishable Key into JavaScript (in upvotes.html) was obtained from the Code Institute course videos.

JavaScript

The complete Stripe payment processing script (stripe.js file) was obtained from Code Institute course videos.

Python / Django

MothRadar being my first independent Django project, I had to still tackle the basics of Django together with building a full-fledged app. Thus I had to rely heavily on external resources, primarily on Corey Schafer's YouTube Django series tutorial and then on Code Institute course videos as well.
While I never wanted to just copy-paste any code, especially not without understanding it, in the beginning stages of my project (the users app and the initial stage of the tickets app) I was coding along with the tutorials a lot, while adapting the code to the specific needs of my app. In later stages I relied mostly on Django documentation and occasionally on Stack Overflow to implement the desired functionality.
Thus, while the concrete app implementation is definitely my own, the significant influence of Corey's videos is inevitable. This includes the idea to use Bootstrap cards to display the main items (tickets and comments), a sidebar, as well as the use of Crispy Forms to render Django forms.
I have honestly tried to do all the coding as independently as I could once my understanding of Django and of documentation broadened, but at this stage of my coding education building a full Django app of this size completely independently would have been impossible, especially in the given timeframe.

Media

Acknowledgements

Above all I would like to thank Chris (ckz8780) who helped me immensely on this project with his knowledge of Django. While never providing me with complete solutions, on multiple occasions he was able to point me to the right direction to look for them. He also helped me debug the Stripe payment functionality when I got completely stuck. Thanks, Chris!

Also, many thanks to all my fellow CI Slack members who also helped either by pointing me to various Django resources, by sharing their experiences, having walked the same path before me, or simply by encouraging me when I lost my momentum. Anna, Jo, Simen, Johan and Anthony - I owe you all bigtime!!

Finally, a special THANK YOU to my mentor Narender, who always patiently tries and manages to find a way to push my limits just that tiny bit further. :)