From 22aac0da99594406b261f14c135812c855c196ef Mon Sep 17 00:00:00 2001 From: zuber Date: Wed, 20 Jan 2010 17:41:48 +0100 Subject: [PATCH] =?utf8?q?Usuni=C4=99cie=20aplikacji=20api.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- apps/api/__init__.py | 0 apps/api/admin.py | 9 - apps/api/forms.py | 143 ----- apps/api/handlers/__init__.py | 0 apps/api/handlers/library_handlers.py | 601 ------------------ apps/api/handlers/manage_handlers.py | 137 ---- apps/api/handlers/text_handler.py | 190 ------ apps/api/handlers/toolbar_handlers.py | 33 - apps/api/management/__init__.py | 4 - apps/api/management/commands/__init__.py | 4 - apps/api/management/commands/doc_share.py | 40 -- apps/api/management/commands/doc_update.py | 50 -- .../management/commands/doc_update_text.py | 51 -- apps/api/management/commands/doc_upload.py | 43 -- apps/api/models.py | 73 --- apps/api/resources.py | 55 -- apps/api/response.py | 107 ---- apps/api/tests/__init__.py | 181 ------ apps/api/tests/data/clean/$meta | 0 apps/api/tests/data/clean/.hg/00changelog.i | Bin 57 -> 0 bytes apps/api/tests/data/clean/.hg/dirstate | Bin 112 -> 0 bytes apps/api/tests/data/clean/.hg/requires | 3 - .../tests/data/clean/.hg/store/00changelog.i | Bin 367 -> 0 bytes .../tests/data/clean/.hg/store/00manifest.i | Bin 255 -> 0 bytes .../tests/data/clean/.hg/store/data/$meta.i | Bin 64 -> 0 bytes .../data/clean/.hg/store/data/.hgignore.i | Bin 64 -> 0 bytes .../tests/data/clean/.hg/store/data/.hgtags.i | Bin 118 -> 0 bytes apps/api/tests/data/clean/.hg/store/fncache | 3 - apps/api/tests/data/clean/.hg/store/undo | Bin 52 -> 0 bytes apps/api/tests/data/clean/.hg/undo.branch | 1 - apps/api/tests/data/clean/.hg/undo.dirstate | Bin 112 -> 0 bytes apps/api/tests/data/clean/.hgignore | 0 apps/api/tests/data/clean/.hgtags | 1 - apps/api/tests/data/simple/$meta | 0 apps/api/tests/data/simple/.hg/00changelog.i | Bin 57 -> 0 bytes apps/api/tests/data/simple/.hg/branch | 1 - .../tests/data/simple/.hg/branchheads.cache | 4 - apps/api/tests/data/simple/.hg/dirstate | Bin 112 -> 0 bytes apps/api/tests/data/simple/.hg/requires | 3 - .../tests/data/simple/.hg/store/00changelog.i | Bin 759 -> 0 bytes .../tests/data/simple/.hg/store/00manifest.i | Bin 588 -> 0 bytes .../tests/data/simple/.hg/store/data/$meta.i | Bin 64 -> 0 bytes .../data/simple/.hg/store/data/.hgignore.i | Bin 64 -> 0 bytes .../data/simple/.hg/store/data/.hgtags.i | Bin 118 -> 0 bytes .../data/simple/.hg/store/data/sample.parts.i | Bin 64 -> 0 bytes .../data/simple/.hg/store/data/sample.xml.i | Bin 77 -> 0 bytes .../simple/.hg/store/data/sample__pl.xml.i | Bin 79 -> 0 bytes apps/api/tests/data/simple/.hg/store/fncache | 6 - apps/api/tests/data/simple/.hg/store/undo | Bin 52 -> 0 bytes .../.hg/strip-backup/26716f931d95-backup | Bin 867 -> 0 bytes apps/api/tests/data/simple/.hg/undo.branch | 1 - apps/api/tests/data/simple/.hg/undo.dirstate | Bin 112 -> 0 bytes apps/api/tests/data/simple/.hgignore | 0 apps/api/tests/data/simple/.hgtags | 1 - apps/api/urls.py | 90 --- apps/api/utils.py | 77 --- apps/api/views.py | 38 -- 57 files changed, 1950 deletions(-) delete mode 100644 apps/api/__init__.py delete mode 100644 apps/api/admin.py delete mode 100644 apps/api/forms.py delete mode 100644 apps/api/handlers/__init__.py delete mode 100755 apps/api/handlers/library_handlers.py delete mode 100644 apps/api/handlers/manage_handlers.py delete mode 100755 apps/api/handlers/text_handler.py delete mode 100644 apps/api/handlers/toolbar_handlers.py delete mode 100644 apps/api/management/__init__.py delete mode 100644 apps/api/management/commands/__init__.py delete mode 100644 apps/api/management/commands/doc_share.py delete mode 100644 apps/api/management/commands/doc_update.py delete mode 100644 apps/api/management/commands/doc_update_text.py delete mode 100644 apps/api/management/commands/doc_upload.py delete mode 100644 apps/api/models.py delete mode 100644 apps/api/resources.py delete mode 100644 apps/api/response.py delete mode 100644 apps/api/tests/__init__.py delete mode 100644 apps/api/tests/data/clean/$meta delete mode 100644 apps/api/tests/data/clean/.hg/00changelog.i delete mode 100644 apps/api/tests/data/clean/.hg/dirstate delete mode 100644 apps/api/tests/data/clean/.hg/requires delete mode 100644 apps/api/tests/data/clean/.hg/store/00changelog.i delete mode 100644 apps/api/tests/data/clean/.hg/store/00manifest.i delete mode 100644 apps/api/tests/data/clean/.hg/store/data/$meta.i delete mode 100644 apps/api/tests/data/clean/.hg/store/data/.hgignore.i delete mode 100644 apps/api/tests/data/clean/.hg/store/data/.hgtags.i delete mode 100644 apps/api/tests/data/clean/.hg/store/fncache delete mode 100644 apps/api/tests/data/clean/.hg/store/undo delete mode 100644 apps/api/tests/data/clean/.hg/undo.branch delete mode 100644 apps/api/tests/data/clean/.hg/undo.dirstate delete mode 100644 apps/api/tests/data/clean/.hgignore delete mode 100644 apps/api/tests/data/clean/.hgtags delete mode 100644 apps/api/tests/data/simple/$meta delete mode 100644 apps/api/tests/data/simple/.hg/00changelog.i delete mode 100644 apps/api/tests/data/simple/.hg/branch delete mode 100644 apps/api/tests/data/simple/.hg/branchheads.cache delete mode 100644 apps/api/tests/data/simple/.hg/dirstate delete mode 100644 apps/api/tests/data/simple/.hg/requires delete mode 100644 apps/api/tests/data/simple/.hg/store/00changelog.i delete mode 100644 apps/api/tests/data/simple/.hg/store/00manifest.i delete mode 100644 apps/api/tests/data/simple/.hg/store/data/$meta.i delete mode 100644 apps/api/tests/data/simple/.hg/store/data/.hgignore.i delete mode 100644 apps/api/tests/data/simple/.hg/store/data/.hgtags.i delete mode 100644 apps/api/tests/data/simple/.hg/store/data/sample.parts.i delete mode 100644 apps/api/tests/data/simple/.hg/store/data/sample.xml.i delete mode 100644 apps/api/tests/data/simple/.hg/store/data/sample__pl.xml.i delete mode 100644 apps/api/tests/data/simple/.hg/store/fncache delete mode 100644 apps/api/tests/data/simple/.hg/store/undo delete mode 100644 apps/api/tests/data/simple/.hg/strip-backup/26716f931d95-backup delete mode 100644 apps/api/tests/data/simple/.hg/undo.branch delete mode 100644 apps/api/tests/data/simple/.hg/undo.dirstate delete mode 100644 apps/api/tests/data/simple/.hgignore delete mode 100644 apps/api/tests/data/simple/.hgtags delete mode 100644 apps/api/urls.py delete mode 100644 apps/api/utils.py delete mode 100755 apps/api/views.py diff --git a/apps/api/__init__.py b/apps/api/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/api/admin.py b/apps/api/admin.py deleted file mode 100644 index 332935d8..00000000 --- a/apps/api/admin.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.contrib import admin - -from api import models - -class PullRequestAdmin(admin.ModelAdmin): - list_display = ('comitter', 'timestamp', 'comment', 'document', 'source_revision') - -admin.site.register(models.PullRequest, PullRequestAdmin) -admin.site.register(models.PartCache) diff --git a/apps/api/forms.py b/apps/api/forms.py deleted file mode 100644 index 22cadb9e..00000000 --- a/apps/api/forms.py +++ /dev/null @@ -1,143 +0,0 @@ -# -*- encoding: utf-8 -*- - -__author__= "Łukasz Rekucki" -__date__ = "$2009-09-20 21:34:52$" -__doc__ = "Micro-forms for the API." - -from django import forms -from api.models import PullRequest -from django.contrib.auth.models import User - -import re -from django.utils import simplejson as json - -class MergeRequestForm(forms.Form): - # should the target document revision be updated or shared - type = forms.ChoiceField(choices=(('update', 'Update'), ('share', 'Share')) ) - - # - # if type == update: - # * user's revision which is the base of the merge - # if type == share: - # * revision which will be pulled to the main branch - # - # NOTE: the revision doesn't have to be owned by the user - # who requests the merge: - # a) Every user can update his branch - # b) Some users can push their changes - # -> if they can't, they leave a PRQ - # c) Some users can pull other people's changes - # d) Some users can update branches owned by special - # users associated with PRQ's - revision = forms.RegexField('[0-9a-f]{40}') - - # any additional comments that user wants to add to the change - message = forms.CharField(required=False) - -class DocumentUploadForm(forms.Form): - ocr_file = forms.FileField(label='Source OCR file', required=False) - ocr_data = forms.CharField(widget=forms.HiddenInput(), required=False) - - bookname = forms.RegexField(regex=r'[0-9\.\w_-]+', \ - label='Publication name', help_text='Example: słowacki__beniowski__pieśń_1') - - generate_dc = forms.BooleanField(required=False, \ - initial=True, label=u"Generate DublinCore template") - - - def clean(self): - clean_data = self.cleaned_data - - ocr_file = clean_data['ocr_file'] - ocr_data = clean_data['ocr_data'] - - if not ocr_file and not ocr_data: - raise forms.ValidationError( - "You must either provide file descriptor or raw data." ) - - return clean_data - -PRQ_USER_RE = re.compile(r"^\$prq-(\d{1,10})$", re.UNICODE) - -class DocumentRetrieveForm(forms.Form): - revision = forms.RegexField(regex=r'latest|[0-9a-z]{40}', required=False) - user = forms.CharField(required=False) - - def clean_user(self): - # why, oh why doesn't django implement this!!! - # value = super(DocumentRetrieveForm, self).clean_user() - value = self.cleaned_data['user'] - - if value.startswith('$'): - # library user (... maybe later) - if value == '$library': - raise forms.ValidationError("Invalid user name '%s'" % value) - - m = PRQ_USER_RE.match(value) - - if m: - try: - return value - except: - raise forms.ValidationError("User doesn't exist.") - raise forms.ValidationError("Invalid user name '%s'" % value) - try: - return value - except: - raise forms.ValidationError("User doesn't exist.") - - -class TextRetrieveForm(DocumentRetrieveForm): - chunk = forms.CharField(required=False) - format = forms.CharField(required=False) - - def clean_format(self): - value = self.cleaned_data['format'] - if not value: - return 'raw' - - if value not in ('nl', 'raw'): - raise forms.ValidationError("Invalid text format") - return value - -class TextUpdateForm(DocumentRetrieveForm): - message = forms.CharField(required=False) - contents = forms.CharField(required=False) - chunks = forms.CharField(required=False) - - format = forms.CharField(required=False) - - def clean_format(self): - value = self.cleaned_data['format'] - if not value: - return 'raw' - - if value not in ('nl', 'raw'): - raise forms.ValidationError("Invalid text format") - return value - - def clean_message(self): - value = self.cleaned_data['message'] - - if value: - return u"$USER$ " + value - else: - return u"$AUTO$ XML content update." - - def clean_chunks(self): - value = self.cleaned_data['chunks'] - - try: - return json.loads(value) - except Exception, e: - forms.ValidationError("Invalid JSON: " + e.message) - - - def clean(self): - if self.cleaned_data['contents'] and self.cleaned_data['chunks']: - raise forms.ValidationError("Pass either contents or chunks - not both ") - - if not self.cleaned_data['contents'] and not self.cleaned_data['chunks']: - raise forms.ValidationError("You must pass contents or chunks.") - - return self.cleaned_data \ No newline at end of file diff --git a/apps/api/handlers/__init__.py b/apps/api/handlers/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/api/handlers/library_handlers.py b/apps/api/handlers/library_handlers.py deleted file mode 100755 index e2d986ee..00000000 --- a/apps/api/handlers/library_handlers.py +++ /dev/null @@ -1,601 +0,0 @@ -# -*- encoding: utf-8 -*- -import os.path - -import logging -log = logging.getLogger('platforma.api.library') - -from piston.handler import BaseHandler, AnonymousBaseHandler -from piston.utils import rc - -from datetime import date - -from django.core.urlresolvers import reverse -from django.db import IntegrityError - -import librarian -import librarian.html -import difflib -import wlrepo - -from explorer.models import GalleryForDocument - -# internal imports -import api.forms as forms -import api.response as response -from api.utils import validate_form, hglibrary, natural_order -from api.models import PartCache, PullRequest - -from pygments import highlight -from pygments.lexers import DiffLexer -from pygments.formatters import HtmlFormatter - -# -import settings - - -def is_prq(username): - return username.startswith('$prq-') - -def prq_for_user(username): - try: - return PullRequest.objects.get(id=int(username[5:])) - except: - return None - -def check_user(request, user): - log.info("user: %r, perm: %r" % (request.user, request.user.get_all_permissions()) ) - #pull request - if is_prq(user): - if not request.user.has_perm('api.view_prq'): - yield response.AccessDenied().django_response({ - 'reason': 'access-denied', - 'message': "You don't have enough priviliges to view pull requests." - }) - # other users - elif request.user.username != user: - if not request.user.has_perm('api.view_other_document'): - yield response.AccessDenied().django_response({ - 'reason': 'access-denied', - 'message': "You don't have enough priviliges to view other people's document." - }) - pass - -# -# Document List Handlers -# -# TODO: security check -class BasicLibraryHandler(AnonymousBaseHandler): - allowed_methods = ('GET',) - - @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() ] - return {'documents' : document_list} - -# -# This handler controlls the document collection -# -class LibraryHandler(BaseHandler): - allowed_methods = ('GET', 'POST') - anonymous = BasicLibraryHandler - - @hglibrary - def read(self, request, lib): - """Return the list of documents.""" - - documents = {} - - for docid in lib.documents(): - documents[docid] = { - 'url': reverse('document_view', args=[docid]), - 'name': docid, - 'parts': [] - } - - parts = PartCache.objects.defer('part_id')\ - .values_list('part_id', 'document_id').distinct() - - document_tree = dict(documents) - - for part, docid in parts: - # this way, we won't display broken links - if not documents.has_key(part): - log.info("NOT FOUND: %s", part) - continue - - parent = documents[docid] - child = documents[part] - - # not top-level anymore - document_tree.pop(part) - parent['parts'].append(child) - - for doc in documents.itervalues(): - doc['parts'].sort(key=natural_order(lambda d: d['name'])) - - return {'documents': sorted(document_tree.itervalues(), - key=natural_order(lambda d: d['name']) ) } - - - @validate_form(forms.DocumentUploadForm, 'POST') - @hglibrary - def create(self, request, form, lib): - """Create a new document.""" - - if form.cleaned_data['ocr_data']: - data = form.cleaned_data['ocr_data'] - else: - data = request.FILES['ocr_file'].read().decode('utf-8') - - if data is None: - return response.BadRequest().django_response('You must pass ocr_data or ocr_file.') - - if form.cleaned_data['generate_dc']: - data = librarian.wrap_text(data, unicode(date.today())) - - docid = form.cleaned_data['bookname'] - - try: - lock = lib.lock() - try: - log.info("DOCID %s", docid) - doc = lib.document_create(docid) - # document created, but no content yet - try: - doc = doc.quickwrite('xml', data.encode('utf-8'), - '$AUTO$ XML data uploaded.', user=request.user.username) - except Exception,e: - import traceback - # rollback branch creation - lib._rollback() - raise wlrepo.LibraryException(traceback.format_exc()) - - url = reverse('document_view', args=[doc.id]) - - return response.EntityCreated().django_response(\ - body = { - 'url': url, - 'name': doc.id, - 'revision': doc.revision }, - url = url ) - finally: - lock.release() - except wlrepo.LibraryException, e: - import traceback - return response.InternalError().django_response({ - "reason": traceback.format_exc() - }) - except wlrepo.DocumentAlreadyExists: - # Document is already there - return response.EntityConflict().django_response({ - "reason": "already-exists", - "message": "Document already exists." % docid - }) - -# -# Document Handlers -# - -class DiffHandler(BaseHandler): - allowed_methods = ('GET',) - - @hglibrary - def read(self, request, docid, lib): - '''Return diff between source_revision and target_revision)''' - revision = request.GET.get('revision') - if not revision: - return '' - source_document = lib.document(docid) - target_document = lib.document_for_revision(revision) - print source_document, target_document - - diff = difflib.unified_diff( - source_document.data('xml').splitlines(True), - target_document.data('xml').splitlines(True), - 'source', - 'target') - - s = ''.join(list(diff)) - return highlight(s, DiffLexer(), HtmlFormatter(cssclass="pastie")) - - -# -# Document Meta Data -# -class DocumentHandler(BaseHandler): - allowed_methods = ('GET', 'PUT') - - @validate_form(forms.DocumentRetrieveForm, 'GET') - @hglibrary - def read(self, request, form, docid, lib): - """Read document's meta data""" - log.info(u"User '%s' wants to edit %s(%s) as %s" % \ - (request.user.username, docid, form.cleaned_data['revision'], form.cleaned_data['user']) ) - - user = form.cleaned_data['user'] or request.user.username - rev = form.cleaned_data['revision'] or 'latest' - - for error in check_user(request, user): - return error - - try: - doc = lib.document(docid, user, rev=rev) - except wlrepo.RevisionMismatch, e: - # the document exists, but the revision is bad - return response.EntityNotFound().django_response({ - 'reason': 'revision-mismatch', - 'message': e.message, - 'docid': docid, - 'user': user, - }) - except wlrepo.RevisionNotFound, e: - # the user doesn't have this document checked out - # or some other weird error occured - # try to do the checkout - try: - if user == request.user.username: - mdoc = lib.document(docid) - doc = mdoc.take(user) - elif is_prq(user): - prq = prq_for_user(user) - # commiter's document - prq_doc = lib.document_for_revision(prq.source_revision) - doc = prq_doc.take(user) - else: - return response.EntityNotFound().django_response({ - 'reason': 'document-not-found', - 'message': e.message, - 'docid': docid, - 'user': user, - }) - except wlrepo.RevisionNotFound, e: - return response.EntityNotFound().django_response({ - 'reason': 'document-not-found', - 'message': e.message, - 'docid': docid, - 'user': user - }) - - return { - 'name': doc.id, - 'user': user, - 'html_url': reverse('dochtml_view', args=[doc.id]), - 'text_url': reverse('doctext_view', args=[doc.id]), - # 'dc_url': reverse('docdc_view', args=[doc.id]), - 'gallery_url': reverse('docgallery_view', args=[doc.id]), - 'merge_url': reverse('docmerge_view', args=[doc.id]), - 'revision': doc.revision, - 'timestamp': doc.revision.timestamp, - # 'public_revision': doc.revision, - # 'public_timestamp': doc.revision.timestamp, - } - - -# @hglibrary -# def update(self, request, docid, lib): -# """Update information about the document, like display not""" -# return -# -# -# -class DocumentHTMLHandler(BaseHandler): - allowed_methods = ('GET') - - @validate_form(forms.DocumentRetrieveForm, 'GET') - @hglibrary - def read(self, request, form, docid, lib, stylesheet='full'): - """Read document as html text""" - try: - revision = form.cleaned_data['revision'] - user = form.cleaned_data['user'] or request.user.username - document = lib.document_for_revision(revision) - - if document.id != docid: - return response.BadRequest().django_response({ - 'reason': 'name-mismatch', - 'message': 'Provided revision is not valid for this document' - }) - - if document.owner != user: - return response.BadRequest().django_response({ - 'reason': 'user-mismatch', - 'message': "Provided revision doesn't belong to user %s" % user - }) - - for error in check_user(request, user): - return error - - return librarian.html.transform(document.data('xml'), is_file=False, \ - parse_dublincore=False, stylesheet=stylesheet,\ - options={ - "with-paths": 'boolean(1)', - }) - - except (wlrepo.EntryNotFound, wlrepo.RevisionNotFound), e: - return response.EntityNotFound().django_response({ - 'reason': 'not-found', 'message': e.message}) - except librarian.ValidationError, e: - return response.InternalError().django_response({ - 'reason': 'xml-non-valid', 'message': e.message or u''}) - except librarian.ParseError, e: - return response.InternalError().django_response({ - 'reason': 'xml-parse-error', 'message': e.message or u'' }) - -# -# Image Gallery -# - -class DocumentGalleryHandler(BaseHandler): - allowed_methods = ('GET', 'POST') - - - def read(self, request, docid): - """Read meta-data about scans for gallery of this document.""" - galleries = [] - from urllib import quote - - for assoc in GalleryForDocument.objects.filter(document=docid): - dirpath = os.path.join(settings.MEDIA_ROOT, assoc.subpath) - - if not os.path.isdir(dirpath): - log.warn(u"[WARNING]: missing gallery %s", dirpath) - continue - - gallery = {'name': assoc.name, 'pages': []} - - for file in os.listdir(dirpath): - if not isinstance(file, unicode): - try: - file = file.decode('utf-8') - except: - log.warn(u"File %r in gallery %r is not unicode. Ommiting."\ - % (file, dirpath) ) - file = None - - if file is not None: - name, ext = os.path.splitext(os.path.basename(file)) - - if ext.lower() not in [u'.png', u'.jpeg', u'.jpg']: - log.warn(u"Ignoring: %s %s", name, ext) - url = None - - url = settings.MEDIA_URL + assoc.subpath + u'/' + file - - if url is None: - url = settings.MEDIA_URL + u'/missing.png' - - gallery['pages'].append( quote(url.encode('utf-8')) ) - - gallery['pages'].sort() - galleries.append(gallery) - - return galleries - - def create(self, request, docid): - if not request.user.is_superuser: - return rc.FORBIDDEN - - new_path = request.POST.get('path') - - if new_path: - gallery, created = GalleryForDocument.objects.get_or_create( - document = docid, - defaults = { - 'subpath': new_path, - } - ) - - if not created: - gallery.subpath = new_path - gallery.save() - - return rc.CREATED - - return rc.BAD_REQUEST - -# -# Dublin Core handlers -# -# @requires librarian -# -#class DocumentDublinCoreHandler(BaseHandler): -# allowed_methods = ('GET', 'POST') -# -# @hglibrary -# def read(self, request, docid, lib): -# """Read document as raw text""" -# try: -# revision = request.GET.get('revision', 'latest') -# -# if revision == 'latest': -# doc = lib.document(docid) -# else: -# doc = lib.document_for_revision(revision) -# -# -# if document.id != docid: -# return response.BadRequest().django_response({'reason': 'name-mismatch', -# 'message': 'Provided revision is not valid for this document'}) -# -# bookinfo = dcparser.BookInfo.from_string(doc.data('xml')) -# return bookinfo.serialize() -# except (EntryNotFound, RevisionNotFound), e: -# return response.EntityNotFound().django_response({ -# 'exception': type(e), 'message': e.message}) -# -# @hglibrary -# def create(self, request, docid, lib): -# try: -# bi_json = request.POST['contents'] -# revision = request.POST['revision'] -# -# if request.POST.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_revision(revision) -# -# if current != orig: -# return response.EntityConflict().django_response({ -# "reason": "out-of-date", -# "provided": orig.revision, -# "latest": current.revision }) -# -# xmldoc = parser.WLDocument.from_string(current.data('xml')) -# document.book_info = dcparser.BookInfo.from_json(bi_json) -# -# # zapisz -# ndoc = current.quickwrite('xml', \ -# document.serialize().encode('utf-8'),\ -# message=msg, user=request.user.username) -# -# try: -# # return the new revision number -# return { -# "document": ndoc.id, -# "subview": "dc", -# "previous_revision": current.revision, -# "revision": ndoc.revision, -# 'timestamp': ndoc.revision.timestamp, -# "url": reverse("docdc_view", args=[ndoc.id]) -# } -# except Exception, e: -# if ndoc: lib._rollback() -# raise e -# except RevisionNotFound: -# return response.EntityNotFound().django_response() - -class MergeHandler(BaseHandler): - allowed_methods = ('POST',) - - @validate_form(forms.MergeRequestForm, 'POST') - @hglibrary - def create(self, request, form, docid, lib): - """Create a new document revision from the information provided by user""" - try: - revision = form.cleaned_data['revision'] - - # fetch the main branch document - doc = lib.document(docid) - - # fetch the base document - user_doc = lib.document_for_revision(revision) - base_doc = user_doc.latest() - - if base_doc != user_doc: - return response.EntityConflict().django_response({ - "reason": "out-of-date", - "provided": str(user_doc.revision), - "latest": str(base_doc.revision) - }) - - if form.cleaned_data['type'] == 'update': - # update is always performed from the file branch - # to the user branch - user_doc_new = base_doc.update(request.user.username) - - if user_doc_new == user_doc: - return response.SuccessAllOk().django_response({ - "result": "no-op" - }) - - # shared document is the same - doc_new = doc - - if form.cleaned_data['type'] == 'share': - if not base_doc.up_to_date(): - return response.BadRequest().django_response({ - "reason": "not-fast-forward", - "message": "You must first update your branch to the latest version." - }) - - anwser, info = base_doc.would_share() - - if not anwser: - return response.SuccessAllOk().django_response({ - "result": "no-op", "message": info - }) - - # check for unresolved conflicts - if base_doc.has_conflict_marks(): - return response.BadRequest().django_response({ - "reason": "unresolved-conflicts", - "message": "There are unresolved conflicts in your file. Fix them, and try again." - }) - - if not request.user.has_perm('api.share_document'): - # User is not permitted to make a merge, right away - # So we instead create a pull request in the database - try: - prq, created = PullRequest.objects.get_or_create( - comitter = request.user, - document = docid, - status = "N", - defaults = { - 'source_revision': str(base_doc.revision), - 'comment': form.cleaned_data['message'] or '$AUTO$ Document shared.', - } - ) - - # there can't be 2 pending request from same user - # for the same document - if not created: - prq.source_revision = str(base_doc.revision) - prq.comment = prq.comment + 'u\n\n' + (form.cleaned_data['message'] or u'') - prq.save() - - return response.RequestAccepted().django_response(\ - ticket_status=prq.status, \ - ticket_uri=reverse("pullrequest_view", args=[prq.id]) ) - except IntegrityError: - return response.EntityConflict().django_response({ - 'reason': 'request-already-exist' - }) - - changed = base_doc.share(form.cleaned_data['message']) - - # update shared version if needed - if changed: - doc_new = doc.latest() - else: - doc_new = doc - - # the user wersion is the same - user_doc_new = base_doc - - # The client can compare parent_revision to revision - # to see if he needs to update user's view - # Same goes for shared view - - return response.SuccessAllOk().django_response({ - "result": "success", - "name": user_doc_new.id, - "user": user_doc_new.owner, - - "revision": user_doc_new.revision, - 'timestamp': user_doc_new.revision.timestamp, - - "parent_revision": user_doc.revision, - "parent_timestamp": user_doc.revision.timestamp, - - "shared_revision": doc_new.revision, - "shared_timestamp": doc_new.revision.timestamp, - - "shared_parent_revision": doc.revision, - "shared_parent_timestamp": doc.revision.timestamp, - }) - except wlrepo.OutdatedException, e: - return response.BadRequest().django_response({ - "reason": "not-fast-forward", - "message": e.message - }) - except wlrepo.LibraryException, e: - return response.InternalError().django_response({ - "reason": "merge-error", - "message": e.message - }) diff --git a/apps/api/handlers/manage_handlers.py b/apps/api/handlers/manage_handlers.py deleted file mode 100644 index f2d41b1b..00000000 --- a/apps/api/handlers/manage_handlers.py +++ /dev/null @@ -1,137 +0,0 @@ -# -*- encoding: utf-8 -*- - -import logging -log = logging.getLogger('platforma.api.manage') - -__author__= "Łukasz Rekucki" -__date__ = "$2009-09-25 15:49:50$" -__doc__ = "Module documentation." - -from piston.handler import BaseHandler -from wlrepo import UpdateException - -from api.utils import hglibrary -from api.models import PullRequest -from api.response import * -import datetime - -class PullRequestListHandler(BaseHandler): - allowed_methods = ('GET',) - - def read(self, request): - if request.user.has_perm('change_pullrequest'): - return PullRequest.objects.all() - else: - return PullRequest.objects.filter(commiter=request.user) - - -class PullRequestHandler(BaseHandler): - allowed_methods = ('GET', 'PUT') - - def read(self, request, prq_id): - return PullRequest.objects.get(id=prq_id) - - def update(self, request, prq_id): - """Change the status of request""" - - if not request.user.has_perm('change_pullrequest'): - return AccessDenied().django_response("Insufficient priviliges") - - prq = PullRequest.objects.get(id=prq_id) - - if not prq: - return EntityNotFound().django_response() - - action = request.PUT.get('action', None) - - if action == 'accept': - return self.accept_merge(request.user, prq) - elif action == 'reject' and prq.status in ['N', 'R']: - return self.reject_merge(request.user, prq) - else: - return BadRequest().django_response() - - - @hglibrary - def accept_merge(self, user, prq, lib): - if prq.status not in ['N', 'E']: - return BadRequest().django_response({ - 'reason': 'invalid-state', - 'message': "This pull request is alredy resolved. Can't accept." - }) - - src_doc = lib.document_for_revision( prq.source_revision ) - - lock = lib.lock() - try: - if not src_doc.up_to_date(): - # This revision is no longer up to date, thus - # it needs to be updated, before push: - # - # Q: where to put the updated revision ? - # A: create a special user branch named prq-#prqid - prq_doc = src_doc.take("$prq-%d" % prq.id) - - # This could be not the first time we try this, - # so the prq_doc could already be there - # and up to date - - try: - prq_doc = prq_doc.update(user.username) - prq.source_revision = str(prq_doc.revision) - src_doc = prq_doc - except UpdateException, e: - # this can happen only if the merge program - # is misconfigured - try returning an entity conflict - # TODO: put some useful infor here - prq.status = 'E' - prq.save() - return EntityConflict().django_response({ - 'reason': 'update-failed', - 'message': e.message }) - - # check if there are conflicts - if src_doc.has_conflict_marks(): - prq.status = 'E' - prq.save() - # Now, the user must resolve the conflict - return EntityConflict().django_response({ - "reason": "unresolved-conflicts", - "message": "There are conflict in the document. Resolve the conflicts retry accepting." - }) - - # So, we have an up-to-date, non-conflicting document - changed = src_doc.share(prq.comment) - - if not changed: - # this is actually very bad, but not critical - log.error("Unsynched pull request: %d" % prq.id) - - # sync state with repository - prq.status = 'A' - prq.merged_revision = str(src_doc.shared().revision) - prq.merged_timestamp = datetime.datetime.now() - prq.save() - - return SuccessAllOk().django_response({ - 'status': prq.status, - 'merged_into': prq.merged_revision, - 'merged_at': prq.merged_timestamp - }) - finally: - lock.release() - - def reject_merge(self, prq, lib): - prq.status = 'R' - prq.save() - - return SuccessAllOk().django_response({ - 'status': prq.status - }) - - - - - - - diff --git a/apps/api/handlers/text_handler.py b/apps/api/handlers/text_handler.py deleted file mode 100755 index ba8bf717..00000000 --- a/apps/api/handlers/text_handler.py +++ /dev/null @@ -1,190 +0,0 @@ -# -*- encoding: utf-8 -*- - -__author__= "Łukasz Rekucki" -__date__ = "$2009-10-19 14:34:42$" -__doc__ = "Module documentation." - -#import api.forms as forms -#import api.response as response -# -#from api.utils import validate_form, hglibrary -#from api.models import PartCache -# - -# -#from piston.handler import BaseHandler -# -#from wlrepo import * - -import re -from library_handlers import * - -import librarian -from librarian import parser - -# -# Document Text View -# - -XINCLUDE_REGEXP = r"""<(?:\w+:)?include\s+[^>]*?href=("|')wlrepo://(?P[^\1]+?)\1\s*[^>]*?>""" -# -# -# - -class DocumentTextHandler(BaseHandler): - allowed_methods = ('GET', 'POST') - - @validate_form(forms.TextRetrieveForm, 'GET') - @hglibrary - def read(self, request, form, docid, lib): - """Read document as raw text""" - try: - revision = form.cleaned_data['revision'] - chunk = form.cleaned_data['chunk'] - user = form.cleaned_data['user'] or request.user.username - format = form.cleaned_data['format'] - - document = lib.document_for_revision(revision) - - if document.id != docid: - return response.BadRequest().django_response({ - 'reason': 'name-mismatch', - 'message': 'Provided revision is not valid for this document' - }) - - if document.owner != user: - return response.BadRequest().django_response({ - 'reason': 'user-mismatch', - 'message': "Provided revision doesn't belong to user %s" % user - }) - - for error in check_user(request, user): - return error - - if not chunk: - return document.data('xml') - - xdoc = parser.WLDocument.from_string(document.data('xml'), parse_dublincore=False) - - xchunk = xdoc.chunk(chunk) - - if xchunk is None: - return response.EntityNotFound().django_response({ - 'reason': 'no-chunk-in-document', - 'path': chunk - }) - - return librarian.serialize_children(xchunk, format=format) - - except librarian.ParseError, e: - return response.EntityNotFound().django_response({ - 'reason': 'invalid-document-state', - 'exception': type(e), - 'message': e.message - }) - except (EntryNotFound, RevisionNotFound), e: - return response.EntityNotFound().django_response({ - 'reason': 'not-found', - 'exception': type(e), 'message': e.message - }) - - @validate_form(forms.TextUpdateForm, 'POST') - @hglibrary - def create(self, request, form, docid, lib): - lock = lib.lock(); - try: - revision = form.cleaned_data['revision'] - msg = form.cleaned_data['message'] - user = form.cleaned_data['user'] or request.user.username - - # do not allow changing not owned documents - # (for now... ) - - if user != request.user.username: - return response.AccessDenied().django_response({ - 'reason': 'insufficient-priviliges', - }) - - current = lib.document(docid, user) - orig = lib.document_for_revision(revision) - - if current != orig: - return response.EntityConflict().django_response({ - "reason": "out-of-date", - "provided_revision": orig.revision, - "latest_revision": current.revision }) - - if form.cleaned_data['contents']: - data = form.cleaned_data['contents'] - else: - chunks = form.cleaned_data['chunks'] - data = current.data('xml') - log.info(data[:600]) - log.info(chunks) - - xdoc = parser.WLDocument.from_string(data, parse_dublincore=False) - errors = xdoc.merge_chunks(chunks) - - if len(errors): - return response.EntityConflict().django_response({ - "reason": "invalid-chunks", - "message": "Unable to merge following parts into the document: %s " % ",".join(errors) - }) - - data = xdoc.serialize() - - - # try to find any Xinclude tags - includes = [m.groupdict()['link'] for m in (re.finditer(\ - XINCLUDE_REGEXP, data, flags=re.UNICODE) or []) ] - - log.info("INCLUDES: %s", includes) - - # TODO: provide useful routines to make this simpler - def xml_update_action(lib, resolve): - try: - f = lib._fileopen(resolve('parts'), 'r') - stored_includes = json.loads(f.read()) - f.close() - except: - stored_includes = [] - - if stored_includes != includes: - f = lib._fileopen(resolve('parts'), 'w+') - f.write(json.dumps(includes)) - f.close() - - lib._fileadd(resolve('parts')) - - # update the parts cache - PartCache.update_cache(docid, current.owner,\ - stored_includes, includes) - - # now that the parts are ok, write xml - f = lib._fileopen(resolve('xml'), 'w+') - f.write(data.encode('utf-8')) - f.close() - - ndoc = None - ndoc = current.invoke_and_commit(\ - xml_update_action, lambda d: (msg, user) ) - - try: - # return the new revision number - return response.SuccessAllOk().django_response({ - "document": ndoc.id, - "user": user, - "subview": "xml", - "previous_revision": current.revision, - "revision": ndoc.revision, - 'timestamp': ndoc.revision.timestamp, - "url": reverse("doctext_view", args=[ndoc.id]) - }) - except Exception, e: - if ndoc: lib._rollback() - raise e - except RevisionNotFound, e: - return response.EntityNotFound(mimetype="text/plain").\ - django_response(e.message) - finally: - lock.release() diff --git a/apps/api/handlers/toolbar_handlers.py b/apps/api/handlers/toolbar_handlers.py deleted file mode 100644 index 09a70856..00000000 --- a/apps/api/handlers/toolbar_handlers.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- encoding: utf-8 -*- - -import logging -log = logging.getLogger('platforma.api.toolbar') - -__author__= "Łukasz Rekucki" -__date__ = "$2009-09-25 15:55:33$" -__doc__ = "Module documentation." - -from django.views.generic.simple import direct_to_template -from piston.handler import BaseHandler -from piston.utils import rc - -import settings - -import toolbar.models - -class ToolbarHandler(BaseHandler): - allowed_methods = ('GET',) - - def read(self, request): - groups = toolbar.models.ButtonGroup.objects.all() - return [g.to_dict(with_buttons=True) for g in groups] - -class ScriptletsHandler(BaseHandler): - allowed_methods = ('GET',) - - def read(self, request): - scriptlets = toolbar.models.Scriptlet.objects.all() - - return direct_to_template(request, 'toolbar_api/scriptlets.js', - extra_context = {'scriptlets': scriptlets }, - mimetype='text/javascript' ) \ No newline at end of file diff --git a/apps/api/management/__init__.py b/apps/api/management/__init__.py deleted file mode 100644 index 5ff26b2a..00000000 --- a/apps/api/management/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# To change this template, choose Tools | Templates -# and open the template in the editor. - - diff --git a/apps/api/management/commands/__init__.py b/apps/api/management/commands/__init__.py deleted file mode 100644 index 5ff26b2a..00000000 --- a/apps/api/management/commands/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# To change this template, choose Tools | Templates -# and open the template in the editor. - - diff --git a/apps/api/management/commands/doc_share.py b/apps/api/management/commands/doc_share.py deleted file mode 100644 index 08d61fe1..00000000 --- a/apps/api/management/commands/doc_share.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -# -*- conding: utf-8 -*- -__author__="lreqc" -__date__ ="$2009-09-08 14:31:26$" - -from django.core.management.base import NoArgsCommand - -# from optparse import make_option - -class Command(NoArgsCommand): - - def handle(self, *args, **options): - client = Client() - if not options['username'] or not options['password']: - raise CommandError("You must provide login data") - - client.login(username=options['username'], \ - password=options['password']) - - print options['username'], options['password'] - - docid = args[0] - - url = reverse("document_view", args=[docid]) - print "Quering %s" % url - resp = client.get(url) - - result = json.loads(resp.content) - print result - - print "Current revision for '%s': %s" % (docid, result['user_revision']) - url = reverse("docmerge_view", args=[docid]) - print "Sending POST to %s" % url - resp = client.post(url, { - 'type': 'share', - 'target_revision': result['user_revision'], - 'message': 'Sharing.. :)' - }) - - print resp.status_code, resp.content diff --git a/apps/api/management/commands/doc_update.py b/apps/api/management/commands/doc_update.py deleted file mode 100644 index 525bb4a8..00000000 --- a/apps/api/management/commands/doc_update.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -# -*- conding: utf-8 -*- -__author__="lreqc" -__date__ ="$2009-09-08 14:31:26$" - -from django.core.management.base import BaseCommand -from django.utils import simplejson as json -from django.test.client import Client -from django.core.urlresolvers import reverse - -from optparse import make_option - -class Command(BaseCommand): - - option_list = BaseCommand.option_list + ( - make_option('-u', '--user', action='store', dest='username'), - make_option('-p', '--password', action='store', dest='password'), - ) - - def handle(self, *args, **options): - client = Client() - if not options['username'] or not options['password']: - raise CommandError("You must provide login data") - - client.login(username=options['username'], \ - password=options['password']) - - print options['username'], options['password'] - - filename = args[0] - docid = args[1] - - url = reverse("document_view", args=[docid]) - print "Quering %s" % url - resp = client.get(url) - - result = json.loads(resp.content) - print result - - print "Current revision for '%s': %s" % (docid, result['user_revision']) - url = reverse("doctext_view", args=[ docid, result['user_revision'] ]) - print "Sending PUT to %s" % url - - resp = client.put(url, { - 'contents': open(filename).read(), - 'message': options['message'] or '' - - }) - - print resp.status_code, resp.content diff --git a/apps/api/management/commands/doc_update_text.py b/apps/api/management/commands/doc_update_text.py deleted file mode 100644 index 167db64f..00000000 --- a/apps/api/management/commands/doc_update_text.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -# -*- conding: utf-8 -*- -__author__="lreqc" -__date__ ="$2009-09-08 14:31:26$" - -from django.core.management.base import BaseCommand -from django.utils import simplejson as json -from django.test.client import Client -from django.core.urlresolvers import reverse - -from optparse import make_option - -class Command(BaseCommand): - - option_list = BaseCommand.option_list + ( - make_option('-u', '--user', action='store', dest='username'), - make_option('-p', '--password', action='store', dest='password'), - make_option('-m', '--message', action='store', dest='message'), - ) - - def handle(self, *args, **options): - client = Client() - if not options['username'] or not options['password']: - raise CommandError("You must provide login data") - - client.login(username=options['username'], \ - password=options['password']) - - print options['username'], options['password'] - - filename = args[0] - docid = args[1] - - url = reverse("document_view", args=[docid]) - print "Quering %s" % url - resp = client.get(url) - - result = json.loads(resp.content) - print result - - print "Current revision for '%s': %s" % (docid, result['user_revision']) - url = reverse("doctext_view", args=[ docid, result['user_revision'] ]) - print "Sending PUT to %s" % url - - resp = client.put(url, { - 'contents': open(filename).read(), - 'message': options['message'] or '' - - }) - - print resp.status_code, resp.content diff --git a/apps/api/management/commands/doc_upload.py b/apps/api/management/commands/doc_upload.py deleted file mode 100644 index 414c4746..00000000 --- a/apps/api/management/commands/doc_upload.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# -*- conding: utf-8 -*- -__author__="lreqc" -__date__ ="$2009-09-08 14:31:26$" - -from django.core.management.base import BaseCommand -from django.utils import simplejson as json -from django.test.client import Client -from django.core.urlresolvers import reverse - -from optparse import make_option - -class Command(BaseCommand): - - option_list = BaseCommand.option_list + ( - make_option('-u', '--user', action='store', dest='username'), - make_option('-p', '--password', action='store', dest='password'), - make_option('-d', '--dublin-core', action='store_true', dest='dc'), - ) - - def handle(self, *args, **options): - client = Client() - if not options['username'] or not options['password']: - raise CommandError("You must provide login data") - - client.login(username=options['username'], \ - password=options['password']) - - print options['username'], options['password'] - - filename = args[0] - bookname = args[1] - - print "Uploading '%s' as document '%s'" % (filename, bookname) - print options['dc'] - print "With DC template" if options['dc'] is not None else "" - - print client.post( reverse("document_list_view"),\ - { - 'bookname': bookname, - 'ocr_file': open(filename), - 'generate_dc': options['dc'] or False } ) - diff --git a/apps/api/models.py b/apps/api/models.py deleted file mode 100644 index cd9a3958..00000000 --- a/apps/api/models.py +++ /dev/null @@ -1,73 +0,0 @@ -from django.db import models -from django.contrib.auth.models import User - -# Create your models here. -class PartCache(models.Model): - document_id = models.CharField(max_length=255) - user_id = models.CharField(max_length=64, blank=True) - part_id = models.CharField(max_length=255) - - @classmethod - def update_cache(me, docid, userid, old, new): - old = set(old) - new = set(new) - - related = me.objects.filter(user_id=userid, document_id=docid) - - missing = old.difference(new) - related.filter(part_id__in=missing).delete() - - created = new.difference(old) - - for part in created: - me.objects.create(user_id=userid, document_id=docid, part_id=part) - - -class PullRequest(models.Model): - REQUEST_STATUSES = { - "N": "New", - "E": "Edited/Conflicting", - "R": "Rejected", - "A": "Accepted & merged", - } - - comitter = models.ForeignKey(User) # the user who request the pull - comment = models.TextField() # addtional comments to the request - - timestamp = models.DateTimeField(auto_now_add=True) - - # document to merge - document = models.CharField(max_length=255) - - # revision to be merged into the main branch - source_revision = models.CharField(max_length=40, unique=True) - target_revision = models.CharField(max_length=40, blank=True) - - # current status - status = models.CharField(max_length=1, choices=REQUEST_STATUSES.items()) - - # comment to the status change of request (if applicable) - response_comment = models.TextField(blank=True) - - # revision number in which the changes were merged (if any) - merged_revision = models.CharField(max_length=40, blank=True, null=True) - merge_timestamp = models.DateTimeField(blank=True, null=True) - - def __unicode__(self): - return unicode(self.comitter) + u':' + self.document - - - class Meta: - permissions = ( - ("view_prq", "Can view pull request's contents."), - ) - - -# This is actually an abstract Model, but if we declare -# it as so Django ignores the permissions :( -class Document(models.Model): - class Meta: - permissions = ( - ("share_document", "Can share documents without pull requests."), - ("view_other_document", "Can view other's documents."), - ) \ No newline at end of file diff --git a/apps/api/resources.py b/apps/api/resources.py deleted file mode 100644 index fbdb89b8..00000000 --- a/apps/api/resources.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- encoding: utf-8 -*- - -__author__= "Łukasz Rekucki" -__date__ = "$2009-09-25 15:53:00$" -__doc__ = "Module documentation." - -from piston.resource import Resource -from api.utils import DjangoAuth - -authdata = {'authentication': DjangoAuth()} - -# -# Library resources -# - -import api.handlers.library_handlers as dh -from api.handlers.text_handler import DocumentTextHandler - -library_resource = Resource(dh.LibraryHandler, **authdata) -document_resource = Resource(dh.DocumentHandler, **authdata) -document_text_resource = Resource(DocumentTextHandler, **authdata) -document_html_resource = Resource(dh.DocumentHTMLHandler, **authdata) -# document_dc_resource = Resource(dh.DocumentDublinCoreHandler, **authdata) -document_gallery = Resource(dh.DocumentGalleryHandler, **authdata) -document_merge = Resource(dh.MergeHandler, **authdata) -diff_resource = Resource(dh.DiffHandler, **authdata) - -import api.handlers.manage_handlers as mh - -pullrequest_collection = Resource(mh.PullRequestListHandler, **authdata) -pullrequest_rsrc = Resource(mh.PullRequestHandler, **authdata) - -# -# Toolbar resources -# -import api.handlers.toolbar_handlers as th -toolbar_buttons = Resource(th.ToolbarHandler, **authdata) -scriptlets = Resource(th.ScriptletsHandler, **authdata) - - - -__all__ = [ - 'library_resource', - 'document_resource', - 'document_text_resource', - 'document_html_resource', -# 'document_dc_resource', - 'document_gallery', - 'document_merge', - 'toolbar_buttons', - 'scriptlets', - 'pullrequest_collection', - 'pullrequest_rsrc', - 'diff_resource', -] \ No newline at end of file diff --git a/apps/api/response.py b/apps/api/response.py deleted file mode 100644 index 46750158..00000000 --- a/apps/api/response.py +++ /dev/null @@ -1,107 +0,0 @@ -# -*- encoding: utf-8 -*- - -__author__= "Łukasz Rekucki" -__date__ = "$2009-09-26 00:32:18$" -__doc__ = "Extensible HTTP Responses." - -from django.http import HttpResponse -from django.utils import simplejson as json - -MIME_PLAIN = 'text/plain' -MIME_JSON = 'application/json' - -class ResponseObject(object): - - def __init__(self, code, mimetype=MIME_JSON): - self._code = code - self._mime = mimetype - - def django_response(self, body=None): - if body is None: - data = '' - elif self._mime == MIME_JSON: - data = json.dumps(body, default=lambda o: repr(o) ) - else: - # data = u"%s\n%s" % (self.MESSAGE, unicode(body)) - data = unicode(body).encode('utf-8') - - return HttpResponse(content=data, status=self._code, \ - content_type=self._mime+'; charset=utf-8' ) - -class SuccessAllOk(ResponseObject): - def __init__(self, **kwargs): - ResponseObject.__init__(self, 200, **kwargs) - -class EntityCreated(ResponseObject): - - def __init__(self, **kwargs): - ResponseObject.__init__(self, 201, **kwargs) - - def django_response(self, url, body): - response = ResponseObject.django_response(self, body) - response['Location'] = url - return response - -class RequestAccepted(ResponseObject): - - def __init__(self, **kwargs): - ResponseObject.__init__(self, 202, **kwargs) - - def django_response(self, ticket_status, ticket_uri): - return ResponseObject.django_response(self, { - 'result': 'accepted', - 'status': ticket_status, - 'refer_to': ticket_uri }) - -class SuccessNoContent(ResponseObject): - - def __init__(self, **kwargs): - ResponseObject.__init__(self, 204, **kwargs) - - def django_response(self): - return ResponseObject.django_response(self, body=None) - -# -# Client errors -# - -class BadRequest(ResponseObject): - - def __init__(self, **kwargs): - ResponseObject.__init__(self, 400, **kwargs) - -class AccessDenied(ResponseObject): - - def __init__(self, **kwargs): - ResponseObject.__init__(self, 403, **kwargs) - - -class EntityNotFound(ResponseObject): - - def __init__(self, **kwargs): - ResponseObject.__init__(self, 404, **kwargs) - -class EntityGone(ResponseObject): - - def __init__(self, **kwargs): - ResponseObject.__init__(self, 410, **kwargs) - - -class EntityConflict(ResponseObject): - - def __init__(self, **kwargs): - ResponseObject.__init__(self, 409, **kwargs) - - -# -# Server side errors -# -class InternalError(ResponseObject): - - def __init__(self, **kwargs): - ResponseObject.__init__(self, 500, **kwargs) - -class NotImplemented(ResponseObject): - - def __init__(self, **kwargs): - ResponseObject.__init__(self, 501, **kwargs) \ No newline at end of file diff --git a/apps/api/tests/__init__.py b/apps/api/tests/__init__.py deleted file mode 100644 index c5d79e70..00000000 --- a/apps/api/tests/__init__.py +++ /dev/null @@ -1,181 +0,0 @@ -from django.test import TestCase -from django.test.client import Client -from django.core.urlresolvers import reverse - -from django.utils import simplejson as json - -from django.contrib.auth.models import User - -import settings -from os.path import join, dirname -from StringIO import StringIO -import shutil -import tempfile - -REPO_TEMPLATES = join(dirname(__file__), 'data') - -def temprepo(name): - from functools import wraps - - def decorator(func): - - - @wraps(func) - def decorated(self, *args, **kwargs): - clean = False - try: - temp = tempfile.mkdtemp("-test", func.__name__) - shutil.copytree(join(REPO_TEMPLATES, name), join(temp, 'repo'), False) - settings.REPOSITORY_PATH = join(temp, 'repo') - func(self, *args, **kwargs) - clean = True - finally: - if not clean and self.response: - print "RESULT", func.__name__, ">>>" - print self.response.content - print "<<<" - else: - shutil.rmtree(temp, True) - - settings.REPOSITORY_PATH = '' - - return decorated - return decorator - - -class SimpleTest(TestCase): - - def setUp(self): - self.response = None - u = User.objects.create_user('admin', 'test@localhost', 'admin') - u.save() - - @temprepo('clean') - def test_documents_get_anonymous(self): - self.response = self.client.get( reverse("document_list_view") ) - self.assert_json_response({ - u'documents': [], - }) - - @temprepo('clean') - def test_documents_get_with_login(self): - self.assertTrue(self.client.login(username='admin', password='admin')) - - self.response = self.client.get( reverse("document_list_view") ) - self.assert_json_response({ - u'documents': [], - }) - - @temprepo('clean') - def test_document_creation(self): - self.assertTrue(self.client.login(username='admin', password='admin')) - - infile = tempfile.NamedTemporaryFile("w+") - infile.write('012340123456789') - infile.flush() - infile.seek(0) - - self.response = self.client.post( reverse("document_list_view"), - data = { - 'bookname': 'testbook', - 'ocr_file': infile, - 'generate_dc': False, - }) - - 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(r['url']) - - result = self.assert_json_response({ - u'public_revision': created_rev, - # u'size': 15, - }) - - - @temprepo('simple') - def test_document_meta_get_with_login(self): - self.assertTrue(self.client.login(username='admin', password='admin')) - - 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'parts': []}, - {u'url': u'/api/documents/sample_pl', u'name': u'sample_pl', u'parts': []} - ], - }) - - self.response = self.client.get( \ - reverse("document_view", args=['sample']) ) - - 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'parts_url': reverse("docparts_view", args=[u'sample']), - u'name': u'sample', - # u'size': 20, - }) - - - @temprepo('simple') - def test_document_text_with_login(self): - self.assertTrue(self.client.login(username='admin', password='admin')) - - self.response = self.client.get( \ - reverse("document_view", args=['sample']) ) - - 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'parts_url': reverse("docparts_view", args=[u'sample']), - u'name': u'sample', - # u'size': 20, - }) - - self.response = self.client.get(resp['text_url']) - self.assertEqual(self.response.status_code, 200) - self.assertEqual(self.response.content, "Ala ma kota\n") - - - @temprepo('simple') - def test_document_text_save(self): - self.assertTrue(self.client.login(username='admin', password='admin')) - TEXT = u"Ala ma kota i psa" - - self.response = self.client.get( - reverse("document_view", args=['sample']) ) - - resp = self.assert_json_response() - - self.response = self.client.post(resp['text_url'], { - 'revision': resp['user_revision'] ,'contents': TEXT }) - result = self.assert_json_response(must_have={u'document': u'sample'} ) - - #self.response = self.client.get(result['url']) - #self.assertEqual(self.response.content, TEXT) - - 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(): - self.assertTrue(result.has_key(k), "Required field '%s' missing in response." % k) - self.assertEqual(result[k], v) - - if exclude is True: - for (k,v) in result.items(): - self.assertTrue(must_have.has_key(k)) - self.assertEqual(must_have[k], v) - - for key in exclude: - self.assertFalse(result.has_key(key)) - - return result - \ No newline at end of file diff --git a/apps/api/tests/data/clean/$meta b/apps/api/tests/data/clean/$meta deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/api/tests/data/clean/.hg/00changelog.i b/apps/api/tests/data/clean/.hg/00changelog.i deleted file mode 100644 index d3a8311050e54c57c5be7cfe169e60a95768812c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmWN_K?=Yi3?y-?PXk>vwV8^xM|9{(@34H6XfTj_ff*zQYPU z3f z%l3+1dS!@iNcrBjpX*ODv|oJwB#q~??ZT}E)u$Bnl!2ihho(aITiwj9-UGK|OO$NsD=BviZ!eEAzh}f4U^!a&Df)zsi^<;gZa>PbR`kRTf#Y zYXz6atgebQF)otH{bP9b&YXV5^yz9X>7P0tuin0R%U`KZOWu8Ny$)pU_#Afa*3LIv ar^}mfoxW9aomnNTWQmUAGI_QvtkqQ=;^b`$9s!`x1o>cqf4hf&ikA{t?#88 z$IakAL)sB+8pv)223sJW3}!JvTm^E3;RW&8NttQocV9GT`ADC6bpOSMtxz>E3gl`K zHq^^VFG)-n|w9fCc0+Ff=X!iF@t1QVOIv^)k{k)ARC+QWHU< d|DgcLWCzNWB&HW5bnNMW0u*Oe$xSUu1OT6)BANgI diff --git a/apps/api/tests/data/clean/.hgignore b/apps/api/tests/data/clean/.hgignore deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/api/tests/data/clean/.hgtags b/apps/api/tests/data/clean/.hgtags deleted file mode 100644 index 0b061138..00000000 --- a/apps/api/tests/data/clean/.hgtags +++ /dev/null @@ -1 +0,0 @@ -d5e516ebd357bca24c6afb737e97db3b3d4f111a $branchbase diff --git a/apps/api/tests/data/simple/$meta b/apps/api/tests/data/simple/$meta deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/api/tests/data/simple/.hg/00changelog.i b/apps/api/tests/data/simple/.hg/00changelog.i deleted file mode 100644 index d3a8311050e54c57c5be7cfe169e60a95768812c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmWN_K?=Yi3n)ruZ_#X`Er;_rpAaae|J!Z0Sm}uU}#(d68G9OK>Sd&7rsw4sr2=Ib Z82&>6kjV~|DM?H(M%TfrlABtR2mmQ-Ag}-c diff --git a/apps/api/tests/data/simple/.hg/requires b/apps/api/tests/data/simple/.hg/requires deleted file mode 100644 index 5175383b..00000000 --- a/apps/api/tests/data/simple/.hg/requires +++ /dev/null @@ -1,3 +0,0 @@ -revlogv1 -store -fncache diff --git a/apps/api/tests/data/simple/.hg/store/00changelog.i b/apps/api/tests/data/simple/.hg/store/00changelog.i deleted file mode 100644 index f2451544100dace4441a882fc545eacf3724a8ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 759 zcmZQzWME`~fC3;@3S~3=hk~n5#a>?y-?PXk>vwV8^xM|9{(@34H6XfTj_ff*zQYPU z3f z%l3+1dS!@iNcrBjpX*ODv|oJwB#q~??ZT}E)u$Bnl!2igh^K*B3=ltnoXqfKRla}lX90yLmmh@B*?iy^_k}R18ZcEc$MnoW zu0sYqY!BvsXNvRw_{cGHQ%-KXI{!ZR>svHR*5&Zk)L6GS_RpW+5^^=)Px^pC#D$5? z;cGZ&^332n`0AL;>KK(E*~IASkE0)UYbV^6=;iX5$M=G{~IzZ2E(>*t-(yiy&r zY4-io`)iJE`THoU;4Cr+W*w W>tkqQ=;^b`$9s!`x1o>cqf4hf&ikA{t?#88 z$IakAL)sB+8pv)2hBP2P17{)!0gqead6{KiP4%~Bj`y8xaBw?c|p6JIdPN))z*NGxD3j zR;>V&&OmY|n8m=r1foH%R55;RHo0celQ*jyW^!y;$@-vcu`E;#i~@x%$am(&iMa(i zsqqCldKI}j3>Fr~hAC#IDM_Y=mX_v8hK7cTCZ>soNlB(@CT597X_i1KW78yaF0eVT z!DJ$koD9UwPz-j0;RW&8NttQocV9GT`ADC6bpOSMtzgwqqGHa})4K(M0mTrI^=!d2 zl{NE^PgK3_Iq`|!hX01l>+5eXkKbp~{ANys&&?SI6%6u+=6O6>%V+L+X4|{t@sb>SBa?WTXMFz6hZ>OpIxEcl diff --git a/apps/api/tests/data/simple/.hg/store/data/$meta.i b/apps/api/tests/data/simple/.hg/store/data/$meta.i deleted file mode 100644 index 2431023a17b1e7b5c96e5db8e264c251f39beab0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64 ncmZQzWME{#0{%n64&J91ElOJ*kA4VBT`S_X^HQPrLGn6+WD*P;%#K*02?S1MgRZ+ diff --git a/apps/api/tests/data/simple/.hg/store/data/sample.xml.i b/apps/api/tests/data/simple/.hg/store/data/sample.xml.i deleted file mode 100644 index b115f4d6a1758906cc71810e4f01acc727168502..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77 zcmZQzWME`~0A3))17$P*hXSUK9INyt-MW=IVP04Bx9fz}gnojm0aK-pIf)9ni3-{I HC5c=B-e46M diff --git a/apps/api/tests/data/simple/.hg/store/data/sample__pl.xml.i b/apps/api/tests/data/simple/.hg/store/data/sample__pl.xml.i deleted file mode 100644 index 920c5742f9629791a8e8bb72c9d49dd40bc7762d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79 zcmZQzWME`~0Dd6F2W2z;hk_2nYq47;r*9No5+%L$OP2oB+os#0YQR*f`_VnA=|}e* L-h8wvJCO?jQ*#;t diff --git a/apps/api/tests/data/simple/.hg/store/fncache b/apps/api/tests/data/simple/.hg/store/fncache deleted file mode 100644 index c228181f..00000000 --- a/apps/api/tests/data/simple/.hg/store/fncache +++ /dev/null @@ -1,6 +0,0 @@ -data/$meta.i -data/.hgignore.i -data/sample.parts.i -data/sample.xml.i -data/sample_pl.xml.i -data/.hgtags.i diff --git a/apps/api/tests/data/simple/.hg/store/undo b/apps/api/tests/data/simple/.hg/store/undo deleted file mode 100644 index 3ad8b3279b620f5cbc3057b2b6d10eb99f0b26f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52 zcmYdEEJ@VY%SbOtOfS~UWH8_|Fvv~J%S=lxE&*~)jLd-o$r*`x>8UyS=|B-vGZQWV Du}=?# diff --git a/apps/api/tests/data/simple/.hg/strip-backup/26716f931d95-backup b/apps/api/tests/data/simple/.hg/strip-backup/26716f931d95-backup deleted file mode 100644 index 424de921845804ff60f1d4facdde7f2b0d54ca0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 867 zcmV-p1DyOwM=>x$T4*^jL0KkKSu2faxBvhdfB(<K$&&y7|Ml@yI_%v) z>W12qZ`n><{5n7aO(91y2~<|M$}|!noTqs9-0KudU~drG&F3Z z$^oGDJtw4O2c&7~lpaaFDeX@xAZTRMO))YCni(1yG|Qq*S-H>#6|h%&|P?W6ap&LK~E!F+v$UntzorO1egr!-PGmONx_zMU@NkuMAb~dBKT;fRNcS|o(iVP`9 zkg_%2#ha63B5#A)m>4u^|6Hv315#uQSPzHzdkMB}#l9~H8gjqE8SJFcfp`GXfpOZ# z3sY3-$l_v4o+^ZzDQzI-n-@d93@TG-VO*xP6@$++CtqwzLC8(sSxuhHGrp>rQ< zn_db;hd~}@j%>i(1;pwM1x3Dd_}_Sx7_oj3r`Ljdax~`VV>SGvJL^)QV5wiLZK{}S z#bQ=Mt3zIYk#7|pYBxCOK(j`vmw?La#CVn0Gb|x9q`kY}+`q+SmT82(Li@nLwb;)1 tc>VWXDRq>?a4G>yCU6Sq1E3UMEdFP9{|K>&Cr*Ehxgwk>NEODj+yEVTdWrx5 diff --git a/apps/api/tests/data/simple/.hg/undo.branch b/apps/api/tests/data/simple/.hg/undo.branch deleted file mode 100644 index 331d858c..00000000 --- a/apps/api/tests/data/simple/.hg/undo.branch +++ /dev/null @@ -1 +0,0 @@ -default \ No newline at end of file diff --git a/apps/api/tests/data/simple/.hg/undo.dirstate b/apps/api/tests/data/simple/.hg/undo.dirstate deleted file mode 100644 index 310d94169efd6c83346d29be1d8fcd225cf795a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112 zcmccmRP6QT@I8xsvVIrWO}}kz>n|w9fCc0+Ff=X!iF@stpa7&e^)k{k)ARC+QWHU< Y|DgcLWCzNWB&HXm>tI#MO)W_T0Cd|SLI3~& diff --git a/apps/api/tests/data/simple/.hgignore b/apps/api/tests/data/simple/.hgignore deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/api/tests/data/simple/.hgtags b/apps/api/tests/data/simple/.hgtags deleted file mode 100644 index 0b061138..00000000 --- a/apps/api/tests/data/simple/.hgtags +++ /dev/null @@ -1 +0,0 @@ -d5e516ebd357bca24c6afb737e97db3b3d4f111a $branchbase diff --git a/apps/api/urls.py b/apps/api/urls.py deleted file mode 100644 index 52b0ccc5..00000000 --- a/apps/api/urls.py +++ /dev/null @@ -1,90 +0,0 @@ -__author__="lreqc" -__date__ ="$2009-09-17 16:16:54$" - -from django.conf.urls.defaults import * -from api.resources import * - -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'}), -# url(r'^hello\.(?P.+)$', hello_resource), - - # HTML Renderer service - url(r'^render$', 'api.views.render'), - - # Toolbar - url(r'^toolbar/buttons$', toolbar_buttons, {'emitter_format': 'json'}, - name="toolbar_buttons" - ), - - url(r'^toolbar/scriptlets$', scriptlets, {'emitter_format': 'json'}, - name="toolbar_scriptlets" - ), - - # Pull requests - url(r"^pull-requests$", pullrequest_collection, - {'emitter_format': 'json'}, name="pullrequest_list" ), - - url(r"^pull-requests/(?P\d+)$", pullrequest_rsrc, - {'emitter_format': 'json'}, name="pullrequest_view" ), - - # Documents - url(r'^documents$', library_resource, - {'emitter_format': 'json'}, name="document_list_view"), - - url(urlpath(r'documents'), library_resource, - name="document_list_view_withformat"), - - #url(urlpath(r'documents', DOC), - # document_resource, name="document_view_withformat"), - - url(urlpath(r'documents', DOC, format=False), - document_resource, {'emitter_format': 'json'}, - name="document_view"), - - url(urlpath(r'documents', DOC, 'gallery', format=False), - document_gallery, {'emitter_format': 'json'}, - name="docgallery_view"), - - # XML - url(urlpath(r'documents', DOC, 'text', format=False), - document_text_resource, {'emitter_format': 'raw'}, - name="doctext_view"), - - # HTML - url(urlpath(r'documents', DOC, 'html', format=False), - document_html_resource, {'emitter_format': 'raw'}, - name="dochtml_view"), - - # DC - #url(urlpath(r'documents', DOC, 'dc'), - # document_dc_resource, - # name="docdc_view_withformat"), - -# url(urlpath(r'documents', DOC, 'dc', format=False), -# document_dc_resource, {'emitter_format': 'json'}, -# name="docdc_view"), - - # MERGE - url(urlpath(r'documents', DOC, 'revision', format=False), - document_merge, {'emitter_format': 'json'}, name="docmerge_view"), - - url(r'documents/(?P[^/]+)/diff$', - diff_resource, {'emitter_format': 'raw'}, name="diff_resource"), - - -# 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 deleted file mode 100644 index 19309ff6..00000000 --- a/apps/api/utils.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- encoding: utf-8 -*- - -__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 - -import wlrepo -import settings - -class TextEmitter(Emitter): - def render(self, request): - return unicode(self.construct()) - -Emitter.register('raw', TextEmitter, 'text/plain; charset=utf-8') -Emitter.register('rawhtml', TextEmitter, 'text/html; charset=utf-8') -Emitter.register('rawxml', TextEmitter, 'application/xml; charset=utf-8') - -class DjangoAuth(object): - - def is_authenticated(self, request): - return request.user.is_authenticated() - - def challenge(self): - 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': str(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 = wlrepo.open_library(settings.REPOSITORY_PATH, 'hg') - kwargs['lib'] = l - return func(self, *args, **kwargs) - return decorated - - - -import re -import locale - -NAT_EXPR = re.compile(r'(\d+)', re.LOCALE | re.UNICODE) -def natural_order(get_key=lambda x: x): - - def getter(key): - nkey = get_key(key) - if not isinstance(nkey, unicode): - ukey = nkey.decode('utf-8') - else: - ukey = nkey - - parts = enumerate( NAT_EXPR.split(ukey)) - return [int(x) if n%2 else locale.strxfrm(x.encode('utf-8')) for (n,x) in parts ] - - return getter - diff --git a/apps/api/views.py b/apps/api/views.py deleted file mode 100755 index 3e9cb015..00000000 --- a/apps/api/views.py +++ /dev/null @@ -1,38 +0,0 @@ -# Create your views here. - -import logging -log = logging.getLogger('platforma.render') - -from django.http import HttpResponse -import librarian -from librarian import html -from lxml import etree -from StringIO import StringIO -import re - -LINE_SWAP_EXPR = re.compile(r'/\s', re.MULTILINE | re.UNICODE); - -def render(request): - style_filename = html.get_stylesheet('partial') - - data = request.POST['fragment'] - path = request.POST['chunk'] - - base, me = path.rsplit('/', 1) - match = re.match(r'([^\[]+)\[(\d+)\]', me) - tag, pos = match.groups() - - style = etree.parse(style_filename) - - data = u'<%s>%s' % (tag, LINE_SWAP_EXPR.sub(u'
\n', data), tag) - log.info(data) - doc = etree.parse( StringIO(data) ) - - opts = { - 'with-paths': 'boolean(1)', - 'base-path': "'%s'" % base, - 'base-offset': pos, - } - - result = doc.xslt(style, **opts) - return HttpResponse( librarian.serialize_children(result.getroot()[0]) ) \ No newline at end of file -- 2.20.1