Skip to content

Instantly share code, notes, and snippets.

@su27
Created December 9, 2016 06:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save su27/591200fda8c20116c7d21d9e4fb3e77b to your computer and use it in GitHub Desktop.
Save su27/591200fda8c20116c7d21d9e4fb3e77b to your computer and use it in GitHub Desktop.
profile for draftjs exporter
# coding: utf-8
import cgi
import cProfile
import re
import urllib
import urlparse
from pstats import Stats
from draftjs_exporter.dom import DOM
from draftjs_exporter.html import HTML
E = DOM.create_element
def url_fix(s):
if not s:
return ''
if isinstance(s, unicode):
s = s.encode('utf8', 'ignore')
scheme, netloc, path, qs, anchor = urlparse.urlsplit(s)
path = urllib.quote(path, '/%')
qs = urllib.quote_plus(qs, ':&=')
return urlparse.urlunsplit((scheme, netloc, path, qs, anchor))
flash_html = ('<object '
'data="//imgcache.qq.com/tencentvideo_v1/player/TencentPlayer.swf?vid=z03172fgqfr&amp;autoplay=1&amp;tpid=0" '
'height="300" '
'type="application/x-shockwave-flash" '
'width="400">'
'<param name="movie" value="//imgcache.qq.com/tencentvideo_v1/player/TencentPlayer.swf?vid=z03172fgqfr&amp;autoplay=1&amp;tpid=0"></param>'
'<param name="wmode" value="transparent"></param>'
'<param name="allowFullScreen" value="true"></param>'
'<param name="allowScriptAccess" value="always"></param>'
'<param name="flashvars" value="autoplay=0&amp;adplay=1"></param>'
'</object>')
def get_video_info(url):
return {
'url': 'http://youtube.com/v/1',
'flash_html': flash_html,
'cover': 'b.jpg',
'title': 'title',
'support_https': True,
'site_name': 'youku',
'site_name_cn': 'youtube',
'html5_video_url': '',
'mp4_video_url': ''
}
def get_subject_info(url):
return {
'type': 'book',
'cover': 'http://a.com/book/1/cover.jpg',
'title': 'good subject',
'url': 'http://a.com/good_book/1',
'summary': 'very good',
'rating': {'value': '9.5', 'max': '10.0', 'reason': ''}
}
class URLDecorator(object):
"""
replace url in plain text to actual html link
"""
SEARCH_RE = re.compile(r'(http://|https://|www\.)([a-zA-Z0-9\.\-%/\?&_=\+#:~!,\'\*\^$]+)')
def __repr__(self):
return '<URLDecorator>'
def replace(self, match, block_type):
u_href = match.group(1) + match.group(2)
if block_type in ('code-block',):
return u_href
text = cgi.escape(u_href, quote=True)
if u_href.startswith("www"):
u_href = "http://" + u_href
props = {'href': u_href, 'rel': 'nofollow'}
return DOM.create_element('a', props, text)
class BrDecorator(object):
"""
replace \n to <br/>
"""
SEARCH_RE = re.compile('\n')
def __repr__(self):
return '<BrDecorator>'
def replace(self, match, block_type):
if block_type in ('code-block',):
return '\n'
return DOM.create_element('br')
class LinkEntity(object):
def render(self, props):
data = props.get('data', {})
url = url_fix(data.get('url', ''))
attrs = {'rel': 'nofollow', 'href': url}
return E('a', attrs, props['children'])
class VideoEntity(object):
def render(self, props):
data = props.get('data', {})
video_url = data.get('url')
caption = data.get('caption')
CAPTION_MAX = 35
info = get_video_info(video_url)
caption = caption or info['title'].decode('utf8')
if info['flash_html'] and info['support_https']:
flash_elem = DOM.parse_html(info['flash_html'])
return E('div', {'class': 'video-wrapper'},
E('div', {'class': 'video-player'},
flash_elem,
E('div', {'class': 'video-title'},
caption[:CAPTION_MAX]
) if caption else None
)
)
else:
return E('div', {'class': 'video-card'},
E('a', {'class': 'video-link', 'href': url_fix(video_url), 'target': '_blank'},
(caption or video_url)[:CAPTION_MAX]
)
)
class SeparatorEntity(object):
def render(self, props):
return E('div', {'class': 'separator'}, E('hr'))
class ImageEntity(object):
def render(self, props):
CAPTION_MAX = 140
data = props.get('data', {})
position = data.get('float', 'center')
caption = data.get('caption', '')
children = [E('div', {'class': 'image-wrapper'},
E('img', {'src': url_fix(data.get('src', ''))})
)]
if caption:
children.append(
E('div', {'class': 'image-caption-wrapper'},
E('div', {'class': 'image-caption'},
caption[:CAPTION_MAX])
)
)
return E('div', {'class': 'image-container image-float-%s' % position},
*children)
class SubjectEntity(object):
def render(self, props):
data = props.get('data', {})
url = data.get('url')
fact_url = ''
TITLE_MAX = 35
ABSTRACT_MAX = 200
if url:
info = get_subject_info(url)
stars = []
reason = info['rating']['reason']
try:
rating = float(info['rating']['value'])
except ValueError:
rating = 0
for i in range(2, 11, 2):
if i <= rating:
stars.append(E('span', {'class': 'rating-star1'}))
elif 0 < i - rating <= 1:
stars.append(E('span', {'class': 'rating-star2'}))
else:
stars.append(E('span', {'class': 'rating-star0'}))
if reason:
stars.append(E('span', {'class': 'rating-reason'}, reason))
else:
stars.append(E('span', {'class': 'rating-score'}, str(rating)))
if info['title']:
url = fact_url if fact_url else url_fix(info['url'])
return E('div', {'class': 'subject-wrapper'},
E('a', {'href': url},
E('div', {'class': 'subject-cover'},
E('img', {'src': url_fix(info['cover'])})
),
E('div', {'class': 'subject-info'},
E('div', {'class': 'subject-title'},
info['title'].decode('utf8')[:TITLE_MAX]
),
E('div', {'class': 'subject-rating'}, *stars),
E('div', {'class': 'subject-summary'},
info['summary'].decode('utf8')[:ABSTRACT_MAX]
)
)
)
)
return E('div', {'class': 'subject-link'},
E('a', {'href': fact_url if fact_url else url}, url))
return E('span', {'data-error': 'no subject url'})
def run():
config = {
'entity_decorators': {
'LINK': LinkEntity(),
'IMAGE': ImageEntity(),
'VIDEO': VideoEntity(),
'SEPARATOR': SeparatorEntity(),
'SUBJECT': SubjectEntity()
},
'composite_decorators': [
URLDecorator(),
BrDecorator(),
],
'block_map': {
'unstyled': {'element': 'p'},
'blockquote': {'element': 'blockquote'},
'atomic': {'element': ''},
'header-one': {'element': 'h1'},
'header-two': {'element': 'h2'},
},
'style_map': {
'ITALIC': {'fontStyle': 'italic'},
'BOLD': {'fontWeight': 'bold'},
}
}
json_data = {
"entityMap": {
"0": {
"type": "IMAGE",
"mutability": "IMMUTABLE",
"data": {
"id": "1111",
"src": "http://www.d.com/1.jpg",
"caption": "Image caption",
"float": "left"
}
},
"1": {
"type": "LINK",
"mutability": "MUTABLE",
"data": {
"url": "https://github.com/icelab/draft-js-ast-exporter/好"
}
},
"2": {
"type": "VIDEO",
"mutability": "IMMUTABLE",
"data": {
"url": "http://v.qq.com/x/cover/01asoh7vfei7w2c/z03172fgqfr.html"
}
},
"3": {
"type": "SEPARATOR",
"mutability": "IMMUTABLE",
"data": {}
},
"4": {
"type": "IMAGE",
"mutability": "IMMUTABLE",
"data": {
"id": "1111",
"src": "http://www.d.com/1.jpg",
"caption": "Image caption",
"float": "left"
}
},
"5": {
"type": "IMAGE",
"mutability": "IMMUTABLE",
"data": {
"id": "1111",
"src": "http://www.d.com/1.jpg",
"caption": "Image caption",
"float": "left"
}
},
"6": {
"type": "IMAGE",
"mutability": "IMMUTABLE",
"data": {
"id": "1111",
"src": "http://www.d.com/1.jpg",
"caption": "Image caption",
"float": "left"
}
},
"7": {
"type": "IMAGE",
"mutability": "IMMUTABLE",
"data": {
"id": "1111",
"src": "http://www.d.com/1.jpg",
"caption": "Image caption",
"float": "left"
}
},
"10": {
"type": "SUBJECT",
"mutability": "IMMUTABLE",
"data": {
"url": "http://www.book.com/1",
}
},
"11": {
"type": "SUBJECT",
"mutability": "IMMUTABLE",
"data": {
"url": "http://www.book.com/1",
}
},
"12": {
"type": "SUBJECT",
"mutability": "IMMUTABLE",
"data": {
"url": "http://www.book.com/1",
}
},
"13": {
"type": "SUBJECT",
"mutability": "IMMUTABLE",
"data": {
"url": "http://www.book.com/1",
}
},
"14": {
"type": "SUBJECT",
"mutability": "IMMUTABLE",
"data": {
"url": "http://www.book.com/1",
}
},
"15": {
"type": "SUBJECT",
"mutability": "IMMUTABLE",
"data": {
"url": "http://www.book.com/1",
}
},
},
"blocks": [
{
"key": "59kd9",
"text": "In your draft-js, exporting your content:",
"type": "unstyled",
"depth": 0,
"inlineStyleRanges": [
{
"offset": 18,
"length": 9,
"style": "BOLD"
},
{
"offset": 33,
"length": 7,
"style": "ITALIC"
}
],
"entityRanges": []
},
{
"key": "9vgd",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 0
}
]
},
{
"key": "0vgd",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 4
}
]
},
{
"key": "8vgd",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 5
}
]
},
{
"key": "avgd",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 6
}
]
},
{
"key": "dvgd",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 7
}
]
},
{
"key": "kst0",
"text": "Find the project on Github.",
"type": "unstyled",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 20,
"length": 6,
"key": 1
}
]
},
{
"key": "8987",
"type": "atomic",
"text": "",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 2
}
]
},
{
"key": "abcd",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 3
}
]
},
{
"key": "c123",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 10
}
]
},
{
"key": "a123",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 11
}
]
},
{
"key": "1123",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 12
}
]
},
{
"key": "2123",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 13
}
]
},
{
"key": "3123",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 14
}
]
},
{
"key": "4123",
"text": "",
"type": "atomic",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 15
}
]
},
{
"key": "ag6qs",
"text": "<b>DraftJS</b> AST Exporter<script>miao()</script>",
"type": "header-two",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": []
},
]
}
exporter = HTML(config)
print "\n<<<---"
pr = cProfile.Profile()
pr.enable()
html = exporter.render(json_data)
pr.disable()
assert html
p = Stats(pr)
p.strip_dirs().sort_stats('cumulative').print_stats(20)
print "\n--->>>"
# print DOM.pretty_print(html)
if __name__ == '__main__':
run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment