diff --git a/README.md b/README.md
index 8c7a91d..cda052f 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,48 @@
# not-my-locker-room
-pip install requests
+### How to use the code in this repo
-Add a yaml file with data for title, tagline, photocredit, heading and a list of
-social media links. See content.yml for an example. Then run
-generate_homepage_from_content.py to build the page. For example:
+First make sure you have all the necessary modules: `pip install -r requirements.txt`
+Add a yaml file with data for title, tagline, photocredit, heading, and a list of
+social media links (see `content.yml` or `dummy_content.yml` for examples). Then run
+`generate_homepage_from_content.py` to build the page. For example:
+```
./generate_homepage_from_content.py --content content.yml --outfile not_my_locker_room.html
+```
or
+```
./generate_homepage_from_content.py --content endorsements.yml --outfile
endorsements.html
+```
+
+You can always run `./generate_homepage_from_content.py -h` for help & usage instructions.
+
+### How to add a new type of social content
+Say you want to embed some content from hip social media platform Latergram, which has an API endpoint that takes a content URL and returns you embed HTML code for that content.
+
+**Step 1**: add a constant for your new content type in the `# keys and accepted values` section:
+```
+CONTENT_TYPE_LATERGRAM = 'latergram'
+```
+We can now match any social media row in the `content.yml` file of `type: latergram` to the appropriate API info.
+
+**Step 2**: add a key/value pair in the `API_INFOS` dict.:
+```
+API_INFOS = {
+ ...
+ CONTENT_TYPE_LATERGRAM: {
+ 'endpoint': 'https://api.latergram.com/oembed/?url=%s',
+ 'response_html_key': 'html',
+ 'encode_url': False
+ },
+ ...
+}
+```
+The key should be the constant for your content type defined in step 1. The value is a dict. with the following pieces of information:
+* `endpoint`: Latergram's API endpoint for getting embed HTML code, prepared for string formatting (i.e. put `%s` wherever the URL to the content you're trying to embed should go). Presumes the endpoint requires no other params. besides a content URL.
+* `response_html_key`: assuming a JSON blob as a response from the endpoint, this is the key where the embed HTML is stored (for instance, look at a [Twitter oEmbed example response](https://dev.twitter.com/rest/reference/get/statuses/oembed#example-response); the `response_html_key` in this case would be `html`.)
+* `encode_url`: set to `True` if the API endpoint expects a URL-encoded content URL, otherwise set to `False`.
\ No newline at end of file
diff --git a/generate_homepage_from_content.py b/generate_homepage_from_content.py
index 3fb093a..708534a 100755
--- a/generate_homepage_from_content.py
+++ b/generate_homepage_from_content.py
@@ -16,15 +16,14 @@
DEFAULT_PAGE_TEMPLATE_PATH = './index_template.html'
DEFAULT_OUTFILE_PATH = 'index_generated.html'
-# CSV keys and accepted values
-CSV_KEY_TYPE = 'type'
-CSV_KEY_URL = 'url'
-CSV_KEY_QUOTE = 'quote'
+# keys and accepted values
+CONTENT_KEY_SOCIAL = 'social'
+CONTENT_KEY_TYPE = 'type'
+CONTENT_KEY_URL = 'url'
+CONTENT_KEY_QUOTE = 'quote'
CONTENT_TYPE_TWITTER = 'twitter'
CONTENT_TYPE_INSTAGRAM = 'instagram'
CONTENT_TYPE_WEBSITE = 'website'
-CONTENT_TYPES = [CONTENT_TYPE_TWITTER, CONTENT_TYPE_INSTAGRAM, CONTENT_TYPE_WEBSITE]
-EMBEDDED_CONTENT_TYPES = [CONTENT_TYPE_TWITTER, CONTENT_TYPE_INSTAGRAM]
# HTML templates
CONTENT_CONTAINER = '
%s
'
@@ -32,6 +31,19 @@
''
# API endpoints and keys
+API_INFOS = {
+ CONTENT_TYPE_TWITTER: {
+ 'endpoint': 'https://publish.twitter.com/oembed?url=%s',
+ 'response_html_key': 'html',
+ 'encode_url': True
+ },
+ CONTENT_TYPE_INSTAGRAM: {
+ 'endpoint': 'https://api.instagram.com/oembed/?url=%s',
+ 'response_html_key': 'html',
+ 'encode_url': False
+ }
+}
+
TWITTER_OEMBED_ENDPOINT = 'https://publish.twitter.com/oembed?url=%s'
TWITTER_HTML_KEY = 'html'
INSTAGRAM_OEMBED_ENDPOINT = 'https://api.instagram.com/oembed/?url=%s'
@@ -68,6 +80,25 @@ def parse_command_line_args():
return parser.parse_args()
+def make_embed_code_getter(endpoint, response_html_key, encode_url):
+ def get_embed_code_from_api(url):
+ if encode_url:
+ query_path = endpoint % urllib.quote(url)
+ else:
+ query_path = endpoint % url
+
+ response = requests.get(query_path)
+ if response.status_code != 200:
+ print ('Request to oEmbed endpoint "%s" failed with '
+ 'status code %d, message %s' %
+ (query_path, response.status_code, response.content))
+ return
+
+ result_json = json.loads(response.content)
+ return result_json[response_html_key]
+ return get_embed_code_from_api
+
+
def get_twitter_embed_code(url):
"""Given a URL, call twitter api for embed code."""
# Twitter API expects url-encoded url
@@ -78,6 +109,7 @@ def get_twitter_embed_code(url):
print ('Request to Twitter oEmbed endpoint for content "%s" failed '
'with status code %d, message %s' %
(url, response.status_code, response.content))
+ return
else:
result_json = json.loads(response.content)
@@ -98,23 +130,6 @@ def get_instagram_embed_code(url):
return result_json[INSTAGRAM_HTML_KEY]
-def html_element_from_embedded_content(url, content_type):
- """Given embeddable content (a source url for twitter or instagram), call
- the appropriate API to get embed code and insert into the appropriate HTML
- wrapper."""
- if content_type == CONTENT_TYPE_TWITTER:
- embed_code = get_twitter_embed_code(url)
- elif content_type == CONTENT_TYPE_INSTAGRAM:
- embed_code = get_instagram_embed_code(url)
- else:
- # We should have validated the content type before calling this func,
- # so this case should never get tripped.
- errString = ('Unexpected type %s (not an embedded content type). This '
- 'should never happen in this func.)' % content_type)
- raise ValueError(errString)
- return CONTENT_CONTAINER % (content_type, embed_code)
-
-
def html_element_from_website_content(url, quote):
"""Given website content (source url, quote), insert into the
appropriate HTML template."""
@@ -122,28 +137,32 @@ def html_element_from_website_content(url, quote):
return CONTENT_CONTAINER % ('website', website_content)
-def html_element_from_content(content_dict):
+def html_element_from_content(content_dict, embed_code_getters):
"""Given a dict representing content (a type, a url, and optionally a
quote), returns the content wrapped in the appropriate HTML element
(ready for insertion on the homepage)."""
- content_type = content_dict.get(CSV_KEY_TYPE)
+ content_type = content_dict.get(CONTENT_KEY_TYPE)
if not content_type:
print 'No content type provided for entry: "%s". Skipping.' % content_dict
return
- elif content_type not in CONTENT_TYPES:
- print ('Unrecognized content type ("%s") in entry: "%s". Skipping.'
- % (content_type, content_dict))
- return
- elif content_type in EMBEDDED_CONTENT_TYPES:
- url = content_dict.get(CSV_KEY_URL)
+ elif content_type == CONTENT_TYPE_WEBSITE:
+ url = content_dict.get(CONTENT_KEY_URL)
+ quote = content_dict.get(CONTENT_KEY_QUOTE)
+ elem = html_element_from_website_content(url, quote)
+ elif content_type in API_INFOS.keys():
+ url = content_dict.get(CONTENT_KEY_URL)
if not url:
print 'No url provided for entry: "%s". Skipping.' % content_dict
return
- elem = html_element_from_embedded_content(url, content_type)
- elif content_type == CONTENT_TYPE_WEBSITE:
- url = content_dict.get(CSV_KEY_URL)
- quote = content_dict.get(CSV_KEY_QUOTE)
- elem = html_element_from_website_content(url, quote)
+ embed_code_getter_func = embed_code_getters.get(content_type)
+ if not embed_code_getter_func:
+ print 'No api info provided for content type "%s", skipping.' % content_type
+ return
+ elem = embed_code_getter_func(url)
+ else:
+ print ('Unrecognized content type ("%s") in entry: "%s". Skipping.'
+ % (content_type, content_dict))
+ return
# make sure to escape any lone '%' signs
return elem.replace('%', '%%')
@@ -153,9 +172,10 @@ def content_from_yaml(filepath):
"""Given the filepath of a yaml file containing content, returns a dict of
content information."""
with open(filepath) as stream:
- contents = yaml.load(stream)
+ contents = yaml.load(stream)
return contents
+
def validate_filepath(filepath):
"""Make sure given filepath is a valid file."""
if not os.path.isfile(filepath):
@@ -166,7 +186,12 @@ def validate_filepath(filepath):
def main():
+ # setup
args = parse_command_line_args()
+ embed_code_getters = {}
+ for content_type, info in API_INFOS.iteritems():
+ embed_code_getter = make_embed_code_getter(info['endpoint'], info['response_html_key'], info['encode_url'])
+ embed_code_getters[content_type] = embed_code_getter
# set filepaths from command line args or default, validate filepaths.
if args.content:
@@ -194,21 +219,22 @@ def main():
# read in content yaml, generate html elements for each piece of content
# (getting embed code from API where appropriate.)
content = content_from_yaml(content_yaml_filepath)
- print 'Loaded %d social media rows from yaml.' % len(content['social'])
+ num_rows = len(content[CONTENT_KEY_SOCIAL])
+ print 'Loaded %d social media rows from yaml.' % num_rows
html_elements_to_add = []
- for s in content['social']:
- elem = html_element_from_content(s)
- if elem:
- html_elements_to_add.append(elem)
+
+ for i, row in enumerate(content[CONTENT_KEY_SOCIAL]):
+ print '\tProcessing social media row %d/%d...' % (i+1, num_rows)
+ elem = html_element_from_content(row, embed_code_getters)
+ if elem:
+ html_elements_to_add.append(elem)
# insert generated content into page template
with open(page_template_filepath) as infile:
page_template = infile.read()
formatted = '\n\n'.join(html_elements_to_add)
-
content['formattedsocial'] = formatted
-
generated_page = Template(page_template).substitute(content)
# write results to file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..e35ad90
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+requests==2.7.0
+PyYAML==3.12