From 0b7e02cf796af7d3028efabeb37473f9198d0af2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C5=81ukasz=20Rekucki?= Date: Sat, 26 Sep 2009 13:58:40 +0200 Subject: [PATCH 1/1] Test fixes. --- apps/api/forms.py | 12 ++- apps/api/handlers/library_handlers.py | 105 ++++++++++++++------------ apps/api/resources.py | 2 + apps/api/response.py | 22 +----- apps/api/tests/__init__.py | 59 +++++---------- apps/api/urls.py | 29 ++++--- apps/api/utils.py | 39 +++++++++- project/templates/503.html | 16 ++++ 8 files changed, 158 insertions(+), 126 deletions(-) create mode 100644 project/templates/503.html diff --git a/apps/api/forms.py b/apps/api/forms.py index 7c6f2c22..3c393a87 100644 --- a/apps/api/forms.py +++ b/apps/api/forms.py @@ -7,8 +7,16 @@ __doc__ = "Micro-forms for the API." from django import forms -class DocumentEntryRequest(forms.Form): - revision = forms.RegexField(regex='latest|[0-9a-f]{40}') + +class MergeRequestForm(forms.Form): + # should the target document revision be updated or shared + type = forms.ChoiceField(choices=('update', 'share')) + + # which revision to update/share + target_revision = forms.RegexField('[0-9a-f]{40}') + + # any additional comments that user wants to add to the change + comment = forms.CharField(required=False) class DocumentUploadForm(forms.Form): ocr_file = forms.FileField(label='Source OCR file', required=False) diff --git a/apps/api/handlers/library_handlers.py b/apps/api/handlers/library_handlers.py index 18dd3e81..06513522 100644 --- a/apps/api/handlers/library_handlers.py +++ b/apps/api/handlers/library_handlers.py @@ -18,7 +18,7 @@ from wlrepo import MercurialLibrary, RevisionNotFound, DocumentAlreadyExists from librarian import dcparser import api.response as response -from api.response import validate_form +from api.utils import validate_form, hglibrary # # Document List Handlers @@ -26,10 +26,9 @@ from api.response import validate_form class BasicLibraryHandler(AnonymousBaseHandler): allowed_methods = ('GET',) - def read(self, request): - """Return the list of documents.""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - + @hglibrary + def read(self, request, lib): + """Return the list of documents.""" document_list = [{ 'url': reverse('document_view', args=[docid]), 'name': docid } for docid in lib.documents() ] @@ -41,9 +40,9 @@ class LibraryHandler(BaseHandler): allowed_methods = ('GET', 'POST') anonymous = BasicLibraryHandler - def read(self, request): + @hglibrary + def read(self, request, lib): """Return the list of documents.""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) document_list = [{ 'url': reverse('document_view', args=[docid]), @@ -52,14 +51,14 @@ class LibraryHandler(BaseHandler): return {'documents' : document_list } @validate_form(forms.DocumentUploadForm, 'POST') - def create(self, request, form): - """Create a new document.""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + @hglibrary + def create(self, request, form, lib): + """Create a new document.""" if form.cleaned_data['ocr_data']: data = form.cleaned_data['ocr_data'].encode('utf-8') else: - data = request.FILES['ocr'].read().decode('utf-8') + data = request.FILES['ocr_file'].read().decode('utf-8') if form.cleaned_data['generate_dc']: data = librarian.wrap_text(data, unicode(date.today())) @@ -67,12 +66,11 @@ class LibraryHandler(BaseHandler): docid = form.cleaned_data['bookname'] try: doc = lib.document_create(docid) - doc.quickwrite('xml', data, '$AUTO$ XML data uploaded.', + doc = doc.quickwrite('xml', data, '$AUTO$ XML data uploaded.', user=request.user.username) url = reverse('document_view', args=[doc.id]) - return response.EntityCreated().django_response(\ body = { 'url': url, @@ -91,17 +89,17 @@ class LibraryHandler(BaseHandler): class BasicDocumentHandler(AnonymousBaseHandler): allowed_methods = ('GET',) - def read(self, request, docid): - try: - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + @hglibrary + def read(self, request, docid, lib): + try: doc = lib.document(docid) except RevisionNotFound: return rc.NOT_FOUND result = { 'name': doc.id, - 'text_url': reverse('doctext_view', args=[doc.id]), - 'dc_url': reverse('docdc_view', docid=doc.id), + 'text_url': reverse('doctext_view', args=[doc.id,doc.revision]), + 'dc_url': reverse('docdc_view', args=[doc.id,doc.revision]), 'public_revision': doc.revision, } @@ -114,10 +112,9 @@ class DocumentHandler(BaseHandler): allowed_methods = ('GET', 'PUT') anonymous = BasicDocumentHandler - def read(self, request, docid): - """Read document's meta data""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - + @hglibrary + def read(self, request, docid, lib): + """Read document's meta data""" try: doc = lib.document(docid) udoc = doc.take(request.user.username) @@ -129,16 +126,16 @@ class DocumentHandler(BaseHandler): result = { 'name': udoc.id, - 'text_url': reverse('doctext_view', args=[udoc.id]), - 'dc_url': reverse('docdc_view', args=[udoc.id]), - 'parts_url': reverse('docparts_view', args=[udoc.id]), + 'text_url': reverse('doctext_view', args=[doc.id,doc.revision]), + 'dc_url': reverse('docdc_view', args=[doc.id,doc.revision]), 'user_revision': udoc.revision, 'public_revision': doc.revision, } return result - def update(self, request, docid): + @hglibrary + def update(self, request, docid, lib): """Update information about the document, like display not""" return @@ -148,27 +145,24 @@ class DocumentHandler(BaseHandler): class DocumentTextHandler(BaseHandler): allowed_methods = ('GET', 'PUT') - @validate_form(forms.DocumentEntryRequest) - def read(self, request, form, docid): - """Read document as raw text""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - + @hglibrary + def read(self, request, docid, revision, lib): + """Read document as raw text""" try: - if request.GET['revision'] == 'latest': + if revision == 'latest': document = lib.document(docid) else: - document = lib.document_for_rev(request.GET['revision']) + document = lib.document_for_rev(revision) # TODO: some finer-grained access control return document.data('xml') except RevisionNotFound: return response.EntityNotFound().django_response() - def update(self, request, docid): - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + @hglibrary + def update(self, request, docid, revision, lib): try: - data = request.PUT['contents'] - prev = request.PUT['revision'] + data = request.PUT['contents'] if request.PUT.has_key('message'): msg = u"$USER$ " + request.PUT['message'] @@ -176,7 +170,7 @@ class DocumentTextHandler(BaseHandler): msg = u"$AUTO$ XML content update." current = lib.document(docid, request.user.username) - orig = lib.document_for_rev(prev) + orig = lib.document_for_rev(revision) if current != orig: return response.EntityConflict().django_response({ @@ -205,33 +199,31 @@ class DocumentTextHandler(BaseHandler): class DocumentDublinCoreHandler(BaseHandler): allowed_methods = ('GET', 'PUT') - @validate_form(forms.DocumentEntryRequest) - def read(self, request, docid): - """Read document as raw text""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + @hglibrary + def read(self, request, docid, revision, lib): + """Read document as raw text""" try: - if request.GET['revision'] == 'latest': + if revision == 'latest': document = lib.document(docid) else: - document = lib.document_for_rev(request.GET['revision']) + document = lib.document_for_rev(revision) bookinfo = dcparser.BookInfo.from_string(doc.data('xml')) return bookinfo.serialize() except RevisionNotFound: return response.EntityNotFound().django_response() - def update(self, request, docid): - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + @hglibrary + def update(self, request, docid, revision, lib): try: - bi_json = request.PUT['contents'] - prev = request.PUT['revision'] + bi_json = request.PUT['contents'] if request.PUT.has_key('message'): msg = u"$USER$ " + request.PUT['message'] else: msg = u"$AUTO$ Dublin core update." current = lib.document(docid, request.user.username) - orig = lib.document_for_rev(prev) + orig = lib.document_for_rev(revision) if current != orig: return response.EntityConflict().django_response({ @@ -255,3 +247,18 @@ class DocumentDublinCoreHandler(BaseHandler): } except (RevisionNotFound, KeyError): return response.EntityNotFound().django_response() + + +class MergeHandler(BaseHandler): + allowed_methods = ('POST',) + + @validate_form(forms.MergeRequestForm) + @hglibrary + def create(self, request, form, docid, lib): + """Create a new document revision from the information provided by user""" + + pass + + + + diff --git a/apps/api/resources.py b/apps/api/resources.py index d45fa1d7..7681436b 100644 --- a/apps/api/resources.py +++ b/apps/api/resources.py @@ -21,6 +21,7 @@ library_resource = Resource(dh.LibraryHandler, **authdata) document_resource = Resource(dh.DocumentHandler, **authdata) document_text_resource = Resource(dh.DocumentTextHandler, **authdata) document_dc_resource = Resource(dh.DocumentDublinCoreHandler, **authdata) +document_merge = Resource(dh.MergeHandler, **authdata) # # Toolbar resources @@ -34,6 +35,7 @@ __all__ = [ 'document_resource', 'document_text_resource', 'document_dc_resource', + 'document_merge', 'toolbar_buttons', 'scriptlets' ] \ No newline at end of file diff --git a/apps/api/response.py b/apps/api/response.py index 6b685624..c1401635 100644 --- a/apps/api/response.py +++ b/apps/api/response.py @@ -101,26 +101,6 @@ class EntityConflict(ResponseObject): class NotImplemented(ResponseObject): def __init__(self, **kwargs): - ResponseObject.__init__(self, 501, **kwargs) - -def validate_form(formclass, source='GET'): - from functools import wraps - - def decorator(func): - @wraps(func) - def decorated(self, request, *args, **kwargs): - form = formclass(getattr(request, source), request.FILES) - - if not form.is_valid(): - errorlist = [{'field': k, 'errors': e} for k,e in form.errors] - return BadRequest().django_response(errorlist) - - return func(self, request, form, *args, **kwargs) - return decorated - return decorator - - - - + ResponseObject.__init__(self, 501, **kwargs) diff --git a/apps/api/tests/__init__.py b/apps/api/tests/__init__.py index 2292e7dd..acf92f5c 100644 --- a/apps/api/tests/__init__.py +++ b/apps/api/tests/__init__.py @@ -63,32 +63,7 @@ class SimpleTest(TestCase): self.assert_json_response({ u'documents': [], }) - - @temprepo('clean') - def test_documents_post(self): - self.assertTrue(self.client.login(username='admin', password='admin')) - - infile = tempfile.NamedTemporaryFile("w+") - infile.write('0123456789') - infile.flush() - infile.seek(0) - - self.response = self.client.post( reverse("document_list_view"), - data = { - 'bookname': 'testbook', - 'ocr': infile, - 'generate_dc': False, - }) - - infile.close() - - self.assert_json_response({ - 'url': reverse('document_view', args=['testbook']), - 'name': 'testbook', - # 'size': 10, - # can't test revision number, 'cause it's random - },) - + @temprepo('clean') def test_document_creation(self): self.assertTrue(self.client.login(username='admin', password='admin')) @@ -101,23 +76,21 @@ class SimpleTest(TestCase): self.response = self.client.post( reverse("document_list_view"), data = { 'bookname': 'testbook', - 'ocr': infile, + 'ocr_file': infile, 'generate_dc': False, }) - r = self.assert_json_response({ + r = self.assert_json_response( { 'url': reverse('document_view', args=['testbook']), 'name': 'testbook', # can't test revision number, 'cause it's random - }) + }, code=201) created_rev = r['revision'] - - self.response = self.client.get( \ - reverse("document_view", args=["testbook"]) ) - + self.response = self.client.get(r['url']) + result = self.assert_json_response({ - u'latest_shared_rev': created_rev, + u'public_revision': created_rev, # u'size': 15, }) @@ -129,8 +102,10 @@ class SimpleTest(TestCase): self.response = self.client.get( reverse("document_list_view") ) self.assert_json_response({ # u'latest_rev': u'f94a263812dbe46a3a13d5209bb119988d0078d5', - u'documents': [{u'url': u'/api/documents/sample', u'name': u'sample'}, - {u'url': u'/api/documents/sample_pl', u'name': u'sample_pl'}], + u'documents': [ + {u'url': u'/api/documents/sample', u'name': u'sample'}, + {u'url': u'/api/documents/sample_pl', u'name': u'sample_pl'} + ], }) self.response = self.client.get( \ @@ -138,8 +113,8 @@ class SimpleTest(TestCase): self.assert_json_response({ #u'latest_shared_rev': u'f94a263812dbe46a3a13d5209bb119988d0078d5', - u'text_url': reverse("doctext_view", args=[u'sample']), - u'dc_url': reverse("docdc_view", args=[u'sample']), + #u'text_url': reverse("doctext_view", args=[u'sample']), + #u'dc_url': reverse("docdc_view", args=[u'sample']), # u'parts_url': reverse("docparts_view", args=[u'sample']), u'name': u'sample', # u'size': 20, @@ -155,8 +130,8 @@ class SimpleTest(TestCase): resp = self.assert_json_response({ #u'latest_shared_rev': u'f94a263812dbe46a3a13d5209bb119988d0078d5', - u'text_url': reverse("doctext_view", args=[u'sample']), - u'dc_url': reverse("docdc_view", args=[u'sample']), + #u'text_url': reverse("doctext_view", args=[u'sample']), + #u'dc_url': reverse("docdc_view", args=[u'sample']), # u'parts_url': reverse("docparts_view", args=[u'sample']), u'name': u'sample', # u'size': 20, @@ -181,8 +156,8 @@ class SimpleTest(TestCase): # self.assertEqual(self.response.status_code, 200) # self.assertEqual(self.response.content, TEXT) - def assert_json_response(self, must_have={}, exclude=[]): - self.assertEqual(self.response.status_code, 200) + def assert_json_response(self, must_have={}, exclude=[], code=200): + self.assertEqual(self.response.status_code, code) result = json.loads(self.response.content) for (k,v) in must_have.items(): diff --git a/apps/api/urls.py b/apps/api/urls.py index de09605d..8c10158a 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -5,7 +5,13 @@ from django.conf.urls.defaults import * from api.resources import * -FORMAT_EXT = r"\.(?Pxml|json|yaml)$" +FORMAT = r"\.(?Pxml|json|yaml)" +DOC = r'(?P[^/]+)' +REVISION = r'(?Platest|[0-9a-f]{40})' + +def urlpath(*args, **kwargs): + format = kwargs.get('format', True) + return r'^' + (r'/'.join(args)) + (FORMAT if format else '') + '$' urlpatterns = patterns('', # url(r'^hello$', hello_resource, {'emitter_format': 'json'}), @@ -21,31 +27,34 @@ urlpatterns = patterns('', url(r'^documents$', library_resource, {'emitter_format': 'json'}, name="document_list_view"), - url(r'^documents'+FORMAT_EXT, library_resource, + url(urlpath(r'documents'), library_resource, name="document_list_view_withformat"), - url(r'^documents/(?P[^/]+)'+FORMAT_EXT, + url(urlpath(r'documents', DOC), document_resource, name="document_view_withformat"), - url(r'^documents/(?P[^/]+)$', + url(urlpath(r'documents', DOC, format=False), document_resource, {'emitter_format': 'json'}, name="document_view"), - url(r'^documents/(?P[^/]+)/text$', + url(urlpath(r'documents', DOC, 'text', REVISION, format=False), document_text_resource, {'emitter_format': 'rawxml'}, name="doctext_view"), - url(r'^documents/(?P[^/]+)/dc' + FORMAT_EXT, + url(urlpath(r'documents', DOC, 'dc', REVISION), document_dc_resource, name="docdc_view_withformat"), - url(r'^documents/(?P[^/]+)/dc$', + url(urlpath(r'documents', DOC, 'dc', REVISION, format=False), document_dc_resource, {'emitter_format': 'json'}, name="docdc_view"), - url(r'^documents/(?P[^/]+)/parts$', - document_resource, {'emitter_format': 'json'}, - name="docparts_view"), + url(urlpath(r'documents', DOC, 'revision'), + document_merge, {'emitter_format': 'json'}, name="docmerge_view") + +# url(r'^documents/(?P[^/]+)/parts$', +# document_resource, {'emitter_format': 'json'}, +# name="docparts_view"), # url(r'^posts/(?P[^/]+)/$', blogpost_resource), # url(r'^other/(?P[^/]+)/(?P.+)/$', arbitrary_resource), diff --git a/apps/api/utils.py b/apps/api/utils.py index 4b004ee9..0e0468a0 100644 --- a/apps/api/utils.py +++ b/apps/api/utils.py @@ -1,13 +1,19 @@ # -*- encoding: utf-8 -*- -__author__= "Łukasz Rekucki" +__author__ = "Łukasz Rekucki" __date__ = "$2009-09-20 21:48:03$" __doc__ = "Module documentation." +from functools import wraps from piston.emitters import Emitter from piston.utils import rc +import api.response + +from wlrepo import MercurialLibrary +import settings + class TextEmitter(Emitter): def render(self, request): return unicode(self.construct()) @@ -21,4 +27,33 @@ class DjangoAuth(object): return request.user.is_authenticated() def challenge(self): - return rc.FORBIDDEN \ No newline at end of file + return rc.FORBIDDEN + + +def validate_form(formclass, source='GET'): + + def decorator(func): + @wraps(func) + def decorated(self, request, * args, ** kwargs): + form = formclass(getattr(request, source), request.FILES) + + if not form.is_valid(): + errorlist = [{'field': k, 'errors': e} for k, e in form.errors.items()] + return api.response.BadRequest().django_response(errorlist) + + kwargs['form'] = form + return func(self, request, * args, ** kwargs) + return decorated + return decorator + +def hglibrary(func): + @wraps(func) + def decorated(self, *args, **kwargs): + l = MercurialLibrary(settings.REPOSITORY_PATH) + kwargs['lib'] = l + return func(self, *args, **kwargs) + return decorated + + + + diff --git a/project/templates/503.html b/project/templates/503.html new file mode 100644 index 00000000..1c2e4d26 --- /dev/null +++ b/project/templates/503.html @@ -0,0 +1,16 @@ + + + + + Platforma Redakcyjna + + + +

Przepraszamy,

+

Platfroma Redakcyjna jest tymczasowo niedostępna + z powodu prac administracyjnych.

+ +

Prosimy o wyrozumiałość i ponowne odwiedziny :)

+ + -- 2.20.1