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)