mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Merge d10786ecfd
into 9a55b2c81b
This commit is contained in:
commit
6ef51107ce
7 changed files with 134 additions and 18 deletions
35
README.md
35
README.md
|
@ -104,22 +104,39 @@ With
|
||||||
|
|
||||||
jrnl --tags
|
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
|
### JSON export
|
||||||
|
|
||||||
Can do:
|
Can do:
|
||||||
|
|
||||||
jrnl --json
|
jrnl --export json
|
||||||
|
|
||||||
Why not create a beautiful [timeline](http://timeline.verite.co/) of your journal?
|
Why not create a beautiful [timeline](http://timeline.verite.co/) of your journal?
|
||||||
|
|
||||||
### Markdown export
|
### 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.
|
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
|
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.
|
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
|
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
|
- `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.
|
- `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.
|
- `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
|
> __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
|
||||||
>
|
>
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
from Entry import Entry
|
from Entry import Entry
|
||||||
|
import exporters
|
||||||
|
import subprocess
|
||||||
import os
|
import os
|
||||||
import parsedatetime.parsedatetime as pdt
|
import parsedatetime.parsedatetime as pdt
|
||||||
import re
|
import re
|
||||||
|
@ -37,6 +39,8 @@ class Journal(object):
|
||||||
'tagsymbols': '@',
|
'tagsymbols': '@',
|
||||||
'highlight': True,
|
'highlight': True,
|
||||||
'linewrap': 80,
|
'linewrap': 80,
|
||||||
|
'folder': os.path.expanduser("~/journal/"),
|
||||||
|
'sync_folder': os.path.expanduser("~/journal/"),
|
||||||
}
|
}
|
||||||
self.config.update(kwargs)
|
self.config.update(kwargs)
|
||||||
|
|
||||||
|
@ -272,6 +276,26 @@ class Journal(object):
|
||||||
self.sort()
|
self.sort()
|
||||||
return entry
|
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):
|
class DayOne(Journal):
|
||||||
"""A special Journal handling DayOne files"""
|
"""A special Journal handling DayOne files"""
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import os
|
||||||
|
try: from slugify import slugify
|
||||||
|
except ImportError: import slugify
|
||||||
try: import simplejson as json
|
try: import simplejson as json
|
||||||
except ImportError: import json
|
except ImportError: import json
|
||||||
|
|
||||||
def to_json(journal):
|
def to_json(journal, output):
|
||||||
"""Returns a JSON representation of the Journal."""
|
"""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"""
|
"""Returns a markdown representation of the Journal"""
|
||||||
out = []
|
out = []
|
||||||
year, month = -1, -1
|
year, month = -1, -1
|
||||||
|
@ -22,4 +28,39 @@ def to_md(journal):
|
||||||
out.append(e.date.strftime("%B"))
|
out.append(e.date.strftime("%B"))
|
||||||
out.append('-' * len(e.date.strftime("%B")) + "\n")
|
out.append('-' * len(e.date.strftime("%B")) + "\n")
|
||||||
out.append(e.to_md())
|
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()
|
||||||
|
|
|
@ -29,6 +29,8 @@ default_config = {
|
||||||
'tagsymbols': '@',
|
'tagsymbols': '@',
|
||||||
'highlight': True,
|
'highlight': True,
|
||||||
'linewrap': 80,
|
'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')
|
journal_path = raw_input(path_query).strip() or os.path.expanduser('~/journal.txt')
|
||||||
default_config['journals']['default'] = os.path.expanduser(journal_path)
|
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?
|
# Encrypt it?
|
||||||
if module_exists("Crypto"):
|
if module_exists("Crypto"):
|
||||||
password = getpass.getpass("Enter password for journal (leave blank for no encryption): ")
|
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:
|
if password:
|
||||||
config['password'] = password
|
config['password'] = password
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
27
jrnl/jrnl.py
27
jrnl/jrnl.py
|
@ -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')
|
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 = 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('--tags', dest='tags', action="store_true", help='Returns a list of all tags and number of occurrences')
|
||||||
exporting.add_argument('--json', dest='json', action="store_true", help='Returns a JSON-encoded version of the Journal')
|
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('--markdown', dest='markdown', action="store_true", help='Returns a Markdown-formated version of the Journal')
|
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('--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('--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('--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()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ def guess_mode(args, config):
|
||||||
"""Guesses the mode (compose, read or export) from the given arguments"""
|
"""Guesses the mode (compose, read or export) from the given arguments"""
|
||||||
compose = True
|
compose = True
|
||||||
export = False
|
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
|
compose = False
|
||||||
export = True
|
export = True
|
||||||
elif args.start_date or args.end_date or args.limit or args.strict or args.short:
|
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:
|
elif args.tags:
|
||||||
print_tags(journal)
|
print_tags(journal)
|
||||||
|
|
||||||
elif args.json: # export to json
|
elif args.export is not False:
|
||||||
print(exporters.to_json(journal))
|
if args.export == 'json': # export to json
|
||||||
|
print(exporters.to_json(journal, args.output))
|
||||||
|
|
||||||
elif args.markdown: # export to json
|
elif args.export == 'markdown' or args.export == 'md': # export to markdown
|
||||||
print(exporters.to_md(journal))
|
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:
|
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.")
|
print("PyCrypto not found. To encrypt or decrypt your journal, install the PyCrypto package from http://www.pycrypto.org.")
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
clint >= 0.3.1
|
clint >= 0.3.1
|
||||||
parsedatetime == 1.1.2
|
parsedatetime == 1.1.2
|
||||||
|
slugify==0.0.1
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -56,7 +56,7 @@ setup(
|
||||||
version = "1.0.1",
|
version = "1.0.1",
|
||||||
description = "A command line journal application that stores your journal in a plain text file",
|
description = "A command line journal application that stores your journal in a plain text file",
|
||||||
packages = ['jrnl'],
|
packages = ['jrnl'],
|
||||||
install_requires = ["parsedatetime >= 1.1.2"],
|
install_requires = ["parsedatetime >= 1.1.2", "slugify >= 0.0.1"],
|
||||||
extras_require = {
|
extras_require = {
|
||||||
'encryption': ["pycrypto"],
|
'encryption': ["pycrypto"],
|
||||||
'highlight': ["clint"]
|
'highlight': ["clint"]
|
||||||
|
|
Loading…
Add table
Reference in a new issue