from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
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
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('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('items')
else:
user_collections = False
collections = Collection.objects.filter(author=user).filter(public=True).order_by('-creation').select_related('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):
col = get_object_or_404(Collection.objects.select_related('items'), pk=collection_pk)
return render_to_response("egonomy_view_collection.html",
{'col':col},
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