├── 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 [![Build Status](https://travis-ci.org/azu/dayone-to-quiver.svg?branch=master)](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 | --------------------------------------------------------------------------------