Fashion Revolution
Live Wall
www.fashrevwall.com
http://tinyurl.com/hu39c5n
What's Fashion Revolution?
- Non for profit working on raising awareness of the true cost of fashion, helping people shop with a conscience.
http://fashionrevolution.org
- 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
Front-End
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(self.api.search, q=hashtag).items(n)
for tweet in results:
tweets.append(tweet)
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:
try:
image_url = tweet.entities['media'][0]['media_url']
except KeyError:
print "No media in tweet with ID: {}".format(tweet.id)
continue
images.append(image_url)
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(default=timezone.now)
The tweet model maps to the tweet PostgreSQL table
Front-End
@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;
display:inline-block;
}
.tweet-text {
background:#fcfcfc;
line-height:1.3em;
text-align: center;
padding: 1em;
font-weight: bold;
padding:0.5em 0;
border: .7em solid #fcfcfc;
display:block;
font-weight:bold;
letter-spacing:0.1em;
text-transform:uppercase;
margin: 0 0 1em 0;
color:#181818;
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;
font-style:italic;
}
.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
Deployment
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
Demo!
www.fashrevwall.com
Summary
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,421