Skip to content

Commit d33593f

Browse files
committed
Add converter of Cadabra notebooks to Jupyter notebooks, and export option in cadabra2-gtk.
1 parent 3bdeba4 commit d33593f

File tree

6 files changed

+205
-44
lines changed

6 files changed

+205
-44
lines changed

core/CMakeLists.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,21 @@ target_link_libraries(cadabra2python
328328
${PYTHON_LIBRARIES}
329329
)
330330

331+
# cadabra2ipynb executable
332+
add_executable(cadabra2ipynb
333+
cadabra2ipynb.cc
334+
CdbPython.cc
335+
InstallPrefix.cc
336+
DataCell.cc
337+
${CADABRA_LIBS_DIR}/whereami/whereami.c
338+
${CADABRA_LIBS_DIR}/base64/base64.cc
339+
)
340+
target_link_libraries(cadabra2ipynb
341+
${Boost_LIBRARIES}
342+
# ${GLIBMM3_LIBRARIES}
343+
${PYTHON_LIBRARIES}
344+
)
345+
331346
# cadabra2cadabra
332347
add_executable(cadabra2cadabra
333348
cadabra2cadabra.cc
@@ -411,6 +426,14 @@ install(
411426
bin
412427
)
413428

429+
# cadabra2ipynb
430+
install(
431+
TARGETS
432+
cadabra2ipynb
433+
DESTINATION
434+
bin
435+
)
436+
414437
# cadabra2cadabra
415438
install(
416439
TARGETS
@@ -436,6 +459,7 @@ if(NOT MSVC)
436459
COMMAND rm -f "\$(destdir_expr)${CMAKE_INSTALL_PREFIX}/man/man1/cadabra2html.1"
437460
COMMAND rm -f "\$(destdir_expr)${CMAKE_INSTALL_PREFIX}/man/man1/cadabra2latex.1"
438461
COMMAND rm -f "\$(destdir_expr)${CMAKE_INSTALL_PREFIX}/man/man1/cadabra2python.1"
462+
COMMAND rm -f "\$(destdir_expr)${CMAKE_INSTALL_PREFIX}/man/man1/cadabra2ipynb.1"
439463
COMMAND rm -f "\$(destdir_expr)${CMAKE_INSTALL_PREFIX}/man/man1/cadabra2-gtk.1"
440464
COMMAND rm -f "\$(destdir_expr)${CMAKE_INSTALL_PREFIX}/man/man1/cadabra-server.1"
441465
COMMAND rm -f "\$(destdir_expr)${CMAKE_INSTALL_PREFIX}/man/man1/cadabra2-cli.1"
@@ -450,6 +474,7 @@ if(NOT MSVC)
450474
../man/man1/cadabra2.1
451475
../man/man1/cadabra2-cli.1
452476
../man/man1/cadabra2cadabra.1
477+
../man/man1/cadabra2ipynb.1
453478
../man/man1/cadabra2html.1
454479
../man/man1/cadabra2latex.1
455480
../man/man1/cadabra2python.1

core/DataCell.cc

Lines changed: 46 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -745,60 +745,62 @@ nlohmann::json cadabra::ipynb2cnb(const nlohmann::json& root)
745745

746746
nlohmann::json cadabra::cnb2ipynb(const nlohmann::json& root)
747747
{
748-
auto nbf = root["nbformat"];
749-
nlohmann::json json;
748+
nlohmann::json ipynb, kernelspec, lang;
750749

751-
if(!nbf)
752-
return json;
750+
ipynb["nbformat"]=4;
751+
ipynb["nbformat_minor"]=4;
753752

754-
json["description"]="Cadabra JSON notebook format";
755-
json["version"]=1.0;
753+
kernelspec["display_name"]="Cadabra2";
754+
kernelspec["language"]="python";
755+
kernelspec["name"]="cadabra2";
756+
757+
lang["codemirror_mode"]="cadabra";
758+
lang["file_extension"]=".ipynb";
759+
lang["mimetype"]="text/cadabra";
760+
lang["name"]="cadabra2";
761+
lang["pygments_lexer"]="cadabra";
762+
763+
ipynb["metadata"] = {
764+
{"kernelspec", kernelspec},
765+
{"language_info", lang}
766+
};
756767

757768
nlohmann::json cells=nlohmann::json::array();
758-
const nlohmann::json& jucells=root["cells"];
759769

760770
// Jupyter notebooks just have a single array of cells; walk
761-
// through and add to our "cells" array.
771+
// through and setup our cells array.
762772

763-
for(unsigned int c=0; c<jucells.size(); ++c) {
773+
for(const auto& cdb: root["cells"]) {
764774
nlohmann::json cell;
765-
if(jucells[c]["cell_type"].get<std::string>()=="markdown")
766-
cell["cell_type"]="latex";
767-
else
768-
cell["cell_type"]="input";
769-
cell["hidden"]=false;
770-
const nlohmann::json& source=jucells[c]["source"];
771-
// Jupyter stores the source line-by-line in an array 'source'.
772-
std::string block;
773-
for(unsigned int l=0; l<source.size(); ++l) {
774-
std::string line=source[l].get<std::string>();
775-
if(line.size()>0) {
776-
if(line[0]=='#') {
777-
std::string sub="";
778-
line=line.substr(1);
779-
int inc=0;
780-
while(line.size()>0 && line[0]=='#') {
781-
if(inc<2) {
782-
sub+="sub";
783-
inc+=1;
784-
}
785-
line=line.substr(1);
786-
}
787-
if(line.size()==0)
788-
continue;
789-
if(line[line.size()-1]=='\n')
790-
line=line.substr(0,line.size()-1);
791-
block+="\\"+sub+"section*{"+trim(line)+"}\n";
792-
}
793-
else
794-
block+=line;
795-
}
775+
if(cdb["cell_type"]=="python") {
776+
cell["cell_type"]="code";
777+
cell["source"]=nlohmann::json::array();
778+
cell["source"].push_back(cdb["source"]);
779+
cell["metadata"]=nlohmann::json::object();
780+
cell["outputs"]=nlohmann::json::array();
781+
cell["execution_count"]={};
796782
}
797-
cell["source"]=block;
798-
cells.push_back(cell);
783+
if(cdb["cell_type"]=="input") {
784+
cell["cell_type"]="code";
785+
cell["source"]=nlohmann::json::array();
786+
cell["source"].push_back(cdb["source"]);
787+
cell["metadata"]=nlohmann::json::object();
788+
cell["outputs"]=nlohmann::json::array();
789+
cell["execution_count"]={};
790+
}
791+
else if(cdb["cell_type"]=="latex") {
792+
cell["cell_type"]="markdown";
793+
cell["source"]=nlohmann::json::array();
794+
cell["source"].push_back(cdb["source"]);
795+
cell["metadata"]=nlohmann::json::object();
796+
}
797+
798+
if(cell.is_null()==false)
799+
cells.push_back(cell);
800+
799801
}
800802

801-
json["cells"] = cells;
803+
ipynb["cells"] = cells;
802804

803-
return json;
805+
return ipynb;
804806
}

core/cadabra2ipynb.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
// Convert a Cadabra .cnb notebook to a Jupyter notebook. input file to pure Python .py, by doing the
3+
// pre-processing stage.
4+
5+
#include <iostream>
6+
#include <CdbPython.hh>
7+
#include <fstream>
8+
#include "nlohmann/json.hpp"
9+
#include "DataCell.hh"
10+
11+
int main(int argc, char **argv)
12+
{
13+
if(argc<2) {
14+
std::cerr << "Usage: cadabra2ipynb [cadabra notebook] [jupyter notebook]\n\n";
15+
std::cerr << "Convert a Cadabra notebook to a Jupyter notebook.\n"
16+
<< "If the Jupyter notebook name is not given, output goes to standard out.\n";
17+
return -1;
18+
}
19+
20+
std::string cdb_file, python_file;
21+
int n=1;
22+
while(n<argc) {
23+
if(cdb_file=="")
24+
cdb_file=argv[n];
25+
else
26+
python_file=argv[n];
27+
++n;
28+
}
29+
30+
auto from=cdb_file.find_last_of("/");
31+
++from;
32+
auto to =cdb_file.find_last_of(".");
33+
std::string t=cdb_file.substr(from, to-from);
34+
t[0]=toupper(t[0]);
35+
std::string title="Cadabra manual: "+t;
36+
37+
std::ifstream file(cdb_file);
38+
if(!file.is_open()) {
39+
std::cerr << "cadabra2ipynb: failed to open " << cdb_file << std::endl;
40+
return -1;
41+
}
42+
nlohmann::json content;
43+
file >> content;
44+
45+
auto python = cadabra::cnb2ipynb(content);
46+
47+
if(python_file!="") {
48+
std::ofstream pythonfile(python_file);
49+
pythonfile << python.dump(3);
50+
}
51+
else {
52+
std::cout << python.dump(3);
53+
}
54+
55+
return 0;
56+
}

frontend/gtkmm/NotebookWindow.cc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ NotebookWindow::NotebookWindow(Cadabra *c, bool ro)
103103
sigc::mem_fun(*this, &NotebookWindow::on_file_save) );
104104
actiongroup->add( Gtk::Action::create("SaveAs", Gtk::Stock::SAVE_AS), Gtk::AccelKey("<control><shift>S"),
105105
sigc::mem_fun(*this, &NotebookWindow::on_file_save_as) );
106+
actiongroup->add( Gtk::Action::create("ExportAsJupyter", "Export as Jupyter notebook"),
107+
sigc::mem_fun(*this, &NotebookWindow::on_file_save_as_jupyter));
106108
actiongroup->add( Gtk::Action::create("ExportHtml", "Export to standalone HTML"),
107109
sigc::mem_fun(*this, &NotebookWindow::on_file_export_html) );
108110
actiongroup->add( Gtk::Action::create("ExportHtmlSegment", "Export to HTML segment"),
@@ -262,6 +264,7 @@ NotebookWindow::NotebookWindow(Cadabra *c, bool ro)
262264
" <separator/>"
263265
" <menuitem action='Save'/>"
264266
" <menuitem action='SaveAs'/>"
267+
" <menuitem action='ExportAsJupyter'/>"
265268
" <menuitem action='ExportHtml'/>"
266269
" <menuitem action='ExportHtmlSegment'/>"
267270
" <menuitem action='ExportLaTeX'/>"
@@ -1510,6 +1513,39 @@ void NotebookWindow::on_file_save_as()
15101513
}
15111514
}
15121515

1516+
void NotebookWindow::on_file_save_as_jupyter()
1517+
{
1518+
Gtk::FileChooserDialog dialog("Please choose a file name to export as Jupyter notebook",
1519+
Gtk::FILE_CHOOSER_ACTION_SAVE);
1520+
1521+
dialog.set_do_overwrite_confirmation(true);
1522+
dialog.set_transient_for(*this);
1523+
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
1524+
dialog.add_button("Select", Gtk::RESPONSE_OK);
1525+
1526+
int result=dialog.run();
1527+
1528+
switch(result) {
1529+
case(Gtk::RESPONSE_OK): {
1530+
std::string ipynb_name = dialog.get_filename();
1531+
std::string out = JSON_serialise(doc);
1532+
auto json=nlohmann::json::parse(out);
1533+
auto ipynb = cnb2ipynb(json);
1534+
std::ofstream file(ipynb_name);
1535+
file << ipynb.dump(3) << std::endl;
1536+
1537+
// if(res.size()>0) {
1538+
// Gtk::MessageDialog md("Error saving Jupyter notebook "+name);
1539+
// md.set_transient_for(*this);
1540+
// md.set_secondary_text(res);
1541+
// md.set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG);
1542+
// md.run();
1543+
// }
1544+
break;
1545+
}
1546+
}
1547+
}
1548+
15131549
void NotebookWindow::on_file_export_html()
15141550
{
15151551
Gtk::FileChooserDialog dialog("Please enter a file name for the HTML document",

frontend/gtkmm/NotebookWindow.hh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <thread>
1818
#include <mutex>
1919

20+
#include "nlohmann/json.hpp"
21+
2022
#include "DocumentThread.hh"
2123
#include "ComputeThread.hh"
2224
#include "Console.hh"
@@ -184,6 +186,7 @@ namespace cadabra {
184186
void on_file_close();
185187
void on_file_save();
186188
void on_file_save_as();
189+
void on_file_save_as_jupyter();
187190
void on_file_export_html();
188191
void on_file_export_html_segment();
189192
void on_file_export_latex();

man/man1/cadabra2ipynb.1

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
.TH CADABRA2IPYNB 1 "Jan 22, 2019" "" ""
2+
.\"
3+
.\" Man page written by Kasper Peeters <[email protected]>
4+
.\"
5+
.\" This program is free software; you can redistribute it and/or modify
6+
.\" it under the terms of the GNU General Public License as published by
7+
.\" the Free Software Foundation; either version 3 of the License, or
8+
.\" (at your option) any later version.
9+
.\"
10+
.\" This program is distributed in the hope that it will be useful,
11+
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
.\" GNU General Public License for more details.
14+
.\"
15+
.\" You should have received a copy of the GNU General Public License
16+
.\" along with this program; if not, write to the Free Software
17+
.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18+
.\"
19+
.\"
20+
.SH NAME
21+
cadabra2ipynb \- convert Cadabra2 notebooks to Jupyter notebooks
22+
.SH SYNOPSIS
23+
.BR "cadabra2cadabra [cadabra notebook file (.cnb)] [Jupyter notebook file (.ipynb)]"
24+
.SH DESCRIPTION
25+
.B The Cadabra2-Gtk notebook front\-end for Cadabra2 produces
26+
notebooks in a JSON format, but these notebooks are not compatible
27+
with Jupyter notebooks. This command converts Cadabra notebooks to
28+
Jupyter notebooks, which can be executed using the Cadabra Jupyter
29+
kernel.
30+
31+
.SH SEE ALSO
32+
.BR cadabra2-gtk (1), cadabra2 (1)
33+
34+
See the web page at
35+
.BR "https://cadabra.science/"
36+
for more information and up\-to\-date sample notebooks.
37+
.SH AUTHORS
38+
Kasper Peeters <[email protected]>
39+

0 commit comments

Comments
 (0)