blob: cdd31feef1a91e8d20ebd19f0bbd92df8cc22dbd [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")
Kapileshwar Singh324d3362015-09-03 12:37:52 +010068 txt = txt.replace(
69 IPythonConf.add_web_base("plotter_scripts/ILinePlot/underscore-min"),
70 "https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min")
Kapileshwar Singh273de832015-08-20 18:15:29 -070071
72 logging.info("Updated Library Paths...")
73 return txt
74
75
76def get_url_from_response(response, file_name):
77 """Get the URL of gist from GitHub API response"""
78
79 resp_data = response.json()
80 url = resp_data["files"][file_name]["raw_url"]
81 url = list(urlparse.urlsplit(url))
82 url[1] = RAWGIT
83 url = urlparse.urlunsplit(url)
84
85 logging.info("gist created at: %s", url)
86 return url
87
88
89def fig_to_json(fig, profile):
90 """Get the underlying data file from figure name"""
91
92 data_dir = IPythonConf.get_data_path(profile)
93
94 return os.path.expanduser(
95 os.path.join(
96 data_dir,
97 fig +
98 ".json"))
99
100
101def create_new_gist(fig, profile, login):
102 """Create a new gist for the data of the figure"""
103
104 path = fig_to_json(fig, profile)
105 file_name = os.path.basename(path)
106
107 with open(path) as file_h:
108 content = file_h.read()
109
110 data = {}
111 data["description"] = "Gist Data: {}".format(file_name)
112 data["public"] = True
113 data["files"] = {}
114 data["files"][file_name] = {}
115 data["files"][file_name]["content"] = content
116 response = requests.post(GITHUB_API_URL, data=json.dumps(data), auth=login)
117 return get_url_from_response(response, file_name)
118
119
120def publish(source, target, profile, login):
121 """Publish the notebook for globally viewable interactive
122 plots
123 """
124
125 regex = r"(ILinePlot|EventPlot)\.generate\(\'(fig_.{32})\', '\/(nbextensions|static)\/'\)"
126 txt = ""
127
128 with open(source, 'r') as file_fh:
129
130 for line in file_fh:
131 match = re.search(regex, line)
132 if match:
133 plot = match.group(1)
134 fig = match.group(2)
135 logging.info("Publishing %s : %s", plot, fig)
136 line = re.sub(
137 regex,
138 plot + ".generate('" + fig + "', '" +
139 create_new_gist(fig, profile, login) + "')",
140 line)
141 txt += line
142
143 txt = change_resource_paths(txt)
144
145 with open(target, 'w') as file_fh:
146 file_fh.write(txt)
147
148 trust = TrustNotebookApp()
149 trust.sign_notebook(target)
150 logging.info("Signed and Saved: %s", target)
151
152def read_login_config(config_file):
153 """returns an HTTPBasicAuth object if the
154 config exists"""
155
156 if not config_file:
157 logging.debug("Anonymous gists will be created")
158 return None
159
160 with open(config_file, 'r') as c_fh:
161 config = ConfigParser()
162 config.readfp(c_fh)
163 username = config.get("login", "username")
164 token = config.get("login", "token")
165
166 logging.info("Received Login info for: %s", username)
167
168 return HTTPBasicAuth(username, token)
169
170def main():
171 """Command Line Invocation Routine"""
172
173 parser = argparse.ArgumentParser(description="""
174 The data for the interactive plots is stored in the ipython profile.
175 In order to make it accessible when the notebook is published or shared,
176 a github gist of the data is created and the links in the notebook are
177 updated. The library links are also updated to their corresponding publicly
178 accessible URLs.
179
180 The login credentials can be added to a config file as follows
181
182 1. Go to settings in your github profile and create a 'Personal Access Token'
183 2. This token can be used in place of your password for BasicAuth APIs
184 3. Create a config file:
185
186 [login]
187 username=<your github username>
188 token=<personal access token>
189
190 and pass the path to the file as -c <config>.
191 The gists can then be viewed in the corresponding github account.
192
193 The absence of this will create an anonymous gist which cannot be deleted/managed.""",
194 prog="publish_interactive_plots.py", formatter_class=RawTextHelpFormatter)
195
196 parser.add_argument(
197 "-p",
198 "--profile",
199 help="ipython profile",
200 default="default",
201 type=str)
202
203 parser.add_argument(
204 "-o",
205 "--outfile",
206 help="name of the output notebook",
207 default="",
208 type=str)
209
210 parser.add_argument(
211 "-c",
212 "--config",
213 help="The path to a config file containing github login credentials",
214 default=None,
215 type=str)
216
217 parser.add_argument("notebook")
218 args = parser.parse_args()
219
220 profile = args.profile
221 notebook = args.notebook
222 outfile = args.outfile
223 config = args.config
224 login = read_login_config(config)
225
226 if outfile == "":
227 outfile = "published_" + os.path.basename(notebook)
228 logging.info("Setting outfile as %s", outfile)
229
230 elif not outfile.endswith(".ipynb"):
231 outfile += ".ipynb"
232
233 publish(notebook, outfile, profile, login)
234
235if __name__ == "__main__":
236 main()