[thotkeeper] r168 committed - Add attachment code recently reverted from trunk.

2 views
Skip to first unread message

thotk...@googlecode.com

unread,
Nov 7, 2014, 12:03:55 AM11/7/14
to thotkee...@googlegroups.com
Revision: 168
Author: cmpi...@gmail.com
Date: Fri Nov 7 05:03:38 2014 UTC
Log: Add attachment code recently reverted from trunk.
https://code.google.com/p/thotkeeper/source/detail?r=168

Modified:
/branches/attachments/lib/tk_data.py
/branches/attachments/lib/tk_main.py
/branches/attachments/lib/tk_resources.xrc

=======================================
--- /branches/attachments/lib/tk_data.py Tue Jun 15 17:38:42 2010 UTC
+++ /branches/attachments/lib/tk_data.py Fri Nov 7 05:03:38 2014 UTC
@@ -10,6 +10,7 @@
#
# Website: http://www.thotkeeper.org/

+import base64
import os
import shutil
import tempfile
@@ -50,9 +51,31 @@
return MySet()


+class TKEntryAttachment:
+ def __init__(self, description='', filename=None, data=None,
+ content_type=None):
+ self.description = description
+ self.filename = filename
+ self.data = data
+ self.content_type = content_type
+
+ def get_data(self):
+ return self.data
+
+ def get_description(self):
+ return self.description
+
+ def get_filename(self):
+ return self.filename
+
+ def get_content_type(self):
+ return self.content_type
+
+
class TKEntry:
def __init__(self, author='', subject='', text='',
- year=None, month=None, day=None, id=None, tags=[]):
+ year=None, month=None, day=None, id=None,
+ tags=[], attachments=[]):
self.author = author
self.subject = subject
self.text = text
@@ -61,6 +84,7 @@
self.day = day
self.id = id
self.tags = tags
+ self.attachments = attachments

def get_author(self):
return self.author
@@ -79,6 +103,9 @@

def get_tags(self):
return self.tags
+
+ def get_attachments(self):
+ return self.attachments

class TKEntries:
def __init__(self):
@@ -328,7 +355,7 @@
</entries>
</diary>

- Version 1 (unreleased): Adds an "id" attribute to entries for the
+ Version 1 (ThotKeeper 0.2): Adds an "id" attribute to entries for the
purposes of distinguishing multiple entries for a given day. Adds
an optional <tags> tag to entries, which contains 1 or more <tag>
tags.
@@ -348,23 +375,61 @@
...
</entries>
</diary>
-
+
+ Version 2 (ThotKeeper 0.5): Adds optional elements (the
+ <attachments> tag and its children) for tracking entry attachments
+ -- such as images -- and descriptions thereof.
+
+ <diary version="1">
+ <author global="True/False">CDATA</author>
+ <entries>
+ <entry year="YYYY" month="M" day="D" id="N">
+ <author>CDATA</author>
+ <subject>CDATA</subject>
+ <tags>
+ <tag>CDATA</tag>
+ ...
+ </tags>
+ <text>CDATA</text>
+ <attachments>
+ <attachment>
+ <description>CDATA</description>
+ <filename>CDATA</filename>
+ <data>base64(DATA)</data>
+ </attachment>
+ ...
+ </attachments>
+ </entry>
+ ...
+ </entries>
+ </diary>
+
"""

+ TKJ_TAG_ATTACHMENT = 'attachment'
+ TKJ_TAG_ATTACHMENTS = 'attachments'
TKJ_TAG_AUTHOR = 'author'
+ TKJ_TAG_DATA = 'data'
+ TKJ_TAG_DESC = 'description'
TKJ_TAG_DIARY = 'diary'
TKJ_TAG_ENTRIES = 'entries'
TKJ_TAG_ENTRY = 'entry'
+ TKJ_TAG_FILENAME = 'filename'
TKJ_TAG_SUBJECT = 'subject'
TKJ_TAG_TAG = 'tag'
TKJ_TAG_TAGS = 'tags'
TKJ_TAG_TEXT = 'text'

_valid_parents = {
+ TKJ_TAG_ATTACHMENT : [ TKJ_TAG_ATTACHMENTS ],
+ TKJ_TAG_ATTACHMENTS : [ TKJ_TAG_ENTRY ],
TKJ_TAG_AUTHOR : [ TKJ_TAG_DIARY, TKJ_TAG_ENTRY ],
+ TKJ_TAG_DESC : [ TKJ_TAG_ATTACHMENT ],
+ TKJ_TAG_DATA : [ TKJ_TAG_ATTACHMENT ],
TKJ_TAG_DIARY : [ ],
TKJ_TAG_ENTRIES : [ TKJ_TAG_DIARY ],
TKJ_TAG_ENTRY : [ TKJ_TAG_ENTRIES ],
+ TKJ_TAG_FILENAME : [ TKJ_TAG_ATTACHMENT ],
TKJ_TAG_SUBJECT : [ TKJ_TAG_ENTRY ],
TKJ_TAG_TAG : [ TKJ_TAG_TAGS ],
TKJ_TAG_TAGS : [ TKJ_TAG_ENTRY ],
@@ -376,10 +441,11 @@
self.buffer = None
self.entries = entries
self.tag_stack = []
+ # If we are loading a file, we want there to be no global
+ # author *unless* one is actually found in the file (but the
+ # default should still be True for new files).
self.entries.set_author_global(False)
- # If we are loading a file, we want there to be no global author
*unless*
- # one is actually found in the file (but the default should still
be
- # True for new files
+ self.cur_attachment = None

def _validate_tag(self, name, parent_tag):
valid_parents = self._valid_parents[name]
@@ -425,9 +491,16 @@
self.buffer = ''
elif name == self.TKJ_TAG_TAGS:
self.cur_entry['tags'] = []
+ elif name == self.TKJ_TAG_ATTACHMENTS:
+ self.cur_entry['attachments'] = []
+ elif name == self.TKJ_TAG_ATTACHMENT:
+ self.cur_attachment = dict(attrs)
elif name == self.TKJ_TAG_SUBJECT \
or name == self.TKJ_TAG_TAG \
- or name == self.TKJ_TAG_TEXT:
+ or name == self.TKJ_TAG_TEXT \
+ or name == self.TKJ_TAG_DESC \
+ or name == self.TKJ_TAG_FILENAME \
+ or name == self.TKJ_TAG_DATA:
self.buffer = ''

def characters(self, ch):
@@ -448,7 +521,8 @@
int(self.cur_entry['month']),
int(self.cur_entry['day']),
int(self.cur_entry['id']),
- self.cur_entry.get('tags',
[])))
+ self.cur_entry.get('tags',
[]),
+
self.cur_entry.get('attachments', [])))
self.cur_entry = None
elif name == self.TKJ_TAG_AUTHOR:
if self.cur_entry:
@@ -462,6 +536,23 @@
self.buffer = None
elif name == self.TKJ_TAG_TAG:
self.cur_entry['tags'].append(self.buffer)
+ elif name == self.TKJ_TAG_DESC:
+ self.cur_attachment['description'] = self.buffer
+ self.buffer = None
+ elif name == self.TKJ_TAG_DATA:
+ self.cur_attachment['data'] = base64.decodestring(self.buffer)
+ self.buffer = None
+ elif name == self.TKJ_TAG_FILENAME:
+ self.cur_attachment['filename'] = self.buffer
+ self.buffer = None
+ elif name == self.TKJ_TAG_ATTACHMENT:
+ attachment =
TKEntryAttachment(self.cur_attachment['description'],
+ self.cur_attachment['filename'],
+ self.cur_attachment['data'],
+
self.cur_attachment['content-type'])
+ self.cur_entry['attachments'].append(attachment)
+ self.cur_attachment = None
+

def parse_data(datafile):
"""Parse an XML file, returning a TKEntries object."""
@@ -491,6 +582,7 @@
year, month, day = entry.get_date()
id = entry.get_id()
tags = entry.get_tags()
+ attachments = entry.get_attachments()
fp.write(' <entry year="%s" month="%s" day="%s" id="%s">\n'
% (year, month, day, id))
author = xml.sax.saxutils.escape(entry.get_author())
@@ -507,6 +599,18 @@
fp.write(' <tag>%s</tag>\n'
%
(xml.sax.saxutils.escape(tag.encode('utf8'))))
fp.write(' </tags>\n')
+ if len(attachments):
+ fp.write(' <attachments>\n')
+ for attachment in attachments:
+ fp.write(' <attachment>\n')
+ fp.write(' <description>%s</description>\n'
+ % (attachment.get_description().encode('utf8')))
+ fp.write(' <filename>%s</filename>\n'
+ % (attachment.get_filename().encode('utf8')))
+ fp.write(' <data>%s</data>\n'
+ % (base64.encodestring(attachment.get_data())))
+ fp.write(' </attachment>\n')
+ fp.write(' </attachments>\n')
fp.write(' <text>%s</text>\n'
%
(xml.sax.saxutils.escape(entry.get_text().encode('utf8'))))
fp.write(' </entry>\n')
=======================================
--- /branches/attachments/lib/tk_main.py Sat Aug 21 15:11:23 2010 UTC
+++ /branches/attachments/lib/tk_main.py Fri Nov 7 05:03:38 2014 UTC
@@ -16,6 +16,7 @@
import time
import string
import tk_data
+import tempfile
import wx
import wx.calendar
import wx.xrc
@@ -568,6 +569,7 @@
self.subject_id = self.resources.GetXRCID('TKEntrySubject')
self.tags_id = self.resources.GetXRCID('TKEntryTags')
self.text_id = self.resources.GetXRCID('TKEntryText')
+ self.attachments_id = self.resources.GetXRCID("TKEntryAttachments")
self.file_new_id = self.resources.GetXRCID('TKMenuFileNew')
self.file_open_id = self.resources.GetXRCID('TKMenuFileOpen')
self.file_save_id = self.resources.GetXRCID('TKMenuFileSave')
@@ -699,6 +701,9 @@
wx.EVT_MENU(self, self.help_update_id, self._HelpUpdateMenu)
wx.EVT_MENU(self, self.help_about_id, self._HelpAboutMenu)

+ # Event handlers for the Attachments list.
+ wx.EVT_LIST_ITEM_ACTIVATED(self, self.attachments_id,
self._AttachmentActivated)
+
# Event handlers for the Tree widget.
wx.EVT_TREE_ITEM_ACTIVATED(self, self.datetree_id,
self._TreeActivated)
wx.EVT_RIGHT_DOWN(self.tree, self._TreePopup)
@@ -915,17 +920,39 @@
else:
self.frame.FindWindowById(self.next_id).Enable(False)
self.frame.FindWindowById(self.date_id).SetLabel(label)
- text = subject = author = tags = ''
+ text = subject = author = tags = attachments = ''
entry = self.entries.get_entry(year, month, day, id)
if entry is not None:
text = entry.get_text()
author = entry.get_author()
subject = entry.get_subject()
tags = ', '.join(entry.get_tags() or [])
+ attachments = entry.get_attachments()
self.frame.FindWindowById(self.author_id).SetValue(author)
self.frame.FindWindowById(self.subject_id).SetValue(subject)
self.frame.FindWindowById(self.text_id).SetValue(text)
self.frame.FindWindowById(self.tags_id).SetValue(tags)
+
+ attachments_list = self.frame.FindWindowById(self.attachments_id)
+ attachments_list.ClearAll()
+ frame_width, xxx = self.frame.GetSizeTuple()
+ attachments_list.InsertColumn(0, "Description",
+ width=int(frame_width * 0.4))
+ attachments_list.InsertColumn(1, "Filename",
+ width=int(frame_width * 0.3))
+ attachments_list.InsertColumn(2, "Content-Type",
+ width=int(frame_width * 0.3))
+ if attachments:
+ attachments.reverse()
+ for attachment in attachments:
+ list_item = wx.ListItem()
+ idx = attachments_list.InsertItem(list_item)
+ att_desc = attachment.get_description()
+ att_name = attachment.get_filename()
+ att_type = attachment.get_content_type()
+ attachments_list.SetStringItem(idx, 0, att_desc)
+ attachments_list.SetStringItem(idx, 1, att_name)
+ attachments_list.SetStringItem(idx, 2, att_type)
self._NotifyEntryLoaded(entry and True or False)

def _NotifyEntryLoaded(self, is_loaded=True):
@@ -1179,7 +1206,22 @@
self._DeleteEntry(entry_year, entry_month, entry_day, entry_id,
skip_verify=True)
new_entries.enumerate_entries(_DeleteEntryCB)
-
+
+ def _LaunchDefaultApplication(self, filename):
+ try:
+ import subprocess
+ if sys.platform.startswith('darwin'):
+ subprocess.call(('open', filename))
+ elif os.name == 'nt':
+ os.startfile(filepath)
+ elif os.name == 'posix':
+ subprocess.call(('xdg-open', filename))
+ except:
+ wx.MessageBox("Failure launching default application.",
+ "Application Launch Failed",
+ wx.OK | wx.ICON_ERROR, self.frame)
+ return
+
### -----------------------------------------------------------------
### Tree Popup Menu Actions
### -----------------------------------------------------------------
@@ -1538,6 +1580,33 @@
return
self._SetEntryFormDate(data.year, data.month, data.day, data.id)

+ def _AttachmentActivated(self, event):
+ item = event.GetItem()
+ list = event.GetEventObject()
+ idx = event.GetIndex()
+ filename = list.GetItem(idx, 1).GetText()
+ entry = self.entries.get_entry(self.entry_form_key.year,
+ self.entry_form_key.month,
+ self.entry_form_key.day,
+ self.entry_form_key.id)
+ for attachment in entry.get_attachments():
+ if attachment.get_filename() == filename:
+ root, ext = os.path.splitext(filename)
+ handle, tmpfile = tempfile.mkstemp(suffix=ext)
+ fp = open(tmpfile, 'w')
+ fp.write(attachment.get_data())
+ fp.close()
+ self._LaunchDefaultApplication(tmpfile)
+ break
+
+ ### -----------------------------------------------------------------
+ ### Debugging Stuff
+ ### -----------------------------------------------------------------
+
+ def _DebugMessage(self, msg):
+ wx.MessageBox(msg, "DEBUG MESSAGE",
+ wx.OK | wx.ICON_ERROR, self.frame)
+ return

def main():
file = None
=======================================
--- /branches/attachments/lib/tk_resources.xrc Sat Aug 21 15:11:23 2010 UTC
+++ /branches/attachments/lib/tk_resources.xrc Fri Nov 7 05:03:38 2014 UTC
@@ -318,20 +318,46 @@
<border>10</border>
</object>
<object class="sizeritem">
- <object class="wxStaticBoxSizer">
- <label>ThotKeeper Entry</label>
- <orient>wxHORIZONTAL</orient>
- <object class="sizeritem">
- <object class="wxTextCtrl" name="TKEntryText">
- <style>wxTE_MULTILINE|wxTE_WORDWRAP</style>
+ <object class="wxNotebook">
+ <object class="notebookpage">
+ <label>ThotKeeper Entry</label>
+ <object class="wxPanel">
+ <object class="wxFlexGridSizer">
+ <cols>1</cols>
+ <rows>1</rows>
+ <growablerows>0</growablerows>
+ <growablecols>0</growablecols>
+ <object class="sizeritem">
+ <object class="wxTextCtrl" name="TKEntryText">
+ <style>wxTE_MULTILINE|wxTE_WORDWRAP</style>
+ </object>
+ <flag>wxALL|wxEXPAND</flag>
+ <border>5</border>
+ </object>
+ </object>
</object>
- <option>1</option>
- <flag>wxALL|wxEXPAND</flag>
- <border>5</border>
+ <selected>1</selected>
+ </object>
+ <object class="notebookpage">
+ <label>Attachments</label>
+ <object class="wxPanel">
+ <object class="wxFlexGridSizer">
+ <cols>1</cols>
+ <rows>1</rows>
+ <growablecols>0</growablecols>
+ <growablerows>0</growablerows>
+ <object class="sizeritem">
+ <object class="wxListCtrl"
name="TKEntryAttachments">
+ <style>wxLC_REPORT|wxLC_SINGLE_SEL</style>
+ </object>
+ <flag>wxALL|wxEXPAND</flag>
+ <border>5</border>
+ </object>
+ </object>
+ </object>
</object>
</object>
- <flag>wxALL|wxEXPAND</flag>
- <border>5</border>
+ <flag>wxEXPAND</flag>
</object>
<growablecols>0</growablecols>
<growablerows>1</growablerows>
Reply all
Reply to author
Forward
0 new messages