This commit is contained in:
Aniket Pant 2013-04-14 03:01:54 -07:00
commit 6ef51107ce
7 changed files with 134 additions and 18 deletions

View file

@ -104,22 +104,39 @@ With
jrnl --tags
you'll get a list of all tags you used in your journal, sorted by most frequent. Tags occuring several times in the same entry are only counted as one.
you'll get a list of all tags you used in your journal, sorted by most frequent. Tags occurring several times in the same entry are only counted as one.
### JSON export
Can do:
jrnl --json
jrnl --export json
Why not create a beautiful [timeline](http://timeline.verite.co/) of your journal?
### Markdown export
jrnl --markdown
jrnl --export markdown # alias md
Markdown is a simple markup language that is human readable and can be used to be rendered to other formats (html, pdf). This README for example is formatted in markdown and github makes it look nice.
### Plain text export
jrnl --export text # alias txt
### Export to multiple files
jrnl --export files
### Support for output file
jrnl --export json -o "~/journal.json"
jrnl --export markdown -o "~/journal.md"
jrnl --export text -o "~/journal.txt"
jrnl --export files -o "~/journal/*.txt"
The output file can be given as a fullpath to a file and in case of a file based export, wildcards can be used.
Encryption
----------
@ -133,6 +150,16 @@ If it is already encrypted, you will first be asked for the current password. Yo
will replace your encrypted journal file by a Journal in plain text. You can also specify a filename, ie. `jrnl --decrypt plain_text_copy.txt`, to leave your original file untouched.
Sync
----
If you wish to sync your default journal to a git repository, you can do it by running the following command
jrnl --sync [TYPE]
`jrnl --sync` works in a similar fashion to export, except it exports the journal to the sync folder provided in the config and then runs `git` commands.
The `[TYPE]` can be `json`, `md`, `txt` and `files`.
Advanced usages
--------------
@ -152,6 +179,8 @@ The configuration file is a simple JSON file with the following options.
- `timeformat`: how to format the timestamps in your journal, see the [python docs](http://docs.python.org/library/time.html#time.strftime) for reference
- `highlight`: if `true` and you have [clint](http://www.nicosphere.net/clint-command-line-library-for-python/) installed, tags will be highlighted in cyan.
- `linewrap`: controls the width of the output. Set to `0` or `false` if you don't want to wrap long lines.
- `folder`: the folder where your export functions will default to
- `sync_folder`: the folder where your exported files will be synced to. This needs to be a git repository for the sync operation to complete.
> __Note on `tagsymbols`:__ Although it seems intuitive to use the `#` character for tags, there's a drawback: on most shells, this is interpreted as a meta-character starting a comment. This means that if you type
>

View file

@ -2,6 +2,8 @@
# encoding: utf-8
from Entry import Entry
import exporters
import subprocess
import os
import parsedatetime.parsedatetime as pdt
import re
@ -37,6 +39,8 @@ class Journal(object):
'tagsymbols': '@',
'highlight': True,
'linewrap': 80,
'folder': os.path.expanduser("~/journal/"),
'sync_folder': os.path.expanduser("~/journal/"),
}
self.config.update(kwargs)
@ -272,6 +276,26 @@ class Journal(object):
self.sort()
return entry
def sync(self, arg):
"""Syncs journal on the basis of supplied argument.
Takes json, md, txt, files as arguments and syncs to sync_folder"""
if arg == "json":
exporters.to_json(self, self.config['sync_folder'] + "journal.json")
elif arg == "md":
exporters.to_md(self, self.config['sync_folder'] + "journal.md")
elif arg == "txt":
exporters.to_txt(self, self.config['sync_folder'] + "journal.txt")
elif arg == "files":
exporters.to_files(self, self.config['sync_folder'] + "*.txt")
os.chdir(self.config['sync_folder']) # change current folder to the sync_folder
subprocess.call(["git", "pull"]) # calls git pull to retrieve any changes
subprocess.call(["git", "add", "."]) # adds the new files to the repository
message = "Site update at " + str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')) # creates a new commit
subprocess.call(["git", "commit", "-m", message]) # commits the changes
subprocess.call(["git", "push"]) # calls git push to make the new changes
return "journal synced to " + self.config['sync_folder']
class DayOne(Journal):
"""A special Journal handling DayOne files"""
def __init__(self, **kwargs):

View file

@ -1,14 +1,20 @@
#!/usr/bin/env python
# encoding: utf-8
import os
try: from slugify import slugify
except ImportError: import slugify
try: import simplejson as json
except ImportError: import json
def to_json(journal):
def to_json(journal, output):
"""Returns a JSON representation of the Journal."""
return json.dumps([e.to_dict() for e in journal.entries], indent=2)
result = json.dumps([e.to_dict() for e in journal.entries], indent=2)
if output is not False:
write_file(result, output)
return result
def to_md(journal):
def to_md(journal, output):
"""Returns a markdown representation of the Journal"""
out = []
year, month = -1, -1
@ -22,4 +28,39 @@ def to_md(journal):
out.append(e.date.strftime("%B"))
out.append('-' * len(e.date.strftime("%B")) + "\n")
out.append(e.to_md())
return "\n".join(out)
result = "\n".join(out)
if output is not False:
write_file(result, output)
return result
def to_txt(journal, output):
"""Returns the complete text of the Journal."""
if output is not False:
write_file(str(journal), output)
return journal
def to_files(journal, output):
"""Turns your journal into separate files for each entry."""
if output is False:
output = journal.config['folder'] + "*.txt" # default path
path, extension = os.path.splitext(os.path.expanduser(output))
head, tail = os.path.split(path)
if tail == '*': # if wildcard is specified
path = head + '/'
if not os.path.exists(path): # if the folder doesn't exist, create it
os.makedirs(path)
for e in journal.entries:
date = e.date.strftime('%Y-%m-%d')
title = slugify(unicode(e.title))
filename = date + '-' + title
result = str(e)
fullpath = path + filename + extension
print fullpath
write_file(result, fullpath)
return ("Journal exported")
def write_file(content, path):
"""Writes content to the file provided"""
f = open(path, 'w+')
f.write(content)
f.close()

View file

@ -29,6 +29,8 @@ default_config = {
'tagsymbols': '@',
'highlight': True,
'linewrap': 80,
'folder': os.path.expanduser("~/journal/"),
'sync_folder': os.path.expanduser("~/journal/"),
}
@ -65,6 +67,16 @@ def install_jrnl(config_path='~/.jrnl_config'):
journal_path = raw_input(path_query).strip() or os.path.expanduser('~/journal.txt')
default_config['journals']['default'] = os.path.expanduser(journal_path)
# Where to export files?
path_query = 'Path to your journal folder (leave blank for ~/journal): '
folder_path = raw_input(path_query).strip() or os.path.expanduser('~/journal')
default_config['folder'] = os.path.expanduser(folder_path)
# Where to sync journal?
path_query = 'Path to folder which is a git repository (leave blank for ~/journal): '
folder_path = raw_input(path_query).strip() or os.path.expanduser('~/journal')
default_config['sync_folder'] = os.path.expanduser(folder_path)
# Encrypt it?
if module_exists("Crypto"):
password = getpass.getpass("Enter password for journal (leave blank for no encryption): ")
@ -90,5 +102,3 @@ def install_jrnl(config_path='~/.jrnl_config'):
if password:
config['password'] = password
return config

View file

@ -42,12 +42,13 @@ def parse_args():
reading.add_argument('-short', dest='short', action="store_true", help='Show only titles or line containing the search tags')
exporting = parser.add_argument_group('Export / Import', 'Options for transmogrifying your journal')
exporting.add_argument('--tags', dest='tags', action="store_true", help='Returns a list of all tags and number of occurences')
exporting.add_argument('--json', dest='json', action="store_true", help='Returns a JSON-encoded version of the Journal')
exporting.add_argument('--markdown', dest='markdown', action="store_true", help='Returns a Markdown-formated version of the Journal')
exporting.add_argument('--tags', dest='tags', action="store_true", help='Returns a list of all tags and number of occurrences')
exporting.add_argument('--export', metavar='TYPE', dest='export', help='Export your journal to Markdown, JSON, Text or multiple files', nargs='?', default=False, const=None)
exporting.add_argument('-o', metavar='OUTPUT', dest='output', help='The output of the file can be provided when using with --export', nargs='?', default=False, const=None)
exporting.add_argument('--encrypt', metavar='FILENAME', dest='encrypt', help='Encrypts your existing journal with a new password', nargs='?', default=False, const=None)
exporting.add_argument('--decrypt', metavar='FILENAME', dest='decrypt', help='Decrypts your journal and stores it in plain text', nargs='?', default=False, const=None)
exporting.add_argument('--delete-last', dest='delete_last', help='Deletes the last entry from your journal file.', action="store_true")
exporting.add_argument('--sync', metavar='TYPE', dest='sync', help='Syncs your journal to a repository. Works similar to export.', nargs='?', default=False, const=None)
return parser.parse_args()
@ -55,7 +56,7 @@ def guess_mode(args, config):
"""Guesses the mode (compose, read or export) from the given arguments"""
compose = True
export = False
if args.json or args.decrypt is not False or args.encrypt is not False or args.markdown or args.tags or args.delete_last:
if args.decrypt is not False or args.encrypt is not False or args.export is not False or args.tags or args.delete_last or args.sync is not False:
compose = False
export = True
elif args.start_date or args.end_date or args.limit or args.strict or args.short:
@ -196,11 +197,21 @@ def cli():
elif args.tags:
print_tags(journal)
elif args.json: # export to json
print(exporters.to_json(journal))
elif args.export is not False:
if args.export == 'json': # export to json
print(exporters.to_json(journal, args.output))
elif args.markdown: # export to json
print(exporters.to_md(journal))
elif args.export == 'markdown' or args.export == 'md': # export to markdown
print(exporters.to_md(journal, args.output))
elif args.export == 'text' or args.export == 'txt': # export to text
print(exporters.to_txt(journal, args.output))
elif args.export == 'files': # export to files
print(exporters.to_files(journal, args.output))
elif args.sync is not False: # syncs journal to repository
print(journal.sync(args.sync))
elif (args.encrypt is not False or args.decrypt is not False) and not PYCRYPTO:
print("PyCrypto not found. To encrypt or decrypt your journal, install the PyCrypto package from http://www.pycrypto.org.")

View file

@ -1,2 +1,3 @@
clint >= 0.3.1
parsedatetime == 1.1.2
slugify==0.0.1

View file

@ -56,7 +56,7 @@ setup(
version = "1.0.1",
description = "A command line journal application that stores your journal in a plain text file",
packages = ['jrnl'],
install_requires = ["parsedatetime >= 1.1.2"],
install_requires = ["parsedatetime >= 1.1.2", "slugify >= 0.0.1"],
extras_require = {
'encryption': ["pycrypto"],
'highlight': ["clint"]