Example #1
0
def image_allographs(request, image_id):
    """Returns a list of all the allographs/annotations for the requested
    image."""
    can_edit = has_edit_permission(request, Annotation)

    annotations = Annotation.objects.filter(image=image_id).exclude_hidden(can_edit).select_related('graph')

    data_allographs = SortedDict()
    for a in annotations:
        if a.graph:
            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)

    context = {
        'annotations_list': data_allographs,
        'can_edit': can_edit
    }

    return render_to_response('digipal/annotations.html', context, context_instance=RequestContext(request))
Example #2
0
def image_allographs(request, image_id):
    """Returns a list of all the allographs/annotations for the requested
    image."""
    can_edit = has_edit_permission(request, Annotation)

    annotations = Annotation.objects.filter(
        image=image_id).exclude_hidden(can_edit).select_related('graph')

    data_allographs = OrderedDict()
    for a in annotations:
        if a.graph:
            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)

    context = {'annotations_list': data_allographs, 'can_edit': can_edit}

    return render_to_response('digipal/annotations.html',
                              context,
                              context_instance=RequestContext(request))
Example #3
0
def idiograph_editor(request):

    formsetScribe = formset_factory(ScribeAdminForm)

    formset = formsetScribe()

    onlyScribeForm = OnlyScribe()

    newContext = {
        'can_edit': has_edit_permission(request, Annotation), 'formset': formset,
        'scribeForm': onlyScribeForm
    }

    return render_to_response('admin/digipal/idiograph_editor.html', newContext,
                              context_instance=RequestContext(request))
Example #4
0
def save_editorial(request, graphs):
    if settings.ARCHETYPE_API_READ_ONLY:
        #        transaction.rollback()
        raise Http404
    else:
        data = {'success': False, 'graphs': []}

        if not graphs or len(graphs) == 0:
            raise Exception('No data provided')

        try:
            graphs = graphs.replace('/"', "'")
            graphs = json.loads(graphs)
            for gr in graphs:
                image = Image.objects.get(id=gr['image'])
                get_data = request.POST.copy()
                _id = gr['id']
                count = 0
                if _id and _id.isdigit():
                    count = Annotation.objects.filter(image=image,
                                                      id=_id).count()
                if 'geoJson' in gr:
                    geo_json = str(gr['geoJson'])
                else:
                    geo_json = False

                if count > 0:
                    annotation = Annotation.objects.get(image=image, id=_id)
                else:
                    annotation = Annotation(image=image, type='editorial')

                form = ImageAnnotationForm(data=get_data)
                if form.is_valid():
                    with transaction.atomic():
                        clean = form.cleaned_data

                        if geo_json:
                            annotation.geo_json = geo_json

                        # set the note (only if different) - see JIRA
                        # DIGIPAL-477
                        for f in ['display_note', 'internal_note']:

                            if getattr(annotation,
                                       f) != clean[f] and f in get_data:
                                setattr(annotation, f, clean[f])

                        if not annotation.id:
                            # set the author only when the annotation is
                            # created
                            annotation.author = request.user

                        if geo_json:
                            annotation.set_graph_group()
                        annotation.save()

                        new_graph = [{}]

                        if 'vector_id' in gr:
                            new_graph[0]['vector_id'] = gr['vector_id']
                        new_graph[0]['annotation_id'] = unicode(annotation.id)
                        if has_edit_permission(request, Annotation):
                            new_graph[0][
                                'internal_note'] = annotation.internal_note
                        new_graph[0]['display_note'] = annotation.display_note

                        data['graphs'].append(new_graph[0])

                        # transaction.commit()
                        data['success'] = True

        # uncomment this to see the error call stack in the django server output
        # except ValueError as e:
        except Exception as e:
            data['success'] = False
            data['errors'] = [u'Internal error: %s' % e]
            # tb = sys.exc_info()[2]

        return HttpResponse(json.dumps(data), content_type='application/json')
Example #5
0
def save(request, graphs):
    """Saves an annotation and creates a cutout of the annotation."""

    if settings.ARCHETYPE_API_READ_ONLY:
        #        transaction.rollback()
        raise Http404
    else:

        data = {'success': False, 'graphs': []}

        try:

            graphs = graphs.replace('/"', "'")
            graphs = json.loads(graphs)

            for gr in graphs:
                graph_object = False

                if 'id' in gr:
                    graph_object = Graph.objects.get(id=gr['id'])

                image = Image.objects.get(id=gr['image'])
                annotation_is_modified = False
                if graph_object:
                    annotation = graph_object.annotation
                    graph = graph_object
                else:
                    graph = Graph()
                    annotation = Annotation(image=image)

                get_data = request.POST.copy()

                if 'geoJson' in gr:
                    geo_json = str(gr['geoJson'])
                else:
                    geo_json = False

                form = ImageAnnotationForm(data=get_data)
                if form.is_valid():
                    with transaction.atomic():
                        clean = form.cleaned_data
                        if geo_json:
                            annotation.geo_json = geo_json
                            annotation_is_modified = True
                        # set the note (only if different) - see JIRA
                        # DIGIPAL-477
                        for f in ['display_note', 'internal_note']:
                            if getattr(annotation, f) != clean[f]:
                                setattr(annotation, f, clean[f])
                                annotation_is_modified = True
                        if not annotation.id:
                            # set the author only when the annotation is
                            # created
                            annotation.author = request.user
                        # annotation.before = clean['before']
                        # annotation.after = clean['after']
                        allograph = clean['allograph']
                        hand = clean['hand']

                        if hand and allograph:

                            scribe = hand.scribe

                            # GN: if this is a new Graph, it has no idiograph
                            # yet, so we test this first
                            if graph.id and (allograph.id !=
                                             graph.idiograph.allograph.id):
                                graph.graph_components.all().delete()

                            idiograph_list = Idiograph.objects.filter(
                                allograph=allograph, scribe=scribe)

                            if idiograph_list:
                                idiograph = idiograph_list[0]
                                idiograph.id
                            else:
                                idiograph = Idiograph(allograph=allograph,
                                                      scribe=scribe)
                                idiograph.save()

                            graph.idiograph = idiograph
                            graph.hand = hand

                            graph.save()  # error is here
                        feature_list_checked = get_data.getlist('feature')

                        feature_list_unchecked = get_data.getlist('-feature')

                        if feature_list_unchecked:

                            for value in feature_list_unchecked:

                                cid, fid = value.split('::')

                                component = Component.objects.get(id=cid)
                                feature = Feature.objects.get(id=fid)
                                gc_list = GraphComponent.objects.filter(
                                    graph=graph, component=component)

                                if gc_list:
                                    gc = gc_list[0]
                                    gc.features.remove(feature)
                                    gc.save()

                                    if not gc.features.all():
                                        gc.delete()

                        if feature_list_checked:

                            for value in feature_list_checked:
                                cid, fid = value.split('::')

                                component = Component.objects.get(id=cid)
                                feature = Feature.objects.get(id=fid)
                                gc_list = GraphComponent.objects.filter(
                                    graph=graph, component=component)

                                if gc_list:
                                    gc = gc_list[0]
                                else:
                                    gc = GraphComponent(graph=graph,
                                                        component=component)
                                    gc.save()

                                gc.features.add(feature)
                                gc.save()

                        aspects = get_data.getlist('aspect')
                        aspects_deleted = get_data.getlist('-aspect')

                        if aspects:
                            for aspect in aspects:
                                aspect_model = Aspect.objects.get(id=aspect)
                                graph.aspects.add(aspect_model)

                        if aspects_deleted:
                            for aspect in aspects_deleted:
                                aspect_model = Aspect.objects.get(id=aspect)
                                graph.aspects.remove(aspect_model)

                        graph.save()

                        # Only save the annotation if it has been modified (or new one)
                        # see JIRA DIGIPAL-477
                        if annotation_is_modified or not annotation.id:
                            annotation.graph = graph
                            annotation.save()
                            # attach the graph to a containing one
                            # cannot be called BEFORE saving the
                            # annotation/graph
                            if geo_json:
                                annotation.set_graph_group()

                        new_graph = json.loads(get_features(graph.id))
                        if 'vector_id' in gr:
                            new_graph[0]['vector_id'] = gr['vector_id']

                        if has_edit_permission(request, Annotation):
                            new_graph[0][
                                'internal_note'] = annotation.internal_note
                        new_graph[0]['display_note'] = annotation.display_note

                        data['graphs'].append(new_graph[0])

                        # transaction.commit()
                        data['success'] = True
                else:
                    # transaction.rollback()
                    data['success'] = False
                    data['errors'] = get_json_error_from_form_errors(form)

        # uncomment this to see the error call stack in the django server output
        # except ValueError as e:
        except Exception as e:
            data['success'] = False
            data['errors'] = [u'Internal error: %s' % e]
            # tb = sys.exc_info()[2]

        return HttpResponse(json.dumps(data), content_type='application/json')
Example #6
0
def image_annotations(request, image_id, annotations_page=True, hand=False):
    """Returns a JSON of all the annotations for the requested image."""

    can_edit = has_edit_permission(request, Annotation)

    if annotations_page:
        annotation_list_with_graph = Annotation.objects.filter(
            image=image_id).with_graph()
    else:
        annotation_list_with_graph = Annotation.objects.filter(
            graph__hand=hand).with_graph()
    annotation_list_with_graph = annotation_list_with_graph.exclude_hidden(
        can_edit)
    annotation_list_with_graph = annotation_list_with_graph.select_related(
        'image', 'graph', 'graph__hand',
        'graph__idiograph__allograph__character').prefetch_related(
            'graph__graph_components__features', 'graph__aspects',
            'graph__graph_components__component', 'image__hands').distinct()

    editorial_annotations = Annotation.objects.filter(
        image=image_id).editorial().select_related('image')
    if not can_edit:
        editorial_annotations = editorial_annotations.editorial(
        ).publicly_visible()
    editorial_annotations = editorial_annotations.exclude_hidden(can_edit)

    annotations = []
    an = {}
    # hands = []
    for a in annotation_list_with_graph:
        # if len(annotations) > 1: break
        an = {}
        annotations.append(an)
        an['vector_id'] = a.vector_id
        an['status_id'] = a.status_id
        an['image_id'] = a.image.id
        an['hidden_hand'] = a.graph.hand.id
        an['character'] = a.graph.idiograph.allograph.character.name
        an['hand'] = a.graph.hand_id
        an['character_id'] = a.graph.idiograph.allograph.character.id
        an['allograph_id'] = a.graph.idiograph.allograph.id
        # Now optimised (see select_related and prefect_related in the query
        # above)
        features = get_features_from_graph(a.graph, True)
        an['num_features'] = len(features['features'])
        an['features'] = [features]
        geo = a.get_geo_json_as_dict().get('geometry', None)
        if geo:
            an['geo_json'] = geo

        an['hidden_allograph'] = '%d::%s' % (a.graph.idiograph.allograph.id,
                                             a.graph.idiograph.allograph.name)

        an['feature'] = '%s' % (a.graph.idiograph.allograph)
        an['graph'] = '%s' % (a.graph.id)
        # hand = a.graph.hand.label
        # hands.append(a.graph.hand.id)

        an['display_note'] = a.display_note
        an['internal_note'] = a.internal_note
        an['id'] = unicode(a.id)

        # gc_list = GraphComponent.objects.filter(graph=a.graph)
        gc_list = a.graph.graph_components.all()

        if gc_list:
            an['features'] = []

            for gc in gc_list:
                for f in gc.features.all():
                    an['features'].append('%d::%d' % (gc.component.id, f.id))
        """
        if a.before:
            an['before'] = '%d::%s' % (a.before.id, a.before.name)

        if a.after:
            an['after'] = '%d::%s' % (a.after.id, a.after.name)
        """

    for e in editorial_annotations:
        an = {}
        annotations.append(an)
        # an['geo_json'] = vectors[unicode(e.id)]['geometry']
        geo = e.get_geo_json_as_dict().get('geometry', None)
        if geo:
            an['geo_json'] = geo
        an['status_id'] = e.status_id
        an['image_id'] = e.image.id
        an['display_note'] = e.display_note
        an['internal_note'] = e.internal_note
        an['vector_id'] = e.vector_id
        an['id'] = unicode(e.id)
        an['is_editorial'] = True

    # convert to dict
    data = {}
    for an in annotations:
        data[an['id']] = an
        an['display_order'] = 0
        if 'geo_json' in an and 'coordinates' in an['geo_json']:
            an['display_order'] = min([
                float(point[0]) for point in an['geo_json']['coordinates'][0]
            ])

    if annotations_page:
        return HttpResponse(json.dumps(data), content_type='application/json')
    else:
        return data
Example #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))
Example #8
0
def save_editorial(request, graphs):
    if settings.REJECT_HTTP_API_REQUESTS:
#        transaction.rollback()
        raise Http404
    else:
        data = {
            'success': False,
            'graphs': []
        }

        if not graphs or len(graphs) == 0:
            raise Exception('No data provided')

        try:
            graphs = graphs.replace('/"', "'")
            graphs = json.loads(graphs)
            for gr in graphs:
                image = Image.objects.get(id=gr['image'])
                get_data = request.POST.copy()
                _id = gr['id']
                count = 0
                if _id and _id.isdigit():
                    count = Annotation.objects.filter(image=image, id=_id).count()
                if 'geoJson' in gr:
                    geo_json = str(gr['geoJson'])
                else:
                    geo_json = False

                if count > 0:
                    annotation = Annotation.objects.get(image=image, id=_id)
                else:
                    annotation = Annotation(image=image, type='editorial')

                form = ImageAnnotationForm(data=get_data)
                if form.is_valid():
                    with transaction.atomic():
                        clean = form.cleaned_data

                        if geo_json:
                            annotation.geo_json = geo_json

                        # set the note (only if different) - see JIRA DIGIPAL-477
                        for f in ['display_note', 'internal_note']:

                            if getattr(annotation, f) != clean[f] and f in get_data:
                                setattr(annotation, f, clean[f])

                        if not annotation.id:
                            # set the author only when the annotation is created
                            annotation.author = request.user

                        if geo_json:
                            annotation.set_graph_group()
                        annotation.save()

                        new_graph = [{}]

                        if 'vector_id' in gr:
                            new_graph[0]['vector_id'] = gr['vector_id']
                        new_graph[0]['annotation_id'] = unicode(annotation.id)
                        if has_edit_permission(request, Annotation):
                            new_graph[0]['internal_note'] = annotation.internal_note
                        new_graph[0]['display_note'] = annotation.display_note

                        data['graphs'].append(new_graph[0])

                        #transaction.commit()
                        data['success'] = True

        # uncomment this to see the error call stack in the django server output
        #except ValueError as e:
        except Exception as e:
            data['success'] = False
            data['errors'] = [u'Internal error: %s' % e]
            #tb = sys.exc_info()[2]

        return HttpResponse(json.dumps(data), content_type='application/json')
Example #9
0
def save(request, graphs):

    """Saves an annotation and creates a cutout of the annotation."""

    if settings.REJECT_HTTP_API_REQUESTS:
#        transaction.rollback()
        raise Http404
    else:

        data = {
            'success': False,
            'graphs': []
        }

        try:

            graphs = graphs.replace('/"', "'")
            graphs = json.loads(graphs)

            for gr in graphs:
                graph_object = False

                if 'id' in gr:
                    graph_object = Graph.objects.get(id=gr['id'])

                image = Image.objects.get(id=gr['image'])
                annotation_is_modified = False
                if graph_object:
                    annotation = graph_object.annotation
                    graph = graph_object
                else:
                    graph = Graph()
                    annotation = Annotation(image=image)

                get_data = request.POST.copy()

                if 'geoJson' in gr:
                    geo_json = str(gr['geoJson'])
                else:
                    geo_json = False


                form = ImageAnnotationForm(data=get_data)
                if form.is_valid():
                    with transaction.atomic():
                        clean = form.cleaned_data
                        if geo_json:
                            annotation.geo_json = geo_json
                            annotation_is_modified = True
                        # set the note (only if different) - see JIRA DIGIPAL-477
                        for f in ['display_note', 'internal_note']:
                            if getattr(annotation, f) != clean[f]:
                                setattr(annotation, f, clean[f])
                                annotation_is_modified = True
                        if not annotation.id:
                            # set the author only when the annotation is created
                            annotation.author = request.user
                        #annotation.before = clean['before']
                        #annotation.after = clean['after']
                        allograph = clean['allograph']
                        hand = clean['hand']

                        if hand and allograph:

                            scribe = hand.scribe

                            # GN: if this is a new Graph, it has no idiograph yet, so we test this first
                            if graph.id and (allograph.id != graph.idiograph.allograph.id):
                                graph.graph_components.all().delete()

                            idiograph_list = Idiograph.objects.filter(allograph=allograph,
                                    scribe=scribe)

                            if idiograph_list:
                                idiograph = idiograph_list[0]
                                idiograph.id
                            else:
                                idiograph = Idiograph(allograph=allograph, scribe=scribe)
                                idiograph.save()

                            graph.idiograph = idiograph
                            graph.hand = hand

                            graph.save() # error is here
                        feature_list_checked = get_data.getlist('feature')

                        feature_list_unchecked = get_data.getlist('-feature')

                        if feature_list_unchecked:

                            for value in feature_list_unchecked:

                                cid, fid = value.split('::')

                                component = Component.objects.get(id=cid)
                                feature = Feature.objects.get(id=fid)
                                gc_list = GraphComponent.objects.filter(graph=graph,
                                        component=component)

                                if gc_list:
                                    gc = gc_list[0]
                                    gc.features.remove(feature)
                                    gc.save()

                                    if not gc.features.all():
                                        gc.delete()

                        if feature_list_checked:

                            for value in feature_list_checked:
                                cid, fid = value.split('::')

                                component = Component.objects.get(id=cid)
                                feature = Feature.objects.get(id=fid)
                                gc_list = GraphComponent.objects.filter(graph=graph,
                                        component=component)

                                if gc_list:
                                    gc = gc_list[0]
                                else:
                                    gc = GraphComponent(graph=graph, component=component)
                                    gc.save()

                                gc.features.add(feature)
                                gc.save()

                        aspects = get_data.getlist('aspect')
                        aspects_deleted = get_data.getlist('-aspect')

                        if aspects:
                            for aspect in aspects:
                                aspect_model = Aspect.objects.get(id=aspect)
                                graph.aspects.add(aspect_model)

                        if aspects_deleted:
                            for aspect in aspects_deleted:
                                aspect_model = Aspect.objects.get(id=aspect)
                                graph.aspects.remove(aspect_model)

                        graph.save()

                        # Only save the annotation if it has been modified (or new one)
                        # see JIRA DIGIPAL-477
                        if annotation_is_modified or not annotation.id:
                            annotation.graph = graph
                            annotation.save()
                            # attach the graph to a containing one
                            # cannot be called BEFORE saving the annotation/graph
                            if geo_json:
                                annotation.set_graph_group()

                        new_graph = json.loads(get_features(graph.id))
                        if 'vector_id' in gr:
                            new_graph[0]['vector_id'] = gr['vector_id']

                        if has_edit_permission(request, Annotation):
                            new_graph[0]['internal_note'] = annotation.internal_note
                        new_graph[0]['display_note'] = annotation.display_note

                        data['graphs'].append(new_graph[0])

                        #transaction.commit()
                        data['success'] = True
                else:
                    #transaction.rollback()
                    data['success'] = False
                    data['errors'] = get_json_error_from_form_errors(form)

        # uncomment this to see the error call stack in the django server output
        #except ValueError as e:
        except Exception as e:
            data['success'] = False
            data['errors'] = [u'Internal error: %s' % e]
            #tb = sys.exc_info()[2]

        return HttpResponse(json.dumps(data), content_type='application/json')
Example #10
0
def image_annotations(request, image_id, annotations_page=True, hand=False):
    """Returns a JSON of all the annotations for the requested image."""

    can_edit = has_edit_permission(request, Annotation)

    if annotations_page:
        annotation_list_with_graph = Annotation.objects.filter(image=image_id).with_graph()
    else:
        annotation_list_with_graph = Annotation.objects.filter(graph__hand=hand).with_graph()
    annotation_list_with_graph = annotation_list_with_graph.exclude_hidden(can_edit)
    annotation_list_with_graph = annotation_list_with_graph.select_related('image', 'graph', 'graph__hand', 'graph__idiograph__allograph__character').prefetch_related('graph__graph_components__features', 'graph__aspects', 'graph__graph_components__component', 'image__hands').distinct()

    editorial_annotations = Annotation.objects.filter(image=image_id).editorial().select_related('image')
    if not can_edit:
        editorial_annotations = editorial_annotations.editorial().publicly_visible()
    editorial_annotations = editorial_annotations.exclude_hidden(can_edit)

    annotations = []
    an = {}
    #hands = []
    for a in annotation_list_with_graph:
        #if len(annotations) > 1: break
        an = {}
        annotations.append(an)
        an['vector_id'] = a.vector_id
        an['status_id'] = a.status_id
        an['image_id'] = a.image.id
        an['hidden_hand'] = a.graph.hand.id
        an['character'] = a.graph.idiograph.allograph.character.name
        an['hand'] = a.graph.hand_id
        an['character_id'] = a.graph.idiograph.allograph.character.id
        an['allograph_id'] = a.graph.idiograph.allograph.id
        # Now optimised (see select_related and prefect_related in the query above)
        features = get_features_from_graph(a.graph, True)
        an['num_features'] = len(features['features'])
        an['features'] = [features]
        geo = a.get_geo_json_as_dict().get('geometry', None)
        if geo:
            an['geo_json'] = geo

        an['hidden_allograph'] = '%d::%s' % (a.graph.idiograph.allograph.id,
        a.graph.idiograph.allograph.name)

        an['feature'] = '%s' % (a.graph.idiograph.allograph)
        an['graph'] = '%s' % (a.graph.id)
        #hand = a.graph.hand.label
        #hands.append(a.graph.hand.id)

        an['display_note'] = a.display_note
        an['internal_note'] = a.internal_note
        an['id'] = unicode(a.id)

        #gc_list = GraphComponent.objects.filter(graph=a.graph)
        gc_list = a.graph.graph_components.all()

        if gc_list:
            an['features'] = []

            for gc in gc_list:
                for f in gc.features.all():
                    an['features'].append('%d::%d' % (gc.component.id, f.id))

        """
        if a.before:
            an['before'] = '%d::%s' % (a.before.id, a.before.name)

        if a.after:
            an['after'] = '%d::%s' % (a.after.id, a.after.name)
        """

    for e in editorial_annotations:
        an = {}
        annotations.append(an)
        #an['geo_json'] = vectors[unicode(e.id)]['geometry']
        geo = e.get_geo_json_as_dict().get('geometry', None)
        if geo:
            an['geo_json'] = geo
        an['status_id'] = e.status_id
        an['image_id'] = e.image.id
        an['display_note'] = e.display_note
        an['internal_note'] = e.internal_note
        an['vector_id'] = e.vector_id
        an['id'] = unicode(e.id)
        an['is_editorial'] = True

    # convert to dict
    data = {}
    for an in annotations:
        data[an['id']] = an
        an['display_order'] = 0
        if 'geo_json' in an and 'coordinates' in an['geo_json']:
            an['display_order'] = min([float(point[0]) for point in an['geo_json']['coordinates'][0]])

    if annotations_page:
        return HttpResponse(json.dumps(data), content_type='application/json')
    else:
        return data
Example #11
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))
Example #12
0
def update_text_image_link(request, image, ret):
    if ret is None:
        ret = {}
    ret['newids'] = {}

    from digipal.models import has_edit_permission, Annotation
    if not has_edit_permission(request, Annotation):
        set_message(ret, 'Insufficient rights to edit annotations', 'error')
        return ret

    # print 'TEXT IMAGE LINK: image #%s' % image.id
    links = dputils.get_request_var(request, 'links', None)
    if links:
        ''' links = [
                        [
                            [["", "clause"], ["type", "address"]],
                            {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[270,-1632],[270,-984],[3006,-984],[3006,-1632],[270,-1632]]]},"properties":null}
                        ],
                        [...]
                    ]
        '''
        for link in json.loads(links):
            # print link
            attrs, geojson = link[0], link[1]
            clientid = (geojson.get('properties', {})
                        or {}).pop('clientid', '')
            serverid = geojson.pop('id', None)
            action = geojson.pop('action', 'update')

            # 1. find the annotation
            if not clientid and not serverid:
                raise Exception(
                    'Cannot find annotation, need either "id" or "clientid"')

            filter = {}
            if serverid:
                filter['id'] = serverid
            else:
                filter['clientid'] = clientid
            annotation = Annotation.objects.filter(**filter).first()

            # 2. delete or create annotation
            if action == 'deleted':
                if annotation:
                    # print 'delete annotation'
                    annotation.delete()
                    annotation = None
            else:
                # create annotation
                if not annotation:
                    if serverid:
                        raise Exception(
                            'Cannot find annotation by server id %s' % serverid)
                    # print 'create annotation'
                    author = request.user
                    annotation = Annotation(
                        clientid=clientid, image=image, author=author, type='text')

                # update annotation and link
                # print 'update annotation'
                annotation.set_geo_json_from_dict(geojson)
                annotation.save()
                if not serverid:
                    ret['newids'][clientid] = annotation.id

                # update the text-annotation
                from digipal_text.models import TextAnnotation
                # text_annotation = TextAnnotation(annotation=annotation, element=json.dumps(attrs))
                # TODO: assume a single element per annotation for the moment
                text_annotation = TextAnnotation.objects.filter(
                    annotation=annotation).first()
                if not attrs:
                    # delete it
                    if text_annotation:
                        # print 'delete link'
                        text_annotation.delete()
                else:
                    if not text_annotation:
                        # print 'create link'
                        text_annotation = TextAnnotation(annotation=annotation)
                    # print 'update link'
                    text_annotation.elementid = json.dumps(attrs)
                    text_annotation.save()

    return ret
Example #13
0
    def _build_queryset(self, request, term):
        """ View for Hand record drill-down """
        context = {}
        self.graphs_count = 0
        
        undefined = u''
        
        scribe = request.GET.get('scribe', undefined)
        # alternative names are for backward compatibility with old-style graph search page  
        script = request.GET.get('script', undefined)
        chartype = request.GET.get('chartype', undefined)
        character = request.GET.get('character', undefined)
        allograph = request.GET.get('allograph', undefined)
        component = request.GET.get('component', undefined)
        feature = request.GET.get('feature', undefined)
        repository = request.GET.get('repository', undefined)
        index = request.GET.get('index', undefined)
        
        
        excluded_images = None
        from digipal.utils import is_staff
        if not is_staff(request):
            excluded_images = Image.filter_permissions(Image.objects.all(), [MediaPermission.PERM_PRIVATE])
        
        none = u'-1'
        one_or_more = u'-2'
        
        from datetime import datetime
        
        t0 = datetime.now()
        t4 = datetime.now()
        
        wheres = []

        if self.search_hands:
            graphs = Graph.objects.filter(hand__id__in=self.search_hands.queryset)
        else:
        
            # .order_by('item_part__current_item__repository__name', 'item_part__current_item__shelfmark', 'descriptions__description','id')
            # Although we are listing hands on the front-end, we search for graphs and not for hand.
            # Two reasons: 
            #    searching for character and allograh at the same time through a Hand model would generate two separate joins to graph
            #        this would bring potentially invalid results and it is also much slower
            #    it is faster than excluding all the hands without a graph (yet another expensive join)
            #
            if term:
                term = term.replace('"', '')
                graphs = Graph.objects.filter(
                        Q(hand__descriptions__description__icontains=term) | \
                        Q(hand__scribe__name__icontains=term) | \
                        Q(hand__assigned_place__name__icontains=term) | \
                        Q(hand__assigned_date__date__icontains=term) | \
                        Q(hand__item_part__current_item__shelfmark__icontains=term) | \
                        Q(hand__item_part__current_item__repository__name__icontains=term) | \
                        Q(hand__item_part__current_item__repository__place__name__icontains=term) | \
                        Q(hand__item_part__historical_items__catalogue_number__icontains=term) | \
                        # JIRA 423
                        Q(hand__item_part__historical_items__name__icontains=term) | \
                        Q(hand__item_part__group__historical_items__name__icontains=term) | \
                        Q(hand__item_part__display_label__icontains=term) | \
                        Q(hand__item_part__group__display_label__icontains=term)
                        )
            else:
                graphs = Graph.objects.all()
                
            t1 = datetime.now()
            
            if index:
                graphs = graphs.filter(hand__item_part__historical_items__catalogue_number__iexact=index)
            if repository:
                matches = re.match(ur'^([^,]+?),([^,]+)$', repository)
                if matches:
                    graphs = graphs.filter(Q(hand__item_part__current_item__repository__place__name__iexact=matches.group(1).strip()) & Q(hand__item_part__current_item__repository__name__iexact=matches.group(2).strip()))
            if scribe:
                graphs = graphs.filter(hand__scribe__name__icontains=scribe)
            if script:
                graphs = graphs.filter(hand__script__name=script)
        
        if chartype:
            graphs = graphs.filter(idiograph__allograph__character__ontograph__ontograph_type__name=chartype)
        if character:
            graphs = graphs.filter(idiograph__allograph__character__name=character)
        if allograph:
            graphs = graphs.filter(idiograph__allograph__name=allograph)

        # we discard freak graph records (i.e. without annotation) to prevent errors further down the line. 
        graphs = graphs.filter(annotation__isnull=False)
        
        # if the user is not logged in we exclude graphs where the allograph is hidden
        from digipal.models import has_edit_permission
        if not has_edit_permission(request, self.get_model()):
            graphs = graphs.exclude(idiograph__allograph__hidden=True)
            
        # exclude private images
        if excluded_images and excluded_images.count():
            graphs = graphs.exclude(annotation__image__in=excluded_images)
        
        # condition on component
        if component:
            component_where = Q(graph_components__component__name=component)
            if feature in [undefined, none]:
                # If no feature is specified we find all the graph which are supposed to have a component
                # according to their idiograph
                component_where = component_where | Q(idiograph__allograph__allograph_components__component__name=component)
            wheres.append(component_where)

        # condition on feature
        if feature not in [undefined, none, one_or_more]:
            wheres.append(Q(graph_components__features__name=feature))
        if feature in [one_or_more]:
            wheres.append(Q(graph_components__features__id__isnull=False))

        # ANDs all the Q() where clauses together
        if wheres:
            where_and = wheres.pop(0)
            for where in wheres:
                where_and = where_and & where    
            
            graphs = graphs.filter(where_and)
        
        # Treat the feature=none case 
        if feature == none:
            excluded_q = Q(graph_components__features__id__isnull=False)
            if component:
                excluded_q = excluded_q & Q(graph_components__component__name=component)
            excluded_graphs = Graph.objects.filter(excluded_q)
            graphs = graphs.exclude(id__in=excluded_graphs.values_list('id', flat=True))
        
        from digipal.utils import set_left_joins_in_queryset, get_str_from_queryset
        set_left_joins_in_queryset(graphs)
        #print get_str_from_queryset(graphs)
        
        t2 = datetime.now()
    
        # Get the graphs then id of all the related Hands
        # We use values_list because it is much faster, we don't need to fetch all the Hands at this stage
        # That will be done after pagination in the template
        # Distinct is needed here.
        #graphs = graphs.distinct().order_by('hand__scribe__name', 'hand__id', 'idiograph__allograph__character__ontograph__sort_order')
        chrono('graph filter:')
        graphs = graphs.distinct().order_by('hand__scribe__name', 'hand__id')
        chrono(':graph filter')

        #print graphs.query
        chrono('graph values_list:')
        graph_ids = graphs.values_list('id', 'hand_id')
        chrono(':graph values_list')
        
#        chrono('len:')
#        l = len(graph_ids)
#        print graph_ids.query
#        chrono(':len')
        
        # Build a structure that groups all the graph ids by hand id
        # context['hand_ids'] = [[1, 101, 102], [2, 103, 104]]
        # In the above we have two hands: 1 and 2. For hand 1 we have Graph 101 and 102.
        chrono('hand_ids:')
        context['hand_ids'] = [[0]]
        last = 0
        for g in graph_ids:
            if g[1] != context['hand_ids'][-1][0]:
                context['hand_ids'].append([g[1]])
            context['hand_ids'][-1].append(g[0])
        del(context['hand_ids'][0])
        chrono(':hand_ids')

        t3 = datetime.now()

        self.graphs_count = len(graph_ids)
        
        t4 = datetime.now()
        
        #print 'search %s; hands query: %s + graph count: %s' % (t4 - t0, t3 - t2, t4 - t3)
            
        t5 = datetime.now()
        self._queryset = context['hand_ids']
        
        return self._queryset
Example #14
0
    def _build_queryset(self, request, term):
        """ View for Hand record drill-down """
        context = {}
        self.graphs_count = 0

        undefined = u''

        scribe = request.GET.get('scribe', undefined)
        # alternative names are for backward compatibility with old-style graph
        # search page
        script = request.GET.get('script', undefined)
        chartype = request.GET.get('chartype', undefined)
        character = request.GET.get('character', undefined)
        allograph = request.GET.get('allograph', undefined)
        component = request.GET.get('component', undefined)
        feature = request.GET.get('feature', undefined)
        repository = request.GET.get('repository', undefined)
        index = request.GET.get('index', undefined)

        excluded_images = None
        from digipal.utils import is_staff
        if not is_staff(request):
            excluded_images = Image.filter_permissions(
                Image.objects.all(), [MediaPermission.PERM_PRIVATE])

        none = u'-1'
        one_or_more = u'-2'

        from datetime import datetime

        t0 = datetime.now()
        t4 = datetime.now()

        wheres = []

        if self.search_hands:
            graphs = Graph.objects.filter(
                hand__id__in=self.search_hands.queryset)
        else:

            # .order_by('item_part__current_item__repository__name', 'item_part__current_item__shelfmark', 'descriptions__description','id')
            # Although we are listing hands on the front-end, we search for graphs and not for hand.
            # Two reasons:
            #    searching for character and allograh at the same time through a Hand model would generate two separate joins to graph
            #        this would bring potentially invalid results and it is also much slower
            #    it is faster than excluding all the hands without a graph (yet another expensive join)
            #
            if term:
                term = term.replace('"', '')
                graphs = Graph.objects.filter(
                    Q(hand__descriptions__description__icontains=term) |
                    Q(hand__scribe__name__icontains=term) |
                    Q(hand__assigned_place__name__icontains=term) |
                    Q(hand__assigned_date__date__icontains=term) |
                    Q(hand__item_part__current_item__shelfmark__icontains=term) |
                    Q(hand__item_part__current_item__repository__name__icontains=term) |
                    Q(hand__item_part__current_item__repository__place__name__icontains=term) |
                    Q(hand__item_part__historical_items__catalogue_number__icontains=term) | \
                    # JIRA 423
                    Q(hand__item_part__historical_items__name__icontains=term) | \
                    Q(hand__item_part__group__historical_items__name__icontains=term) | \
                    Q(hand__item_part__display_label__icontains=term) | \
                    Q(hand__item_part__group__display_label__icontains=term)
                )
            else:
                graphs = Graph.objects.all()

            t1 = datetime.now()

            if index:
                graphs = graphs.filter(
                    hand__item_part__historical_items__catalogue_number__iexact=index)
            if repository:
                matches = re.match(ur'^([^,]+?),([^,]+)$', repository)
                if matches:
                    graphs = graphs.filter(Q(hand__item_part__current_item__repository__place__name__iexact=matches.group(
                        1).strip()) & Q(hand__item_part__current_item__repository__name__iexact=matches.group(2).strip()))
            if scribe:
                graphs = graphs.filter(hand__scribe__name__icontains=scribe)
            if script:
                graphs = graphs.filter(hand__script__name=script)

        if chartype:
            graphs = graphs.filter(
                idiograph__allograph__character__ontograph__ontograph_type__name=chartype)
        if character:
            graphs = graphs.filter(
                idiograph__allograph__character__name=character)
        if allograph:
            graphs = graphs.filter(idiograph__allograph__name=allograph)

        # we discard freak graph records (i.e. without annotation) to prevent
        # errors further down the line.
        graphs = graphs.filter(annotation__isnull=False)

        # if the user is not logged in we exclude graphs where the allograph is
        # hidden
        from digipal.models import has_edit_permission
        if not has_edit_permission(request, self.get_model()):
            graphs = graphs.exclude(idiograph__allograph__hidden=True)

        # exclude private images
        if excluded_images and excluded_images.count():
            graphs = graphs.exclude(annotation__image__in=excluded_images)

        # condition on component
        if component:
            component_where = Q(graph_components__component__name=component)
            if feature in [undefined, none]:
                # If no feature is specified we find all the graph which are supposed to have a component
                # according to their idiograph
                component_where = component_where | Q(
                    idiograph__allograph__allograph_components__component__name=component)
            wheres.append(component_where)

        # condition on feature
        if feature not in [undefined, none, one_or_more]:
            wheres.append(Q(graph_components__features__name=feature))
        if feature in [one_or_more]:
            wheres.append(Q(graph_components__features__id__isnull=False))

        # ANDs all the Q() where clauses together
        if wheres:
            where_and = wheres.pop(0)
            for where in wheres:
                where_and = where_and & where

            graphs = graphs.filter(where_and)

        # Treat the feature=none case
        if feature == none:
            excluded_q = Q(graph_components__features__id__isnull=False)
            if component:
                excluded_q = excluded_q & Q(
                    graph_components__component__name=component)
            excluded_graphs = Graph.objects.filter(excluded_q)
            graphs = graphs.exclude(
                id__in=excluded_graphs.values_list('id', flat=True))

        from digipal.utils import set_left_joins_in_queryset, get_str_from_queryset
        set_left_joins_in_queryset(graphs)
        # print get_str_from_queryset(graphs)

        t2 = datetime.now()

        # Get the graphs then id of all the related Hands
        # We use values_list because it is much faster, we don't need to fetch all the Hands at this stage
        # That will be done after pagination in the template
        # Distinct is needed here.
        #graphs = graphs.distinct().order_by('hand__scribe__name', 'hand__id', 'idiograph__allograph__character__ontograph__sort_order')
        chrono('graph filter:')
        graphs = graphs.distinct().order_by('hand__scribe__name', 'hand__id')
        chrono(':graph filter')

        # print graphs.query
        chrono('graph values_list:')
        graph_ids = graphs.values_list('id', 'hand_id')
        chrono(':graph values_list')

#        chrono('len:')
#        l = len(graph_ids)
#        print graph_ids.query
#        chrono(':len')

        # Build a structure that groups all the graph ids by hand id
        # context['hand_ids'] = [[1, 101, 102], [2, 103, 104]]
        # In the above we have two hands: 1 and 2. For hand 1 we have Graph 101
        # and 102.
        chrono('hand_ids:')
        context['hand_ids'] = [[0]]
        last = 0
        for g in graph_ids:
            if g[1] != context['hand_ids'][-1][0]:
                context['hand_ids'].append([g[1]])
            context['hand_ids'][-1].append(g[0])
        del(context['hand_ids'][0])
        chrono(':hand_ids')

        t3 = datetime.now()

        self.graphs_count = len(graph_ids)

        t4 = datetime.now()

        # print 'search %s; hands query: %s + graph count: %s' % (t4 - t0, t3 -
        # t2, t4 - t3)

        t5 = datetime.now()
        self._queryset = context['hand_ids']

        return self._queryset
Example #15
0
def update_text_image_link(request, image, ret):
    if ret is None:
        ret = {}
    ret['newids'] = {}

    from digipal.models import has_edit_permission, Annotation
    if not has_edit_permission(request, Annotation):
        set_message(ret, 'Insufficient rights to edit annotations', 'error')
        return ret

    # print 'TEXT IMAGE LINK: image #%s' % image.id
    links = dputils.get_request_var(request, 'links', None)
    if links:
        ''' links = [
                        [
                            [["", "clause"], ["type", "address"]],
                            {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[270,-1632],[270,-984],[3006,-984],[3006,-1632],[270,-1632]]]},"properties":null}
                        ],
                        [...]
                    ]
        '''
        for link in json.loads(links):
            # print link
            attrs, geojson = link[0], link[1]
            clientid = (geojson.get('properties', {})
                        or {}).pop('clientid', '')
            serverid = geojson.pop('id', None)
            action = geojson.pop('action', 'update')

            # 1. find the annotation
            if not clientid and not serverid:
                raise Exception(
                    'Cannot find annotation, need either "id" or "clientid"')

            filter = {}
            if serverid:
                filter['id'] = serverid
            else:
                filter['clientid'] = clientid
            annotation = Annotation.objects.filter(**filter).first()

            # 2. delete or create annotation
            if action == 'deleted':
                if annotation:
                    # print 'delete annotation'
                    annotation.delete()
                    annotation = None
            else:
                # create annotation
                if not annotation:
                    if serverid:
                        raise Exception(
                            'Cannot find annotation by server id %s' %
                            serverid)
                    # print 'create annotation'
                    author = request.user
                    annotation = Annotation(clientid=clientid,
                                            image=image,
                                            author=author,
                                            type='text')

                # update annotation and link
                # print 'update annotation'
                annotation.set_geo_json_from_dict(geojson)
                annotation.save()
                if not serverid:
                    ret['newids'][clientid] = annotation.id

                # update the text-annotation
                from digipal_text.models import TextAnnotation
                # text_annotation = TextAnnotation(annotation=annotation, element=json.dumps(attrs))
                # TODO: assume a single element per annotation for the moment
                text_annotation = TextAnnotation.objects.filter(
                    annotation=annotation).first()
                if not attrs:
                    # delete it
                    if text_annotation:
                        # print 'delete link'
                        text_annotation.delete()
                else:
                    if not text_annotation:
                        # print 'create link'
                        text_annotation = TextAnnotation(annotation=annotation)
                    # print 'update link'
                    text_annotation.elementid = json.dumps(attrs)
                    text_annotation.save()

    return ret