├── test
├── mocha.opts
├── fixtures
│ ├── expected.entry.json
│ ├── entry.doentry
│ └── Journal.dayone
│ │ └── entries
│ │ ├── 04C8B7E7B84A4605A560D7927411D7D7.doentry
│ │ └── 0A0DB4326DE74BEA83229B8FB16DFBD0.doentry
├── dayone-to-quiver-test.js
└── Entry-test.js
├── .travis.yml
├── .babelrc
├── bin
├── usage.txt
└── cmd.js
├── src
├── cli
│ └── cli.js
├── DayOneEntry.js
├── QuiverEntryBuilder.js
└── dayone-to-quiver.js
├── LICENSE
├── package.json
├── README.md
└── .gitignore
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --compilers js:espower-babel/guess
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js: stable
4 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"],
3 | "plugins": ["add-module-exports"]
4 | }
--------------------------------------------------------------------------------
/bin/usage.txt:
--------------------------------------------------------------------------------
1 | $ dayone-to-quiver --entry Journal.dayone --output output.qvnotebook
2 |
3 | --entry path to Journal.dayone
4 | --output output path
--------------------------------------------------------------------------------
/test/fixtures/expected.entry.json:
--------------------------------------------------------------------------------
1 | {
2 | "created_at" : 1338541220,
3 | "tags" : [
4 | "memo"
5 | ],
6 | "title" : "Title",
7 | "body": "text body\nbody2",
8 | "updated_at" : 1338541220,
9 | "uuid" : "9686AA1A-A5E9-41FF-9260-C3E0D0E9D4CB"
10 | }
--------------------------------------------------------------------------------
/bin/cmd.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const argv = require('minimist')(process.argv.slice(2));
3 | const fs = require("fs");
4 | const execute = require("../lib/cli/cli").execute;
5 | if (!argv["entry"] || !argv["output"]) {
6 | const usage = fs.readFileSync(__dirname + "/usage.txt", "utf-8");
7 | console.log(usage);
8 | process.exit(1);
9 | }
10 | const exitStatus = execute(argv);
11 | process.exit(exitStatus);
--------------------------------------------------------------------------------
/src/cli/cli.js:
--------------------------------------------------------------------------------
1 | // LICENSE : MIT
2 | "use strict";
3 | import {dayOneToQuiver} from "../dayone-to-quiver";
4 | const path = require("path");
5 | export function execute(argv) {
6 | const entryPath = path.join(process.cwd(), argv["entry"]);
7 | const outputPath = path.join(process.cwd(), argv["output"]);
8 | try {
9 | dayOneToQuiver(entryPath, outputPath);
10 | return 0;
11 | } catch (error) {
12 | console.error(error);
13 | return 1;
14 | }
15 | }
--------------------------------------------------------------------------------
/test/fixtures/entry.doentry:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Creation Date
6 | 2012-06-01T09:00:20Z
7 | Entry Text
8 | # Title
9 | text body
10 | body2
11 | Starred
12 |
13 | UUID
14 | 04C8B7E7B84A4605A560D7927411D7D7
15 | Tags
16 |
17 | memo
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/test/fixtures/Journal.dayone/entries/04C8B7E7B84A4605A560D7927411D7D7.doentry:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Creation Date
6 | 2012-06-01T09:00:20Z
7 | Entry Text
8 | # Title
9 | text body
10 | body2
11 | Starred
12 |
13 | UUID
14 | 04C8B7E7B84A4605A560D7927411D7D7
15 | Tags
16 |
17 | memo
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/test/dayone-to-quiver-test.js:
--------------------------------------------------------------------------------
1 | import assert from "power-assert";
2 | import {getEntries} from "../src/dayone-to-quiver";
3 | import DayOneEntry from "../src/DayOneEntry";
4 | describe("dayone-to-quiver", function () {
5 | describe("getEntries", ()=> {
6 | it("should return dayOne entry", () => {
7 | const pathToDayOne = __dirname + "/fixtures/Journal.dayone";
8 | const entries = getEntries(pathToDayOne);
9 | assert(entries.length > 0);
10 | entries.forEach(entry => {
11 | assert(entry instanceof DayOneEntry);
12 | })
13 | });
14 | });
15 |
16 | });
--------------------------------------------------------------------------------
/test/fixtures/Journal.dayone/entries/0A0DB4326DE74BEA83229B8FB16DFBD0.doentry:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Activity
6 | Stationary
7 | Creation Date
8 | 2015-02-01T05:01:13Z
9 | Entry Text
10 | # Flux
11 | どうにかしたい
12 | Starred
13 |
14 | Tags
15 |
16 | Time Zone
17 | Asia/Tokyo
18 | UUID
19 | 0A0DB4326DE74BEA83229B8FB16DFBD0
20 |
21 |
22 |
--------------------------------------------------------------------------------
/test/Entry-test.js:
--------------------------------------------------------------------------------
1 | import assert from "power-assert";
2 | import Entry from "../src/DayOneEntry"
3 | const fs = require("fs");
4 | const plist = require("plist");
5 | describe('DayOneEntry', function () {
6 | it("should has expected json", function () {
7 | const entryXML = fs.readFileSync(__dirname + "/fixtures/entry.doentry", "utf-8");
8 | const entryJSON = plist.parse(entryXML);
9 | const entry = new Entry(entryJSON);
10 | const expected = require("./fixtures/expected.entry.json");
11 |
12 | assert.deepEqual(entry.tags, expected.tags);
13 | assert(typeof entry.uuid === "string");
14 | assert.equal(entry.body, expected.body);
15 | assert.equal(entry.title, expected.title);
16 | });
17 | });
--------------------------------------------------------------------------------
/src/DayOneEntry.js:
--------------------------------------------------------------------------------
1 | const uuid = require('node-uuid');
2 | const moment = require("moment");
3 | const removeMd = require('remove-markdown');
4 | export default class DayOneEntry {
5 | constructor(entryObject) {
6 | const createdDate = moment(entryObject["Creation Date"]);
7 | const entryText = entryObject['Entry Text'];
8 | const entryLines = entryText.split("\n");
9 | const title = removeMd(entryLines[0]);
10 | const body = entryLines.slice(1).join("\n");
11 | this.created_at = createdDate.unix();
12 | this.updated_at = createdDate.unix();
13 | this.title = title;
14 | this.body = body;
15 | this.tags = entryObject["Tags"] || [];
16 | this.uuid = uuid.v4().toUpperCase();
17 | // no title item
18 | this.isFragment = !(this.title || this.title.trim().length > 0);
19 | }
20 | }
--------------------------------------------------------------------------------
/src/QuiverEntryBuilder.js:
--------------------------------------------------------------------------------
1 | // LICENSE : MIT
2 | "use strict";
3 | export default class QuiverEntryBuilder {
4 | /**
5 | *
6 | * @param {DayOneEntry} dayOneEntry
7 | */
8 | constructor(dayOneEntry) {
9 | this.entry = dayOneEntry;
10 | }
11 |
12 | toMeta() {
13 | var entry = this.entry;
14 | return {
15 | "created_at": entry.created_at,
16 | "tags": entry.tags,
17 | "title": entry.title,
18 | "updated_at": entry.updated_at,
19 | "uuid": entry.uuid
20 | }
21 | }
22 |
23 | toContent() {
24 | var entry = this.entry;
25 | return {
26 | "title": entry.title,
27 | "cells": [
28 | {
29 | "type": "markdown",
30 | "data": entry.body
31 | }
32 | ]
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 azu
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dayone-to-quiver",
3 | "repository": {
4 | "type": "git",
5 | "url": "git+https://github.com/azu/dayone-to-quiver.git"
6 | },
7 | "author": "azu",
8 | "email": "azuciao@gmail.com",
9 | "homepage": "https://github.com/azu/dayone-to-quiver",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/azu/dayone-to-quiver/issues"
13 | },
14 | "version": "1.0.3",
15 | "description": "DayOne Journal to Quiver",
16 | "main": "lib/dayone-to-quiver.js",
17 | "files": [
18 | "bin",
19 | "lib",
20 | "src"
21 | ],
22 | "bin": {
23 | "dayone-to-quiver": "bin/cmd.js"
24 | },
25 | "directories": {
26 | "test": "test"
27 | },
28 | "scripts": {
29 | "build": "babel src --out-dir lib --source-maps",
30 | "watch": "babel src --out-dir lib --watch --source-maps",
31 | "prepublish": "npm run --if-present build",
32 | "test": "mocha"
33 | },
34 | "keywords": [
35 | "quiver",
36 | "dayone"
37 | ],
38 | "dependencies": {
39 | "glob": "^6.0.4",
40 | "minimist": "^1.2.0",
41 | "mkdirp": "^0.5.1",
42 | "moment": "^2.11.1",
43 | "node-uuid": "^1.4.7",
44 | "plist": "^1.2.0",
45 | "remove-markdown": "0.0.6",
46 | "xml2js": "^0.4.16"
47 | },
48 | "devDependencies": {
49 | "babel-cli": "^6.4.0",
50 | "babel-plugin-add-module-exports": "^0.1.3-alpha",
51 | "babel-preset-es2015": "^6.3.13",
52 | "espower-babel": "^4.0.1",
53 | "mocha": "^2.3.4",
54 | "power-assert": "^1.2.0"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/dayone-to-quiver.js:
--------------------------------------------------------------------------------
1 | // LICENSE : MIT
2 | "use strict";
3 | const fs = require("fs");
4 | const path = require("path");
5 | const plist = require("plist");
6 | const mkdirp = require("mkdirp");
7 | import DayOneEntry from "./DayOneEntry";
8 | import QuiverEntryBuilder from "./QuiverEntryBuilder";
9 | /**
10 | *
11 | * @param {string} dayOneFilePath
12 | * @returns
13 | */
14 | export function getEntries(dayOneFilePath) {
15 | const entryDir = path.join(dayOneFilePath, "entries");
16 | const entriesFilePathList = fs.readdirSync(entryDir);
17 | return entriesFilePathList.map(filePath => {
18 | const entryXML = fs.readFileSync(path.join(entryDir, filePath), "utf-8");
19 | const entryJSON = plist.parse(entryXML);
20 | return new DayOneEntry(entryJSON);
21 | });
22 | }
23 |
24 | export function dayOneToQuiver(dayOneFilePath, outputDir) {
25 | const entries = getEntries(dayOneFilePath);
26 | /*
27 | B59AC519-2A2C-4EC8-B701-E69F54F40A85.qvnote/
28 | ├── content.json
29 | ├── meta.json
30 | */
31 | entries.filter(entry => !entry.isFragment).forEach(entry => {
32 | const builder = new QuiverEntryBuilder(entry);
33 | const qvnoteDir = path.join(outputDir, `${entry.uuid}.qvnote`);
34 | // create dir
35 | mkdirp.sync(qvnoteDir);
36 | // create meta
37 | fs.writeFileSync(path.join(qvnoteDir, 'meta.json'), JSON.stringify(builder.toMeta(), null, 2));
38 | // create content
39 | fs.writeFileSync(path.join(qvnoteDir, 'content.json'), JSON.stringify(builder.toContent(), null, 2));
40 | console.log("Created " + qvnoteDir);
41 | });
42 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dayone-to-quiver [](https://travis-ci.org/azu/dayone-to-quiver)
2 |
3 | Convert DayOne's Journal.dayone to [Quiver notebook](https://github.com/HappenApps/Quiver/wiki/Quiver-Data-Format "Quiver notebook") file.
4 |
5 | ## Support
6 |
7 | dayone-to-quiver support DayOne v1 Journal.dayone format.
8 |
9 | We don't know DayOne v2, but welcome to pull request.
10 |
11 | ### Supported Quiver Data Format
12 |
13 | - [x] created_at
14 | - [x] updated_at is same with created_at
15 | - [x] tags
16 | - [x] title
17 | - [x] content
18 | - [x] uuid
19 | - [ ] Image
20 |
21 | ```
22 | {
23 | "created_at" : 1417080157,
24 | "tags" : [
25 | "quiver"
26 | ],
27 | "title" : "02 - Cells",
28 | "updated_at" : 1417080595,
29 | "uuid" : "9686AA1A-A5E9-41FF-9260-C3E0D0E9D4CB"
30 | }
31 | ```
32 |
33 | - [Quiver Data Format · HappenApps/Quiver Wiki](https://github.com/HappenApps/Quiver/wiki/Quiver-Data-Format "Quiver Data Format · HappenApps/Quiver Wiki")
34 |
35 | ## Installation
36 |
37 | npm install -g dayone-to-quiver
38 |
39 | ## Usage
40 |
41 | ```sh
42 | $ dayone-to-quiver --entry Journal.dayone --output output.qvnotebook
43 |
44 | --entry path to Journal.dayone
45 | --output output path
46 | ```
47 |
48 | Convert DayOne's Journal.dayone to [Quiver notebook](https://github.com/HappenApps/Quiver/wiki/Quiver-Data-Format "Quiver notebook") format.
49 |
50 | ## Tests
51 |
52 | npm test
53 | # example
54 | ./bin/cmd.js --entry test/fixtures/Journal.dayone --output test/fixtures/Tutorial.qvnotebook
55 |
56 |
57 | ## Contributing
58 |
59 | 1. Fork it!
60 | 2. Create your feature branch: `git checkout -b my-new-feature`
61 | 3. Commit your changes: `git commit -am 'Add some feature'`
62 | 4. Push to the branch: `git push origin my-new-feature`
63 | 5. Submit a pull request :D
64 |
65 | ## License
66 |
67 | MIT
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### https://raw.github.com/github/gitignore/608690d6b9a78c2a003affc792e49a84905b3118/Node.gitignore
2 |
3 | # Logs
4 | logs
5 | *.log
6 |
7 | # Runtime data
8 | pids
9 | *.pid
10 | *.seed
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
19 | .grunt
20 |
21 | # node-waf configuration
22 | .lock-wscript
23 |
24 | # Compiled binary addons (http://nodejs.org/api/addons.html)
25 | build/Release
26 |
27 | # Dependency directory
28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
29 | node_modules
30 |
31 | # Debug log from npm
32 | npm-debug.log
33 |
34 |
35 | ### https://raw.github.com/github/gitignore/608690d6b9a78c2a003affc792e49a84905b3118/Global/JetBrains.gitignore
36 |
37 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
38 |
39 | *.iml
40 |
41 | ## Directory-based project format:
42 | .idea/
43 | # if you remove the above rule, at least ignore the following:
44 |
45 | # User-specific stuff:
46 | # .idea/workspace.xml
47 | # .idea/tasks.xml
48 | # .idea/dictionaries
49 |
50 | # Sensitive or high-churn files:
51 | # .idea/dataSources.ids
52 | # .idea/dataSources.xml
53 | # .idea/sqlDataSources.xml
54 | # .idea/dynamic.xml
55 | # .idea/uiDesigner.xml
56 |
57 | # Gradle:
58 | # .idea/gradle.xml
59 | # .idea/libraries
60 |
61 | # Mongo Explorer plugin:
62 | # .idea/mongoSettings.xml
63 |
64 | ## File-based project format:
65 | *.ipr
66 | *.iws
67 |
68 | ## Plugin-specific files:
69 |
70 | # IntelliJ
71 | out/
72 |
73 | # mpeltonen/sbt-idea plugin
74 | .idea_modules/
75 |
76 | # JIRA plugin
77 | atlassian-ide-plugin.xml
78 |
79 | # Crashlytics plugin (for Android Studio and IntelliJ)
80 | com_crashlytics_export_strings.xml
81 | crashlytics.properties
82 | crashlytics-build.properties
83 |
84 |
85 | /lib
86 |
--------------------------------------------------------------------------------