Gitlab CSE Unil

Commit f2e1f9ab authored by Julien Furrer's avatar Julien Furrer
Browse files

Mis en place architecture pour environment spécifique

Utilisé CIMAF comme environement de test.
Nombreuses adaptations, notamment pour le gestionaire d'image
parent 46db4303
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('adim', '0005_anobj_env'),
]
operations = [
migrations.CreateModel(
name='EnvParam',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('cimaf_cut_pos', models.IntegerField(null=True, blank=True)),
('cimaf_cut_margin', models.IntegerField(null=True, blank=True)),
('anobj', models.OneToOneField(to='adim.AnObj')),
],
options={
'verbose_name': 'Environment params',
},
),
]
# coding=utf-8
from annotables import AOType, AOSchema, AOChoice, AOAttribute, AnObj, AnObjMembership
from annotables import AOType, AOSchema, AOChoice, AOAttribute, AnObj, AnObjMembership, EnvParam
from annotations import Annotation
__all__ = (
'AOType', 'AOSchema', 'AOChoice', 'AOAttribute', 'AnObj',
'AnObjMembership', 'Annotation'
'AnObjMembership', 'Annotation', 'EnvParam'
)
......@@ -20,7 +20,7 @@ from eav.models import BaseSchema, BaseAttribute, BaseChoice
from adim_utils.decorators import cache
__all__ = ('AOType', 'AOSchema', 'AOChoice', 'AOAttribute', 'AnObj', 'AnObjMembership')
__all__ = ('AOType', 'AOSchema', 'AOChoice', 'AOAttribute', 'AnObj', 'AnObjMembership', 'EnvParam')
# code from from uuid._random_getnode()
RANDOM_NODE = random.randrange(0, 1 << 48L) | 0x010000000000L
......@@ -141,7 +141,6 @@ class AnObj(models.Model):
:return:
"""
new_anobj = False
# set the uuid upon object creation
if not self.uuid:
self.set_uuid(no_save=True)
new_anobj = True
......@@ -151,6 +150,10 @@ class AnObj(models.Model):
if len(self.owners.all()) == 0:
self.owners.add(self.owner)
if self.env:
envparam, _ = EnvParam.objects.get_or_create(anobj=self)
envparam.init()
def set_uuid(self, no_save=False):
"""
Calculate and set the UUID, only if not already set
......@@ -252,3 +255,29 @@ class AnObjMembership(models.Model):
class Meta:
app_label = "adim"
verbose_name = "Annotable Object Membership"
class EnvParam(models.Model):
anobj = models.OneToOneField(AnObj)
# ----- CIMAF
cimaf_cut_pos = models.IntegerField(blank=True, null=True)
cimaf_cut_margin = models.IntegerField(blank=True, null=True)
class Meta:
app_label = "adim"
verbose_name = "Environment params"
def init(self):
need_to_save = False
if self.anobj.env == 'cimaf':
# --- CIMAF ---
if self.cimaf_cut_pos is None:
self.cimaf_cut_pos = self.anobj.image.width / 2
need_to_save = True
if self.cimaf_cut_margin is None:
self.cimaf_cut_margin = 25
need_to_save = True
if need_to_save:
self.save()
......@@ -2,7 +2,7 @@
from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from rest_framework import serializers
from adim.models import AnObj, Annotation
from adim.models import AnObj, Annotation, EnvParam
import json
ANOBJ_THUMB_CACHE_BASE_KEY = 'anobj_thumb_url'
......@@ -34,6 +34,11 @@ class AnnotationSerializer(serializers.ModelSerializer):
# else:
# return ""
class EnvParamSerializer(serializers.ModelSerializer):
class Meta:
model = EnvParam
fields = ('cimaf_cut_pos', 'cimaf_cut_margin')
class BaseAnObjSerializer(serializers.ModelSerializer):
# owner_name = serializers.ReadOnlyField(source='owner.username')
......@@ -49,11 +54,32 @@ class BaseAnObjSerializer(serializers.ModelSerializer):
# image_thumb = serializers.Field(source='thumb_url')
# image_thumb = serializers.SerializerMethodField('get_image_thumb')
# envparam = serializers.SerializerMethodField()
# envparam = EnvParamField(queryset=EnvParam.objects.all())
envparam = EnvParamSerializer()
class Meta:
model = AnObj
fields = ('id', 'uuid', 'name', 'owners', 'owner', 'owner_name', 'annotations', 'sharing_mode', 'locked')
fields = ('id', 'uuid', 'name', 'owners', 'owner', 'owner_name', 'annotations', 'sharing_mode', 'locked',
'env', 'envparam')
read_only_fields = ('members',)
def update(self, instance, validated_data):
try:
envparam_data = validated_data.pop('envparam')
except KeyError:
envparam_data = None
anobj = super(BaseAnObjSerializer, self).update(instance, validated_data)
if envparam_data and anobj.env:
envparam = anobj.envparam
envparam_serializer = self.fields.fields.get('envparam')
if envparam_serializer:
envparam_serializer.update(envparam, envparam_data)
return anobj
def get_annotations(self, anobj):
request = self.context.get('request')
annot_serializer = AnnotationSerializer(instance=anobj.annotations.filter(owner=request.user), many=True,
......@@ -63,6 +89,12 @@ class BaseAnObjSerializer(serializers.ModelSerializer):
def get_owner_name(self, anobj):
return anobj.owners.all()[0].username
def get_envparam(self, anobj):
if anobj.env:
return EnvParamSerializer(instance=anobj.envparam, context=self.context).data
else:
return None
# def get_sharing_opts(self, anobj):
# request = self.context.get('request')
# if request is not None and request.user == anobj.owner:
......@@ -86,14 +118,14 @@ class BaseAnObjSerializer(serializers.ModelSerializer):
# cache.set(cache_key, im_url, None)
# return im_url
#
def attrs_as_dict(self, obj):
return None
# obj_dict = dict((a.schema.name, getattr(obj, a.schema.name)) for a in obj.attrs.all())
# return json.loads(json.dumps(obj_dict, default=unicode))
def attrs_as_json(self, obj):
return json.dumps(self.attrs_as_dict(obj), default=unicode)
#
# def attrs_as_dict(self, obj):
# return None
# # obj_dict = dict((a.schema.name, getattr(obj, a.schema.name)) for a in obj.attrs.all())
# # return json.loads(json.dumps(obj_dict, default=unicode))
#
# def attrs_as_json(self, obj):
# return json.dumps(self.attrs_as_dict(obj), default=unicode)
class AnObjSerializer(BaseAnObjSerializer):
......@@ -102,7 +134,7 @@ class AnObjSerializer(BaseAnObjSerializer):
class Meta:
model = AnObj
fields = ('id', 'uuid', 'name', 'owners', 'owner', 'owner_name', 'annotations', 'members',
'sharing_mode', 'sharing_opts', 'locked', 'allow_public_publishing', 'env')
'sharing_mode', 'sharing_opts', 'locked', 'allow_public_publishing', 'env', 'envparam')
class SharedAnObjSerializer(BaseAnObjSerializer):
......@@ -111,7 +143,7 @@ class SharedAnObjSerializer(BaseAnObjSerializer):
class Meta:
model = AnObj
fields = ('id', 'uuid', 'name', 'owners', 'owner', 'owner_name', 'annotations', 'locked',
'sharing_mode', 'sharing_opts', 'allow_public_publishing')
'sharing_mode', 'sharing_opts', 'allow_public_publishing', 'env', 'envparam')
def get_sharing_opts(self, anobj):
if anobj.sharing_mode > 15:
......
......@@ -113,7 +113,7 @@ class AnObjViewSet(viewsets.ModelViewSet):
if self.action == 'list':
q = q | Q(members=user)
return AnObj.objects.filter(q).distinct()
return AnObj.objects.select_related('envparam').filter(q).distinct()
@detail_route(methods=['get', 'patch', 'post', 'delete'])
def members(self, request, pk=None):
......@@ -182,6 +182,10 @@ class AnObjViewSet(viewsets.ModelViewSet):
return Response({'publish_mode': membership.publish_mode})
@detail_route(methods=['patch'])
def env_param(self, request, pk=None):
pass
class UAnObjViewSet(AnObjViewSet):
"""
......
......@@ -20,13 +20,14 @@
*/
define([
"underscore",
"paper",
"signals",
// ----- app
"adim/view"
],
function (paper, Signal, view) {
function (_, paper, Signal, view) {
// ----- Locale variables -----------------------------
var _events = {
......@@ -255,6 +256,15 @@ function (paper, Signal, view) {
//return paper.project.currentStyle.strokeWidth;
}
var _updateStrokeWidth = _.debounce(setStrokeWidth, 300);
function deltaStrokeWidth(delta) {
var dVal = Math.abs(delta); // Unsigned delta
var dDir = dVal / delta; // Delta direction [-1,1]
var d = (1 + Math.floor(dVal/3)) * dDir;
setStrokeWidth(getStrokeWidth() + d, false);
_updateStrokeWidth(getStrokeWidth());
}
function strokeWidthSelectedItems(width, fireAnnotationChange) {
_applyAttributeToSelection({
setCallbackName: 'setStrokeWidth',
......@@ -354,8 +364,8 @@ function (paper, Signal, view) {
_events.zoomChanged.dispatch(zoom);
}
function deltaZoom(delta, fixedPt) {
var dMax = 20, dMin = 5; // Max and Min delta used for normalization
function deltaZoom(delta, fixedPt, dMin) {
var dMax = 20; dMin = isNaN(dMin) ? 5 : dMin; // Max and Min delta used for normalization
if (delta !== 0) {
var dVal = Math.abs(delta); // Unsigned delta
var dDir = dVal / delta; // Delta direction [-1,1]
......@@ -388,6 +398,7 @@ function (paper, Signal, view) {
setStrokeWidth: setStrokeWidth,
getStrokeWidth: getStrokeWidth,
deltaStrokeWidth: deltaStrokeWidth,
setOpacity: setOpacity,
getOpacity: getOpacity,
......
......@@ -81,11 +81,15 @@ define([
var discordantstrTool = tools.tools['discordantstr'];
var cmsareaTool = tools.tools['cmsarea'];
var cutPos = annotableData.envparam && annotableData.envparam.cimaf_cut_pos;
var cutMargin = annotableData.envparam && annotableData.envparam.cimaf_cut_margin;
view.events.imageLoaded.add(function(p){
var cutPos = p.raster.scaling.x * p.raster.width / 2 ;
var cutMargin = 25;
cutPos = cutPos || p.raster.scaling.x * p.raster.width / 2 ;
cutMargin = cutMargin || 25;
concordantstrTool.listeners.onCutPositionChanged(cutPos);
concordantstrTool.listeners.onCutMarginChanged(cutMargin);
discordantstrTool.listeners.onCutPositionChanged(cutPos);
cmsareaTool.listeners.onCutPositionChanged(cutPos);
cmsareaTool.listeners.onSetAreaWidth(cutMargin*2);
......
......@@ -322,7 +322,7 @@ function ($, paper, Signal, config, view, io, tools, attributes, ui, Users, them
activeTool = null;
}
});
//### DEBUG CODE ###
window.ADIM = { tools: tools };
console.timeEnd("main dom ready");
......
......@@ -14,7 +14,7 @@ define(["paper", "helper/utils"], function (paper, utils) {
var activated = false;
var _cutPosition = 0;
var _cutThreshold = 10; // Le dépassement minimum audela du couteau pour que la concordance soit valide
var striaStyle = {
strokeWidth: 3,
......@@ -85,6 +85,10 @@ define(["paper", "helper/utils"], function (paper, utils) {
_cutPosition = newCutPosition;
}
function _cutMarginChanged(newCutMargin) {
_cutThreshold = Math.floor(1.5 * newCutMargin);
}
function onToolActivated(toolName) {
activated = toolName === TOOL_TYPE;
}
......@@ -101,7 +105,7 @@ define(["paper", "helper/utils"], function (paper, utils) {
var _striaChanged = false;
var _cutThreshold = 50; // Le dépassement minimum audela du couteau pour que la concordance soit valide
var defaultProperties = {
confidence: 3,
......@@ -182,7 +186,6 @@ define(["paper", "helper/utils"], function (paper, utils) {
if (_editedSegmentIdx !== null) {
editStria(event);
} else if (_editedStria) {
// TODO: valider la position par rapport au couteau
if (_editedStria.segments[_editedStriaLeftIdx].point.x + event.delta.x <= _cutPosition - _cutThreshold &&
_editedStria.segments[_editedStriaRightIdx].point.x + event.delta.x >= _cutPosition + _cutThreshold) {
......@@ -473,6 +476,7 @@ define(["paper", "helper/utils"], function (paper, utils) {
importJSON: importJSON,
listeners: {
onCutPositionChanged: _cutPositionChanged,
onCutMarginChanged: _cutMarginChanged,
onToolActivated: onToolActivated
}
};
......
......@@ -256,7 +256,7 @@ define([
paper.project.deselectAll();
_events.annotationSelected.dispatch(null);
paper.view.draw();
if (toolHitResult.type !== 'pixel') {
if (toolHitResult && toolHitResult.type !== 'pixel') {
hitItem = toolHitResult && toolHitResult.item;
if (hitItem && hitItem.parent.data.type) {
hitItem = hitItem.parent;
......
......@@ -528,6 +528,7 @@ function($, _, Signal, paper, config, view, io, tools, attributes, Users, export
// Annotable Event handler
// ------------------------
io.events.annotableChanged.add(function(data){
console.log("annotableChanged");
// Update name displayed in the navbar,
// only if the modification is applied to the currently edited anobj
if (data.id === config.annotable.id) {
......@@ -965,9 +966,13 @@ function($, _, Signal, paper, config, view, io, tools, attributes, Users, export
}, 500);
}
}
else if (event.altKey || event.metaKey) {
else if (event.metaKey) {
event.preventDefault();
attributes.deltaZoom(event.deltaY / 20, [event.offsetX, event.offsetY]);
attributes.deltaZoom(event.deltaY / 20, [event.offsetX, event.offsetY], 2)
}
else if (event.altKey) {
event.preventDefault();
attributes.deltaStrokeWidth(event.deltaY / 20);
}
else {
event.preventDefault();
......
......@@ -42,14 +42,14 @@ define([
initialize: function () {
this.annotateBaseUrl = document.location.protocol + "//" + document.location.host + config.baseUrl;
this.infoPanel = $("#aom-info-panel");
this.thumbEl = this.$el.find(".panel-body .aom-image-thumb");
this.propListEl = this.$el.find(".panel-body .aom-info-prop");
this.sharingPropEl = this.$el.find(".panel-body .aom-info-share");
this.advancedPropEl = this.$el.find(".aom-info-advanced");
this.publicPublishEl = this.$("#aom-prop-publishall");
this.propOwnerEl = this.$el.find(".aom-prop-owner").prev('dt').andSelf();
//this.propOwnerEl = this.$el.find(".aom-prop-owner").closest('.form-group');
//this.propOwnerEl = this.$el.find(".aom-prop-owner").prev('dt').andSelf();
this.sharingPropEl.find(".aom-prop-locked-info").popover({
placement: 'bottom',
......@@ -60,8 +60,9 @@ define([
viewport: {selector: '.aom-info-share', padding: 15}
});
// All elements that depends on a sharing mode
// All elements that depends on a sharing mode, linear
this.propShMdParamsAll = this.$el.find("[data-shmd]");
// Elements that depends on a sharing mode, grouped by sharing mode
this.propShMdParams = _.reduce(this.propShMdParamsAll, function(memo, node){
var shm = node.getAttribute('data-shmd');
if (shm) {
......@@ -74,6 +75,20 @@ define([
return memo;
}, {});
// All elements that depends on an environment
this.propEnvParamsAll = this.$el.find("[data-env]");
// Elements that depends on a sharing mode, grouped by sharing mode
this.propEnvParams = _.reduce(this.propEnvParamsAll, function(memo, node){
var env = node.getAttribute('data-env');
if (env) {
_.each(env.split(","), function(m){
(!memo[m]) ? memo[m] = $(node) : memo[m].push(node);
});
}
return memo;
}, {});
this.initZeroClipboard();
this.$('[data-toggle="tooltip"]').tooltip();
......@@ -117,11 +132,13 @@ define([
"click .aom-shm-ctrl-members-remove-but": 'removeMemberClick',
"click .aom-shm-ctrl-members-select-but": 'selectAllMemberClick',
"click .aom-info-advanced h5": 'toggleAdvancedParam',
"click .aom-prop-collapsible h5": 'toggleCollapsibleParam',
"change #aom-prop-env-id": "setEnvironment",
"change .aom-shm-ctrl-showkey": 'showRegkeyValue',
"click .aom-prop-regkey-save": 'saveRegKey'
"click .aom-prop-regkey-save": 'saveRegKey',
"click .aom-prop-env-save": 'savePropEnv'
},
_updateItemProp: function(itemModel) {
......@@ -139,14 +156,21 @@ define([
ttpShared = itemModel.get('ttpShared') === true, // sharingMode > 15 are reserved for TTP
locked = itemModel.get('locked') === true,
owned = itemModel.get('owned') === true,
publicPublish = itemModel.get('allow_public_publishing') === true;
publicPublish = itemModel.get('allow_public_publishing') === true,
env = itemModel.get('env');
this.sharingPropEl
.toggleClass("aom-prop-locked", locked)
.toggleClass("aom-prop-unlocked", !locked);
this.propListEl
$("#aom-info-panel")
.toggleClass("aom-prop-locked", locked)
.toggleClass("aom-prop-unlocked", !locked);
.toggleClass("aom-prop-unlocked", !locked)
.toggleClass("aom-ttp-shared", ttpShared);
//this.sharingPropEl
// .toggleClass("aom-prop-locked", locked)
// .toggleClass("aom-prop-unlocked", !locked);
//
//this.propListEl
// .toggleClass("aom-prop-locked", locked)
// .toggleClass("aom-prop-unlocked", !locked);
if (ttpShared) {
this.$(".aom-shm-frontdrop").css("visibility", "hidden"); // Inhibbit the locking overlay for ttp sharing (not applied even for locked ttp images)
......@@ -175,16 +199,34 @@ define([
sharingModeSelect.val([sharingMode]);
this.sharingPropEl.find(".aom-prop-sharingmode-info").text(sharingModeSelect.find("option:selected").text());
this.publicPublishEl.val([publicPublish]);
// Show/hide controls specific to the sharing mode
this.propShMdParamsAll.addClass("hidden");
if (sharingMode) {
// Show controls specific to the sharing mode
if (this.propShMdParams[sharingMode]) {
this.propShMdParams[sharingMode].removeClass("hidden");
// Set values to specific controls
this._setOptCtrlValue(this.propShMdParams[sharingMode], sharingMode, itemModel);
}
}
// Show/hide controls specific to the environment
this.propEnvParamsAll.addClass("hidden");
if (env) {
if (this.propEnvParams[env]) {
this.propEnvParams[env].removeClass("hidden");
// Set values to env controls
var envparam = itemModel.get('envparam');
switch (env) {
case 'cimaf':
this.propEnvParams[env].find("[name=aom-prop-cutpos]")
.val(envparam.cimaf_cut_pos);
this.propEnvParams[env].find("[name=aom-prop-cutmargin]")
.val(envparam.cimaf_cut_margin);
break;
}
}
}
},
_setOptCtrlValue: function(shmGroupElem, sharingMode, itemModel) {
......@@ -301,11 +343,13 @@ define([
*/
setMode: function(mode) {
var isShared = mode === 'shared';
this.sharingPropEl.find(".aom-prop-form").toggleClass('hidden', isShared);
this.propListEl.find(".aom-prop-lock-but").toggleClass('hidden', isShared);
this.infoPanel.toggleClass('aom-shared-mode', isShared);
this.propOwnerEl.toggleClass('hidden', !isShared);
this.sharingPropEl.find(".aom-info-share-form").toggleClass('hidden', !isShared);
//this.sharingPropEl.find(".aom-prop-form").toggleClass('hidden', isShared);
//this.propListEl.find(".aom-prop-lock-but").toggleClass('hidden', isShared);
//this.propOwnerEl.toggleClass('hidden', !isShared);
//this.sharingPropEl.find(".aom-info-share-form").toggleClass('hidden', !isShared);
//this.advancedPropEl.toggleClass('hidden', isShared);
},
// ########################
......@@ -511,10 +555,10 @@ define([
// Advanced Parameters
// -------------------
toggleAdvancedParam: function(event) {
toggleCollapsibleParam: function(event) {
$(event.currentTarget)
.blur()
.siblings(".aom-advanced-form").toggleClass('hidden').end()
.siblings(".aom-prop-collapsible-content").toggleClass('hidden').end()
.find('span[class^="icon-"]').toggleClass("icon-plus-sign").toggleClass("icon-minus-sign");
//.closest(".aom-info-advanced").toggleClass("collapsed");
},
......@@ -544,6 +588,36 @@ define([
}
}
})
},
// Save Environment specific params
savePropEnv: function(event) {
var me = this,
$el = $(event.target),
$env_el = $el.closest("[data-env]");
switch ($env_el.data('env')) {
case 'cimaf':
var cut_pos = $env_el.find("[name=aom-prop-cutpos]").val();
var cut_margin = $env_el.find("[name=aom-prop-cutmargin]").val();
me.currentModel.save({
'envparam': {
cimaf_cut_pos: cut_pos,
cimaf_cut_margin: cut_margin
}
}, {
patch: true,
success: function() {
me._updateItemProp(me.currentModel);
//if (me.currentModel._editedAnObj) {
// document.location.reload();
//}
}
});
break;
}
}
......
......@@ -1723,6 +1723,47 @@ fieldset[disabled] .btn-adim-layer.active {
margin: 0;
font-size: 12px;
}
.aom-info-panel .aom-prop-lock-but {
margin-top: -5px;
}
.aom-info-panel .aom-prop-unlocked-status,
.aom-info-panel .aom-prop-locked-status {
display: none;