blob: 77360ec9cda640d4df6fa33b83e29c04edfbebd0 [file] [log] [blame]
Kapileshwar Singh273de832015-08-20 18:15:29 -07001#!/usr/bin/env python
2# Copyright 2015-2015 ARM Limited
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17
18"""This is a script to publish a notebook containing Ipython graphs
19The static data is published as an anonymous gist. GitHub does not
20allow easy deletions of anonymous gists.
21"""
22
23import json
24import os
25import re
26import requests
27import argparse
28import urlparse
29from IPython.nbformat.sign import TrustNotebookApp
30from requests.auth import HTTPBasicAuth
31from argparse import RawTextHelpFormatter
32from ConfigParser import ConfigParser
33
34# Logging Configuration
35import logging
36from trappy.plotter import IPythonConf
37
38logging.basicConfig(level=logging.INFO)
39
40RAWGIT = "rawgit.com"
41GITHUB_API_URL = "https://api.github.com/gists"
42
43
44def change_resource_paths(txt):
45 """Change the resource paths from local to
46 Web URLs
47 """
48
49 # Replace the path for d3-tip
50 txt = txt.replace(
51 IPythonConf.add_web_base("plotter_scripts/EventPlot/d3.tip.v0.6.3"),
52 "http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3")
53 txt = txt.replace(
54 IPythonConf.add_web_base("plotter_scripts/EventPlot/d3.v3.min"),
55 "http://d3js.org/d3.v3.min")
56 txt = txt.replace(
57 IPythonConf.add_web_base("plotter_scripts/EventPlot/EventPlot"),
58 "https://rawgit.com/sinkap/7f89de3e558856b81f10/raw/46144f8f8c5da670c54f826f0c634762107afc66/EventPlot")
59 txt = txt.replace(
60 IPythonConf.add_web_base("plotter_scripts/ILinePlot/synchronizer"),
61 "http://dygraphs.com/extras/synchronizer")
62 txt = txt.replace(
63 IPythonConf.add_web_base("plotter_scripts/ILinePlot/dygraph-combined"),
64 "http://cdnjs.cloudflare.com/ajax/libs/dygraph/1.1.1/dygraph-combined")
65 txt = txt.replace(
66 IPythonConf.add_web_base("plotter_scripts/ILinePlot/ILinePlot"),
67 "https://rawgit.com/sinkap/648927dfd6985d4540a9/raw/69d6f1f9031ae3624c15707315ce04be1a9d1ac3/ILinePlot")
68
69 logging.info("Updated Library Paths...")
70 return txt
71
72
73def get_url_from_response(response, file_name):
74 """Get the URL of gist from GitHub API response"""
75
76 resp_data = response.json()
77 url = resp_data["files"][file_name]["raw_url"]
78 url = list(urlparse.urlsplit(url))
79 url[1] = RAWGIT
80 url = urlparse.urlunsplit(url)
81
82 logging.info("gist created at: %s", url)
83 return url
84
85
86def fig_to_json(fig, profile):
87 """Get the underlying data file from figure name"""
88
89 data_dir = IPythonConf.get_data_path(profile)
90
91 return os.path.expanduser(
92 os.path.join(
93 data_dir,
94 fig +
95 ".json"))
96
97
98def create_new_gist(fig, profile, login):
99 """Create a new gist for the data of the figure"""
100
101 path = fig_to_json(fig, profile)
102 file_name = os.path.basename(path)
103
104 with open(path) as file_h:
105 content = file_h.read()
106
107 data = {}
108 data["description"] = "Gist Data: {}".format(file_name)
109 data["public"] = True
110 data["files"] = {}
111 data["files"][file_name] = {}
112 data["files"][file_name]["content"] = content
113 response = requests.post(GITHUB_API_URL, data=json.dumps(data), auth=login)
114 return get_url_from_response(response, file_name)
115
116
117def publish(source, target, profile, login):
118 """Publish the notebook for globally viewable interactive
119 plots
120 """
121
122 regex = r"(ILinePlot|EventPlot)\.generate\(\'(fig_.{32})\', '\/(nbextensions|static)\/'\)"
123 txt = ""
124
125 with open(source, 'r') as file_fh:
126
127 for line in file_fh:
128 match = re.search(regex, line)
129 if match:
130 plot = match.group(1)
131 fig = match.group(2)
132 logging.info("Publishing %s : %s", plot, fig)
133 line = re.sub(
134 regex,
135 plot + ".generate('" + fig + "', '" +
136 create_new_gist(fig, profile, login) + "')",
137 line)
138 txt += line
139
140 txt = change_resource_paths(txt)
141
142 with open(target, 'w') as file_fh:
143 file_fh.write(txt)
144
145 trust = TrustNotebookApp()
146 trust.sign_notebook(target)
147 logging.info("Signed and Saved: %s", target)
148
149def read_login_config(config_file):
150 """returns an HTTPBasicAuth object if the
151 config exists"""
152
153 if not config_file:
154 logging.debug("Anonymous gists will be created")
155 return None
156
157 with open(config_file, 'r') as c_fh:
158 config = ConfigParser()
159 config.readfp(c_fh)
160 username = config.get("login", "username")
161 token = config.get("login", "token")
162
163 logging.info("Received Login info for: %s", username)
164
165 return HTTPBasicAuth(username, token)
166
167def main():
168 """Command Line Invocation Routine"""
169
170 parser = argparse.ArgumentParser(description="""
171 The data for the interactive plots is stored in the ipython profile.
172 In order to make it accessible when the notebook is published or shared,
173 a github gist of the data is created and the links in the notebook are
174 updated. The library links are also updated to their corresponding publicly
175 accessible URLs.
176
177 The login credentials can be added to a config file as follows
178
179 1. Go to settings in your github profile and create a 'Personal Access Token'
180 2. This token can be used in place of your password for BasicAuth APIs
181 3. Create a config file:
182
183 [login]
184 username=<your github username>
185 token=<personal access token>
186
187 and pass the path to the file as -c <config>.
188 The gists can then be viewed in the corresponding github account.
189
190 The absence of this will create an anonymous gist which cannot be deleted/managed.""",
191 prog="publish_interactive_plots.py", formatter_class=RawTextHelpFormatter)
192
193 parser.add_argument(
194 "-p",
195 "--profile",
196 help="ipython profile",
197 default="default",
198 type=str)
199
200 parser.add_argument(
201 "-o",
202 "--outfile",
203 help="name of the output notebook",
204 default="",
205 type=str)
206
207 parser.add_argument(
208 "-c",
209 "--config",
210 help="The path to a config file containing github login credentials",
211 default=None,
212 type=str)
213
214 parser.add_argument("notebook")
215 args = parser.parse_args()
216
217 profile = args.profile
218 notebook = args.notebook
219 outfile = args.outfile
220 config = args.config
221 login = read_login_config(config)
222
223 if outfile == "":
224 outfile = "published_" + os.path.basename(notebook)
225 logging.info("Setting outfile as %s", outfile)
226
227 elif not outfile.endswith(".ipynb"):
228 outfile += ".ipynb"
229
230 publish(notebook, outfile, profile, login)
231
232if __name__ == "__main__":
233 main()