Skip to content

paulio11/P4-Squigl-Twitter-Clone

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Squigl

Link to live site

Squigl on multiple devicies

Contents

  1. Introduction
  2. Project Planning
    1. Github Project
    2. Database Schema
    3. Epics and User Stories
    4. Timeboxing
  3. User Experience
    1. Wireframes
    2. Design Choices
      1. Typography
      2. Images
      3. Colour Scheme
      4. Layout
  4. Features
    1. Site Wide Features
    2. Specific Pages
    3. Unimplemented Features
  5. Bugs and Fixes
  6. Technologies
    1. Main Languages Used
    2. Frameworks
    3. Libraries
    4. Software and Other
  7. Testing
  8. Deployment
  9. Credits
    1. Code
    2. Text
    3. Images
    4. Acknowledgements

Introduction

Squigl is a Twitter clone, a social network built using the Django framework. Deployed to Gitpod.

This is the fourth milestone project required to complete my Diploma in Full Stack Software Development at Code Institute. I was required to build a Full-Stack site based on business logic used to control a centrally-owned dataset. With an authentication mechanism that provides role-based access to the site's data. This was achieved using HTML, CSS, JavaScript, Python and the Django framework paired with a relational database.

The name comes from the use of ~ in front of usernames, used here in a similar fashion to @username tags on Twitter. The tilde symbol is on the same keyboard key as # (on a UK keyboard) so it was a natural pairing for me. Its appearance as a squiggly line inspired the project’s name.

The idea behind Squigl was to create an alternative to Twitter. I started on this project during the beginning of the recent Elon Musk Twitter drama. Basing this project on a pre-existing website helped aid planning early on, establishing expected goals and requirements for end users.

Squigl allows users to post short posts on their customizable profile pages. Follow other users, like, and comment on posts. Send private messages to each other. Be notified when they are mentioned. See popular trending topics or search for users and posts by a phrase keyword.

Back to top πŸ”Ί

Project Planning

GitHub Project

The GitHub project board feature was used to keep track of what I was working on and what still needed to be done. I created a user story for each feature and moved them when necessary throughout the development of the site.

Github Project Board

Database Schema

The models required for this project are:

  • Post - for user posts.
  • Reply - for replies to the posts.
  • Message - for private messaging between users
  • CustomUser - my custom user model which includes additional fields for a user to customize their profile.

Database Schema

CustomUser Model

Name Type Details Notes
id Primary Key unique
username CharField unique, max_length=20, validators=[alphanumeric] Used by the user to login and for their url, therefor it has to be unique. Alphanumeric validator ensures that user tagging works as punctuation breaks the script.
email EmailField unique Used when a user attempts to reset their password.
name CharField max_length=30 Included a max length so it doesn't break the size of html elements.
avatar ResizedImageField blank=True, upload_to='avatars/', size=[150, 150], crop=['middle', 'center'], force_format='WEBP' The user's avatar is displayed as a small square. Cropped and resized to keep the file size low and the aspect ratio correct.
profile_background ResizedImageField blank=True, upload_to='backgrounds/', size=[600, 200], crop=['middle', 'center'], force_format='WEBP' Cropped and resized to keep the file size low and the aspect ratio correct.
about CharField max_length=200, blank=true Text to display on a user's profile.
website CharField max_length=50, blank=true A website link to display on a user's profile.
verified BooleanField default=False Text to display on a user's profile.
following ManyToManyField to self (CustomUser), blank=true A list of followed users.
strikes IntegerField default=0 Number of strikes gained, for moderation.

Post Model

Name Type Details Notes
id Primary Key unique
user Foreign Key to CustomUser The author of the post.
date DateTimeField auto_now_add=True Date of post, automatically added on creation.
post TextField max_length=400 Text content of the post.
image ResizedImageField blank=True, upload_to='post-images/', size=[600, None], force_format='WEBP' Optional image for post. Cropped to a width of 600px and converted to WEBP.
link CharField max_length=50, blank=True Optional website link.
likes ManyToManyField to CustomUser, blank=True A list of users that liked the post.
repost_post ForeignKey to self (Post), blank=True If this post is a repost this is the original post.
reported ManyToManyField to CustomUser, blank=True A list of user that have reported the post (for moderation).
read ManyToManyField to CustomUser, blank=True A list of users that have marked the post read (for mentions).

Reply Model

Name Type Details Notes
id Primary Key unique
user Foreign Key to CustomUser The author of the reply.
post Foreign Key to Post The parent post of the reply.
date DateTimeField auto_now_add=True Date of reply, automatically added on creation.
reply TextField max_length=400 Text content of the reply.
hidden BooleanField default=False The author of the parent post can hide inappropriate replies.
reported ManyToManyField to CustomUser, blank=True A list of user that have reported the reply (for moderation).
read ManyToManyField to CustomUser, blank=True A list of users that have marked the reply read (for mentions).

Message Model

Name Type Details Notes
id Primary Key unique
sender Foreign Key to CustomUser The user sending the message.
recipient Foreign Key to CustomUser The user receiving the message.
date DateTimeField auto_now_add=True Date of message, automatically added on creation.
message TextField max_length=400 Text content of the message.
sender_del BooleanField default=False True if the sender has deleted the message.
recipient_del BooleanField default=False True if the recipient has deleted the message.
read BooleanField default=False A user can mark a message as read, to remove it from unread messages.
reported BooleanField default=False A user can report a message (for moderation).

Epics and User Stories

There will be three types of users visiting Squigl. A new or logged out user, a registered user, and moderators. User stories were logged as issues on GitHub to track them throughout the project - Project Issues. They were subject to manual testing at the end of the project to determine if I was successful with my objectives.

Epic - New or Logged Out Users

User Story Achieved?
As a new user I can sign up so that I can have my own account and use the full feature set of the website βœ“
As a logged out user I can sign in so that I can return to my account βœ“
As a logged out user I can search squigl so that I can find users, posts and replies that I am looking for βœ“

Epic - A registered user can view a feed of posts from followed users

User Story Achieved?
As a user I can view my feed so that I can see my own posts and posts of users I follow βœ“
As a user I can follow or unfollow other users so that their posts appear in my feed βœ“

Epic - A registered user can customize their profile

User Story Achieved?
As a user I can upload an avatar so that it can represent me as a user βœ“
As a user I can add a link to my profile so that I can share something important to me or another website relevant to my account βœ“
As a user I can add a short description to my profile so that other users can find out more about me βœ“
As a user I can add an image as a background to my profile so that I can further customize my profile βœ“

Epic - A registered user can create new posts and manage their posts

User Story Achieved?
As a user I can create a new post so that I can share something with my followers βœ“
As a user I can add an image to my post so that I can share an image with my followers βœ“
As a user I can add a link to a website to my post so that I can share a website with my followers βœ“
As a user I can like my own or someone else's post so that I can show my support βœ“
As a user I can delete my own posts so that I can remove them from my profile if necessary βœ“
As a user I can edit my posts so that I can change them if necessary βœ“

Epic - A registered user can reply to posts and manage their replies

User Story Achieved?
As a user I can add a reply to my own posts or someone else's so that I can start or add to a conversation related to the post βœ“
As a user I can edit my replies so that I can change what I said or fix a mistake βœ“
As a user I can delete my replies so that I can remove them if I want βœ“
As a user I can hide a reply to my post so that it can be hidden from other users if inappropriate or irrelevant βœ“

Epic - A registered user can privately message other registered users

User Story Achieved?
As a user I can send a private message to another user so that we can have a private conversation βœ“
As a user I can have a message inbox so that I can read my private messages βœ“
As a user I can reply to my private messages so that I can quickly respond to the sender and keep a conversation going βœ“
As a user I can delete a message so that I keep my inbox clear and/or remove no longer useful messages βœ“

Epic - A registered user can report inappropriate content

User Story Achieved?
As a user I can report a post so that moderators are notified of inappropriate content βœ“
As a user I can report a reply so that moderators are notified of inappropriate content βœ“
As a user I can report a message so that moderators are notified of inappropriate content βœ“

Epic - A registered user can keep their account secure

User Story Achieved?
As a user I can log out so that my account remains secure and private when not in use βœ“
As a user I can delete my account so that I can leave the website and remove all my content βœ“
As a user I can change my password so that my account can remain secure βœ“
As a user I can reset my password so that I can still log in if I have forgotten my password βœ“

Epic - A registered user be part of a conversation

User Story Achieved?
As a user I can search squigl so that I can find users, posts and replies that I am looking for βœ“
As a user I can repost another user's post so that I can share it with my own followers βœ“
As a user I can create a hashtag in my posts and replies so that I can be part of a larger conversation and contribute to trending topics βœ“
As a user I can tag other users in my posts, replies and messages so that I can link directly to their profile βœ“
As a user I can see trending hashtags so that I am aware of current popular topics of conversation βœ“

Epic - Moderation

User Story Achieved?
As a moderator I can see reported items so that I can act upon them βœ“
As a moderator I can delete or mark okay a reported item so that it can either be deleted or removed from the reported items list where appropriate βœ“
As a moderator I can message users so that we can talk to them with clearly labeled official messages βœ“
As a moderator I can see a list of users with strikes so that troublesome users are clearly viewable and action can be take if necessary βœ“
As a moderator I can ban or unban a user so that they can be banned or unbanned if necessary βœ“

Timeboxing

Using MoSCoW prioritisation I categorised the features needed to meet the requirements of the user stories into the following categories:

Must Do

The final version of Squigl must be able to do the following:

  • User signup
  • User login
  • Make new posts
  • View posts
  • Edit posts
  • Delete posts

This would produce the minimal viable product.

Should Do

A more fully featured product would contain these features:

  • Search
  • Replying to posts
  • Reading replies
  • Editing replies
  • Deleting replies
  • Post liking
  • Follow and unfollow users
  • User settings

Could Do

And if there is extra time before then end these features could be included:

  • User profile customisation
  • User to user private messaging
  • Notifications

An early version of Squigl would contain the following - every must do feature, from should do: post replying, user following, and from could do: user profile customisation (time permitting). Futher features can be added in later versions.

Fortunately due to the length of this project I was able to produce every feature.

Back to top πŸ”Ί

User Experience

Wireframes

Balsamiq for Desktop was used ahead of development to plan the basic skeleton of all pages. You can download my wireframes file here.

Feed page wireframe
Post page wireframe
New post form wireframe
User page wireframe
Search page wireframe
Mobile wireframe

Design Choices

Typography

Fonts are imported from Google Fonts. The font used for the website logo and some usernames is Fredoka One. To keep visual clutter to a minimum only two fonts are used. Fredoka One for elements that are important to the current page, and for the rest of the content the default bootstrap font is used. Simply increasing the font-weight creates enough contrast between titles and body text.

Squigl logo

Images

The only images used on the website are those added by users. These include profile backgrounds, user avatars and images shared in posts. This keeps the focus where it should be - on the user generated content. A placeholder image is used in cases where a user has not yet uploaded an avatar.

Placeholder avatar

Colour Scheme

Squigl uses a restrained colour-scheme. The design focuses mainly on off black text on a white background. The main content column is highlighted by a grey gradient background to draw user's eyes to the middle of the page. Colour mostly comes from the content added by users.

The colours used in the site logo represent two of the main pillars of squigl - users and trending topics. The letter "i" coloured goldenrod represents the users, as the "i" looks like a person and the colour is also used for the verified tick next to usernames. The indigo ~ is the same colour used for hashtags.

Colour palette

Layout

In desktop view the website is structured into three columns. The first being for navigation, the centre for the main content, and the right sidebar is for extra content. The centre column is the largest to highlight it's importance and draw the eye.

For smaller screens such as mobile the navigation shrinks and the right side bar hides - keeping focus on the centre column. The menu will include links to the Trending Hashtags and the Who To Follow pages when the right sidebar is hidden due to screen size.

Desktop layout
Small desktop layout
Tablet layout
Mobile layout

Back to top πŸ”Ί

Features

Site-wide features

Header

  • The header featured on every page is made of three parts. The site logo, a search box, and the logged in user's info.
  • The logo returns a logged in user to their feed.
  • Having search in the header encourages users to explore squigl.
  • The user info links the user to their page and helps show that the user is signed in.

Header

Navigation

  • The user will navigate to all the main pages of the site using the menu in the left sidebar. Depending on the status of the current user this menu changes.
  • A logged out or new user will see the options to login and sign up.
  • A logged in user will see the full list of menu options.
  • A moderator will see two further options, one for the Django admin and another for the moderation tools.
  • Labels are removed from the menu at lower screen widths, on mobile this is then replaced with an expandable hamburger menu.
  • If the logged in user has any unread mentions or messages the relevant menu link is highlighted and includes a count.
Logged out navigation
Full desktop navigation
Portrait tablet navigation
Expanded mobile navigation

Scroll To Top Button

  • A back to top button appears when scrolling below the header to further assist navigation.
  • The JavaScript code that makes this work can be found in this file.

Centre Column

  • The centre column houses the main content on every page, and is the only column to remain visible at every screen size.

Sidebar

  • On the majority of pages the sidebar will show a list of recent trending hashtags and a random list of unfollowed users.
  • On the post page the user will find the reply form here.

User Tagging

  • User's can mention/tag other users in their posts, replies and messages by using ~ in front of their username.
  • This functionality is made possible using JavaScript, the code that makes this work can be found in templates/base.html.
  • This code is included in the html file because it requires django template tags to function.

Hashtags

  • Users can include hashtags in their posts and replies by using # in front of the desired keyword.
  • This functionality is made possible using JavaScript, the code that makes this work can be found in templates/base.html.
  • This code is included in the html file because it requires django template tags to function.

Example of a post with a tagged user and hashtags:

A post containing user tags and hashtags

Trending Hashtags

  • JavaScript looks for a hidden <div> containing the last 100 posts that include # or in other words, a hashtag.
  • The hashtag form inner html is extracted, the number of occurrences are counted, then sorted by value.
  • Finally keys from this created JavaScript object are appended into the page html.
  • The code that makes this work can be found in this file.

Trending Hashtags

Modals

Modals are used where it's possible to make permanent irreversible changes. Giving the user a chance to reconsider.

  • Deleting a post
  • Deleting a reply
  • Deleting a message
  • Reporting a post
  • Reporting a reply
  • Reporting a message
  • Moderator functions
  • Account deletion

Modal example:

Modal example

Django Messages

Django messages are implemented to provide the user with confirmation of their actions.

Example message:

Message example

Specific pages

Error pages

  • There are error pages for error 404, 500, and a third one for squigl specific error messages.
  • A bootstrap alert is used to display the relevant error message.
  • A button using JavaScript can send the user back one page if they want.
  • Custom error messages are sent to the page from the relevant views.py file, often the else result of an if statement.
else:
        e = 'You can not delete this post because you are not the author.'
        return render(request, 'error.html', {'e': e})

Error 404 example:

Error display

Login and Sign up

  • The default django login and sign up forms are used. Using accounts/forms.py I have added placeholder text to the html inputs to assist the user when signing up.
  • In my CustomUser model I made email a requirement, this is so the user can reset their password, and this is reflected while signing up.
  • The login page includes a link to password reset.
  • Login is required for most of the functionality. A @login_required decorator is used where possible. In cases where this is not an alert will notify the user.
  • A logged out user will be redirected to the login page if they attempt any of these actions:
    • Change account settings such as password
    • View, send, mark read, delete, report or reply to messages
    • Any moderation only action
    • View feed
    • Make a new post
    • Repost a post
    • Report a post
    • Follow or unfollow a suer
    • View mentions
    • Mark posts and replies as read

New user sign up form

User sign up form

Feed

  • The home page for a logged in user.
  • The feed displays a timeline of all the user's post and the posts of followed users.
  • If there are no posts to display the user is encouraged to find users to follow.
  • 20 posts are shown at a time, with pagination at the bottom so the user can navigate further into the timeline.
def feed(request):
    following = request.user.following
    posts = Post.objects.filter(
        Q(user__in=following.all()) | Q(user=request.user))
    paginator = Paginator(posts, 20)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    return render(request, 'social/feed.html', {
        'post_count': posts.count(),
        'page_obj': page_obj,
    })

An example feed:

Feed

Search

  • All users can search squigl.
  • The user input in the search form is used to filter through posts, replies, and users.
  • Results can be filtered using the navigation below the page title.
  • The number of results in each category are displayed.
  • Replies shown in search results contain a link to the post they are a part of.
def search(request):
    if request.method == 'POST':
        query = request.POST['query'].strip().lower()
        users = CustomUser.objects.filter(
            Q(username__icontains=query) | Q(name__icontains=query)).order_by(
                'username')
        posts = Post.objects.filter(
            post__icontains=query).order_by('-date')
        replies = Reply.objects.filter(
            reply__icontains=query).exclude(hidden=True).order_by('-date')
        return render(request, 'social/search.html', {
            'query': query,
            'posts': posts,
            'users': users,
            'replies': replies,
        })
    else:
        return render(request, 'social/search.html')

Searching for users example:

User search

Searching for posts example:

Post search

User Page

  • Every user that signs up has a profile page.
  • This is where a user's posts will be shown regardless of if you follow them or not.
  • Includes buttons to Message and Follow/Unfollow the user.
  • If this is the logged in user's page then a Edit Profile button is shown instead.
  • The user can choose to upload and display an avatar, a profile background image, a short description about themselves, change their profile name, and add a link to a website.
  • User stats are also shown: Number of followers, number of users they are following, and number of posts.
  • Like the Feed page, user posts are paginated.
def user(request, user):
    queryset = CustomUser.objects
    user = get_object_or_404(queryset, username=user)
    posts = Post.objects.filter(user_id=user.id).order_by('-date')
    following = False
    paginator = Paginator(posts, 20)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    if request.user.is_authenticated:
        if request.user.following.filter(id=user.id):
            following = True
    return render(request, 'social/user.html', {
        'user': user,
        'page_obj': page_obj,
        'post_count': posts.count(),
        'following': following,
    })

User page example:

User page

New Post

  • Placeholder text is included to guide a user when making a new post. You can see the code here.
  • A user has the option to include an image and/or a link to a website.
  • Using the django-reszied package images are scaled to a width of 600px, this ensures images being shared with users of the website are not a large file size. This also has hosting benefits as well. Images are stored on Cloudinary.
  • Once the form is submitted the user is redirected to the page for that post.
def new_post(request):
    if request.method == 'POST':
        form = PostForm(request.POST, request.FILES or None)
        if form.is_valid():
            post = form.save(commit=False)
            post.user = request.user
            post.save()
            return redirect('post', post.id)
    else:
        return render(request, 'new/new-post.html', {'form': PostForm()})

New post form:

New post form

Post Page

  • Each post has its own page, this can be accessed by clicking the post timestamp or permalink in the footer.
  • On the post page a user can read and post their own replies.
  • The date of the post is represented in a few ways.
    • If the post is less than a minute old it shows as "Now".
    • If the post is less than 24 hours old then "X hours ago" is shown.
    • If the post is greater than 24 hours old then the published date will be shown.
  • Each post shows a like count, reply count, repost count and a menu with further options. These are also shown wherever a post is displayed (feed, user page and search results).
  • If the logged in user has replied and/or liked a post the relevant icons will be filled in.
  • Clicking the repost icon or count will let the user repost the post by including it in a post of their own.
  • A user can like a post without the need for a page reload thanks to Ajax. This code can be found here.
  • Each post contains a drop down menu which includes the option to Report the post and a Permalink. If the logged in user is the author of the post, they will also see the Edit/Delete option.
  • Each reply contains a button to Report the reply. If the logged in user is the author of the reply, they will also see the Edit/Delete option.
def post(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    replies = Reply.objects.filter(post_id=post_id).order_by('-date')
    if request.method == 'POST':
        form = ReplyForm(data=request.POST)
        if form.is_valid():
            form.instance.user = request.user
            form.instance.post = post
            form.save()
            return HttpResponseRedirect(request.path_info)
    else:
        return render(request, 'social/post.html', {
            'post': post,
            'replies': replies,
            'form': ReplyForm(),
        })

An example of a post with replies:

New post form

Mentions

  • If a user is mentioned in a public post or reply they will be notified of this by an unread mention count in the menu.
  • A user can view these posts and replies on their Mentions page.
  • Posts and replies are excluded if the logged in user exists in their read ManyToManyField.
  • Posts and replies can be marked as Read to clear the notification, and be excluded in the future.
def mentions(request):
    posts = Post.objects.filter(post__icontains=request.user).exclude(
        read=request.user).order_by('-date')
    replies = Reply.objects.filter(reply__icontains=request.user).exclude(
        hidden=True).exclude(read=request.user).order_by('-date')
    return render(request, 'social/mentions.html', {
        'posts': posts,
        'replies': replies,
    })

An example of a user's mentions:

User mentions

Messages

  • Two logged in users can privately message each other.
  • A user can either be messaged using the Message button on their user page, or by replying to a message they have previously sent.
  • As with mentions above, a user is notified of any unread messages with a count in the menu.
  • A user can view unread, read and sent messages using the message page navigation.
  • Each message has a drop down menu with the options Mark Read, Reply, Report, and Delete.
  • If a sent message has been read by the recipient, the sender is notified.
  • Messages from website moderators (django staff role) are labeled.
  • A user can chose to delete a message. The message remains in the database until both the sender and recipient have both chosen to delete it.
def messages(request):
    unread_messages = Message.objects.filter(
        recipient=request.user).filter(read=False).order_by('-date')
    messages = Message.objects.filter(
        recipient=request.user).filter(read=True).exclude(
            recipient_del=True).order_by('-date')
    sent_messages = Message.objects.filter(
        sender=request.user).exclude(sender_del=True).order_by('-date')
    return render(request, 'dm/messages.html', {
        'unread_messages': unread_messages,
        'messages': messages,
        'sent_messages': sent_messages})

Example unread messages:

Unread messages

Example sent messages:

Sent messages

Moderation

  • Squigl moderators have access to the moderation page.
  • Posts, replies and messages reported by users can be viewed here by moderators.
  • A moderator can decide whether to ignore a report and mark the content as okay, or if necessary delete the content which will remove it and give the author a strike.
  • Users with strikes can be viewed and messaged and banned or unbanned if necessary.
  • All tables can be sorted by clicking on a column header. The code for this can be found here.
def moderation(request):
    if request.user.is_staff:
        reported_posts = Post.objects.annotate(
            nreports=Count('reported')).filter(nreports__gt=0)
        reported_replies = Reply.objects.annotate(
            nreports=Count('reported')).filter(nreports__gt=0)
        reported_messages = Message.objects.filter(reported=True)
        users = CustomUser.objects.filter(strikes__gt=0).order_by('-strikes')
        return render(
            request,
            'moderation/moderation.html', {
                'reported_posts': reported_posts,
                'reported_replies': reported_replies,
                'reported_messages': reported_messages,
                'users': users,
                })
    else:
        e = 'Only Squigl moderators can view this page.'
        return render(request, 'error.html', {'e': e})

Example reported posts:

Sent messages

Example reported post modal:

Sent messages

Example naughty users list:

Sent messages

Unimplemented Features

If possible I would have liked to have implemented the following features:

  • A dark mode theme.
  • The ability to post other media such as videos including YouTube embeds.
  • A server side trending hashtags list. The current implementation is built with JavaScript that runs client side.
  • Improvements to the user pages, including a gallery of their shared images, and a list of their liked posts.
  • Reposts would notify the author of the original post.
  • Further styling. In my opinion this still looks too much like a Bootstrap website, even though there are almost 700 lines of CSS code!
  • Inline private messaging. Replies to messages appearing as a conversation thread instead of individual separate messages.
  • Some form of indication that a post or reply has been updated by the author.
  • URLs shown as part of a post or on a user profile automatically converted into a working link without the need for a user to include "http://" etc.

Back to top πŸ”Ί

Bugs and Fixes

A lot of bug fixes relate to features that are no longer a part of this project so they are not documented. They are still visible via commits with the Fix preset. There are currently no known unfixed bugs.

Bug Cause Fixed?
Sign page up not working Incorrect url in view, using 'accounts' where the included default django view requires 'registration' βœ“
Avatar in header displaying incorrectly Wrong class applied to element βœ“
Reply form not working Missing data=request.POST parameter βœ“
Incorrect display of 'Follower' and 'Followers' on user page Incorrect comparison operator in django html tag βœ“
Followers count not working ManyToManyField in CustomUser model had a relation ship with the wrong model βœ“
New reposts not linking to original post old_post variable assignment outside of if statement βœ“
EditProfile success url broken Unnecessary request.user in kwargs βœ“
Change password field missing on change password page Hide password div javascript running on every page βœ“
Hide reply not working Missing save() reply function in view βœ“
Reply indicator in post footer not working Missing request.user from if statement βœ“
User links and hashtags not working in reposts Incorrect class on element βœ“
HTML table sorting not functioning Incorrect url value in javascript if statement βœ“
Mentions notification in menu not functioning Incorrect value in if statement βœ“
CollectStatic failing Missing STATICFILES_STORAGE variable in settings.py βœ“
Suggested users showing when not logged in Beginning of if statement on the wrong line βœ“
Error page not shown when attempting to delete a message when not a moderator Incorrect file path in render request βœ“

Back to top πŸ”Ί

Technologies

Main Languages Used

Frameworks

  • Bootstrap - A frontend toolkit.
  • Django - A high-level Python web framework.

Libraries

  • jQuery - A fast, small, and feature-rich JavaScript library. JQuery is linked in the <head> element instead of at the bottom of <body>, due to some of my inline JavaScript requiring JQuery.
  • crispy-bootstrap5 - A bootstrap5 template pack for django-crispy-forms. Includes django-crispy-forms.
  • dj-database-url - Enables the use of database URLS in Django.
  • django-cloudinary-storage - A Django package that provides Cloudinary storages for both media and static files as well as management commands for removing unnecessary files.
  • django-resized - Used to resize images uploaded by the user.
  • gunicorn - Gunicorn β€˜Green Unicorn’ is a Python WSGI HTTP Server for UNIX.
  • oauthlib - A generic, spec-compliant, thorough implementation of the OAuth request-signing logic.
  • Pillow - A Python Imaging Library that adds image processing capabilities to your Python interpreter.
  • psycopg2 - Psycopg is the most popular PostgreSQL database adapter for the Python programming language.
  • PyJWT - JSON Web Token implementation in Python.
  • pytz - Allows accurate and cross platform timezone calculations using Python 2.4 or higher.
  • requests-oauthlib - OAuthlib authentication support for Requests.
  • sqlparse - sqlparse is a non-validating SQL parser for Python.
  • Coverage - To check full for automated test coverage.
  • model-bakery - To create database objects for testing.

Software and Other

Back to top πŸ”Ί

Testing

Testing information can be found here.

Back to top πŸ”Ί

Deployment

Deployment steps can be found here.

Back to top πŸ”Ί

Credits

Code

  • Code to make sure usernames are alphanumeric from StackOverflow.
  • CSS for reply speech bubbles from Codepen.
  • JavaScript to count occurrences of hashtags from StackOverflow.
  • Code to strip irrelevant time since information from post dates from StackOverflow.
  • JavaScript to sort HTML tables from StackOverflow.
  • Scroll to Top button code from W3 Schools

Text

  • Profile descriptions and posts from real life companies/individuals are taken from their respective Twitter profiles.

Images

  • Icons from Font Awesome.
  • User avatars and profile backgrounds from real life companies/individuals are taken from their respective Twitter profiles.

Acknowledgements

Thanks again to my mentor Oluwaseun Owonikoko and the Code Institute students from class June 2022 for their help, testing and suggestions throughout the project.

Back to top πŸ”Ί