Created
December 9, 2016 06:24
-
-
Save su27/591200fda8c20116c7d21d9e4fb3e77b to your computer and use it in GitHub Desktop.
profile for draftjs exporter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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&autoplay=1&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&autoplay=1&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&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