By clicking “Accept”, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information.
Custom Code
Web Development


An MVP flash/python photography competition application with a MongoDB backend.

Personal Project
This functioning MVP application runs a fully automated photography competition once a week for registered users. Users can register an account, login and upload a single image once a week. In order to take part in the competition users must also vote on their favourite image (not their own). The app has broader applications as well as a firm and extensively tested codebase.


To create a photography competition app whereby the users were also the judges. The app is fully custom coded and based on the lightweight Flask framework. This application is targeted towards photography enthusiasts. Anyone who takes photographs for fun and loves to share them with others. Other photo sharing platforms have indicated an opportunity for an application that successfully gamifies photography and this aims to achieve that. Snapathon hopes to entice users to register and upload their images in weekly competitions pitting their best images against others'.


The application works perfectly. Users can register for accounts, login and start competing immediately. The results is an interesting photography application that does not yet exist in the app marketplace. It is an innovative platform that promotes quality photography. Furthermore the underlying idea does not have to be limited to photography, it could be widened to include other art forms, or indeed any arena that could feasibly accommodate an online competition. Essentially its value could expand to encompass an ever-widening user base.



For the typography "Anton" was used for the main page headings. This is a thick, bold and very immediately noticeable font. For the sub-headings we deferred to the old faithfuls of "Roboto" and "Open Sans" for a feeling of clarity and consistency.

Colour Palette

The design merges the feeling of a fun gamified application with the idea of a dimly lit museum. Essentially the tone is both extroverted and introverted, not too much colour as that would detract from the photography, but it needed enough punch to be noticeable. Dark tones for the backgrounds, a light grey for most of the text and a dark yellow accent colour. Yellow is a creative colour so it meshes well with a photography application.


The MVP features for this project were the following:

  1. The ability of a user to register an account using an email address and a password.
  2. When registering to be able to confirm the new password to avoid typos.
  3. To register a unique username as separate from the registered email.
  4. The ability to login using their email and password.
  5. When a user logs in they are brought directly to their profile page.
  6. They can enter competitions and vote when they are logged in, and only registered and logged in users can compete or vote.
  7. A user's profile page shows their uploaded photograph entries, other user's photographs they have voted for and any photographs of theirs that have won awards.
  8. A user's profile page also shows their total number of points.
  9. A user can change their username, email address & password.
  10. A user can upload images to the weekly competitions, and they can edit or delete those images at any point.
  11. If a user uploads an image they get a single vote which they can use to vote for any image that is not theirs. Voting takes place on Saturday & Sunday until 22:00PM.
  12. Users can vote by clicking the "Vote" button under their photo of choice.
  13. Points & awards are calculated automatically on Sunday evening.
  14. The Browse page allows registered and unregistered users to look through the entire collection of uploaded images and filter them by awards or keywords.
  15. Anywhere an image is displayed, it can be clicked on and the user will be brought to that photograph's view page which displays more detailed information about the image as well as a larger version of the photo.

These are the ground level MVP features to make the competition work. Features such as photo "likes", the ability of users to comment on images, the ability to follow other users are all  expected at some level, because most social media platforms follow such a similar formula, however because they are not essential to the working of this application, they can be implemented in future updates.


This application was developed to be fully responsive and accessible on all devices.

Mobile Devices

  • Page flow is the same as on larger screens, except the layout is stacked vertically.
  • Some of the images are removed to give more space.
  • The timeline changes its layout to stack vertically instead of information switching from left to right, but the flow of information is the same.
  • The contact page layout is same, albeit with less space.

Tablet Devices

  • Very similar layout to desktop, blue sky image is visible and keywords stack 2 to a row as on desktop.
  • The timeline also has room enough to stack its branches left and right.
  • There is more space around the forms, but the layout remains mostly the same as Desktop.

More Information

Database Architecture

The schema for this project is based on 2 main collections that feed into each other (users & photos) and 2 functional collections that are generated by the MongoDB GridFS system which is used to store the image data.

The database design for this project, while non-relational, does use primary keys and relational connections in order to achieve the application's goals. It primarily takes advantage of the non-relational structure by using logic in such a way that the relationships are joined by references to each other, but remain independent for many of their read and write functions.

For example, while the Photo collection documents of type "entry" have a reference to the user who created them, they do not need to call on the User collection when displaying all their details on the "Photo Details" pages of the application.


Semantic HTML

Wherever possible and contextually logical, semantic HTML5 is used so as to give the best experience to users who rely on screen readers.

Aria Labels

  • No buttons or elements were used without clear text references to their function.
  • The only exception to this is in the Admin User Control Page where we used icons to represent editing and deleting a user. To maintain good accessibility we were sure to include aria-labels on both of these icons.
  • This helped to ensure that functionality would remain evident for visually challenged users without the need for aria-labels.

Zoom Ratio

The application is comfortable to view on a screen zoom of up to 300% which is well above the recommended 200% as per accessibility standards.

Skip to Main Link

All pages contain a visibly hidden but keyboard accessible "skip to main" link. It is the first tabbable element on every page and it allows keyboard users to skip all the navigation tabs, which can be really annoying as there are two navbars, one main and one sidebar for mobile. For users relying on keyboard access, without a skip to main link they would have to tab at least 12 times before reaching the page content.

Focus Highlights

All tabbable navigation has an obvious focus highlight, either an outline, or that it mimics the hover style exactly, so the keyboard user knows when they are entering a particular element.


The following security features were integrated into this application:

1. CSRF Protection

To protect against cross site request forgery we used the Flask-WTF module's CSRF protection by adding it globally and by adding a hidden input containing the crsf token in all forms used in the application.

This protection is particularly important for this application, as it relies on cookie sessions.

2. Securing the upload filenames

To stop attackers using the upload function to insert system requests into the server, and /or mess with configuration files, we've integrated secure_filename from werkzeug.utils before the uploaded files are saved to the database. This function transforms any dodgy filenames into flat safe ones.

3. Approved File Extensions

The application limits the type of files that can be uploaded to a pre-approved subset. This was achieved by using a config instruction: app.config['UPLOAD_EXTENSIONS'] = ['.jpg', '.png', '.gif', '.svg', '.jpeg', '.heic'] Which was then referenced in all methods dealing with uploaded files: the compete(), the edit_profile() and the register() functions.

If a user attempts to upload any other extension type, the application throws a 415 error, which further limits the ability of users with malicious intent to upload damaging files to the database.

4. Validating File Contents

As a final and very important check on the file uploaded, we used imghdr.what() to verify that the filetype is an image. This is an important extra failsafe, as malicious users could upload a non-image file with one of the approved extensions masking its true nature.

The validate_image_type() function takes in 512 Bytes of data from the file uploaded before it's saved anywhere and checks what type of file it is.

The code in save_file() checks that if file_extension != validate_image_type( the save function won't go ahead and an error will be thrown. Thus if the extension is different from the actual file type the upload will fail.

5. Uploaded file size

The application only allows files under 560KBs to be uploaded. This is a useful validation for two reasons: it stops overly massive images from being uploaded that would slow the application down, and it stops a decent amount of potential security threats where hackers upload malicious programmes.

This was initially achieved using a config instruction: app.config['MAX_CONTENT_LENGTH'] = 750 * 750 whereby Flask would automatically halt any large requests and return a 413 status code.

Rejecting large files worked and was straightforward, however Flask had big issues returning the appropriate error message and page, therefore we changed the manner in which we limited the allowed file size. Instead of using a config variable, we added a custom function that only allowed 560KBs and under and we ensured it was called any time a file was uploaded.

This worked to both limit the file sizes and throw the correct error message for the user.

6. Flask-Talisman and Content Security policy

Flask-Talisman is integrated to incorporate a somewhat comprehensive CSP quickly and easily. Flask-Talisman also comes with a number of other security benefits such as (taken from the Flask-Talisman GitHub repo):

   - Forcing https connection.
   - Enables HTTP Strict Transport Security.
   - Sets Flask's session cookie to secure, so it will never be set if your application is somehow accessed via a non-secure connection.
   - Sets Flask's session cookie to httponly, preventing JavaScript from being able to access its content. CSRF via Ajax uses a separate cookie and should be unaffected.
   - Sets X-Frame-Options to SAMEORIGIN to avoid clickjacking.
   - Sets X-XSS-Protection to enable a cross site scripting filter for IE and Safari (note Chrome has removed this and Firefox never supported it).
   - Sets X-Content-Type-Options to prevent content type sniffing.
   - Sets a strict Referrer-Policy of strict-origin-when-cross-origin that governs which referrer information should be included with requests made.

In addition to the above listed benefits, Flask-Talisman also sets a Content Security Policy that was adapted and made less strict to allow for using certain CDNs such as Google Fonts, FontAwesome, Materialize & the application's own custom scripts.

7. Access Control

Users must login to their accounts and many of the app's functionality is based on a user being authenticated.

  • Logged in users may edit their accounts, delete their accounts and delete their photos.
  • They are not able to access the admin control center.
  • They are not able to delete, edit or update other user accounts.
  • Admins are not able to edit or delete user images, but they are able to edit user usernames and email addresses should they need to.
  • Admins can also completely delete a user's account.
  • Admins may also delete user profile images if they are deemed inappropriate.

These varying accesses are managed by the login function, which is secured by Werkzeug's password hashing and reading methods.

8. Request Methods

  • The application does not serve any database altering methods using GET requests.
  • All EDIT & DELETE functions are POST requests.
  • Additionally, in order to delete a user account, either the user, or the admin, who is doing the deleting, must enter their account password again, to confirm the deletion.
  • This is to ensure that even IF a CSRF attack were to somehow bypass the WTF-Forms protection detailed above, they would not be able to successfully delete an account without knowing the user's or admin's password.

The application is written in Python, uses the Flask framework and a Mongo DB.
Previous Project
Next Project
You're all caught up.
There are no more recent projects
That's everything.
There are no more projects.

Need a


Let's talk.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.