mirror of
https://github.com/Southampton-RSG/breccia-mapper.git
synced 2026-03-03 03:17:07 +00:00
fix: distinguish kinds of relationship with orgs
Refactor node/edge style method to improve performance
This commit is contained in:
@@ -41,6 +41,7 @@ class RelationshipSerializer(serializers.ModelSerializer):
|
|||||||
class OrganisationRelationshipSerializer(serializers.ModelSerializer):
|
class OrganisationRelationshipSerializer(serializers.ModelSerializer):
|
||||||
source = PersonSerializer()
|
source = PersonSerializer()
|
||||||
target = OrganisationSerializer()
|
target = OrganisationSerializer()
|
||||||
|
kind = serializers.ReadOnlyField(default='organisation-relationship')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.OrganisationRelationship
|
model = models.OrganisationRelationship
|
||||||
@@ -48,4 +49,5 @@ class OrganisationRelationshipSerializer(serializers.ModelSerializer):
|
|||||||
'pk',
|
'pk',
|
||||||
'source',
|
'source',
|
||||||
'target',
|
'target',
|
||||||
|
'kind',
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,33 +7,24 @@ var network_style = [
|
|||||||
selector: 'node[name]',
|
selector: 'node[name]',
|
||||||
style: {
|
style: {
|
||||||
label: 'data(name)',
|
label: 'data(name)',
|
||||||
|
width: '50px',
|
||||||
|
height: '50px',
|
||||||
'text-halign': 'center',
|
'text-halign': 'center',
|
||||||
'text-valign': 'center',
|
'text-valign': 'center',
|
||||||
'font-size': 8,
|
'text-wrap': 'wrap',
|
||||||
'background-color': function (ele) {
|
'text-max-width': '100px',
|
||||||
switch (ele.data('kind')) {
|
'font-size': 12,
|
||||||
case 'person':
|
'background-color': 'data(nodeColor)',
|
||||||
return '#0099cc'
|
'shape': 'data(nodeShape)'
|
||||||
default:
|
|
||||||
return '#669933'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'shape': function (ele) {
|
|
||||||
switch (ele.data('kind')) {
|
|
||||||
case 'person':
|
|
||||||
return 'ellipse'
|
|
||||||
default:
|
|
||||||
return 'rectangle'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: 'edge',
|
selector: 'edge',
|
||||||
style: {
|
style: {
|
||||||
'mid-target-arrow-shape': 'triangle',
|
'mid-target-arrow-shape': 'data(lineArrowShape)',
|
||||||
'curve-style': 'straight',
|
'curve-style': 'straight',
|
||||||
'width': 1,
|
'width': 1,
|
||||||
|
'line-color': 'data(lineColor)'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -65,7 +56,9 @@ function get_network() {
|
|||||||
data: {
|
data: {
|
||||||
id: 'person-' + person.pk.toString(),
|
id: 'person-' + person.pk.toString(),
|
||||||
name: person.name,
|
name: person.name,
|
||||||
kind: 'person'
|
kind: 'person',
|
||||||
|
nodeColor: '#0099cc',
|
||||||
|
nodeShape: 'elipse'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -79,7 +72,8 @@ function get_network() {
|
|||||||
data: {
|
data: {
|
||||||
id: 'organisation-' + item.pk.toString(),
|
id: 'organisation-' + item.pk.toString(),
|
||||||
name: item.name,
|
name: item.name,
|
||||||
kind: 'organisation'
|
nodeColor: '#669933',
|
||||||
|
nodeShape: 'rectangle'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -94,10 +88,11 @@ function get_network() {
|
|||||||
data: {
|
data: {
|
||||||
id: 'relationship-' + relationship.pk.toString(),
|
id: 'relationship-' + relationship.pk.toString(),
|
||||||
source: 'person-' + relationship.source.pk.toString(),
|
source: 'person-' + relationship.source.pk.toString(),
|
||||||
target: 'person-' + relationship.target.pk.toString()
|
target: 'person-' + relationship.target.pk.toString(),
|
||||||
|
lineArrowShape: 'triangle'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} catch {
|
} catch (exc) {
|
||||||
// Exception thrown if a node in the relationship does not exist
|
// Exception thrown if a node in the relationship does not exist
|
||||||
// This is probably because it's been filtered out
|
// This is probably because it's been filtered out
|
||||||
}
|
}
|
||||||
@@ -107,17 +102,20 @@ function get_network() {
|
|||||||
relationship_set = JSON.parse(document.getElementById('organisation-relationship-set-data').textContent);
|
relationship_set = JSON.parse(document.getElementById('organisation-relationship-set-data').textContent);
|
||||||
|
|
||||||
for (var relationship of relationship_set) {
|
for (var relationship of relationship_set) {
|
||||||
console.log(relationship)
|
|
||||||
try {
|
try {
|
||||||
cy.add({
|
cy.add({
|
||||||
group: 'edges',
|
group: 'edges',
|
||||||
data: {
|
data: {
|
||||||
id: 'organisation-relationship-' + relationship.pk.toString(),
|
id: 'organisation-relationship-' + relationship.pk.toString(),
|
||||||
source: 'person-' + relationship.source.pk.toString(),
|
source: 'person-' + relationship.source.pk.toString(),
|
||||||
target: 'organisation-' + relationship.target.pk.toString()
|
target: 'organisation-' + relationship.target.pk.toString(),
|
||||||
|
lineColor: {
|
||||||
|
'organisation-membership': '#669933'
|
||||||
|
}[relationship.kind] || 'black',
|
||||||
|
lineArrowShape: 'none'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} catch {
|
} catch (exc) {
|
||||||
// Exception thrown if a node in the relationship does not exist
|
// Exception thrown if a node in the relationship does not exist
|
||||||
// This is probably because it's been filtered out
|
// This is probably because it's been filtered out
|
||||||
}
|
}
|
||||||
@@ -128,7 +126,8 @@ function get_network() {
|
|||||||
name: 'cose',
|
name: 'cose',
|
||||||
randomize: true,
|
randomize: true,
|
||||||
animate: false,
|
animate: false,
|
||||||
idealEdgeLength: function (edge) { return 64; }
|
idealEdgeLength: function (edge) { return 64; },
|
||||||
|
nodeRepulsion: function (node) { return 8192; }
|
||||||
});
|
});
|
||||||
|
|
||||||
layout.run();
|
layout.run();
|
||||||
|
|||||||
@@ -16,37 +16,34 @@ from people import forms, models, serializers
|
|||||||
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
|
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
|
||||||
|
|
||||||
|
|
||||||
def filter_by_form_answers(model: typing.Type, answerset_model: typing.Type,
|
def filter_by_form_answers(model: typing.Type, answerset_model: typing.Type, relationship_key: str):
|
||||||
relationship_key: str):
|
|
||||||
"""Build a filter to select based on form responses."""
|
"""Build a filter to select based on form responses."""
|
||||||
def inner(form, at_date):
|
def inner(form, at_date):
|
||||||
answerset_set = answerset_model.objects.filter(
|
answerset_set = answerset_model.objects.filter(
|
||||||
Q(replaced_timestamp__gte=at_date)
|
Q(replaced_timestamp__gte=at_date)
|
||||||
| Q(replaced_timestamp__isnull=True),
|
| Q(replaced_timestamp__isnull=True),
|
||||||
timestamp__lte=at_date)
|
timestamp__lte=at_date
|
||||||
|
)
|
||||||
|
|
||||||
# Filter answers to relationship questions
|
# Filter answers to relationship questions
|
||||||
for field, values in form.cleaned_data.items():
|
for field, values in form.cleaned_data.items():
|
||||||
if field.startswith(f'{form.question_prefix}question_') and values:
|
if field.startswith(f'{form.question_prefix}question_') and values:
|
||||||
answerset_set = answerset_set.filter(
|
answerset_set = answerset_set.filter(question_answers__in=values)
|
||||||
question_answers__in=values)
|
|
||||||
|
|
||||||
return model.objects.filter(
|
return model.objects.filter(pk__in=answerset_set.values_list(relationship_key, flat=True))
|
||||||
pk__in=answerset_set.values_list(relationship_key, flat=True))
|
|
||||||
|
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
|
||||||
filter_relationships = filter_by_form_answers(models.Relationship,
|
filter_relationships = filter_by_form_answers(
|
||||||
models.RelationshipAnswerSet,
|
models.Relationship, models.RelationshipAnswerSet, 'relationship'
|
||||||
'relationship')
|
)
|
||||||
|
|
||||||
filter_organisations = filter_by_form_answers(models.Organisation,
|
filter_organisations = filter_by_form_answers(
|
||||||
models.OrganisationAnswerSet,
|
models.Organisation, models.OrganisationAnswerSet, 'organisation'
|
||||||
'organisation')
|
)
|
||||||
|
|
||||||
filter_people = filter_by_form_answers(models.Person, models.PersonAnswerSet,
|
filter_people = filter_by_form_answers(models.Person, models.PersonAnswerSet, 'person')
|
||||||
'person')
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkView(LoginRequiredMixin, TemplateView):
|
class NetworkView(LoginRequiredMixin, TemplateView):
|
||||||
@@ -95,41 +92,60 @@ class NetworkView(LoginRequiredMixin, TemplateView):
|
|||||||
if not all(map(lambda f: f.is_valid(), all_forms.values())):
|
if not all(map(lambda f: f.is_valid(), all_forms.values())):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
# Filter on timestamp__date doesn't seem to work on MySQL
|
||||||
|
# To compare datetimes we need at_date to be midnight at
|
||||||
|
# the *end* of the day in question - so add one day to each
|
||||||
|
|
||||||
relationship_at_date = all_forms['relationship'].cleaned_data['date']
|
relationship_at_date = all_forms['relationship'].cleaned_data['date']
|
||||||
if not relationship_at_date:
|
if not relationship_at_date:
|
||||||
relationship_at_date = timezone.now().date()
|
relationship_at_date = timezone.now().date()
|
||||||
|
relationship_at_date += timezone.timedelta(days=1)
|
||||||
|
|
||||||
person_at_date = all_forms['person'].cleaned_data['date']
|
person_at_date = all_forms['person'].cleaned_data['date']
|
||||||
if not person_at_date:
|
if not person_at_date:
|
||||||
person_at_date = timezone.now().date()
|
person_at_date = timezone.now().date()
|
||||||
|
person_at_date += timezone.timedelta(days=1)
|
||||||
|
|
||||||
organisation_at_date = all_forms['organisation'].cleaned_data['date']
|
organisation_at_date = all_forms['organisation'].cleaned_data['date']
|
||||||
if not organisation_at_date:
|
if not organisation_at_date:
|
||||||
organisation_at_date = timezone.now().date()
|
organisation_at_date = timezone.now().date()
|
||||||
|
organisation_at_date += timezone.timedelta(days=1)
|
||||||
# Filter on timestamp__date doesn't seem to work on MySQL
|
|
||||||
# To compare datetimes we need at_date to be midnight at
|
|
||||||
# the *end* of the day in question - so add one day here
|
|
||||||
relationship_at_date += timezone.timedelta(days=1)
|
|
||||||
|
|
||||||
context['person_set'] = serializers.PersonSerializer(
|
context['person_set'] = serializers.PersonSerializer(
|
||||||
filter_people(all_forms['person'], person_at_date),
|
filter_people(all_forms['person'], person_at_date), many=True
|
||||||
many=True).data
|
).data
|
||||||
|
|
||||||
context['organisation_set'] = serializers.OrganisationSerializer(
|
context['organisation_set'] = serializers.OrganisationSerializer(
|
||||||
filter_organisations(all_forms['organisation'], organisation_at_date),
|
filter_organisations(all_forms['organisation'], organisation_at_date), many=True
|
||||||
many=True).data
|
).data
|
||||||
|
|
||||||
context['relationship_set'] = serializers.RelationshipSerializer(
|
context['relationship_set'] = serializers.RelationshipSerializer(
|
||||||
filter_relationships(all_forms['relationship'], relationship_at_date),
|
filter_relationships(all_forms['relationship'], relationship_at_date), many=True
|
||||||
many=True).data
|
).data
|
||||||
|
|
||||||
context['organisation_relationship_set'] = serializers.OrganisationRelationshipSerializer(
|
context['organisation_relationship_set'] = serializers.OrganisationRelationshipSerializer(
|
||||||
models.OrganisationRelationship.objects.all(), many=True
|
models.OrganisationRelationship.objects.all(), many=True
|
||||||
).data
|
).data
|
||||||
|
|
||||||
logger.info('Found %d distinct relationships matching filters',
|
for person in models.Person.objects.all():
|
||||||
len(context['relationship_set']))
|
try:
|
||||||
|
context['organisation_relationship_set'].append(
|
||||||
|
{
|
||||||
|
'pk': f'membership-{person.pk}',
|
||||||
|
'source': serializers.PersonSerializer(person).data,
|
||||||
|
'target': serializers.OrganisationSerializer(
|
||||||
|
person.current_answers.organisation
|
||||||
|
).data,
|
||||||
|
'kind': 'organisation-membership'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
'Found %d distinct relationships matching filters', len(context['relationship_set'])
|
||||||
|
)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user