Several minor improvements to CSV importing and exporting.
This commit is contained in:
parent
f72297be32
commit
e6fbbfec41
1
NEWS
1
NEWS
|
@ -2,6 +2,7 @@
|
|||
* When you export your notebook as an HTML or CSV file, the saved filename is
|
||||
now based on the name of your notebook, for instance "my-to-do-list.html"
|
||||
or "ideas-for-my-novel.csv".
|
||||
* Several minor improvements to CSV importing and exporting.
|
||||
* Converted the existing HTML and CSV export features to work as separate
|
||||
export plugins. This means that a new export format can be implemented as
|
||||
a new plugin.
|
||||
|
|
|
@ -1,18 +1,30 @@
|
|||
import re
|
||||
import csv
|
||||
from cStringIO import StringIO
|
||||
from model.User import User
|
||||
|
||||
|
||||
FILE_LINK_PATTERN = re.compile( u'<a\s+href="[^"]*\/files\/download\?file_id=[^"]+"[^>]*>', re.IGNORECASE )
|
||||
IMAGE_PATTERN = re.compile( u'<img [^>]* ?/?>', re.IGNORECASE )
|
||||
NEWLINE_PATTERN = re.compile( u'[\r\n]+' )
|
||||
|
||||
|
||||
def export( database, notebook, notes, response_headers ):
|
||||
"""
|
||||
Format the given notes as a CSV file and return it as a streaming generator.
|
||||
"""
|
||||
buffer = StringIO()
|
||||
writer = csv.writer( buffer )
|
||||
writer = csv.writer( buffer, quoting = csv.QUOTE_NONNUMERIC )
|
||||
|
||||
response_headers[ u"Content-Disposition" ] = u"attachment; filename=%s.csv" % notebook.friendly_id
|
||||
response_headers[ u"Content-Type" ] = u"text/csv;charset=utf-8"
|
||||
|
||||
def prepare_contents( contents ):
|
||||
contents = FILE_LINK_PATTERN.sub( '<a>', contents )
|
||||
contents = IMAGE_PATTERN.sub( '', contents )
|
||||
contents = NEWLINE_PATTERN.sub( ' ', contents )
|
||||
return contents.strip()
|
||||
|
||||
def stream():
|
||||
writer.writerow( ( u"contents", u"title", u"note_id", u"startup", u"username", u"revision_date" ) )
|
||||
yield buffer.getvalue()
|
||||
|
@ -24,7 +36,7 @@ def export( database, notebook, notes, response_headers ):
|
|||
user = database.load( User, note.user_id )
|
||||
|
||||
writer.writerow( (
|
||||
note.contents and note.contents.strip().encode( "utf8" ) or None,
|
||||
note.contents and prepare_contents( note.contents ).encode( "utf8" ) or None,
|
||||
note.title and note.title.strip().encode( "utf8" ) or None,
|
||||
note.object_id,
|
||||
note.startup and 1 or 0,
|
||||
|
|
|
@ -41,7 +41,7 @@ class Test_export_csv( object ):
|
|||
self.note2 = Note.create( note_id, u"<h3>other title</h3>whee", notebook_id = self.notebook.object_id, user_id = self.user.object_id )
|
||||
self.database.save( self.note2, commit = False )
|
||||
|
||||
def test_export_csv( self, note_contents = None ):
|
||||
def test_export_csv( self, note_contents = None, expected_contents = None ):
|
||||
if not note_contents:
|
||||
note_contents = u"<h3>blah</h3>foo"
|
||||
|
||||
|
@ -87,7 +87,10 @@ class Test_export_csv( object ):
|
|||
expected_note = expected_notes[ note_count ]
|
||||
|
||||
assert expected_note
|
||||
assert contents.decode( "utf8" ) == expected_note.contents.strip()
|
||||
if expected_contents and note_id == note3.object_id:
|
||||
assert contents.decode( "utf8" ) == expected_contents.replace( "\n", " " ).strip()
|
||||
else:
|
||||
assert contents.decode( "utf8" ) == expected_note.contents.replace( "\n", " " ).strip()
|
||||
|
||||
if expected_note.title:
|
||||
assert title.decode( "utf8" ) == expected_note.title.strip()
|
||||
|
@ -115,6 +118,18 @@ class Test_export_csv( object ):
|
|||
def test_export_csv_with_trailing_newline_in_contents( self ):
|
||||
self.test_export_csv( note_contents = u"<h3>blah</h3>foo\n" )
|
||||
|
||||
def test_export_csv_with_file_attachment_in_contents( self ):
|
||||
self.test_export_csv(
|
||||
note_contents = u"<h3>blah</h3>foo<a href=\"/files/download?file_id=blah"e_filename=False\">file</a>",
|
||||
expected_contents = "<h3>blah</h3>foo<a>file</a>",
|
||||
)
|
||||
|
||||
def test_export_csv_with_image_in_contents( self ):
|
||||
self.test_export_csv(
|
||||
note_contents = u"<h3>blah</h3>foo<a href=\"/files/download?file_id=blah"e_filename=False\"><img src=\"whee.png\" /></a>",
|
||||
expected_contents = "<h3>blah</h3>foo<a></a>",
|
||||
)
|
||||
|
||||
def test_export_csv_with_blank_username( self ):
|
||||
self.user._User__username = None
|
||||
self.database.save( self.user )
|
||||
|
|
|
@ -5,6 +5,7 @@ from view.Tags import Html, Head, Title, Style, Meta, Body, H1, Div, Span, Hr, A
|
|||
|
||||
class Html_file( Html ):
|
||||
NOTE_LINK_PATTERN = re.compile( u'<a\s+href="[^"]*(?:\/notebooks\/)?[^>]+[?&]note_id=([a-z0-9]*)"[^>]*>', re.IGNORECASE )
|
||||
FILE_LINK_PATTERN = re.compile( u'<a\s+href="[^"]*\/files\/download\?file_id=[^"]+"[^>]*>', re.IGNORECASE )
|
||||
IMAGE_PATTERN = re.compile( u'<img [^>]* ?/?>', re.IGNORECASE )
|
||||
|
||||
def __init__( self, notebook, notes ):
|
||||
|
@ -14,6 +15,7 @@ class Html_file( Html ):
|
|||
# images since they're not presently included with the download
|
||||
for note in notes:
|
||||
contents = self.NOTE_LINK_PATTERN.sub( r'<a href="#note_\1">', note.contents )
|
||||
contents = self.FILE_LINK_PATTERN.sub( '<a>', contents )
|
||||
contents = self.IMAGE_PATTERN.sub( '', contents )
|
||||
relinked_notes[ note.object_id ] = contents
|
||||
|
||||
|
|
Reference in New Issue
Block a user