Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generalize script #10

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -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`.
114 changes: 70 additions & 44 deletions generate_homepage_from_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,34 @@
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 = '<div class="content %s">%s</div>'
WEBSITE_CONTENT_TEMPLATE = '<div class="website-content">%(website_text)s</div>\n' \
'<div class="website-url"><a href="%(website_url)s">%(website_url)s</a></div>'

# 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'
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -98,52 +130,39 @@ 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."""
website_content = WEBSITE_CONTENT_TEMPLATE % {"website_text": quote, "website_url": url}
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('%', '%%')
Expand All @@ -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):
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
requests==2.7.0
PyYAML==3.12