Fashion Revolution

Live Wall

What's Fashion Revolution?

  • Non for profit working on raising awareness of the true cost of fashion, helping people shop with a conscience.

  • They challenge the fashion industry to provide greater transparency, safer work places, fair pay.

Fashion Revolution Week

  • Series of events around the world calling for accountability through all steps in the clothes-making process: debates, online discussions, seminars, ...

18-24 April 2016

  • 1,134 people killed and 2,500 injured when a garment factory complex collapsed in Bangladesh in April 2013.

The Challenge

Build a Live Wall Feed with images of clothing labels posted on Twitter with the #FashRevWall hashtag during Fashion Revolution Week... 

... in only two weeks!

The Tech Stack

Front-End stuff

  • HTML/Sass
  • Django Templates
  • Bootstrap
  • Grunt
  • npm

Back-End stuff

  • Django
  • Tweepy
  • Pytest
  • PostgreSQL
  • Heroku

The Team

Projet Manager and Front-End Lead

Back-End Lead

Back-End Developer

Front-End Developer

Lili Kastillo

Raquel Alegre

Mariza Dima

Karen Lee

Twitter Client 

Data storage



Tweet DB table

Author Date Tweet
lili 2016-04-23 So excited about #FashRevWall
raquel 2016-04-23 #FashRevWall is about to start!

Tweepy queries Twitter API and filters data

Heroku deployment

Twitter Client

Represents a client that connects to Twitter to retrieve tweets based on a
search criteria using Twitter REST API and Tweepy.
import json
import tweepy
from tweepy import OAuthHandler

class TwitterClient:
    def __init__(self):
        self.api = self._get_twitter_api()

    def _get_twitter_api(self):
        Since we are only reading public information from Twitter, we don't need
        access token/secret values.
        with open('secrets.json') as secrets_file:
            secrets = json.load(secrets_file)

        consumer_key = secrets['consumer_key']
        consumer_secret = secrets['consumer_secret']
        access_token = secrets['access_token']
        access_secret = secrets['access_secret']

        self.auth = OAuthHandler(consumer_key, consumer_secret)
        self.auth.set_access_token(access_token, access_secret)

        return tweepy.API(self.auth)

    def get_tweets_by_hashtag(self, hashtag, n):
        Receives a string hashtag and returns the list of last n Tweets
        containing it.
        tweets = []
        results = tweepy.Cursor(, q=hashtag).items(n)
        for tweet in results:
        return tweets

    def get_images_by_hashtag(self, hashtag, n):
        Receives a string hashtag and returns the list of last n Tweets
        containing it.
        images = []
        tweets = self.get_tweets_by_hashtag(hashtag, n)
        for tweet in tweets:
                image_url = tweet.entities['media'][0]['media_url']
            except KeyError:
                print "No media in tweet with ID: {}".format(
        return images

Tweepy: Python module for Twitter's API

Django Models and DBs

from django.db import models
from django.utils import timezone

class Tweet(models.Model):
    image_url = models.CharField(max_length=200, unique=True)
    user = models.CharField(max_length=100)
    created_at = models.DateTimeField(

The tweet model maps to the tweet PostgreSQL table


@import "bootstrap-custom";

$max-width: 63em; 
$max-text-width: 45em;
$font-size-base: 16px;
$font-size-text: 1.2em;

@font-face {
    font-family: "Kelson Sans";
    src: url("kelson-font/Kelson_Sans_Regular.otf") format("opentype");
@font-face {
    font-family: "Kelson Sans";
    font-weight: bold;
    src: url("kelson-font/Kelson_Sans_Bold.otf") format("opentype");

body {
    background: #ef5b58;
    font-family: "Kelson Sans"; 
    font-size: $font-size-base;
h1 {
    font-family: "Kelson Sans";
    font-size: 3em;
    line-height: 1.5em;
    text-decoration: underline;;

a {
    padding: 0;
    margin: 0;
.container {
    max-width: $max-text-width;
    padding: 2em;
    font-size: $font-size-text;
    line-height: $font-size-text*1.4;

.color-container {
    background-color: #ef5b58;
    padding: 4em 0;
    p {
        text-align: center;

.twitter-image {
    width: 17em;
    height: 17em;
    display: block;
    background-repeat: no-repeat;
    background-position: center; 
    background-size: cover;
    border: 1em solid #fcfcfc;  
    border-bottom-width: 0;
    box-shadow: 1px 1px 1px 1px rgba(0,0,0,.1) inset;

.tweet-list-container {
    width: auto;
    margin: 0 auto;
.tweet-container {
    box-shadow:4px 4px 0px rgba(0,0,0,.1);  

.tweet-container:hover {
    transform: scale(1.01);
#twitter-images-list {
    list-style: none;
    margin: 0 auto;
    display: block;
    padding: 3em 0;
    display: flex;
    max-width: $max-width;
    flex-direction: row;
    flex-wrap: wrap;

#twitter-images-list li {
    margin: 1.5em auto;


.tweet-text {
    text-align: center;
    padding: 1em;
    font-weight: bold;
    padding:0.5em 0;
    border: .7em solid #fcfcfc;  
    margin: 0 0 1em 0;
    font-family: "Kelson Sans";

#top-bar {
    height: 200px;
#top-bar #logo-wrapper {
    background: url('../img/FashRev_pattern.png');
    height: 200px;
    padding: 0;
#top-bar #logo {
    height: 200px;
    width: 200px;
    margin: 0 auto;
    display: block;
    position: relative;
    top: 40px;
.white-container {
    background: #ffffff;
    padding-top: 40px;
    padding-bottom: 40px;
.hashtag {
    font-weight: bold;
    font-size: 25px;
a {
    font-weight: bold;
    color: #000000;
.video-wrapper {
    position: relative;
    padding-bottom: 56.25%; /* 16:9 */
    padding-top: 25px;
    height: 0; 

.video-wrapper iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;

We used bootstrap for the grid and coded the rest



Django + Heroku = Dream Team

Performance Optimization

 80 out of 100 is not bad at all, but there is more we could have done.

What we did

  • Minified CSS, HTML & JavaScript
  • Lazyloading the images

What we could also do:

  • Host the images and optimise them
  • Use a CDN
  • Moar caching
  • Serve the HTML gzipped

Team Work

  • Slack
  • Face to face meetings/hacking sessions
  • GitHub issues
  • Code reviews






Things we liked:

  • We learned stuff
  • Fashion Revolution founder was very involved
  • Contributed to a good cause
  • It was very exciting to see the web go live
  • We met new people to work with
  • We had fun!



Things we didn't like so much:

  • Time was very tight!
  • Not as many users as expected
  • Not so much publicity about our work to get more pictures on the wall
  • Heroku constraints (more $$$ = more freedom)

Fashion Revolution

By Raquel Alegre

Fashion Revolution

Presentation for Women Hack for Non-Profits about worked carried out on the Fashion Revolution Live Wall - 2017

  • 1,286