From 639c46e8f85ff867b4206ab0fad1e93c5f8bc5d9 Mon Sep 17 00:00:00 2001 From: "Wang,Jeff" Date: Wed, 21 Mar 2018 13:30:33 -0700 Subject: [PATCH 1/4] Add manual Save feature. --- visualdl/logic/pybind.cc | 23 ++++++++++++++++------- visualdl/logic/sdk.h | 2 +- visualdl/python/storage.py | 3 +++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/visualdl/logic/pybind.cc b/visualdl/logic/pybind.cc index d24ace6c6..ccea54def 100644 --- a/visualdl/logic/pybind.cc +++ b/visualdl/logic/pybind.cc @@ -86,6 +86,7 @@ PYBIND11_MODULE(core, m) { })) .def("set_mode", &vs::LogWriter::SetMode) .def("as_mode", &vs::LogWriter::AsMode) + .def("save", &vs::LogWriter::Save) // clang-format off #define WRITER_ADD_SCALAR(T) \ .def("new_scalar_" #T, [](vs::LogWriter& self, const std::string& tag) { \ @@ -127,10 +128,15 @@ PYBIND11_MODULE(core, m) { ADD_SCALAR_READER(int64_t); #undef ADD_SCALAR_READER -#define ADD_SCALAR_WRITER(T) \ - py::class_>(m, "ScalarWriter__" #T, R"pbdoc(PyBind class. Must instantiate through the LogWriter.)pbdoc") \ - .def("set_caption", &cp::Scalar::SetCaption) \ - .def("add_record", &cp::Scalar::AddRecord, R"pbdoc(add a record with the step and value)pbdoc"); +#define ADD_SCALAR_WRITER(T) \ + py::class_>( \ + m, \ + "ScalarWriter__" #T, \ + R"pbdoc(PyBind class. Must instantiate through the LogWriter.)pbdoc") \ + .def("set_caption", &cp::Scalar::SetCaption) \ + .def("add_record", \ + &cp::Scalar::AddRecord, \ + R"pbdoc(add a record with the step and value)pbdoc"); ADD_SCALAR_WRITER(int); ADD_SCALAR_WRITER(float); ADD_SCALAR_WRITER(double); @@ -192,9 +198,12 @@ PYBIND11_MODULE(core, m) { .def("record", &cp::ImageReader::record) .def("timestamp", &cp::ImageReader::timestamp); -#define ADD_HISTOGRAM_WRITER(T) \ - py::class_>(m, "HistogramWriter__" #T, R"pbdoc(PyBind class. Must instantiate through the LogWriter.)pbdoc") \ - .def("add_record", &cp::Histogram::AddRecord, R"pbdoc(add a record with the step and histogram_value)pbdoc"); +#define ADD_HISTOGRAM_WRITER(T) \ + py::class_>(m, "HistogramWriter__" #T, \ + R"pbdoc(PyBind class. Must instantiate through the LogWriter.)pbdoc") \ + .def("add_record", \ + &cp::Histogram::AddRecord, \ + R"pbdoc(add a record with the step and histogram_value)pbdoc"); ADD_FULL_TYPE_IMPL(ADD_HISTOGRAM_WRITER) #undef ADD_HISTOGRAM_WRITER diff --git a/visualdl/logic/sdk.h b/visualdl/logic/sdk.h index 810dc5c9f..83ed47850 100644 --- a/visualdl/logic/sdk.h +++ b/visualdl/logic/sdk.h @@ -44,7 +44,7 @@ class LogWriter { mode_ = mode; storage_.AddMode(mode); } - + void Save() { storage_.PersistToDisk(); } LogWriter AsMode(const std::string& mode); Tablet AddTablet(const std::string& tag); diff --git a/visualdl/python/storage.py b/visualdl/python/storage.py index bf17fc1d8..11d5de765 100644 --- a/visualdl/python/storage.py +++ b/visualdl/python/storage.py @@ -222,6 +222,9 @@ def histogram(self, tag, num_buckets, type='float'): } return types[type](tag, num_buckets) + def save(self): + self.writer.save() + def __enter__(self): return self From 24e78de89b948282aa619db6d130dd1b5b07f9be Mon Sep 17 00:00:00 2001 From: "Wang,Jeff" Date: Wed, 21 Mar 2018 15:48:42 -0700 Subject: [PATCH 2/4] Fix the incorrect style --- README.md | 4 ++-- demo/keras/TUTORIAL_EN.md | 2 +- demo/mxnet/TUTORIAL_EN.md | 4 ++-- demo/pytorch/TUTORIAL_EN.md | 2 +- docs/quick_start_en.md | 4 ++-- visualdl/storage/storage.cc | 32 ++++++++++++-------------------- visualdl/storage/tablet.cc | 18 +++++++++--------- visualdl/storage/tablet.h | 5 +++-- 8 files changed, 32 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 031196fae..f1a359a58 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ VisualDL provides both Python SDK and C++ SDK in order to fit more use cases. ### Python SDK -VisualDL now supports both Python 2 and Python 3. +VisualDL now supports both Python 2 and Python 3. Below is an example of creating a simple Scalar component and inserting data from different timestamps: ```python @@ -162,7 +162,7 @@ pip install --upgrade dist/visualdl-*.whl ### Run a demo from scratch ``` # vdl_create_scratch_log is a helper commend that creates mock data. -vdl_create_scratch_log +vdl_create_scratch_log visualDL --logdir=scratch_log --port=8080 ``` that will start a server locally on port 8080, then diff --git a/demo/keras/TUTORIAL_EN.md b/demo/keras/TUTORIAL_EN.md index 74a1cd31d..70098653e 100644 --- a/demo/keras/TUTORIAL_EN.md +++ b/demo/keras/TUTORIAL_EN.md @@ -87,4 +87,4 @@ The histograms of the training parameters is as follows:

-The full demonstration code can be downloaded in [here](https://github.com/PaddlePaddle/VisualDL/blob/develop/demo/keras/keras_mnist_demo.py). \ No newline at end of file +The full demonstration code can be downloaded in [here](https://github.com/PaddlePaddle/VisualDL/blob/develop/demo/keras/keras_mnist_demo.py). diff --git a/demo/mxnet/TUTORIAL_EN.md b/demo/mxnet/TUTORIAL_EN.md index a00257985..34aae9315 100644 --- a/demo/mxnet/TUTORIAL_EN.md +++ b/demo/mxnet/TUTORIAL_EN.md @@ -4,7 +4,7 @@ Here we will show you how to use VisualDL in MXNet so that you can visualize the We will use the MXNet Convolution Neural Network to train the [MNIST](http://yann.lecun.com/exdb/mnist/) dataset as an example. ## Install MXNet -Please install MXNet according to MXNet's [official website](https://mxnet.incubator.apache.org/install/index.html) +Please install MXNet according to MXNet's [official website](https://mxnet.incubator.apache.org/install/index.html) and verify that the installation is successful. >>> import mxnet as mx @@ -58,7 +58,7 @@ lenet_model.fit(train_iter, ``` That's all. In the training process of MXNet, our callback function is called to record the accuracy at the end of each training batch. -The rate of accuracy will continue to rise until more than 95%. +The rate of accuracy will continue to rise until more than 95%. The following is the accuracy of the two epochs:

diff --git a/demo/pytorch/TUTORIAL_EN.md b/demo/pytorch/TUTORIAL_EN.md index 7801b604f..f5d1e2778 100644 --- a/demo/pytorch/TUTORIAL_EN.md +++ b/demo/pytorch/TUTORIAL_EN.md @@ -167,7 +167,7 @@ for epoch in range(5): # loop over the dataset multiple times print('Finished Training') ``` -PyTorch support ONNX standard and it can export its model into ONNX. +PyTorch support ONNX standard and it can export its model into ONNX. PyTorch runs a single round of inference to trace the graph. We use a dummy input to run the model to produce the ONNX model ```python diff --git a/docs/quick_start_en.md b/docs/quick_start_en.md index 2fa179cda..1fc6a0da5 100644 --- a/docs/quick_start_en.md +++ b/docs/quick_start_en.md @@ -27,10 +27,10 @@ logw = LogWriter("./random_log", sync_cycle=10000) ``` The first parameter points to a folder; the second parameter `sync_cycle` specifies out of how memory operations should be -store the data into hard drive. +store the data into hard drive. ### sync_cycle -Writing is a heavy operation. Setting `sync_cycle` might slow down your training. +Writing is a heavy operation. Setting `sync_cycle` might slow down your training. A good starting point is to set the `sync_cycle` to be at least twice the amount of data point your would like to capture. There are different modes for model training, such as training, validating and testing. All these correspond to `mode' in VisualDL. diff --git a/visualdl/storage/storage.cc b/visualdl/storage/storage.cc index 29a8704a7..8dc4acdc8 100644 --- a/visualdl/storage/storage.cc +++ b/visualdl/storage/storage.cc @@ -32,9 +32,7 @@ Storage::Storage(const Storage& other) dir_ = other.dir_; } -Storage::~Storage() { - PersistToDisk(); -} +Storage::~Storage() { PersistToDisk(); } void Storage::AddMode(const std::string& x) { // avoid duplicate modes. @@ -54,13 +52,9 @@ Tablet Storage::AddTablet(const std::string& x) { return Tablet(&(*tablets_)[x], this); } -void Storage::SetDir(const std::string& dir) { - *dir_ = dir; -} +void Storage::SetDir(const std::string& dir) { *dir_ = dir; } -std::string Storage::dir() const { - return *dir_; -} +std::string Storage::dir() const { return *dir_; } void Storage::PersistToDisk() { PersistToDisk(*dir_); } @@ -70,27 +64,25 @@ void Storage::PersistToDisk(const std::string& dir) { fs::SerializeToFile(*data_, meta_path(dir)); for (auto tag : data_->tags()) { - if (modified_tablet_set_.count(tag) > 0){ - auto it = tablets_->find(tag); - CHECK(it != tablets_->end()) << "tag " << tag << " not exist."; - fs::SerializeToFile(it->second, tablet_path(dir, tag)); + if (modified_tablet_set_.count(tag) > 0) { + auto it = tablets_->find(tag); + CHECK(it != tablets_->end()) << "tag " << tag << " not exist."; + fs::SerializeToFile(it->second, tablet_path(dir, tag)); } } modified_tablet_set_.clear(); } -Storage* Storage::parent() { - return this; -} +Storage* Storage::parent() { return this; } void Storage::MarkTabletModified(const std::string tag) { - modified_tablet_set_.insert(tag); + modified_tablet_set_.insert(tag); } void Storage::AddTag(const std::string& x) { - *data_->add_tags() = x; - WRITE_GUARD - } + *data_->add_tags() = x; + WRITE_GUARD +} // StorageReader std::vector StorageReader::all_tags() { diff --git a/visualdl/storage/tablet.cc b/visualdl/storage/tablet.cc index fc8db3783..b36f6f082 100644 --- a/visualdl/storage/tablet.cc +++ b/visualdl/storage/tablet.cc @@ -18,18 +18,18 @@ limitations under the License. */ namespace visualdl { void Tablet::SetTag(const std::string& mode, const std::string& tag) { - auto internal_tag = mode + "/" + tag; - string::TagEncode(internal_tag); - internal_encoded_tag_ = internal_tag; - data_->set_tag(internal_tag); - WRITE_GUARD + auto internal_tag = mode + "/" + tag; + string::TagEncode(internal_tag); + internal_encoded_tag_ = internal_tag; + data_->set_tag(internal_tag); + WRITE_GUARD } Record Tablet::AddRecord() { - parent()->MarkTabletModified(internal_encoded_tag_); - IncTotalRecords(); - WRITE_GUARD - return Record(data_->add_records(), parent()); + parent()->MarkTabletModified(internal_encoded_tag_); + IncTotalRecords(); + WRITE_GUARD + return Record(data_->add_records(), parent()); } TabletReader Tablet::reader() { return TabletReader(*data_); } diff --git a/visualdl/storage/tablet.h b/visualdl/storage/tablet.h index b0907445b..f0c2cc86c 100644 --- a/visualdl/storage/tablet.h +++ b/visualdl/storage/tablet.h @@ -29,11 +29,12 @@ struct TabletReader; * Tablet is a helper for operations on storage::Tablet. */ struct Tablet { - enum Type { kScalar = 0, kHistogram = 1, kImage = 2, kUnknown = -1}; + enum Type { kScalar = 0, kHistogram = 1, kImage = 2, kUnknown = -1 }; DECL_GUARD(Tablet); - Tablet(storage::Tablet* x, Storage* parent) : data_(x), x_(parent), internal_encoded_tag_("") {} + Tablet(storage::Tablet* x, Storage* parent) + : data_(x), x_(parent), internal_encoded_tag_("") {} static Type type(const std::string& name) { if (name == "scalar") { From bd8534485b6edcc53278d8a45a10d214f5f159c2 Mon Sep 17 00:00:00 2001 From: "Wang,Jeff" Date: Wed, 21 Mar 2018 17:30:15 -0700 Subject: [PATCH 3/4] Add feature to record Text --- visualdl/logic/pybind.cc | 27 ++++++++++++++++++++++++--- visualdl/logic/sdk.cc | 31 +++++++++++++++++++++++++++++++ visualdl/logic/sdk.h | 34 ++++++++++++++++++++++++++++++++++ visualdl/python/storage.py | 8 ++++++++ 4 files changed, 97 insertions(+), 3 deletions(-) diff --git a/visualdl/logic/pybind.cc b/visualdl/logic/pybind.cc index ccea54def..f5e292f57 100644 --- a/visualdl/logic/pybind.cc +++ b/visualdl/logic/pybind.cc @@ -74,9 +74,14 @@ PYBIND11_MODULE(core, m) { #undef READER_ADD_HISTOGRAM // clang-format on - .def("get_image", [](vs::LogReader& self, const std::string& tag) { + .def("get_image", + [](vs::LogReader& self, const std::string& tag) { + auto tablet = self.tablet(tag); + return vs::components::ImageReader(self.mode(), tablet); + }) + .def("get_text", [](vs::LogReader& self, const std::string& tag) { auto tablet = self.tablet(tag); - return vs::components::ImageReader(self.mode(), tablet); + return vs::components::TextReader(tablet); }); // clang-format on @@ -113,7 +118,11 @@ PYBIND11_MODULE(core, m) { int step_cycle) { auto tablet = self.AddTablet(tag); return vs::components::Image(tablet, num_samples, step_cycle); - }); + }) + .def("new_text", [](vs::LogWriter& self, const std::string& tag) { + auto tablet = self.AddTablet(tag); + return vs::components::Text(tablet); + }); //------------------- components -------------------- #define ADD_SCALAR_READER(T) \ @@ -198,6 +207,18 @@ PYBIND11_MODULE(core, m) { .def("record", &cp::ImageReader::record) .def("timestamp", &cp::ImageReader::timestamp); + py::class_(m, "TextWriter") + .def("set_caption", &cp::Text::SetCaption) + .def("add_record", &cp::Text::AddRecord); + + py::class_(m, "TextReader") + .def("records", &cp::TextReader::records) + .def("ids", &cp::TextReader::ids) + .def("timestamps", &cp::TextReader::timestamps) + .def("caption", &cp::TextReader::caption) + .def("total_records", &cp::TextReader::total_records) + .def("size", &cp::TextReader::size); + #define ADD_HISTOGRAM_WRITER(T) \ py::class_>(m, "HistogramWriter__" #T, \ R"pbdoc(PyBind class. Must instantiate through the LogWriter.)pbdoc") \ diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index 3edc8c276..db58e73ea 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -321,6 +321,37 @@ DECL_BASIC_TYPES_CLASS_IMPL(class, ScalarReader) DECL_BASIC_TYPES_CLASS_IMPL(struct, Histogram) DECL_BASIC_TYPES_CLASS_IMPL(struct, HistogramReader) +std::vector TextReader::records() const { + std::vector res; + for (int i = 0; i < total_records(); i++) { + res.push_back(reader_.record(i).data(0).template Get()); + } + return res; +} + +std::vector TextReader::ids() const { + std::vector res; + for (int i = 0; i < reader_.total_records(); i++) { + res.push_back(reader_.record(i).id()); + } + return res; +} + +std::vector TextReader::timestamps() const { + std::vector res; + for (int i = 0; i < reader_.total_records(); i++) { + res.push_back(reader_.record(i).timestamp()); + } + return res; +} + +std::string TextReader::caption() const { + CHECK(!reader_.captions().empty()) << "no caption"; + return reader_.captions().front(); +} + +size_t TextReader::size() const { return reader_.total_records(); } + } // namespace components } // namespace visualdl diff --git a/visualdl/logic/sdk.h b/visualdl/logic/sdk.h index 83ed47850..313563c04 100644 --- a/visualdl/logic/sdk.h +++ b/visualdl/logic/sdk.h @@ -284,6 +284,40 @@ struct HistogramReader { TabletReader reader_; }; +struct Text { + Text(Tablet tablet) : tablet_(tablet) {} + void SetCaption(const std::string cap) { + tablet_.SetCaptions(std::vector({cap})); + } + + void AddRecord(int id, std::string value) { + auto record = tablet_.AddRecord(); + record.SetId(id); + auto entry = record.AddData(); + + time_t time = std::time(nullptr); + record.SetTimeStamp(time); + entry.Set(value); + } + +private: + Tablet tablet_; +}; + +struct TextReader { + TextReader(TabletReader reader) : reader_(reader) {} + + std::vector records() const; + std::vector ids() const; + std::vector timestamps() const; + std::string caption() const; + size_t total_records() const { return reader_.total_records(); } + size_t size() const; + +private: + TabletReader reader_; +}; + } // namespace components } // namespace visualdl diff --git a/visualdl/python/storage.py b/visualdl/python/storage.py index 11d5de765..4b519dd66 100644 --- a/visualdl/python/storage.py +++ b/visualdl/python/storage.py @@ -115,6 +115,10 @@ def histogram(self, tag, type='float'): check_tag_name_valid(tag) return type2scalar[type](tag) + def text(self, tag): + check_tag_name_valid(tag) + return self.reader.get_text(tag) + def __enter__(self): return self @@ -222,6 +226,10 @@ def histogram(self, tag, num_buckets, type='float'): } return types[type](tag, num_buckets) + def text(self, tag): + check_tag_name_valid(tag) + return self.writer.new_text(tag) + def save(self): self.writer.save() From 5b61d0ea39ae038a2be5755a26653059215f4dfe Mon Sep 17 00:00:00 2001 From: "Wang,Jeff" Date: Thu, 22 Mar 2018 16:11:50 -0700 Subject: [PATCH 4/4] Create a simple UI for text preview. --- frontend/mock/data/plugin/texts/tags.js | 39 +++++ frontend/mock/data/plugin/texts/texts.js | 38 +++++ frontend/src/common/component/AppMenu.vue | 5 + frontend/src/router/index.js | 6 + frontend/src/service.js | 4 + frontend/src/texts/Texts.vue | 167 ++++++++++++++++++++++ frontend/src/texts/index.js | 9 ++ frontend/src/texts/ui/Chart.vue | 146 +++++++++++++++++++ frontend/src/texts/ui/ChartPage.vue | 78 ++++++++++ frontend/src/texts/ui/Config.vue | 73 ++++++++++ 10 files changed, 565 insertions(+) create mode 100644 frontend/mock/data/plugin/texts/tags.js create mode 100644 frontend/mock/data/plugin/texts/texts.js create mode 100644 frontend/src/texts/Texts.vue create mode 100644 frontend/src/texts/index.js create mode 100644 frontend/src/texts/ui/Chart.vue create mode 100644 frontend/src/texts/ui/ChartPage.vue create mode 100644 frontend/src/texts/ui/Config.vue diff --git a/frontend/mock/data/plugin/texts/tags.js b/frontend/mock/data/plugin/texts/tags.js new file mode 100644 index 000000000..a89f897cc --- /dev/null +++ b/frontend/mock/data/plugin/texts/tags.js @@ -0,0 +1,39 @@ +/** + * get mock data + * + * @param {string} path request path + * @param {Object} queryParam query params + * @param {Object} postParam post params + * @return {Object} + */ +module.exports = function (path, queryParam, postParam) { + return { + // moock delay + _timeout: 0, + // mock http status + _status: 200, + // mock response data + _data: { + status: 0, + msg: 'SUCCESS', + data: { + "test": { + "layer3/generated/animal": { + "displayName": "layer3/generated/animal", + "description": "" + } + }, + "train": { + "layer3/generated/animal": { + "displayName": "layer3/generated/animal", + "description": "" + }, + "layer3/generated/flower": { + "displayName": "layer3/generated/flower", + "description": "" + }, + } + } + } + }; +}; diff --git a/frontend/mock/data/plugin/texts/texts.js b/frontend/mock/data/plugin/texts/texts.js new file mode 100644 index 000000000..80c62985c --- /dev/null +++ b/frontend/mock/data/plugin/texts/texts.js @@ -0,0 +1,38 @@ +/** + * get mock data + * + * @param {string} path request path + * @param {Object} queryParam query params + * @param {Object} postParam post params + * @return {Object} + */ +module.exports = function (path, queryParam, postParam) { + if (queryParam.run === 'train') { + return { + // moock delay + _timeout: 0, + // mock http status + _status: 200, + // mock response data + _data: { + status: 0, + msg: 'SUCCESS', + data: [[1511842145.705075, 1, "Hello 1"], [1511842145.7388, 2, "Hello 2"], [1511842145.774563, 3, "Hello 3"], [1511842145.806828, 4, "Hello 4"], [1511842145.838082, 5, "Hello 5"], [1511842145.868955, 6, "Hello 6"], [1511842145.899323, 7, "Hello 7"], [1511842145.930518, 8, "Hello 8"], [1511842145.96089, 9, "Hello 7"], [1511842146.460557, 11, "Hello 11"], [1511842146.4952, 12, "Hello 12"], [1511842146.525936, 13, "Hello 13"], [1511842146.556059, 14, "Hello 14"], [1511842146.648703, 15, "Hello 15"], [1511842146.683295, 16, "Hello 16"], [1511842146.719782, 17, "Hello 17"], [1511842146.752392, 18, "Hello 18"]] + } + } + } + else { + return { + // moock delay + _timeout: 0, + // mock http status + _status: 200, + // mock response data + _data: { + status: 0, + msg: 'SUCCESS', + data: [[1511842145.514333, 0, "Hello 0"], [1511842146.427384, 10, "Hello 10"], [1511842147.260405, 20, "Hello 20"], [1511842148.019018, 30, "Hello 30 "], [1511842148.793569, 40, "Hello 40 "], [1511842149.610228, 50, "Hello 50 "], [1511842150.437095, 60, "Hello 60"], [1511842151.254679, 70, "Hello 70"], [1511842152.039353, 80, "Hello 80"], [1511842152.800043, 90, "Hello 90"]] + } + } + } +}; diff --git a/frontend/src/common/component/AppMenu.vue b/frontend/src/common/component/AppMenu.vue index 0999df9bd..8041865ca 100644 --- a/frontend/src/common/component/AppMenu.vue +++ b/frontend/src/common/component/AppMenu.vue @@ -41,6 +41,11 @@ export default { url: '/graphs', title: 'GRAPHS', name: 'graphs' + }, + { + url: '/texts', + title: 'TEXTS', + name: 'texts' } ] } diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 6832582f2..353f25cd4 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -5,6 +5,7 @@ import Scalars from '@/scalars/Scalars' import Histogram from '@/histogram/Histogram' import Images from '@/images/Images' import Graph from '@/graph/Graph' +import Texts from '@/texts/Texts' Vue.use(Router) @@ -30,5 +31,10 @@ export default new Router({ name: 'Graph', component: Graph }, + { + path: '/texts', + name: 'Texts', + component: Texts + } ] }) diff --git a/frontend/src/service.js b/frontend/src/service.js index 743936df6..012074d8e 100644 --- a/frontend/src/service.js +++ b/frontend/src/service.js @@ -15,3 +15,7 @@ export const getPluginHistogramsTags = makeService('/data/plugin/histograms/tags export const getPluginHistogramsHistograms = makeService('/data/plugin/histograms/histograms'); export const getPluginGraphsGraph = makeService('/data/plugin/graphs/graph'); + +export const getPluginTextsTags = makeService('/data/plugin/texts/tags'); + +export const getPluginTextsTexts = makeService('/data/plugin/texts/texts'); diff --git a/frontend/src/texts/Texts.vue b/frontend/src/texts/Texts.vue new file mode 100644 index 000000000..9afefc5de --- /dev/null +++ b/frontend/src/texts/Texts.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/frontend/src/texts/index.js b/frontend/src/texts/index.js new file mode 100644 index 000000000..dc142957d --- /dev/null +++ b/frontend/src/texts/index.js @@ -0,0 +1,9 @@ +import {router} from 'san-router'; + +import Texts from './Texts'; + +router.add({ + target: '#content', + rule: '/texts', + Component: Texts +}); diff --git a/frontend/src/texts/ui/Chart.vue b/frontend/src/texts/ui/Chart.vue new file mode 100644 index 000000000..c83e37047 --- /dev/null +++ b/frontend/src/texts/ui/Chart.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/frontend/src/texts/ui/ChartPage.vue b/frontend/src/texts/ui/ChartPage.vue new file mode 100644 index 000000000..ff82eca5f --- /dev/null +++ b/frontend/src/texts/ui/ChartPage.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/frontend/src/texts/ui/Config.vue b/frontend/src/texts/ui/Config.vue new file mode 100644 index 000000000..c09cbe04f --- /dev/null +++ b/frontend/src/texts/ui/Config.vue @@ -0,0 +1,73 @@ + + + + +