Skip to content

Commit

Permalink
Finish support for subclaims
Browse files Browse the repository at this point in the history
  • Loading branch information
mhauru committed Apr 5, 2022
1 parent 2f58608 commit c35b653
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 29 deletions.
28 changes: 28 additions & 0 deletions eap_backend/eap_api/migrations/0012_auto_20220404_1627.py
@@ -0,0 +1,28 @@
# Generated by Django 3.2.8 on 2022-04-04 16:27

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('eap_api', '0011_auto_20220404_1544'),
]

operations = [
migrations.RemoveField(
model_name='propertyclaim',
name='parent',
),
migrations.AddField(
model_name='propertyclaim',
name='goal',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='property_claims', to='eap_api.toplevelnormativegoal'),
),
migrations.AddField(
model_name='propertyclaim',
name='property_claim',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='property_claims', to='eap_api.propertyclaim'),
),
]
22 changes: 20 additions & 2 deletions eap_backend/eap_api/models.py
Expand Up @@ -77,8 +77,19 @@ class SystemDescription(CaseItem):

class PropertyClaim(CaseItem):
shape = Shape.ROUNDED_RECTANGLE
parent = models.ForeignKey(
TopLevelNormativeGoal, related_name="property_claims", on_delete=models.CASCADE
goal = models.ForeignKey(
TopLevelNormativeGoal,
null=True,
blank=True,
related_name="property_claims",
on_delete=models.CASCADE,
)
property_claim = models.ForeignKey(
"self",
null=True,
blank=True,
related_name="property_claims",
on_delete=models.CASCADE,
)
level = models.PositiveIntegerField()

Expand All @@ -90,6 +101,13 @@ def save(self, *args, **kwargs):
# doesn't have a level.
parent_level = 0
self.level = parent_level + 1
# TODO Is this the right place to assert these things?
has_goal_parent = bool(self.goal)
has_claim_parent = bool(self.property_claim)
if has_claim_parent and has_goal_parent:
raise ValueError("A PropertyClaim shouldn't have two parents.")
if not (has_claim_parent or has_goal_parent):
raise ValueError("A PropertyClaim should have a parent.")
super().save(*args, **kwargs)


Expand Down
20 changes: 17 additions & 3 deletions eap_backend/eap_api/serializers.py
Expand Up @@ -70,10 +70,21 @@ class Meta:


class PropertyClaimSerializer(serializers.ModelSerializer):
parent_id = serializers.PrimaryKeyRelatedField(
source="parent", queryset=TopLevelNormativeGoal.objects.all(), write_only=True
goal_id = serializers.PrimaryKeyRelatedField(
source="goal",
queryset=TopLevelNormativeGoal.objects.all(),
write_only=True,
required=False,
)
property_claim_id = serializers.PrimaryKeyRelatedField(
source="property_claim",
queryset=PropertyClaim.objects.all(),
write_only=True,
required=False,
)
level = serializers.IntegerField(read_only=True)
arguments = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
property_claims = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

class Meta:
model = PropertyClaim
Expand All @@ -82,8 +93,11 @@ class Meta:
"name",
"short_description",
"long_description",
"parent_id",
"goal_id",
"property_claim_id",
"level",
"arguments",
"property_claims",
)


Expand Down
36 changes: 21 additions & 15 deletions eap_backend/eap_api/views.py
Expand Up @@ -35,49 +35,49 @@
"model": TopLevelNormativeGoal,
"children": ["context", "system_description", "property_claims"],
"fields": ("name", "short_description", "long_description", "keywords"),
"parent_type": ("assurance_case", False),
"parent_types": [("assurance_case", False)],
},
"context": {
"serializer": ContextSerializer,
"model": Context,
"children": [],
"fields": ("name", "short_description", "long_description"),
"parent_type": ("goal", False),
"parent_types": [("goal", False)],
},
"system_description": {
"serializer": SystemDescriptionSerializer,
"model": SystemDescription,
"children": [],
"fields": ("name", "short_description", "long_description"),
"parent_type": ("goal", False),
"parent_types": [("goal", False)],
},
"property_claims": {
"serializer": PropertyClaimSerializer,
"model": PropertyClaim,
"children": ["arguments"],
"children": ["arguments", "property_claims"],
"fields": ("name", "short_description", "long_description"),
"parent_type": ("parent", False),
"parent_types": [("goal", False), ("property_claim", False)],
},
"arguments": {
"serializer": ArgumentSerializer,
"model": Argument,
"children": ["evidential_claims"],
"fields": ("name", "short_description", "long_description"),
"parent_type": ("property_claim", True),
"parent_types": [("property_claim", True)],
},
"evidential_claims": {
"serializer": EvidentialClaimSerializer,
"model": EvidentialClaim,
"children": ["evidence"],
"fields": ("name", "short_description", "long_description"),
"parent_type": ("argument", False),
"parent_types": [("argument", False)],
},
"evidence": {
"serializer": EvidenceSerializer,
"model": Evidence,
"children": [],
"fields": ("name", "short_description", "long_description", "URL"),
"parent_type": ("evidential_claim", True),
"parent_types": [("evidential_claim", True)],
},
}

Expand Down Expand Up @@ -145,7 +145,7 @@ def get_json_tree(id_list, obj_type):
return objs


def save_json_tree(data, obj_type, parent_id=None):
def save_json_tree(data, obj_type, parent_id=None, parent_type=None):
"""Recursively write items in an assurance case tree.
Create a new assurance case like the one described by data, including all
Expand All @@ -167,11 +167,15 @@ def save_json_tree(data, obj_type, parent_id=None):
# so that e.g. the new object gets a unique ID even if `data` specifies an
# ID.
this_data = {k: data[k] for k in TYPE_DICT[obj_type]["fields"]}
if parent_id is not None:
parent_type, plural = TYPE_DICT[obj_type]["parent_type"]
if plural:
parent_id = [parent_id]
this_data[parent_type + "_id"] = parent_id
if parent_id is not None and parent_type is not None:
for parent_type_tmp, plural in TYPE_DICT[obj_type]["parent_types"]:
# TODO This is silly. It's all because some parent_type names are written
# with a plural s in the end while others are not.
if parent_type not in parent_type_tmp:
continue
if plural:
parent_id = [parent_id]
this_data[parent_type_tmp + "_id"] = parent_id
serializer_class = TYPE_DICT[obj_type]["serializer"]
serializer = serializer_class(data=this_data)
if serializer.is_valid():
Expand All @@ -188,7 +192,9 @@ def save_json_tree(data, obj_type, parent_id=None):
if child_type not in data:
continue
for child_data in data[child_type]:
retval = save_json_tree(child_data, child_type, parent_id=id)
retval = save_json_tree(
child_data, child_type, parent_id=id, parent_type=obj_type
)
# If one of the subcalls returns an error, return.
if retval.status_code != success_http_code:
return retval
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/components/CaseContainer.js
Expand Up @@ -208,10 +208,14 @@ class CaseContainer extends Component {
this.setState({ showEditLayer: true });
}

showCreateLayer(itemType, parentId, event) {
showCreateLayer(itemType, parentId, parentType, event) {
console.log("in showCreateLayer", this, parentId);
event.preventDefault();
this.setState({ createItemType: itemType, createItemParentId: parentId });
this.setState({
createItemType: itemType,
createItemParentId: parentId,
createItemParentType: parentType,
});
this.setState({ showCreateLayer: true });
}

Expand All @@ -233,6 +237,7 @@ class CaseContainer extends Component {
showCreateLayer: false,
createItemType: null,
createItemParentId: null,
createItemParentType: null,
});
}

Expand Down Expand Up @@ -352,6 +357,7 @@ class CaseContainer extends Component {
<ItemCreator
type={this.state.createItemType}
parentId={this.state.createItemParentId}
parentType={this.state.createItemParentType}
updateView={this.updateView.bind(this)}
/>
</Box>
Expand Down Expand Up @@ -427,6 +433,7 @@ class CaseContainer extends Component {
{this.state.showCreateLayer &&
this.state.createItemType &&
this.state.createItemParentId &&
this.state.createItemParentType &&
this.createLayer()}
{this.state.showConfirmDeleteLayer && this.confirmDeleteLayer()}

Expand Down Expand Up @@ -487,6 +494,7 @@ class CaseContainer extends Component {
<ItemCreator
type="TopLevelNormativeGoal"
parentId={this.state.id}
parentType="AssuranceCase"
updateView={this.updateView.bind(this)}
/>
}
Expand Down
15 changes: 12 additions & 3 deletions frontend/src/components/ItemCreator.js
Expand Up @@ -4,6 +4,14 @@ import { Box, Button, Form, FormField, Heading, TextInput } from "grommet";
import React, { useState } from "react";
import configData from "../config.json";

const TYPE_NAME_TO_DB_TYPE_NAME = {
AssuranceCase: "assurance_case_id",
TopLevelNormativeGoal: "goal_id",
PropertyClaim: "property_claim_id",
Argument: "argument_id",
EvidentialClaim: "evidential_claim_id",
};

function ItemCreator(props) {
const [parentId, setParentId] = useState(1);
const [name, setName] = useState("Name");
Expand Down Expand Up @@ -44,12 +52,13 @@ function ItemCreator(props) {
if (
configData.navigation[props.type]["parent_relation"] === "many-to-many"
) {
request_body[configData["navigation"][props.type]["parent_db_name"]] = [
request_body[TYPE_NAME_TO_DB_TYPE_NAME[props.parentType]] = [
parseInt(props.parentId),
];
} else {
request_body[configData["navigation"][props.type]["parent_db_name"]] =
parseInt(props.parentId);
request_body[TYPE_NAME_TO_DB_TYPE_NAME[props.parentType]] = parseInt(
props.parentId
);
}
const requestOptions = {
method: "POST",
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/components/ItemEditor.js
Expand Up @@ -144,7 +144,9 @@ function ItemEditor(props) {
<Button
pad="small"
key={childType}
onClick={(e) => props.createItemLayer(childType, props.id, e)}
onClick={(e) =>
props.createItemLayer(childType, props.id, props.type, e)
}
label={"Create new " + childType}
/>
))}
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/config.json
Expand Up @@ -34,10 +34,8 @@
"PropertyClaim": {
"api_name": "propertyclaims",
"db_name": "property_claims",
"parent_name": "TopLevelNormativeGoal",
"parent_db_name": "parent_id",
"shape": "rounded",
"children": ["Argument"],
"children": ["Argument", "PropertyClaim"],
"parent_relation": "one-to-many"
},
"Argument": {
Expand Down

0 comments on commit c35b653

Please sign in to comment.