Esempio n. 1
0
def text_viewer_view(request, item_partid=0, master_location_type='', master_location=''):

    from digipal.utils import is_model_visible
    if not is_model_visible('textcontentxml', request):
        raise Http404('Text view not enabled')

    from digipal.models import ItemPart
    context = {'item_partid': item_partid, 'item_part': ItemPart.objects.filter(id=item_partid).first()}

    # Define the content of content type and location type drop downs
    # on top of each panel
    context['dd_content_types'] = [
        {'key': 'transcription', 'label': 'Transcription', 'icon': 'align-left', 'attrs': [['data-class', 'Text']]},
        {'key': 'translation', 'label': 'Translation', 'icon': 'indent-left', 'attrs': [['data-class', 'Text']]},
        {'key': 'image', 'label': 'Image', 'icon': 'picture', 'attrs': [['data-class', 'Image']]},
    ]
    context['dd_location_types'] = [
        {'key': 'whole', 'label': 'Whole text', 'icon': 'book'},
        {'key': 'locus', 'label': 'Locus', 'icon': 'file'},
        {'key': 'entry', 'label': 'Entry', 'icon': 'entry'},
        {'key': 'section', 'label': 'Section', 'icon': 'section'},
        {'key': 'sync', 'label': 'Synchronise with', 'icon': 'link'},
    ]
    context['statuses'] = TextContentXMLStatus.objects.all().order_by('sort_order')

    context['body_class'] = 'page-text-viewer'

    update_viewer_context(context, request)

    return render(request, 'digipal_text/text_viewer.html', context)
Esempio n. 2
0
def get_search_types(request=None):
    from content_type.search_hands import SearchHands
    from content_type.search_manuscripts import SearchManuscripts
    from content_type.search_scribes import SearchScribes
    from content_type.search_graphs import SearchGraphs
    search_hands = SearchHands()
    from digipal.utils import is_model_visible
    ret = [search_model for search_model in [SearchManuscripts(), search_hands, SearchScribes(
    ), SearchGraphs(search_hands)] if is_model_visible(search_model.get_model(), request or True)]

    return ret
Esempio n. 3
0
        def render(self, context):
            ret = self.nodelist.render(context)

            args, kwargs = self.get_resolved_arguments(context)
            link = get_link_from_obj(*args, **kwargs)
            obj = args[0]

            request = context.get('request', None)
            if request and not dputils.is_model_visible(obj, request):
                link['url'] = None

            ret = ret.strip() or link['content']
            if link['url']:
                ret = u'<a href="%s">%s</a>' % (link['url'], ret)

            return ret
Esempio n. 4
0
def get_search_types(request=None):
    from content_type.search_hands import SearchHands
    from content_type.search_manuscripts import SearchManuscripts
    from content_type.search_scribes import SearchScribes
    from content_type.search_graphs import SearchGraphs
    search_hands = SearchHands()
    from digipal.utils import is_model_visible
    ret = [
        search_model for search_model in [
            SearchManuscripts(), search_hands,
            SearchScribes(),
            SearchGraphs(search_hands)
        ] if is_model_visible(search_model.get_model(), request or True)
    ]

    return ret
Esempio n. 5
0
        def render(self, context):
            ret = self.nodelist.render(context)

            args, kwargs = self.get_resolved_arguments(context)
            link = get_link_from_obj(*args, **kwargs)
            obj = args[0]

            request = context.get('request', None)
            if request and not dputils.is_model_visible(obj, request):
                link['url'] = None

            ret = ret.strip() or link['content']
            if link['url']:
                ret = u'<a href="%s">%s</a>' % (link['url'], ret)

            return ret
Esempio n. 6
0
def text_viewer_view(request, item_partid=0, master_location_type='', master_location=''):

    from digipal.utils import is_model_visible
    if not is_model_visible('textcontentxml', request):
        raise Http404('The Text Viewer is not enabled on this site')

    from digipal.models import ItemPart
    context = {'item_partid': item_partid,
               'item_part': ItemPart.objects.filter(id=item_partid).first()}

    if not context['item_part']:
        raise Http404('This document doesn\'t exist')
    if not can_user_see_main_text(context['item_part'], request):
        raise Http404('This document is not publicly accessible')

    # Define the content of content type and location type drop downs
    # on top of each panel
    context['dd_content_types'] = [
        {'key': 'transcription', 'label': 'Transcription',
            'icon': 'align-left', 'attrs': [['data-class', 'Text']]},
        {'key': 'translation', 'label': 'Translation',
            'icon': 'indent-left', 'attrs': [['data-class', 'Text']]},
        {'key': 'image', 'label': 'Image', 'icon': 'picture',
            'attrs': [['data-class', 'Image']]},
    ]
    context['dd_location_types'] = [
        {'key': 'whole', 'label': 'Whole text', 'icon': 'book'},
        {'key': 'locus', 'label': 'Locus', 'icon': 'file'},
        {'key': 'entry', 'label': 'Entry', 'icon': 'entry'},
        {'key': 'section', 'label': 'Section', 'icon': 'section'},
        {'key': 'sync', 'label': 'Synchronise with', 'icon': 'link'},
    ]
    context['dd_download_formats'] = [
        {'key': 'html', 'label': 'HTML', 'icon': 'download'},
        {'key': 'tei', 'label': 'TEI', 'icon': 'download'},
        {'key': 'plain', 'label': 'Plain Text', 'icon': 'download'},
    ]
    context['statuses'] = TextContentXMLStatus.objects.all(
    ).order_by('sort_order')

    context['body_class'] = 'page-text-viewer'

    context['text_editor_options'] = settings.TEXT_EDITOR_OPTIONS

    update_viewer_context(context, request)

    return render(request, 'digipal_text/text_viewer.html', context)
Esempio n. 7
0
def image(request, image_id):
    """The view for the front-end annotator page"""
    from digipal.utils import request_invisible_model, raise_404

    try:
        image = Image.objects.get(id=image_id)
    except Image.DoesNotExist:
        raise_404('This Image record does not exist')

    # 404 if content type Image not visible
    request_invisible_model(Image, request, 'Image')

    # 404 if image is private and user not staff
    if image.is_private_for_user(request):
        raise_404('This Image is currently not publicly available')

    is_admin = has_edit_permission(request, Image)

    # annotations_count = image.annotation_set.all().values('graph').count()
    # annotations = image.annotation_set.all()
    annotations = Annotation.objects.filter(
        image_id=image_id,
        graph__isnull=False).exclude_hidden(is_admin).select_related(
            'graph__hand', 'graph__idiograph__allograph')
    dimensions = {
        'width': image.dimensions()[0],
        'height': image.dimensions()[1]
    }
    hands = image.hands.count()
    url = request.path
    url = url.split('/')
    url.pop(len(url) - 1)
    url = url[len(url) - 1]
    # Check for a vector_id in image referral, if it exists the request has
    # come via Scribe/allograph route
    vector_id = request.GET.get('graph', '') or request.GET.get(
        'vector_id', '')
    hands_list = []
    hand = {}
    hands_object = Hand.objects.filter(images=image_id)
    data_allographs = OrderedDict()

    for h in hands_object.values():
        if h['label'] == None:
            label = "None"
        else:
            label = mark_safe(h['label'])
        hand = {'id': h['id'], 'name': label.encode('cp1252')}
        hands_list.append(hand)

    # annotations by allograph
    for a in annotations:
        if a.graph and a.graph.hand:
            hand_label = a.graph.hand
            allograph_name = a.graph.idiograph.allograph
            if hand_label in data_allographs:
                if allograph_name not in data_allographs[hand_label]:
                    data_allographs[hand_label][allograph_name] = []
            else:
                data_allographs[hand_label] = OrderedDict()
                data_allographs[hand_label][allograph_name] = []
            data_allographs[hand_label][allograph_name].append(a)

    image_link = urlresolvers.reverse('admin:digipal_image_change',
                                      args=(image.id, ))
    form = ImageAnnotationForm(auto_id=False)
    form.fields['hand'].queryset = image.hands.all()

    width, height = image.dimensions()
    image_server_url = image.zoomify
    zoom_levels = settings.ANNOTATOR_ZOOM_LEVELS

    from digipal.models import OntographType
    from digipal.utils import is_model_visible

    images = Image.objects.none()
    if image.item_part:
        images = image.item_part.images.exclude(id=image.id).prefetch_related(
            'hands', 'annotation_set')
        images = Image.filter_permissions_from_request(images, request)
        images = Image.sort_query_set_by_locus(images, True)

    from digipal_text.models import TextContentXML

    context = {
        'form':
        form.as_ul(),
        'dimensions':
        dimensions,
        'images':
        images,
        'image':
        image,
        'height':
        height,
        'width':
        width,
        'image_server_url':
        image_server_url,
        'hands_list':
        hands_list,
        'image_link':
        image_link,
        'annotations':
        annotations.count(),
        'annotations_list':
        data_allographs,
        'url':
        url,
        'hands':
        hands,
        'is_admin':
        is_admin,
        'no_image_reason':
        image.get_media_unavailability_reason(),
        # True is the user can edit the database
        'can_edit':
        has_edit_permission(request, Annotation),
        'ontograph_types':
        OntographType.objects.order_by('name'),
        'zoom_levels':
        zoom_levels,
        'repositories':
        Repository.objects.filter(currentitem__itempart__images=image_id),
        # hide all annotations and all annotation tools from the user
        'hide_annotations':
        int(not is_model_visible('graph', request)),
        'PAGE_IMAGE_SHOW_MSDATE':
        settings.PAGE_IMAGE_SHOW_MSDATE,
        'text_content_xmls':
        TextContentXML.objects.filter(text_content__item_part=image.item_part),
    }

    if settings.PAGE_IMAGE_SHOW_MSSUMMARY:
        context['document_summary'] = image.get_document_summary()

    context['annotations_switch_initial'] = 1 - int(
        context['hide_annotations'] or ((request.GET.get(
            'annotations', 'true')).strip().lower() in ['0', 'false']))

    context[
        'show_image'] = context['can_edit'] or not context['no_image_reason']

    if vector_id:
        context['vector_id'] = vector_id

    return render_to_response('digipal/image_annotation.html',
                              context,
                              context_instance=RequestContext(request))
Esempio n. 8
0
 def __getitem__(self, index):
     from digipal.utils import is_model_visible
     return is_model_visible(index, self.request)
Esempio n. 9
0
def image(request, image_id):
    """The view for the front-end annotator page"""
    from digipal.utils import request_invisible_model, raise_404

    try:
        image = Image.objects.get(id=image_id)
    except Image.DoesNotExist:
        raise_404('This Image record does not exist')

    # 404 if content type Image not visible
    request_invisible_model(Image, request, 'Image')

    # 404 if image is private and user not staff
    if image.is_private_for_user(request):
        raise_404('This Image is currently not publicly available')

    is_admin = has_edit_permission(request, Image)

    #annotations_count = image.annotation_set.all().values('graph').count()
    #annotations = image.annotation_set.all()
    annotations = Annotation.objects.filter(image_id=image_id, graph__isnull=False).exclude_hidden(is_admin).select_related('graph__hand', 'graph__idiograph__allograph')
    dimensions = {
        'width': image.dimensions()[0],
        'height': image.dimensions()[1]
        }
    hands = image.hands.count()
    url = request.path
    url = url.split('/')
    url.pop(len(url) - 1)
    url = url[len(url) - 1]
    # Check for a vector_id in image referral, if it exists the request has
    # come via Scribe/allograph route
    vector_id = request.GET.get('graph', '') or request.GET.get('vector_id', '')
    hands_list = []
    hand = {}
    hands_object = Hand.objects.filter(images=image_id)
    data_allographs = SortedDict()

    for h in hands_object.values():
        if h['label'] == None:
            label = "None"
        else:
            label = mark_safe(h['label'])
        hand = {'id': h['id'], 'name': label.encode('cp1252')}
        hands_list.append(hand)

    #annotations by allograph
    for a in annotations:
        if a.graph and a.graph.hand:
            hand_label = a.graph.hand
            allograph_name = a.graph.idiograph.allograph
            if hand_label in data_allographs:
                if allograph_name not in data_allographs[hand_label]:
                    data_allographs[hand_label][allograph_name] = []
            else:
                data_allographs[hand_label] = SortedDict()
                data_allographs[hand_label][allograph_name] = []
            data_allographs[hand_label][allograph_name].append(a)


    image_link = urlresolvers.reverse('admin:digipal_image_change', args=(image.id,))
    form = ImageAnnotationForm(auto_id=False)
    form.fields['hand'].queryset = image.hands.all()

    width, height = image.dimensions()
    image_server_url = image.zoomify
    zoom_levels = settings.ANNOTATOR_ZOOM_LEVELS

    from digipal.models import OntographType
    from digipal.utils import is_model_visible

    images = image.item_part.images.exclude(id=image.id).prefetch_related('hands', 'annotation_set')
    images = Image.filter_permissions_from_request(images, request)
    images = Image.sort_query_set_by_locus(images, True)

    from digipal_text.models import TextContentXML

    context = {
               'form': form.as_ul(), 'dimensions': dimensions,
               'images': images,
               'image': image, 'height': height, 'width': width,
               'image_server_url': image_server_url, 'hands_list': hands_list,
               'image_link': image_link, 'annotations': annotations.count(),
               'annotations_list': data_allographs, 'url': url,
               'hands': hands, 'is_admin': is_admin,
               'no_image_reason': image.get_media_unavailability_reason(),
               # True is the user can edit the database
               'can_edit': has_edit_permission(request, Annotation),
               'ontograph_types': OntographType.objects.order_by('name'),
               'zoom_levels': zoom_levels,
               'repositories': Repository.objects.filter(currentitem__itempart__images=image_id),
               # hide all annotations and all annotation tools from the user
               'hide_annotations': int(not is_model_visible('graph', request)),
               'PAGE_IMAGE_SHOW_MSDATE': settings.PAGE_IMAGE_SHOW_MSDATE,
               'text_content_xmls': TextContentXML.objects.filter(text_content__item_part=image.item_part),
               }

    if settings.PAGE_IMAGE_SHOW_MSSUMMARY:
        context['document_summary'] = image.get_document_summary()

    context['annotations_switch_initial'] =  1 - int(context['hide_annotations'] or ((request.REQUEST.get('annotations', 'true')).strip().lower() in ['0', 'false']))

    context['show_image'] = context['can_edit'] or not context['no_image_reason']

    if vector_id:
        context['vector_id'] = vector_id

    return render_to_response('digipal/image_annotation.html', context, context_instance=RequestContext(request))
Esempio n. 10
0
def text_api_view(request, item_partid, content_type, location_type=u'default', location=''):

    format = dputils.get_request_var(request, 'format', 'html').strip().lower()
    if request.is_ajax():
        format = 'json'

    from digipal.utils import is_model_visible
    if not is_model_visible('textcontentxml', request):
        raise Http404('Text view not enabled')
    max_size = MAX_FRAGMENT_SIZE if format == 'json' else None

    response = None

    # DELEGATE TO A CUSTOM VIEW FOR THE GIVEN CONTENT TYPE

    # Look up the content_type in the function name
    # e.g. content_type = image => text_api_view_image
    text_api_view_content_type = globals().get(
        'text_api_view_' + content_type, None)
    content_type_record = None

    if not text_api_view_content_type:
        # Look up the content_type in the TextContentType table
        # e.g. content_type = translation or transcription, we assume it must
        # be a TextContentXML
        from digipal_text.models import TextContentType
        content_type_record = TextContentType.objects.filter(
            slug=content_type).first()

        if content_type_record:
            text_api_view_content_type = text_api_view_text

    if text_api_view_content_type:
        response = text_api_view_content_type(
            request, item_partid, content_type,
            location_type, location, content_type_record, max_size=max_size)

    # we didn't find a custom function for this content type
    if response is None:
        response = {'status': 'error',
                    'message': 'Invalid Content Type (%s)' % content_type}

    # If sublocation is not defined by specific function we just return
    # the desired sublocation.
    # If specific function want to remove they can set it to []
    response['sub_location'] = response.get(
        'sub_location', get_sub_location_from_request(request))
    if not response['sub_location']:
        del response['sub_location']

    # HANDLE location_type = sync

    # We take care of syncing logic here, customisations don't need to worry
    # about it.
    # Sync in => sync out. For UI/client logic purpose.
    # If we don't return sync, client assumes we can't support sync.
    # Only exception is in resolve_default_location() below.
    if response.get('location_type', '') == 'sync':
        response['location_type'] = location_type
        response['location'] = location
        response['content'] = 'Synchronising panel...'
        set_message(response, 'Synchronising panel...', '')

    ret = None

    # RESPONSE FORMATTING

    if format == 'json':
        ret = HttpResponse(json.dumps(response),
                           content_type='application/json')

    if format == 'html':
        context = {'response': response}
        context['display_classes'] = ' '.join(
            (dputils.get_request_var(request, 'ds', '').split(',')))
        context['content_type_key'] = content_type
        ret = render(request, 'digipal_text/text_view.html', context)

    if format == 'tei':
        tei = get_tei_from_text_response(response, item_partid, content_type)
        ret = HttpResponse(tei, content_type='text/xml; charset=utf-8')

    if format == 'plain':
        plain_text = dputils.get_plain_text_from_xmltext(
            response.get('content', ''))
        ret = HttpResponse(
            plain_text, content_type='text/plain; charset=utf-8')

    if not ret:
        raise Exception('Unknown output format: "%s"' % format)

    return ret
Esempio n. 11
0
 def __getitem__(self, index):
     from digipal.utils import is_model_visible
     return is_model_visible(index, self.request)
Esempio n. 12
0
def text_api_view(request, item_partid, content_type, location_type=u'default', location=''):

    format = request.REQUEST.get('format', 'html').strip().lower()
    if request.is_ajax(): format = 'json'

    from digipal.utils import is_model_visible
    if not is_model_visible('textcontentxml', request):
        raise Http404('Text view not enabled')
    max_size = MAX_FRAGMENT_SIZE if format == 'json' else None

    response = None

    # delegate to a custom function if it exists

    # Look up the content_type in the function name
    # e.g. content_type = image => text_api_view_image
    function = globals().get('text_api_view_' + content_type, None)

    if function:
        response = function(request, item_partid, content_type, location_type, location, max_size=max_size)
    else:
        # Look up the content_type in the TextContentType table
        # e.g. content_type = translation or transcription, we assume it must be a TextContentXML
        from digipal_text.models import TextContentType
        content_type_record = TextContentType.objects.filter(slug=content_type).first()

        if content_type_record:
            response = text_api_view_text(request, item_partid, content_type, location_type, location, content_type_record, max_size=max_size)

    # we didn't find a custom function for this content type
    if response is None:
        response = {'status': 'error', 'message': 'Invalid Content Type (%s)' % content_type}

    # If sublocation is not defined by specific funciton we just return
    # the desired sublocation.
    # If specific function want to remove they can set it to []
    response['sub_location'] = response.get('sub_location', get_sub_location_from_request(request))
    if not response['sub_location']:
        del response['sub_location']

    if location_type == 'sync':
        # dummy response in case of syncing with another panel
        response['location'] = location
        response['location_type'] = location_type
        response['content'] = 'Syncing...'
        set_message(response, 'Syncing...', '')

    ret = None

    if format == 'json':
        ret = HttpResponse(json.dumps(response), content_type='application/json')

    if format == 'html':
        context = {'response': response}
        context['display_classes'] = ' '.join((request.REQUEST.get('ds', '').split(',')))
        context['content_type_key'] = content_type
        ret = render(request, 'digipal_text/text_view.html', context)

    if format == 'tei':
        tei = get_tei_from_text_response(response, item_partid, content_type)
        ret = HttpResponse(tei, content_type='text/xml; charset=utf-8')

    if not ret:
        raise Exception('Unknown output format: "%s"' % format)

    return ret
Esempio n. 13
0
def text_viewer_view(request,
                     item_partid=0,
                     master_location_type='',
                     master_location=''):

    from digipal.utils import is_model_visible
    if not is_model_visible('textcontentxml', request):
        raise Http404('The Text Viewer is not enabled on this site')

    from digipal.models import ItemPart
    context = {
        'item_partid': item_partid,
        'item_part': ItemPart.objects.filter(id=item_partid).first()
    }

    if not context['item_part']:
        raise Http404('This document doesn\'t exist')
    if not can_user_see_main_text(context['item_part'], request):
        raise Http404('This document is not publicly accessible')

    # Define the content of content type and location type drop downs
    # on top of each panel
    context['dd_content_types'] = [
        {
            'key': 'transcription',
            'label': 'Transcription',
            'icon': 'align-left',
            'attrs': [['data-class', 'Text']]
        },
        {
            'key': 'translation',
            'label': 'Translation',
            'icon': 'indent-left',
            'attrs': [['data-class', 'Text']]
        },
        {
            'key': 'image',
            'label': 'Image',
            'icon': 'picture',
            'attrs': [['data-class', 'Image']]
        },
    ]
    context['dd_location_types'] = [
        {
            'key': 'whole',
            'label': 'Whole text',
            'icon': 'book'
        },
        {
            'key': 'locus',
            'label': 'Locus',
            'icon': 'file'
        },
        {
            'key': 'entry',
            'label': 'Entry',
            'icon': 'entry'
        },
        {
            'key': 'section',
            'label': 'Section',
            'icon': 'section'
        },
        {
            'key': 'sync',
            'label': 'Synchronise with',
            'icon': 'link'
        },
    ]
    context['dd_download_formats'] = [
        {
            'key': 'html',
            'label': 'HTML',
            'icon': 'download'
        },
        {
            'key': 'tei',
            'label': 'TEI',
            'icon': 'download'
        },
        {
            'key': 'plain',
            'label': 'Plain Text',
            'icon': 'download'
        },
    ]
    context['statuses'] = TextContentXMLStatus.objects.all().order_by(
        'sort_order')

    context['body_class'] = 'page-text-viewer'

    context['text_editor_options'] = settings.TEXT_EDITOR_OPTIONS

    update_viewer_context(context, request)

    return render(request, 'digipal_text/text_viewer.html', context)
Esempio n. 14
0
def text_api_view(request,
                  item_partid,
                  content_type,
                  location_type=u'default',
                  location=''):

    format = dputils.get_request_var(request, 'format', 'html').strip().lower()
    if request.is_ajax():
        format = 'json'

    from digipal.utils import is_model_visible
    if not is_model_visible('textcontentxml', request):
        raise Http404('Text view not enabled')
    max_size = MAX_FRAGMENT_SIZE if format == 'json' else None

    response = None

    # DELEGATE TO A CUSTOM VIEW FOR THE GIVEN CONTENT TYPE

    # Look up the content_type in the function name
    # e.g. content_type = image => text_api_view_image
    text_api_view_content_type = globals().get('text_api_view_' + content_type,
                                               None)
    content_type_record = None

    if not text_api_view_content_type:
        # Look up the content_type in the TextContentType table
        # e.g. content_type = translation or transcription, we assume it must
        # be a TextContentXML
        from digipal_text.models import TextContentType
        content_type_record = TextContentType.objects.filter(
            slug=content_type).first()

        if content_type_record:
            text_api_view_content_type = text_api_view_text

    if text_api_view_content_type:
        response = text_api_view_content_type(request,
                                              item_partid,
                                              content_type,
                                              location_type,
                                              location,
                                              content_type_record,
                                              max_size=max_size)

    # we didn't find a custom function for this content type
    if response is None:
        response = {
            'status': 'error',
            'message': 'Invalid Content Type (%s)' % content_type
        }

    # If sublocation is not defined by specific function we just return
    # the desired sublocation.
    # If specific function want to remove they can set it to []
    response['sub_location'] = response.get(
        'sub_location', get_sub_location_from_request(request))
    if not response['sub_location']:
        del response['sub_location']

    # HANDLE location_type = sync

    # We take care of syncing logic here, customisations don't need to worry
    # about it.
    # Sync in => sync out. For UI/client logic purpose.
    # If we don't return sync, client assumes we can't support sync.
    # Only exception is in resolve_default_location() below.
    if response.get('location_type', '') == 'sync':
        response['location_type'] = location_type
        response['location'] = location
        response['content'] = 'Synchronising panel...'
        set_message(response, 'Synchronising panel...', '')

    ret = None

    # RESPONSE FORMATTING

    if format == 'json':
        ret = HttpResponse(json.dumps(response),
                           content_type='application/json')

    if format == 'html':
        context = {'response': response}
        context['display_classes'] = ' '.join(
            (dputils.get_request_var(request, 'ds', '').split(',')))
        context['content_type_key'] = content_type
        ret = render(request, 'digipal_text/text_view.html', context)

    if format == 'tei':
        tei = get_tei_from_text_response(response, item_partid, content_type)
        ret = HttpResponse(tei, content_type='text/xml; charset=utf-8')

    if format == 'plain':
        plain_text = dputils.get_plain_text_from_xmltext(
            response.get('content', ''))
        ret = HttpResponse(plain_text,
                           content_type='text/plain; charset=utf-8')

    if not ret:
        raise Exception('Unknown output format: "%s"' % format)

    return ret