├── .appveyor.yml ├── .gitignore ├── .tm_properties ├── .travis.yml ├── LICENSE ├── README.rst ├── dayone ├── __init__.py ├── cli.py └── journal.py ├── setup.py └── tests ├── Journal.dayone ├── entries │ ├── 0CDC15D3E7654024B621B5367C33BC7C.doentry │ ├── 19A4A5E666B44887AFABB0557CE41E77.doentry │ ├── 1B23D9650AAF46339900F7D6DA053A29.doentry │ ├── 1B3607772E2C40B191FE2FD496556FCA.doentry │ ├── 31A6C3FEB30C4C07AA4784128EC30AAB.doentry │ ├── 3A9B24552540483189DE7CE503D35CD9.doentry │ ├── 3C7ECC11273046319215D278378AB6CE.doentry │ ├── 57BD0F8052EC4C33B9F95DCEAFD6B48F.doentry │ ├── 58603C5631FC4203B74D6357BB86AD72.doentry │ ├── 6C8D05AF597C4CC291F16D54A88064C9.doentry │ ├── 76A763B68E7A4DB5ABE4C5DE56E3800A.doentry │ ├── 91714FAB68094F318704E05A3EF3CE36.doentry │ ├── A2D2A3CF852F4B4B8834096A33EB76EB.doentry │ ├── BEA88B4AA31C4AA6B4569CA83DCEBAEA.doentry │ ├── CE818E1D93984E86A64773503B2F9C50.doentry │ ├── CE9ADDBB8CE94555963CE9C58F26FDD0.doentry │ ├── DF25F4C8EDE14FC3AFBA3EF33C042F64.doentry │ └── FA73DA5E3A2B41A9A2B4BE8DC4C73DCB.doentry └── photos │ └── 31A6C3FEB30C4C07AA4784128EC30AAB.jpg ├── __init__.py └── test_dayone_journal.py /.appveyor.yml: -------------------------------------------------------------------------------- 1 | build: false 2 | environment: 3 | matrix: 4 | - PYTHON: "C:/Python27" 5 | - PYTHON: "C:/Python34" 6 | init: 7 | - "ECHO %PYTHON%" 8 | - ps: "ls C:/Python*" 9 | install: 10 | - ps: (new-object net.webclient).DownloadFile('https://raw.github.com/pypa/pip/master/contrib/get-pip.py', 'C:/get-pip.py') 11 | - "%PYTHON%/python.exe C:/get-pip.py" 12 | - "%PYTHON%/Scripts/pip.exe install pep8" 13 | test_script: 14 | - "%PYTHON%/Scripts/pip.exe --version" 15 | - "%PYTHON%/python.exe setup.py test" 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | .env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .cache 41 | nosetests.xml 42 | coverage.xml 43 | 44 | # Translations 45 | *.mo 46 | *.pot 47 | 48 | # Django stuff: 49 | *.log 50 | 51 | # Sphinx documentation 52 | docs/_build/ 53 | 54 | # PyBuilder 55 | target/ 56 | -------------------------------------------------------------------------------- /.tm_properties: -------------------------------------------------------------------------------- 1 | exclude = "{$exclude,DayOne.egg-info}" 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - '2.7' 4 | install: 5 | - python setup.py develop 6 | - pip install pep8 7 | script: 8 | - python setup.py test 9 | - pep8 . 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Myles Braithwaite 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of dayone nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | Day One Python Library 3 | ====================== 4 | 5 | .. image:: https://travis-ci.org/myles/dayone.svg?branch=master 6 | :target: https://travis-ci.org/myles/dayone 7 | 8 | A Python library and command line application for `Day One`_. 9 | 10 | .. _Day One: http://dayoneapp.com/ 11 | 12 | Libray Usage 13 | ------------ 14 | 15 | .. code-block:: pycon 16 | 17 | >>> from dayone.journal import Journal, Entry 18 | >>> journal = Journal(path_to_dayone_journal) 19 | >>> 20 | 21 | List entries 22 | ~~~~~~~~~~~~ 23 | 24 | .. code-block:: pycon 25 | 26 | >>> journal.entries 27 | [dayone.journal.Entry(...)] 28 | >>> 29 | 30 | Create an entry 31 | ~~~~~~~~~~~~~~~ 32 | 33 | .. code-block:: pycon 34 | 35 | >>> entry = Entry(path_to_dayone_journal) 36 | >>> entry.text = "Hello, World!" 37 | >>> entry.save_file() 38 | >>> 39 | 40 | Command Line Usage 41 | ------------------ 42 | 43 | First setup your py-dayone envourment. 44 | 45 | .. code-block:: shell-session 46 | 47 | $ py-dayone setup 48 | Path to your DayOne journal. /Users/mb/Dropbox/Apps/Day One/Journal.dayone 49 | Setup py-dayone. 50 | $ 51 | -------------------------------------------------------------------------------- /dayone/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2014, Myles Braithwaite 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | * Neither the name of the Monkey in your Soul nor the names of its 18 | contributors may be used to endorse or promote products derived 19 | from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 28 | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 31 | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | """ 34 | 35 | __version__ = '0.1.0' 36 | __project_name__ = 'DayOne' 37 | __project_link__ = 'https://github.com/myles/py-dayone' 38 | -------------------------------------------------------------------------------- /dayone/cli.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2014, Myles Braithwaite 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | * Neither the name of the Monkey in your Soul nor the names of its 18 | contributors may be used to endorse or promote products derived 19 | from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 28 | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 31 | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | """ 34 | 35 | import os 36 | import json 37 | import argparse 38 | 39 | from clint import resources 40 | from clint.textui import prompt, puts, puts_err, validators, colored 41 | 42 | from .journal import Journal, Entry 43 | 44 | resources.init('myles', 'py-dayone') 45 | 46 | 47 | def setup(args): 48 | if args.get('path', None): 49 | path = args.get('path') 50 | else: 51 | path = prompt.query("Path to your DayOne journal.", 52 | validators=[validators.PathValidator()]) 53 | 54 | path = os.path.abspath(path) 55 | 56 | if not os.path.exists(path): 57 | puts_err(colored.red("%s does not exist." % path)) 58 | return False 59 | 60 | config = json.dumps({'journal_dir': path}) 61 | 62 | resources.user.write('config.json', config) 63 | 64 | puts(colored.green("Setup py-dayone.")) 65 | 66 | 67 | def new(args, config): 68 | e = Entry(config.get('journal_dir')) 69 | 70 | 71 | def main(): 72 | parser = argparse.ArgumentParser( 73 | description="Command line interface for the Day One journaling " + 74 | "application.") 75 | 76 | subparsers = parser.add_subparsers() 77 | 78 | parser_setup = subparsers.add_parser('setup', 79 | help="Setup py-dayone envourment.") 80 | parser_setup.set_defaults(which='setup') 81 | parser_setup.add_argument( 82 | '--path', required=False, help="Path to your DayOne journal.") 83 | 84 | parser_new = subparsers.add_parser('new', 85 | help="Create a new journal entry.") 86 | parser_new.set_defaults(which='new') 87 | parser_new.add_argument( 88 | '--date') 89 | 90 | args = vars(parser.parse_args()) 91 | 92 | if args['which'] == 'setup': 93 | setup(args) 94 | 95 | config_file = resources.user.open('config.json') 96 | 97 | if args['which'] == 'new': 98 | new(args, json.loads(config_file)) 99 | -------------------------------------------------------------------------------- /dayone/journal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2014, Myles Braithwaite 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | * Neither the name of the Monkey in your Soul nor the names of its 18 | contributors may be used to endorse or promote products derived 19 | from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 28 | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 31 | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | """ 34 | 35 | import os 36 | import uuid 37 | import glob 38 | import datetime 39 | import textwrap 40 | import plistlib 41 | from collections import OrderedDict 42 | 43 | import markdown 44 | from pytz import timezone 45 | from tzlocal import get_localzone 46 | 47 | 48 | class Location(object): 49 | """Location data in an entry.""" 50 | 51 | def __init__(self, location_data): 52 | self.location_data = location_data 53 | 54 | def __repr__(self): 55 | return "%s.%s(%s, %s)" % ( 56 | self.__module__, self.__class__.__name__, 57 | self.latitude, self.longitude) 58 | 59 | def __str__(self): 60 | if self.place_name: 61 | return self.place_name 62 | else: 63 | return "%s, %s" % (self.latitude, self.longitude) 64 | 65 | @property 66 | def latitude(self): 67 | return self.location_data.get('Latitude', None) 68 | 69 | @property 70 | def longitude(self): 71 | return self.location_data.get('Longitude', None) 72 | 73 | @property 74 | def place_name(self): 75 | return self.location_data.get('Place Name', None) 76 | 77 | @property 78 | def foursquare_id(self): 79 | return self.location_data.get('Foursquare ID', None) 80 | 81 | @property 82 | def locality(self): 83 | return self.location_data.get('Locality', None) 84 | 85 | @property 86 | def administrative_area(self): 87 | return self.location_data.get('Administrative Area', None) 88 | 89 | @property 90 | def country(self): 91 | return self.location_data.get('Country', None) 92 | 93 | 94 | class Weather(object): 95 | """Location data in an entry.""" 96 | 97 | def __init__(self, weather_data): 98 | self.weather_data = weather_data 99 | 100 | def __repr__(self): 101 | return "%s.%s(%s, %s)" % ( 102 | self.__module__, self.__class__.__name__, 103 | self.description, self.celsius) 104 | 105 | def __str__(self): 106 | return "%s, %s" % (self.description, self.celsius) 107 | 108 | @property 109 | def celsius(self): 110 | return self.weather_data.get('Celsius', None) 111 | 112 | @property 113 | def fahrenheit(self): 114 | return self.weather_data.get('Fahrenheit', None) 115 | 116 | @property 117 | def description(self): 118 | return self.weather_data.get('Description', None) 119 | 120 | @property 121 | def icon_name(self): 122 | return self.weather_data.get('IconName', None) 123 | 124 | @property 125 | def pressure_mb(self): 126 | return self.weather_data.get('Pressure MB', None) 127 | 128 | @property 129 | def relative_humidity(self): 130 | return self.weather_data.get('Relative Humidity', None) 131 | 132 | @property 133 | def service(self): 134 | return self.weather_data.get('Service', None) 135 | 136 | @property 137 | def visibility_km(self): 138 | return self.weather_data.get('Visibility KM', None) 139 | 140 | @property 141 | def wind_bearing(self): 142 | return self.weather_data.get('Wind Bearing', None) 143 | 144 | @property 145 | def wind_speed_kph(self): 146 | return self.weather_data.get('Wind Speed KPH', None) 147 | 148 | 149 | class Entry(object): 150 | """A Journal Entry.""" 151 | 152 | def __init__(self, journal_dir, filename=None): 153 | self.entry_data = {} 154 | self.journal_dir = journal_dir 155 | self.filename = filename 156 | 157 | if self.filename: 158 | if not self.filename.endswith('.doentry'): 159 | self.filename = "%s.doentry" % filename 160 | 161 | self.load_file() 162 | 163 | def __repr__(self): 164 | if self.uuid: 165 | return "%s.%s(%s)" % ( 166 | self.__module__, self.__class__.__name__, self.uuid) 167 | else: 168 | return "%s.%s" % (self.__module__, self.__class__.__name__) 169 | 170 | def __str__(self): 171 | if self.text: 172 | return textwrap.wrap(self.text)[0] 173 | 174 | def load_file(self): 175 | entry_path = os.path.join(self.journal_dir, 'entries', self.filename) 176 | self.entry_data = plistlib.readPlist(entry_path) 177 | 178 | def save_file(self): 179 | if not self.text: 180 | raise Exception 181 | 182 | if self.filename: 183 | entry_path = os.path.join(self.journal_dir, 'entries', 184 | self.filename) 185 | else: 186 | _uuid = str(uuid.uuid4()).replace('-', '').upper() 187 | self.filename = "%s.doentry" % _uuid 188 | self.entry_data['UUID'] = _uuid 189 | self.entry_data['Creation Date'] = datetime.datetime.now() 190 | self.entry_data['Time Zone'] = str(get_localzone()) 191 | self.entry_data['Starred'] = False 192 | entry_path = os.path.join(self.journal_dir, 'entries', 193 | self.filename) 194 | 195 | plistlib.writePlist(self.entry_data, entry_path) 196 | 197 | self.load_file() 198 | 199 | @property 200 | def uuid(self): 201 | return self.entry_data['UUID'] 202 | 203 | @property 204 | def text(self): 205 | return self.entry_data['Entry Text'] 206 | 207 | @text.setter 208 | def text(self, x): 209 | self.entry_data['Entry Text'] = str(x) 210 | 211 | @property 212 | def text_html(self): 213 | return markdown.markdown(self.text, ['markdown.extensions.extra']) 214 | 215 | @property 216 | def tags(self): 217 | return self.entry_data.get('Tags', []) 218 | 219 | @tags.setter 220 | def tags(self, x): 221 | self.entry_data['Tags'] = x 222 | 223 | @property 224 | def photo(self): 225 | photos = glob.glob(os.path.join(self.journal_dir, 226 | "photos", "%s.*" % self.uuid)) 227 | if photos: 228 | return photos[0] 229 | else: 230 | return None 231 | 232 | @property 233 | def creation_date(self): 234 | if self.time_zone: 235 | return self.time_zone.localize(self.entry_data['Creation Date']) 236 | else: 237 | return self.entry_data['Creation Date'] 238 | 239 | @property 240 | def starred(self): 241 | return self.entry_data['Starred'] 242 | 243 | @starred.setter 244 | def starred(self, x): 245 | self.entry_data['Starred'] = bool(x) 246 | 247 | @property 248 | def activity(self): 249 | return self.entry_date['Activity'] 250 | 251 | @property 252 | def location(self): 253 | if self.entry_data.get('Location', None): 254 | return Location(self.entry_data.get('Location')) 255 | else: 256 | return None 257 | 258 | @property 259 | def weather(self): 260 | if self.entry_data.get('Weather', None): 261 | return Weather(self.entry_data.get('Weather')) 262 | else: 263 | return None 264 | 265 | @property 266 | def time_zone(self): 267 | if self.entry_data.get('Time Zone', None): 268 | return timezone(self.entry_data.get('Time Zone', None)) 269 | else: 270 | return get_localzone() 271 | 272 | 273 | class Journal(object): 274 | """A Day One Journal.""" 275 | 276 | def __init__(self, journal_dir): 277 | self.journal_dir = journal_dir 278 | 279 | self.entries_dir = os.path.join(journal_dir, "entries") 280 | self.photos_dir = os.path.join(journal_dir, "photos") 281 | 282 | # if not os.path.exists(self.entries_dir): 283 | # os.makedirs(self.entries_dir) 284 | 285 | # if not os.path.exists(self.photos_dir): 286 | # os.makedirs(self.photos_dir) 287 | 288 | self.journal_name = os.path.basename(self.journal_dir) 289 | 290 | self.entries = [] 291 | 292 | self.get_entries() 293 | 294 | def __repr__(self): 295 | return "%s.%s(%s)" % (self.__module__, self.__class__.__name__, 296 | self.journal_name) 297 | 298 | def get_entries(self): 299 | entries = glob.glob(os.path.join(self.entries_dir, "*.doentry")) 300 | 301 | for entry in entries: 302 | filename = os.path.basename(entry) 303 | self.entries += [Entry(self.journal_dir, filename)] 304 | 305 | self.entries.sort(key=lambda e: e.creation_date, reverse=True) 306 | 307 | def filter_by_date(self, date): 308 | def f(e): 309 | return e.creation_date.date() == date 310 | 311 | return filter(f, self.entries) 312 | 313 | def filter_between_dates(self, start_date, end_date): 314 | def f(e): 315 | return ((e.creation_date.date() >= start_date) and 316 | (e.creation_date.date() <= end_date)) 317 | 318 | return filter(f, self.entries) 319 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from setuptools import setup 4 | 5 | from dayone import __version__, __project_name__, __project_link__ 6 | 7 | 8 | def read(fname): 9 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 10 | 11 | setup( 12 | name=__project_name__, 13 | version=__version__, 14 | 15 | description="A Python Library for the Mac OS X application Day One.", 16 | 17 | author="Myles Braithwaite", 18 | author_email="me@mylesbraithwaite.com", 19 | 20 | license='BSD', 21 | 22 | keywords='dayone', 23 | 24 | url=__project_link__, 25 | 26 | long_description=read('README.rst'), 27 | 28 | classifiers=[ 29 | "Development Status :: 3 - Alpha", 30 | "Topic :: Utilities", 31 | "License :: OSI Approved :: BSD License", 32 | ], 33 | 34 | install_requires=[ 35 | 'pytz', 36 | 'tzlocal', 37 | 'Markdown', 38 | ], 39 | 40 | extras_require={ 41 | 'cli': ['clint', ] 42 | }, 43 | 44 | entry_points={ 45 | 'console_scripts': [ 46 | 'py-dayone = dayone.cli:main [cli]' 47 | ] 48 | }, 49 | 50 | test_suite="tests" 51 | ) 52 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/0CDC15D3E7654024B621B5367C33BC7C.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2014-10-20T13:18:48Z 7 | Entry Text 8 | This is a test entry for the date of 2014-10-20. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | 0CDC15D3E7654024B621B5367C33BC7C 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/19A4A5E666B44887AFABB0557CE41E77.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2015-10-07T10:35:19Z 7 | Entry Text 8 | This is a test entry for the date of 2015-10-07. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | 19A4A5E666B44887AFABB0557CE41E77 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/1B23D9650AAF46339900F7D6DA053A29.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2014-10-20T13:36:23Z 7 | Entry Text 8 | This is a test entry for the date of 2014-10-20. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | 1B23D9650AAF46339900F7D6DA053A29 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/1B3607772E2C40B191FE2FD496556FCA.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2015-10-07T10:39:11Z 7 | Entry Text 8 | This is a test entry for the date of 2015-10-07. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | 1B3607772E2C40B191FE2FD496556FCA 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/31A6C3FEB30C4C07AA4784128EC30AAB.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2014-10-17T15:25:04Z 7 | Entry Text 8 | Hello, World! 9 | Time Zone 10 | America/Toronto 11 | Starred 12 | 13 | UUID 14 | 31A6C3FEB30C4C07AA4784128EC30AAB 15 | Tags 16 | 17 | Testing 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/3A9B24552540483189DE7CE503D35CD9.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2015-10-07T10:42:46Z 7 | Entry Text 8 | This is a test entry for the date of 2015-10-07. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | 3A9B24552540483189DE7CE503D35CD9 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/3C7ECC11273046319215D278378AB6CE.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2015-10-07T11:06:24Z 7 | Entry Text 8 | This is a test entry for the date of 2015-10-07. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | 3C7ECC11273046319215D278378AB6CE 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/57BD0F8052EC4C33B9F95DCEAFD6B48F.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2014-10-20T13:36:57Z 7 | Entry Text 8 | This is a test entry for the date of 2014-10-20. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | 57BD0F8052EC4C33B9F95DCEAFD6B48F 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/58603C5631FC4203B74D6357BB86AD72.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2015-10-07T10:38:33Z 7 | Entry Text 8 | This is a test entry for the date of 2015-10-07. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | 58603C5631FC4203B74D6357BB86AD72 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/6C8D05AF597C4CC291F16D54A88064C9.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2015-10-07T11:06:58Z 7 | Entry Text 8 | This is a test entry for the date of 2015-10-07. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | 6C8D05AF597C4CC291F16D54A88064C9 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/76A763B68E7A4DB5ABE4C5DE56E3800A.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2015-10-07T10:37:24Z 7 | Entry Text 8 | This is a test entry for the date of 2015-10-07. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | 76A763B68E7A4DB5ABE4C5DE56E3800A 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/91714FAB68094F318704E05A3EF3CE36.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2014-10-20T13:35:57Z 7 | Entry Text 8 | This is a test entry for the date of 2014-10-20. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | 91714FAB68094F318704E05A3EF3CE36 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/A2D2A3CF852F4B4B8834096A33EB76EB.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2014-10-20T13:35:30Z 7 | Entry Text 8 | This is a test entry for the date of 2014-10-20. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | A2D2A3CF852F4B4B8834096A33EB76EB 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/BEA88B4AA31C4AA6B4569CA83DCEBAEA.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2015-10-07T11:08:57Z 7 | Entry Text 8 | This is a test entry for the date of 2015-10-07. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | BEA88B4AA31C4AA6B4569CA83DCEBAEA 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/CE818E1D93984E86A64773503B2F9C50.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2014-10-20T13:35:03Z 7 | Entry Text 8 | This is a test entry for the date of 2014-10-20. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | CE818E1D93984E86A64773503B2F9C50 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/CE9ADDBB8CE94555963CE9C58F26FDD0.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2015-10-07T10:43:13Z 7 | Entry Text 8 | This is a test entry for the date of 2015-10-07. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | CE9ADDBB8CE94555963CE9C58F26FDD0 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/DF25F4C8EDE14FC3AFBA3EF33C042F64.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2015-10-07T10:40:30Z 7 | Entry Text 8 | This is a test entry for the date of 2015-10-07. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | DF25F4C8EDE14FC3AFBA3EF33C042F64 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/entries/FA73DA5E3A2B41A9A2B4BE8DC4C73DCB.doentry: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Creation Date 6 | 2014-10-20T13:19:19Z 7 | Entry Text 8 | This is a test entry for the date of 2014-10-20. 9 | Starred 10 | 11 | Time Zone 12 | America/Toronto 13 | UUID 14 | FA73DA5E3A2B41A9A2B4BE8DC4C73DCB 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Journal.dayone/photos/31A6C3FEB30C4C07AA4784128EC30AAB.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myles/dayone/4a80c8c52987b332ad344177e901fcd06fdeb426/tests/Journal.dayone/photos/31A6C3FEB30C4C07AA4784128EC30AAB.jpg -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myles/dayone/4a80c8c52987b332ad344177e901fcd06fdeb426/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_dayone_journal.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import datetime 4 | 5 | from dayone import journal 6 | 7 | 8 | TEST_DIR_PATH = os.path.split(os.path.abspath(__file__))[0] 9 | JOURNAL_DAYONE_PATH = os.path.join(TEST_DIR_PATH, 'Journal.dayone') 10 | 11 | 12 | class TestEntryObject(unittest.TestCase): 13 | def setUp(self): 14 | self.entry = journal.Entry(JOURNAL_DAYONE_PATH, 15 | '31A6C3FEB30C4C07AA4784128EC30AAB.doentry') 16 | 17 | def test_text(self): 18 | self.assertEqual(self.entry.entry_data['Entry Text'], "Hello, World!") 19 | 20 | def test_tags(self): 21 | self.assertEqual(self.entry.entry_data['Tags'], ["Testing"]) 22 | 23 | def test_photo(self): 24 | photo_path = os.path.join(JOURNAL_DAYONE_PATH, 25 | "photos", "%s.jpg" % self.entry.uuid) 26 | self.assertEqual(photo_path, self.entry.photo) 27 | 28 | def test_create_journal_entry(self): 29 | entry = journal.Entry(JOURNAL_DAYONE_PATH) 30 | today = datetime.date.today() 31 | text = "This is a test entry for the date of %s." % today 32 | entry.text = text 33 | entry.save_file() 34 | self.assertEqual(text, entry.text) 35 | 36 | 37 | class TestJournalObject(unittest.TestCase): 38 | def setUp(self): 39 | self.journal = journal.Journal(JOURNAL_DAYONE_PATH) 40 | 41 | def test_get_entries(self): 42 | self.journal.get_entries() 43 | entries = self.journal.entries 44 | self.assertTrue(entries) 45 | 46 | def test_filter_by_date(self): 47 | entries = self.journal.filter_by_date(datetime.date.today()) 48 | self.assertTrue(entries) 49 | 50 | def test_filter_between_dates(self): 51 | TODAY = datetime.date.today() 52 | SEVEN_DAYS_AGO = TODAY - datetime.timedelta(days=7) 53 | entries = self.journal.filter_between_dates(SEVEN_DAYS_AGO, TODAY) 54 | self.assertTrue(entries) 55 | 56 | 57 | if __name__ == '__main__': 58 | unittest.main() 59 | --------------------------------------------------------------------------------