Merge pull request 'master' (#219) from master into Docker
Reviewed-on: SGD-Nord/konova#219
This commit is contained in:
commit
ec38daaedc
36
compensation/migrations/0011_ecoaccount_deductable_rest.py
Normal file
36
compensation/migrations/0011_ecoaccount_deductable_rest.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2022-10-11 11:39
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
from django.db.models import Sum
|
||||||
|
|
||||||
|
|
||||||
|
def fill_deductable_rest(apps, schema_editor):
|
||||||
|
EcoAccount = apps.get_model("compensation", "EcoAccount")
|
||||||
|
accs = EcoAccount.objects.all()
|
||||||
|
for acc in accs:
|
||||||
|
|
||||||
|
deductions = acc.deductions.filter(
|
||||||
|
intervention__deleted=None,
|
||||||
|
)
|
||||||
|
deductions_surfaces = deductions.aggregate(Sum("surface"))["surface__sum"] or 0
|
||||||
|
available_surfaces = acc.deductable_surface or deductions_surfaces
|
||||||
|
rest = available_surfaces - deductions_surfaces
|
||||||
|
|
||||||
|
acc.deductable_rest = rest
|
||||||
|
acc.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('compensation', '0010_auto_20220815_1030'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='ecoaccount',
|
||||||
|
name='deductable_rest',
|
||||||
|
field=models.FloatField(blank=True, default=0, help_text='Amount of deductable rest', null=True),
|
||||||
|
),
|
||||||
|
migrations.RunPython(fill_deductable_rest)
|
||||||
|
]
|
@ -35,6 +35,12 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
|
|||||||
help_text="Amount of deductable surface - can be lower than the total surface due to deduction limitations",
|
help_text="Amount of deductable surface - can be lower than the total surface due to deduction limitations",
|
||||||
default=0,
|
default=0,
|
||||||
)
|
)
|
||||||
|
deductable_rest = models.FloatField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
help_text="Amount of deductable rest",
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
|
||||||
legal = models.OneToOneField(
|
legal = models.OneToOneField(
|
||||||
"intervention.Legal",
|
"intervention.Legal",
|
||||||
@ -100,28 +106,22 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
|
|||||||
"""
|
"""
|
||||||
return self.after_states.all().aggregate(Sum("surface"))["surface__sum"] or 0
|
return self.after_states.all().aggregate(Sum("surface"))["surface__sum"] or 0
|
||||||
|
|
||||||
def get_available_rest(self) -> (float, float):
|
def __calculate_deductable_rest(self):
|
||||||
""" Calculates available rest surface of the eco account
|
""" Calculates available rest surface of the eco account
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
ret_val_total (float): Total amount
|
ret_val_total (float): Total amount
|
||||||
ret_val_relative (float): Amount as percentage (0-100)
|
|
||||||
"""
|
"""
|
||||||
deductions = self.deductions.filter(
|
deductions = self.deductions.filter(
|
||||||
intervention__deleted=None,
|
intervention__deleted=None,
|
||||||
)
|
)
|
||||||
deductions_surfaces = deductions.aggregate(Sum("surface"))["surface__sum"] or 0
|
deductions_surfaces = deductions.aggregate(Sum("surface"))["surface__sum"] or 0
|
||||||
available_surfaces = self.deductable_surface or deductions_surfaces ## no division by zero
|
available_surfaces = self.deductable_surface or deductions_surfaces ## no division by zero
|
||||||
ret_val_total = available_surfaces - deductions_surfaces
|
ret_val = available_surfaces - deductions_surfaces
|
||||||
|
|
||||||
if available_surfaces > 0:
|
return ret_val
|
||||||
ret_val_relative = int((ret_val_total / available_surfaces) * 100)
|
|
||||||
else:
|
|
||||||
ret_val_relative = 0
|
|
||||||
|
|
||||||
return ret_val_total, ret_val_relative
|
|
||||||
|
|
||||||
def quality_check(self) -> EcoAccountQualityChecker:
|
def quality_check(self) -> EcoAccountQualityChecker:
|
||||||
""" Quality check
|
""" Quality check
|
||||||
@ -181,6 +181,29 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
|
|||||||
for team_id in shared_teams:
|
for team_id in shared_teams:
|
||||||
celery_send_mail_deduction_changed_team.delay(self.identifier, self.title, team_id, data_change)
|
celery_send_mail_deduction_changed_team.delay(self.identifier, self.title, team_id, data_change)
|
||||||
|
|
||||||
|
def update_deductable_rest(self):
|
||||||
|
"""
|
||||||
|
Updates deductable_rest, which holds the amount of rest surface for this account.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.deductable_rest = self.__calculate_deductable_rest()
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def get_deductable_rest_relative(self):
|
||||||
|
"""
|
||||||
|
Returns deductable_rest relative to deductable_surface mapped to [0,100]
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
ret_val = int((self.deductable_rest / (self.deductable_surface or 0)) * 100)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
ret_val = 0
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountDocument(AbstractDocument):
|
class EcoAccountDocument(AbstractDocument):
|
||||||
"""
|
"""
|
||||||
@ -272,3 +295,8 @@ class EcoAccountDeduction(BaseResource):
|
|||||||
self.intervention.mark_as_edited(user, edit_comment=DEDUCTION_REMOVED)
|
self.intervention.mark_as_edited(user, edit_comment=DEDUCTION_REMOVED)
|
||||||
self.account.mark_as_edited(user, edit_comment=DEDUCTION_REMOVED)
|
self.account.mark_as_edited(user, edit_comment=DEDUCTION_REMOVED)
|
||||||
super().delete(*args, **kwargs)
|
super().delete(*args, **kwargs)
|
||||||
|
self.account.update_deductable_rest()
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
self.account.update_deductable_rest()
|
||||||
|
@ -37,7 +37,7 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
|
|||||||
av = tables.Column(
|
av = tables.Column(
|
||||||
verbose_name=_("Available"),
|
verbose_name=_("Available"),
|
||||||
orderable=True,
|
orderable=True,
|
||||||
empty_values=[],
|
accessor="deductable_rest",
|
||||||
attrs={
|
attrs={
|
||||||
"th": {
|
"th": {
|
||||||
"class": "w-20",
|
"class": "w-20",
|
||||||
@ -100,13 +100,16 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
|
|||||||
""" Renders the available column for an eco account
|
""" Renders the available column for an eco account
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (str): The identifier value
|
value (float): The deductable_rest
|
||||||
record (EcoAccount): The eco account record
|
record (EcoAccount): The eco account record
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
value_total, value_relative = record.get_available_rest()
|
try:
|
||||||
|
value_relative = record.get_deductable_rest_relative()
|
||||||
|
except ZeroDivisionError:
|
||||||
|
value_relative = 0
|
||||||
html = render_to_string("konova/widgets/progressbar.html", {"value": value_relative})
|
html = render_to_string("konova/widgets/progressbar.html", {"value": value_relative})
|
||||||
return format_html(html)
|
return format_html(html)
|
||||||
|
|
||||||
|
@ -230,7 +230,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
self.assertTrue(self.intervention.log.first().action == UserAction.EDITED)
|
self.assertTrue(self.intervention.log.first().action == UserAction.EDITED)
|
||||||
|
|
||||||
def test_edit_deduction(self):
|
def test_edit_deduction(self):
|
||||||
test_surface = self.eco_account.get_available_rest()[0]
|
test_surface = self.eco_account.deductable_rest
|
||||||
self.eco_account.set_recorded(self.superuser)
|
self.eco_account.set_recorded(self.superuser)
|
||||||
self.intervention.share_with_user(self.superuser)
|
self.intervention.share_with_user(self.superuser)
|
||||||
self.eco_account.refresh_from_db()
|
self.eco_account.refresh_from_db()
|
||||||
|
@ -200,7 +200,8 @@ def detail_view(request: HttpRequest, id: str):
|
|||||||
sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
||||||
diff_states = abs(sum_before_states - sum_after_states)
|
diff_states = abs(sum_before_states - sum_after_states)
|
||||||
# Calculate rest of available surface for deductions
|
# Calculate rest of available surface for deductions
|
||||||
available_total, available_relative = acc.get_available_rest()
|
available_total = acc.deductable_rest
|
||||||
|
available_relative = acc.get_deductable_rest_relative()
|
||||||
|
|
||||||
# Prefetch related data to decrease the amount of db connections
|
# Prefetch related data to decrease the amount of db connections
|
||||||
deductions = acc.deductions.filter(
|
deductions = acc.deductions.filter(
|
||||||
|
@ -221,8 +221,17 @@ class Geometry(BaseResource):
|
|||||||
polygons.append(p)
|
polygons.append(p)
|
||||||
geojson = {
|
geojson = {
|
||||||
"type": "FeatureCollection",
|
"type": "FeatureCollection",
|
||||||
|
"crs": {
|
||||||
|
"type": "name",
|
||||||
|
"properties": {
|
||||||
|
"name": f"urn:ogc:def:crs:EPSG::{geom.srid}"
|
||||||
|
}
|
||||||
|
},
|
||||||
"features": [
|
"features": [
|
||||||
json.loads(x.geojson) for x in polygons
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": json.loads(x.geojson)
|
||||||
|
} for x in polygons
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
return geojson
|
return geojson
|
||||||
|
@ -394,16 +394,12 @@ netgis.MapOpenLayers.prototype.clearAll = function()
|
|||||||
{
|
{
|
||||||
for ( var i = 0; i < this.layers.length; i++ )
|
for ( var i = 0; i < this.layers.length; i++ )
|
||||||
{
|
{
|
||||||
if(this.layers[i] === this.editLayer){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
this.map.removeLayer( this.layers[ i ] );
|
this.map.removeLayer( this.layers[ i ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
this.layers = [this.editLayer];
|
this.layers = [];
|
||||||
|
|
||||||
this.snapFeatures.clear();
|
this.snapFeatures.clear();
|
||||||
this.snapFeatures.push(this.editLayer);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
netgis.MapOpenLayers.prototype.onUpdateStyle = function( e )
|
netgis.MapOpenLayers.prototype.onUpdateStyle = function( e )
|
||||||
@ -1144,15 +1140,9 @@ netgis.MapOpenLayers.prototype.updateEditLayerItem = function()
|
|||||||
|
|
||||||
netgis.MapOpenLayers.prototype.onEditFeaturesLoaded = function( e )
|
netgis.MapOpenLayers.prototype.onEditFeaturesLoaded = function( e )
|
||||||
{
|
{
|
||||||
var json = e;
|
var json = e;
|
||||||
var format = new ol.format.GeoJSON();
|
var self = this;
|
||||||
var features = format.readFeatures( json );
|
window.setTimeout( function() { self.createLayerGeoJSON( "Import", json ); }, 10 );
|
||||||
|
|
||||||
this.editLayer.getSource().addFeatures( features );
|
|
||||||
//this.snapFeatures.push( e.feature );
|
|
||||||
|
|
||||||
if ( features.length > 0 )
|
|
||||||
this.view.fit( this.editLayer.getSource().getExtent(), { padding: [ 40, 40, 40, 40 ] } );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
netgis.MapOpenLayers.prototype.onDragEnter = function( e )
|
netgis.MapOpenLayers.prototype.onDragEnter = function( e )
|
||||||
@ -1267,6 +1257,7 @@ netgis.MapOpenLayers.prototype.createLayerGeoJSON = function( title, data )
|
|||||||
//NOTE: netgis.util.foreach( proj4.defs, function( k,v ) { console.info( "DEF:", k, v ); } )
|
//NOTE: netgis.util.foreach( proj4.defs, function( k,v ) { console.info( "DEF:", k, v ); } )
|
||||||
|
|
||||||
var projcode = projection.getCode();
|
var projcode = projection.getCode();
|
||||||
|
|
||||||
switch ( projcode )
|
switch ( projcode )
|
||||||
{
|
{
|
||||||
case "EPSG:3857":
|
case "EPSG:3857":
|
||||||
@ -1277,14 +1268,14 @@ netgis.MapOpenLayers.prototype.createLayerGeoJSON = function( title, data )
|
|||||||
//console.info( "Import Projection:", projcode );
|
//console.info( "Import Projection:", projcode );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Projection Not Supported
|
// Projection Not Supported
|
||||||
console.warn( "Unsupported Import Projection:", projcode );
|
console.warn( "Unsupported Import Projection:", projcode );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addImportedFeatures( features );
|
this.addImportedFeatures( features );
|
||||||
};
|
};
|
||||||
@ -1304,57 +1295,57 @@ netgis.MapOpenLayers.prototype.createLayerGML = function( title, data )
|
|||||||
//var format = new ol.format.WFS( { featureNS: "ogr", featureType: "RLP_OG_utf8_epsg4326" } );
|
//var format = new ol.format.WFS( { featureNS: "ogr", featureType: "RLP_OG_utf8_epsg4326" } );
|
||||||
//var projection = format.readProjection( data );
|
//var projection = format.readProjection( data );
|
||||||
//var features = format.readFeatures( data, { dataProjection: "EPSG:4326", featureProjection: "EPSG:3857" } );
|
//var features = format.readFeatures( data, { dataProjection: "EPSG:4326", featureProjection: "EPSG:3857" } );
|
||||||
|
|
||||||
//var features = format.readFeatures( data, { dataProjection: this.client.config.map.projection, featureProjection: this.client.config.map.projection } );
|
//var features = format.readFeatures( data, { dataProjection: this.client.config.map.projection, featureProjection: this.client.config.map.projection } );
|
||||||
|
|
||||||
//console.info( "GML:", projection, features, features[ 0 ].getGeometry() );
|
//console.info( "GML:", projection, features, features[ 0 ].getGeometry() );
|
||||||
|
|
||||||
var features = [];
|
var features = [];
|
||||||
|
|
||||||
var parser = new DOMParser();
|
var parser = new DOMParser();
|
||||||
var xml = parser.parseFromString( data, "text/xml" );
|
var xml = parser.parseFromString( data, "text/xml" );
|
||||||
|
|
||||||
// Features
|
// Features
|
||||||
var featureMembers = xml.getElementsByTagName( "gml:featureMember" );
|
var featureMembers = xml.getElementsByTagName( "gml:featureMember" );
|
||||||
|
|
||||||
for ( var f = 0; f < featureMembers.length; f++ )
|
for ( var f = 0; f < featureMembers.length; f++ )
|
||||||
{
|
{
|
||||||
var props = {};
|
var props = {};
|
||||||
|
|
||||||
var node = featureMembers[ f ];
|
var node = featureMembers[ f ];
|
||||||
var child = node.children[ 0 ];
|
var child = node.children[ 0 ];
|
||||||
|
|
||||||
// Attributes
|
// Attributes
|
||||||
for ( var a = 0; a < child.attributes.length; a++ )
|
for ( var a = 0; a < child.attributes.length; a++ )
|
||||||
{
|
{
|
||||||
var attribute = child.attributes[ a ];
|
var attribute = child.attributes[ a ];
|
||||||
props[ attribute.nodeName ] = attribute.nodeValue;
|
props[ attribute.nodeName ] = attribute.nodeValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( var c = 0; c < child.children.length; c++ )
|
for ( var c = 0; c < child.children.length; c++ )
|
||||||
{
|
{
|
||||||
var childNode = child.children[ c ];
|
var childNode = child.children[ c ];
|
||||||
|
|
||||||
if ( childNode.nodeName === "ogr:geometryProperty" ) continue;
|
if ( childNode.nodeName === "ogr:geometryProperty" ) continue;
|
||||||
|
|
||||||
var parts = childNode.nodeName.split( ":" );
|
var parts = childNode.nodeName.split( ":" );
|
||||||
var k = parts[ parts.length - 1 ];
|
var k = parts[ parts.length - 1 ];
|
||||||
var v = childNode.innerHTML;
|
var v = childNode.innerHTML;
|
||||||
|
|
||||||
props[ k ] = v;
|
props[ k ] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Geometry
|
// Geometry
|
||||||
var geomprop = child.getElementsByTagName( "ogr:geometryProperty" )[ 0 ];
|
var geomprop = child.getElementsByTagName( "ogr:geometryProperty" )[ 0 ];
|
||||||
|
|
||||||
//for ( var g = 0; g < geomprop.children.length; g++ )
|
//for ( var g = 0; g < geomprop.children.length; g++ )
|
||||||
{
|
{
|
||||||
var geom = geomprop.children[ 0 ];
|
var geom = geomprop.children[ 0 ];
|
||||||
var proj = geom.getAttribute( "srsName" );
|
var proj = geom.getAttribute( "srsName" );
|
||||||
|
|
||||||
if ( proj && proj !== "EPSG:4326" && proj !== this.client.config.map.projection )
|
if ( proj && proj !== "EPSG:4326" && proj !== this.client.config.map.projection )
|
||||||
console.warn( "Unsupported Import Projection:", proj );
|
console.warn( "Unsupported Import Projection:", proj );
|
||||||
|
|
||||||
switch ( geom.nodeName )
|
switch ( geom.nodeName )
|
||||||
{
|
{
|
||||||
case "gml:Polygon":
|
case "gml:Polygon":
|
||||||
@ -1362,7 +1353,7 @@ netgis.MapOpenLayers.prototype.createLayerGML = function( title, data )
|
|||||||
props[ "geometry" ] = this.gmlParsePolygon( geom, proj );
|
props[ "geometry" ] = this.gmlParsePolygon( geom, proj );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "gml:MultiPolygon":
|
case "gml:MultiPolygon":
|
||||||
{
|
{
|
||||||
props[ "geometry" ] = this.gmlParseMultiPolygon( geom, proj );
|
props[ "geometry" ] = this.gmlParseMultiPolygon( geom, proj );
|
||||||
@ -1370,7 +1361,7 @@ netgis.MapOpenLayers.prototype.createLayerGML = function( title, data )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var feature = new ol.Feature( props );
|
var feature = new ol.Feature( props );
|
||||||
features.push( feature );
|
features.push( feature );
|
||||||
}
|
}
|
||||||
@ -1452,23 +1443,22 @@ netgis.MapOpenLayers.prototype.createLayerShapefile = function( title, shapeData
|
|||||||
|
|
||||||
netgis.MapOpenLayers.prototype.addImportedFeatures = function( features )
|
netgis.MapOpenLayers.prototype.addImportedFeatures = function( features )
|
||||||
{
|
{
|
||||||
// ToDO: Changes in here problematic on initial data loading
|
|
||||||
// Add To Edit Layer
|
// Add To Edit Layer
|
||||||
this.editEventsSilent = true;
|
this.editEventsSilent = true;
|
||||||
this.editLayer.getSource().addFeatures( features );
|
this.editLayer.getSource().addFeatures( features );
|
||||||
this.editEventsSilent = false;
|
this.editEventsSilent = false;
|
||||||
this.updateEditOutput();
|
this.updateEditOutput();
|
||||||
|
|
||||||
// Zoom Imported Features
|
// Zoom Imported Features
|
||||||
if ( features.length > 0 )
|
if ( features.length > 0 )
|
||||||
{
|
{
|
||||||
var extent = features[ 0 ].getGeometry().getExtent();
|
var extent = features[ 0 ].getGeometry().getExtent();
|
||||||
|
|
||||||
for ( var f = 1; f < features.length; f++ )
|
for ( var f = 1; f < features.length; f++ )
|
||||||
{
|
{
|
||||||
ol.extent.extend( extent, features[ f ].getGeometry().getExtent() );
|
ol.extent.extend( extent, features[ f ].getGeometry().getExtent() );
|
||||||
}
|
}
|
||||||
|
|
||||||
var padding = 40;
|
var padding = 40;
|
||||||
this.view.fit( extent, { duration: 300, padding: [ padding, padding, padding, padding ] } );
|
this.view.fit( extent, { duration: 300, padding: [ padding, padding, padding, padding ] } );
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user