src/egonomy/views.py
author Anthony Ly <anthonyly.com@gmail.com>
Fri, 28 Jun 2013 12:10:28 +0200
changeset 201 4b67b2e26925
parent 168 c90576d18319
child 191 d7b30914607d
permissions -rw-r--r--
replace all list-projects by list-style

from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.core.urlresolvers import reverse
from django.db.models.aggregates import Max
from django.http.response import HttpResponseForbidden, HttpResponse
from django.shortcuts import get_object_or_404, render_to_response, redirect
from django.template import RequestContext
from django.utils.translation import ugettext as _
from egonomy.models import ImageMetadata, Image, Fragment, ImageInfo, Collection,\
    CollectionItem
from egonomy.search_indexes import QueryParser
from egonomy.search_indexes.paginator import SearchPaginator
from egonomy.search_indexes.query import ModelRelatedSearchQuerySet
from haystack.query import RelatedSearchQuerySet
from sorl.thumbnail import default, get_thumbnail
from sorl.thumbnail.images import ImageFile
from unicodedata import normalize
import json
import os
import requests
import subprocess
import uuid

import logging
logger = logging.getLogger(__name__)


def home(request):
    
    # We force list() because if not, img_id_list is a valuelistqueryset and not a list of values.
    # We separate image requests and make requests with select_related to optimize database hits.
    nb_display = 4
    img_id_list = list(Image.objects.values_list('id', flat=True).annotate(date_modif=Max('fragment__date_saved')).exclude(date_modif=None).order_by('-date_modif')[:nb_display])
    img_list = Image.objects.filter(id__in=img_id_list).select_related('info', 'metadata')
    frg_list = Fragment.objects.all().order_by('-date_saved').select_related('image', 'image__info', 'image__metadata','author')[:nb_display]
    
    return render_to_response("egonomy_home.html",
                              {'img_list':img_list, 'fragment_list':frg_list, 'current_user_collection_list':current_user_collection_list(request)},
                              context_instance=RequestContext(request))


def annotate_picture(request, image_id):

    img = get_object_or_404(Image.objects.select_related('info', 'metadata'), id=image_id)
    
    # We force list() to get only one database hit instead of 3 (first request, count, [0])
    frg_list = list(Fragment.objects.filter(image=img).order_by('-date_saved').select_related('image', 'image__info', 'image__metadata','author'))
    last_frg = None
    if len(frg_list)>0:
        last_frg = frg_list[0]
    
    return render_to_response("egonomy_annotate_picture.html",
                              {'img': img, 'fragment_list': frg_list, 'last_frg':last_frg,
                               'current_user_collection_list':current_user_collection_list(request)},
                              context_instance=RequestContext(request))


def view_fragment(request, fragment_pk):
    
    frg = get_object_or_404(Fragment.objects.select_related('image', 'image__info', 'image__metadata','author'), pk=fragment_pk)
    frg_list = Fragment.objects.filter(image=frg.image).select_related('image', 'image__info', 'image__metadata','author')
    
    fragment_only = {'true': True, 'false': False, "0": False, "1": True}.get((request.GET.get("fragment_only") or "1").lower())
    
    return render_to_response("egonomy_view_fragment.html",
                              {'fragment': frg, 'fragment_list': frg_list, 'fragment_only':fragment_only,
                               'current_user_collection_list':current_user_collection_list(request)},
                              context_instance=RequestContext(request))

@login_required
def create_fragment(request, image_id, fragment_pk=None):
    
    img = get_object_or_404(Image.objects.select_related('info', 'metadata'), id=image_id)
    frg_list = Fragment.objects.filter(image=img).order_by('-date_saved').select_related('image', 'image__info', 'image__metadata','author')
    frg_to_modify = False
    frg_data = None
    if fragment_pk:
        frg_data = get_object_or_404(Fragment.objects.select_related('author'), pk=fragment_pk)
        # We check if the current user is the fragment's author
        if frg_data.author != request.user:
            return HttpResponseForbidden(_("You are not allowed to modify this fragment."))
        frg_to_modify = True
    else:
        frg_duplicate_pk = request.GET.get("duplicate") or None
        if frg_duplicate_pk:
            frg_data = get_object_or_404(Fragment.objects.select_related('author'), pk=frg_duplicate_pk)
    
    return render_to_response("egonomy_create_fragment.html",
                              {'img': img, 'frg_data': frg_data,  'frg_to_modify': frg_to_modify, 'fragment_list': frg_list},
                              context_instance=RequestContext(request))

@login_required
def save_fragment(request):
    
    frg_title = request.POST["fragment_title"]
    frg_desc = request.POST["fragment_description"]
    frg_kw = request.POST["user_keywords"]
    frg_path = request.POST["fragment_path"]
    frg_image_id = request.POST["image_id"]
    if "fragment_pk" in request.POST:
        frg_pk = request.POST["fragment_pk"]
        frg = get_object_or_404(Fragment.objects.select_related('author'), pk=frg_pk)
        # We check if the current user is the fragment's author
        if frg.author != request.user:
            return HttpResponseForbidden(_("You are not allowed to modify this fragment."))
    else :
        img = get_object_or_404(Image, id=frg_image_id)
        frg = Fragment()
        frg.image = img
        frg.author = request.user
    
    frg.coordinates = frg_path
    frg.title = frg_title
    frg.description = frg_desc
    frg.tags = frg_kw
    frg.save()
    
    return redirect("view_fragment", fragment_pk=frg.pk)


def all_pictures(request):
    
    # Get the cur_page_nb number parameter if possible
    cur_page_nb = request.GET.get("page") or 1
    cur_page_nb = int(cur_page_nb)

    search = None
    nb_results = 1
    if "search" in request.GET:
        search = request.GET["search"]
        field = "all"
        if "field" in request.GET:
            field = request.GET["field"]

    img_list = Image.objects.select_related('info', 'metadata')
    nb = request.GET.get("limit") or getattr(settings,"IMAGES_PER_PAGE", 32)
    if search:
        if not field or field == 'all':
            field = 'text'
        qp = QueryParser(field)
        res = ModelRelatedSearchQuerySet(model=Image).filter(qp.parse(search)).models(ImageMetadata).load_all_queryset(Image, img_list).load_all()
        paginator = SearchPaginator(res, nb)
    else:
        img_list = img_list.order_by('pk').all()
        paginator = Paginator(img_list, nb)
    
    try:
        results = paginator.page(cur_page_nb)
    except (EmptyPage, InvalidPage):
        results = paginator.page(paginator.num_pages)
    
    url_pagination = reverse("all_pictures") + "?limit=" + str(nb)
    if search:
        url_pagination = url_pagination + "&search=" + search

    return render_to_response("egonomy_all_pictures.html",
                              {'results':results, 'nb_pages':paginator.num_pages, 'cur_page_nb':cur_page_nb, "search":search, "nb_results":nb_results, "url_pagination":url_pagination,
                               'current_user_collection_list':current_user_collection_list(request)},
                              context_instance=RequestContext(request))


def all_fragments(request):
        
    # Get the cur_page_nb number parameter if possible
    cur_page_nb = request.GET.get("page") or 1
    cur_page_nb = int(cur_page_nb)

    search = None
    nb_results = 1
    if "search" in request.GET:
        search = request.GET["search"]
        field = "all"
        if "field" in request.GET:
            field = request.GET["field"]

    frg_list = Fragment.objects.select_related('image', 'image__info', 'image__metadata','author')
    nb = request.GET.get("limit") or getattr(settings,"IMAGES_PER_PAGE", 32)
    if search:
        if not field or field == 'all':
            field = 'text'
        qp = QueryParser(field)
        res = RelatedSearchQuerySet().filter(qp.parse(search)).models(Fragment).load_all_queryset(Fragment, frg_list).load_all().highlight()
        paginator = Paginator(res, nb)
    else:
        frg_list = frg_list.order_by('pk').all()
        paginator = Paginator(frg_list, nb)
        
    try:
        results = paginator.page(cur_page_nb)
    except (EmptyPage, InvalidPage):
        results = paginator.page(paginator.num_pages)
        
    url_pagination = reverse("all_fragments") + "?limit=" + str(nb)
    if search:
        url_pagination = url_pagination + "&search=" + search

    return render_to_response("egonomy_all_fragments.html",
                              {"search_fragment":True, 'results':results, 'nb_pages':paginator.num_pages, 'cur_page_nb':cur_page_nb, "search":search, "nb_results":nb_results, "url_pagination":url_pagination,
                               'current_user_collection_list':current_user_collection_list(request)},
                              context_instance=RequestContext(request))


def user_fragments(request, username):
        
    # Get the cur_page_nb number parameter if possible
    cur_page_nb = request.GET.get("page") or 1
    cur_page_nb = int(cur_page_nb)
    # get the username
    user = get_object_or_404(User, username=username)
    
    frg_list = Fragment.objects.filter(author=user).order_by('-date_saved').select_related('image', 'image__info', 'image__metadata','author')
    nb = request.GET.get("limit") or getattr(settings,"IMAGES_PER_PAGE", 32)
    paginator = Paginator(frg_list, nb)
        
    try:
        results = paginator.page(cur_page_nb)
    except (EmptyPage, InvalidPage):
        results = paginator.page(paginator.num_pages)
        
    url_pagination = reverse("user_fragments", args=[username]) + "?limit=" + str(nb)

    return render_to_response("egonomy_all_fragments.html",
                              {"user_fragments":True, 'username':username, 'results':results, 'nb_pages':paginator.num_pages, "search":None, 'cur_page_nb':cur_page_nb, "url_pagination":url_pagination,
                               'current_user_collection_list':current_user_collection_list(request)},
                              context_instance=RequestContext(request))


def delete_fragment(request):
    
    if "fragment_pk" in request.GET:
        frg_pk = request.GET["fragment_pk"]
        frg = get_object_or_404(Fragment.objects.select_related('author'), pk=frg_pk)
        # We check if the current user is the fragment's author
        if frg.author != request.user:
            return HttpResponseForbidden(_("You are not allowed to delete this fragment."))
        # Delete the fragment
        frg.delete()
    if "next" in request.GET:
        # Redirect to the next parameter
        return redirect(request.GET["next"])
    else:
        # Redirect to the next parameter
        return redirect(reverse("user_fragments", args=[request.user.username]))



def senseetive_api(request):
        
    # Get the cur_page_nb number parameter if possible
    image_id = request.GET.get("image") or None
    frg_path = request.GET.get("path") or "MZ"
    if not image_id or frg_path=="MZ":
        return HttpResponseForbidden("The request needs an image and a not null path parameters.")
     
    img = get_object_or_404(Image.objects.select_related('info', 'metadata'), id=image_id)
    frg = Fragment()
    frg.image = img
    frg.coordinates = frg_path
    # We build the svg xml
    file = os.path.join(settings.MEDIA_ROOT, str(img.info.image_file))
    image_file = default.kvstore.get_or_set(ImageFile(img.info.image_file))
    ratio = int(100 * frg.ratio * image_file.ratio)
    svg = '<svg preserveAspectRatio="none" width="' + str(ratio) + 'px" height="100px" viewBox="' + frg.viewbox +'" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">\n\
    <defs>\n\
       <clipPath id="fragment-clip">\n\
           <path d="' + frg_path + '" />\n\
       </clipPath>\n\
    </defs>\n\
    <image xlink:href="' + file + '" x="0" y="0" preserveAspectRatio="none" width="1" height="1" opacity=".1"/>\n\
    <image xlink:href="' + file + '" x="0" y="0" preserveAspectRatio="none" width="1" height="1" clip-path="url(#fragment-clip)"/>\n\
</svg>'
    # We save the svg file
    uid = str(uuid.uuid1())
    svg_file_path = os.path.join(settings.BATIK_RASTERIZER_TEMP_FOLDER, uid + '.svg')
    svg_file = open(svg_file_path,'w')
    svg_file.write(svg)
    svg_file.close()
    # We execute the batik command
    args = ["java", "-jar", settings.BATIK_RASTERIZER_PATH, svg_file.name]
    p = subprocess.Popen(args, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    out, err = p.communicate()
    if p.returncode!=0:
        return HttpResponse("Batik error : " + str(err))
    
    # The picture png is now created, we log into the senseetive api
    params = {
              "xaction":"sessions.create",
              "login":settings.SENSEETIVE_API_USER,
              "pwd":settings.SENSEETIVE_API_PASSWORD,
              "xtenant":settings.SENSEETIVE_API_TENANT,
              "role":"user"
    }
    req = requests.get(url=settings.SENSEETIVE_API_URL, params=params, verify=False)
    resp = req.json()
    if "xsessionid" in resp:
        session_id = resp["xsessionid"]
    else:
        return HttpResponseForbidden("Failed to connect to Senseetive API (1)")
    
    # We create the contents.read request and load the picture
    png_file_path = os.path.join(settings.BATIK_RASTERIZER_TEMP_FOLDER, uid + '.png')
    # (queryid 50d0574a03000043102a5177 for AS algorythm, 
    # 50d0574a0300000f102a5177 pigment)
    # 50d0574a03000021102a5177 Gabor
    files = {
              'image0':open(png_file_path, 'rb').read(),
    }
    params = {
              "xaction":"contents.read",
              "xsessionid":session_id,
              "queryid":"50d0574a03000043102a5177",
              "_order_by_":"score[:desc]",
              "_row_first_":"0",
              "_row_last_":"10",
              "requestfieldname":"image0"
    }
    # We make the request
    req = requests.post(url=settings.SENSEETIVE_API_URL, data=params, files=files, verify=False)
    resp = req.json()
    # Now that we have a response, we can remove the svg and png files
    try:
        os.remove(svg_file_path)
    except OSError:
        pass
    try:
        os.remove(png_file_path)
    except OSError:
        pass
    # We parse the response
    if "contentlist" not in resp:
        return HttpResponseForbidden("Failed to connect to Senseetive API (3) : " + str(resp))
    # We get the content list
    contents = resp["contentlist"]
    keywords = {}
    images_data = []
    for c in contents:
        # For each content's keyword, we associate or add its score.
        score = c["contentimagelist"][0]["score"]
        kwl = c["keywordlist"]
        for kw in kwl:
            kw = normalize('NFC',kw)
            if kw in keywords:
                keywords[kw] = keywords[kw] + score
            else:
                keywords[kw] = score
        # We add the image and its metadatas in the images list. We take the id from the filename "id.jpg".
        images_data.append({"id":c["contentimagelist"][0]["filename"][:-4], "url":"", "keywords":kwl, "title":c["title"]})
    # We generate the thumbnail and add its url for each picture
    images = ImageInfo.objects.filter(id__in=[i["id"] for i in images_data])
    for i in images_data:
        # We allow ourselves to loop in a loop because we know images_data has only 10 entries.
        for img in images:
            if img.id==i["id"]:
                # Get the thumbnail with fixed width
                i["url_width"] = get_thumbnail(img.image_file, '100', format='PNG', crop='center', quality=99).url
                # Get the thumbnail with fixed height
                i["url_height"] = get_thumbnail(img.image_file, 'x100', format='PNG', crop='center', quality=99).url
                break
    
    # We sort the keywords by descending score
    keywords = sorted(keywords, key=keywords.get, reverse=True)
    
    return HttpResponse(json.dumps({"keywords":keywords, "images":images_data}), content_type="application/json")



def all_collections(request):
        
    # Get the cur_page_nb number parameter if possible
    cur_page_nb = request.GET.get("page") or 1
    cur_page_nb = int(cur_page_nb)
    
    collections = Collection.objects.filter(public=True).order_by('-creation').select_related('author', 'items')
    nb = request.GET.get("limit") or getattr(settings,"IMAGES_PER_PAGE", 32)
    paginator = Paginator(collections, nb)
        
    try:
        results = paginator.page(cur_page_nb)
    except (EmptyPage, InvalidPage):
        results = paginator.page(paginator.num_pages)
        
    url_pagination = reverse("all_collections") + "?limit=" + str(nb)

    return render_to_response("egonomy_all_collections.html",
                              {"user_collections":False, 'results':results, 'nb_pages':paginator.num_pages, 'cur_page_nb':cur_page_nb, "url_pagination":url_pagination},
                              context_instance=RequestContext(request))



def user_collections(request, username):
        
    # Get the cur_page_nb number parameter if possible
    cur_page_nb = request.GET.get("page") or 1
    cur_page_nb = int(cur_page_nb)
    # get the username
    user = get_object_or_404(User, username=username)
    
    if user==request.user:
        user_collections = True
        collections = Collection.objects.filter(author=user).order_by('-creation').select_related('author', 'items')
    else:
        user_collections = False
        collections = Collection.objects.filter(author=user).filter(public=True).order_by('-creation').select_related('author', 'items')
        
    nb = request.GET.get("limit") or getattr(settings,"IMAGES_PER_PAGE", 32)
    paginator = Paginator(collections, nb)
        
    try:
        results = paginator.page(cur_page_nb)
    except (EmptyPage, InvalidPage):
        results = paginator.page(paginator.num_pages)
        
    url_pagination = reverse("user_collections", args=[username]) + "?limit=" + str(nb)
    
    return render_to_response("egonomy_all_collections.html",
                              {"user_collections":user_collections, 'username':username, 'results':results, 'nb_pages':paginator.num_pages, 'cur_page_nb':cur_page_nb, "url_pagination":url_pagination},
                              context_instance=RequestContext(request))



@login_required
def new_collection(request):
        
    col_title = request.POST["collection-title"]
    col_desc = request.POST["collection-description"]
    
    col = Collection()
    col.title = col_title
    col.description = col_desc
    col.author = request.user
    col.save()
    
    return redirect("view_collection", collection_pk=col.pk)




def view_collection(request, collection_pk):
    
    display = request.GET.get("display") or "slideshow"
    if display!="slideshow" and display!="mosaic" and display!="geographical":
        display = "slideshow"
    
    col = get_object_or_404(Collection.objects.select_related('author'), pk=collection_pk)
    items = CollectionItem.objects.filter(collection=col).select_related('author', 'content_type', 'object_id', 'content_object').order_by("order")
    
    return render_to_response("egonomy_view_collection.html",
                              {'col':col, 'items':items, 'display':display,
                               'current_user_collection_list':current_user_collection_list(request)},
                              context_instance=RequestContext(request))




def current_user_collection_list(request):
    
    if request.user.is_authenticated():
        return Collection.objects.filter(author=request.user).order_by("title")
    else:
        return None




def add_item_to_collection(request):
    
    col_pk = request.POST["collection-pk"]
    item_desc = request.POST["item-description"]
    item_type = request.POST["item-type"]
    item_id = request.POST["item-id"]
    
    # Test collection
    try:
        col_pk = int(col_pk)
    except:
        return HttpResponse("Collection number invalid.", status_code=400)
    col = get_object_or_404(Collection, pk=col_pk)
    
    if item_type=="image":
        # Test image
        item = get_object_or_404(Image, id=item_id)
    elif item_type=="fragment":
        # Test fragment
        try:
            item_id = int(item_id)
        except:
            return HttpResponse("Fragment number invalid.", status_code=400)
        item = get_object_or_404(Fragment, pk=item_id)
    else:
        return HttpResponse("item-type must be 'image' or 'fragment'.", status_code=400)
    
    col_item = CollectionItem()
    col_item.collection = col
    col_item.content_type = ContentType.objects.get_for_model(item)
    col_item.object_id = str(item_id)
    col_item.content_object = item
    col_item.description = item_desc
    # Get the collection's max order and set the new item's one
    max_order = CollectionItem.objects.filter(collection=col).aggregate(Max('order'))['order__max']
    if not max_order:
        max_order = 0
    col_item.order = max_order + 1
    col_item.save()
        
    return redirect("view_collection", collection_pk=col.pk)