diff --git a/CHANGELOG.md b/CHANGELOG.md index 31eaae39..4ff37400 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ As of v0.2-alpha, this project is attempting to adhere to [Semantic Versioning]( While alpha, however, any version may include breaking changes that may not be specifically noted as such, and breaking changes will not necessarily result in changes to the main version number. +## [v1.6.12-alpha](https://github.com/Lexpedite/blawx/releases/tag/v1.6.12-alpha) 2023-05-09 + +This release chances how code is generated for projects. To upgrade existing files you may need to +open each section of the code and re-save it. + +### Fixes +* Numerical constraints with equality work properly +* Date constraints no longer fail on type errors related to the beginning and end of time +* Scenario editor now displays all relevant constraints in the explanations +* Scenario editor will now display reasoner errors if they occur. +* The OASA example is no longer published by default. +* Scenario Editor and Test Editor no longer crash when dealing with attributed rules in the root section of the project. +* Disequalities and numerical constraints now appear in explanations. + ## [v1.6.11-alpha](https://github.com/Lexpedite/blawx/releases/tag/v1.6.11-alpha) 2023-05-08 ### Fixes diff --git a/blawx/dates.py b/blawx/dates.py index 05150e66..9aa92460 100644 --- a/blawx/dates.py +++ b/blawx/dates.py @@ -47,12 +47,12 @@ date_compare(time(X),ne,time(Y)) :- X \= Y. date_compare(datetime(X),eq,datetime(X)). -date_compare(datetime(bot),gte,datetime(bot)). -date_compare(datetime(bot),lte,datetime(bot)). -date_compare(datetime(eot),gte,datetime(eot)). -date_compare(datetime(eot),lte,datetime(eot)). -date_compare(datetime(bot),lt,datetime(Y)) :- Y \= bot. -date_compare(datetime(eot),gt,datetime(Y)) :- Y \= eot. +%date_compare(datetime(bot),gte,datetime(bot)). +%date_compare(datetime(bot),lte,datetime(bot)). +%date_compare(datetime(eot),gte,datetime(eot)). +%date_compare(datetime(eot),lte,datetime(eot)). +%date_compare(datetime(bot),lt,datetime(Y)) :- Y \= bot. +%date_compare(datetime(eot),gt,datetime(Y)) :- Y \= eot. date_compare(datetime(X),lt,datetime(Y)) :- X #< Y. date_compare(datetime(X),gt,datetime(Y)) :- X #> Y. date_compare(datetime(X),lte,datetime(Y)) :- X #=< Y. @@ -60,12 +60,12 @@ date_compare(datetime(X),ne,datetime(Y)) :- X \= Y. date_compare(date(X),eq,datetime(X)). -date_compare(date(bot),gte,datetime(bot)). -date_compare(date(eot),lte,datetime(eot)). -date_compare(date(eot),gte,datetime(eot)). -date_compare(date(bot),lte,datetime(bot)). -date_compare(date(bot),lt,datetime(Y)) :- Y \= bot. -date_compare(date(eot),gt,datetime(Y)) :- Y \= eot. +%date_compare(date(bot),gte,datetime(bot)). +%date_compare(date(eot),lte,datetime(eot)). +%date_compare(date(eot),gte,datetime(eot)). +%date_compare(date(bot),lte,datetime(bot)). +%date_compare(date(bot),lt,datetime(Y)) :- Y \= bot. +%date_compare(date(eot),gt,datetime(Y)) :- Y \= eot. date_compare(date(X),lt,datetime(Y)) :- X #< Y. date_compare(date(X),gt,datetime(Y)) :- X #> Y. date_compare(date(X),lte,datetime(Y)) :- X #=< Y. @@ -73,12 +73,12 @@ date_compare(date(X),ne,datetime(Y)) :- X \= Y. date_compare(datetime(X),eq,date(X)). -date_compare(datetime(bot),gte,date(bot)). -date_compare(datetime(bot),lte,date(bot)). -date_compare(datetime(eot),gte,date(eot)). -date_compare(datetime(eot),lte,date(eot)). -date_compare(datetime(bot),lt,date(Y)) :- Y \= bot. -date_compare(datetime(eot),gt,date(Y)) :- Y \= eot. +%date_compare(datetime(bot),gte,date(bot)). +%date_compare(datetime(bot),lte,date(bot)). +%date_compare(datetime(eot),gte,date(eot)). +%date_compare(datetime(eot),lte,date(eot)). +%date_compare(datetime(bot),lt,date(Y)) :- Y \= bot. +%date_compare(datetime(eot),gt,date(Y)) :- Y \= eot. date_compare(datetime(X),lt,date(Y)) :- X #< Y. date_compare(datetime(X),gt,date(Y)) :- X #> Y. date_compare(datetime(X),lte,date(Y)) :- X #=< Y. @@ -86,12 +86,12 @@ date_compare(datetime(X),ne,date(Y)) :- X \= Y. date_compare(date(X),eq,date(X)). -date_compare(date(bot),gte,date(bot)). -date_compare(date(bot),lte,date(bot)). -date_compare(date(eot),gte,date(eot)). -date_compare(date(eot),lte,date(eot)). -date_compare(date(bot),lt,date(Y)) :- Y \= bot. -date_compare(date(eot),gt,date(Y)) :- Y \= eot. +%date_compare(date(bot),gte,date(bot)). +%date_compare(date(bot),lte,date(bot)). +%date_compare(date(eot),gte,date(eot)). +%date_compare(date(eot),lte,date(eot)). +%date_compare(date(bot),lt,date(Y)) :- Y \= bot. +%date_compare(date(eot),gt,date(Y)) :- Y \= eot. date_compare(date(X),lt,date(Y)) :- X #< Y. date_compare(date(X),gt,date(Y)) :- X #> Y. date_compare(date(X),lte,date(Y)) :- X #=< Y. diff --git a/blawx/passthrough.py b/blawx/passthrough.py new file mode 100644 index 00000000..f7dec692 --- /dev/null +++ b/blawx/passthrough.py @@ -0,0 +1,19 @@ +blawx_passthrough = """ +#pred blawx_diseq(X,Y) :: '@(X) is not the same object as @(Y)'. + +blawx_diseq(X,Y) :- X \= Y. + +#pred blawx_comparison(X,eq,Y) :: '@(X) is equal to @(Y)'. +#pred blawx_comparison(X,neq,Y) :: '@(X) is not equal to @(Y)'. +#pred blawx_comparison(X,gt,Y) :: '@(X) is greater than @(Y)'. +#pred blawx_comparison(X,gte,Y) :: '@(X) is greater than or equal to @(Y)'. +#pred blawx_comparison(X,lt,Y) :: '@(X) is less than @(Y)'. +#pred blawx_comparison(X,lte,Y) :: '@(X) is less than or equal to @(Y)'. + +blawx_comparison(X,eq,Y) :- X #= Y. +blawx_comparison(X,neq,Y) :- X \= Y. +blawx_comparison(X,gt,Y) :- X #> Y. +blawx_comparison(X,gte,Y) :- X #>= Y. +blawx_comparison(X,lt,Y) :- X #< Y. +blawx_comparison(X,lte,Y) :- X #=< Y. +""" \ No newline at end of file diff --git a/blawx/reasoner.py b/blawx/reasoner.py index 7e015f64..8ee216ac 100644 --- a/blawx/reasoner.py +++ b/blawx/reasoner.py @@ -20,6 +20,7 @@ from .dates import scasp_dates, scasp_now from .aggregates import scasp_aggregates from .events import ec_code +from .passthrough import blawx_passthrough from rest_framework import permissions @@ -563,6 +564,7 @@ def run_test(request,ruledoc,test_name): # maplist(del_attrs, AttVars). # """) + rulefile.write(blawx_passthrough + '\n\n') rulefile.write(ldap_code + '\n\n') rulefile.write(scasp_dates + '\n\n') rulefile.write(scasp_now + '\n\n') @@ -660,6 +662,7 @@ def get_ontology_internal(ruledoc,test_name): maplist(del_attrs, AttVars). """) + rulefile.write(blawx_passthrough + '\n\n') rulefile.write(ldap_code + '\n\n') rulefile.write(scasp_dates + '\n\n') rulefile.write(scasp_now + '\n\n') @@ -1238,6 +1241,7 @@ def interview(request,ruledoc,test_name): %maplist(del_attrs, AttVars). """) + rulefile.write(blawx_passthrough + '\n\n') rulefile.write(ldap_code + '\n\n') rulefile.write(scasp_dates + '\n\n') rulefile.write(scasp_now + '\n\n') diff --git a/blawx/settings.py b/blawx/settings.py index 74658a25..bd2de663 100644 --- a/blawx/settings.py +++ b/blawx/settings.py @@ -13,7 +13,7 @@ from pathlib import Path # For adding a version identifier -BLAWX_VERSION = "v1.6.11-alpha" +BLAWX_VERSION = "v1.6.12-alpha" # Build paths inside the project like this: BASE_DIR / 'subdir'. diff --git a/blawx/static/blawx/blawx-blocks.js b/blawx/static/blawx/blawx-blocks.js index 8a3918b5..18fca2d3 100644 --- a/blawx/static/blawx/blawx-blocks.js +++ b/blawx/static/blawx/blawx-blocks.js @@ -1672,7 +1672,7 @@ scasp_blockset = [{ ], [ "\\=", - "ne" + "neq" ] ] }, diff --git a/blawx/static/blawx/examples/oasa.yaml b/blawx/static/blawx/examples/oasa.yaml index 6ec96ce1..8f6f22ca 100644 --- a/blawx/static/blawx/examples/oasa.yaml +++ b/blawx/static/blawx/examples/oasa.yaml @@ -1,5 +1,5 @@ - model: blawx.ruledoc - pk: 1 + pk: 2 fields: ruledoc_name: Old Age Security Act rule_text: "Old Age Security Act\r\n\r\nPayment of full pension\r\n3.\r\n (1) @@ -151,18 +151,18 @@ scasp_encoding: '' tutorial: '' owner: 3 - published: true + published: false - model: blawx.workspace - pk: 1 + pk: 2 fields: - ruledoc: 1 + ruledoc: 2 workspace_name: root_section xml_content: '' scasp_encoding: '' - model: blawx.workspace - pk: 2 + pk: 3 fields: - ruledoc: 1 + ruledoc: 2 workspace_name: sec_3__subsec_1__para_a_section xml_content: FALSEFALSEFALSE"; case "gte": return ">="; - case "eq": return "=="; + case "eq": return "="; case "neq": return "\\="; } } @@ -304,7 +304,7 @@ sCASP['object_equality'] = function (block) { sCASP['object_disequality'] = function (block) { var value_first_object = sCASP.valueToCode(block, 'first_object', sCASP.ORDER_ATOMIC); var value_second_object = sCASP.valueToCode(block, 'second_object', sCASP.ORDER_ATOMIC); - var code = value_first_object + " \\= " + value_second_object; + var code = "blawx_diseq(" + value_first_object + "," + value_second_object + ")"; return code; }; @@ -632,7 +632,7 @@ sCASP['numerical_constraint'] = function (block) { var value_first_comparator = sCASP.valueToCode(block, 'first_comparator', sCASP.ORDER_ATOMIC); var dropdown_operator = block.getFieldValue('operator'); var value_second_comparator = sCASP.valueToCode(block, 'second_comparator', sCASP.ORDER_ATOMIC); - var code = value_first_comparator + " #" + text2math(dropdown_operator) + " " + value_second_comparator; + var code = "blawx_comparison(" + value_first_comparator + "," + dropdown_operator + "," + value_second_comparator + ")"; return code; }; @@ -1301,7 +1301,7 @@ sCASP['relationship_declaration'] = function(block) { code += 'blawx_as_of(' + text_relationship_name + '(' + parameters + '),datetime(Time)) :- blawx_becomes(' + text_relationship_name + '(' + parameters + '),datetime(BeforeT)), not blawx_becomes(-' + text_relationship_name + '(' + parameters + '), datetime(BetweenT)), BeforeT #< Time,BeforeT #< BetweenT, BetweenT #< Time.\n'; code += 'blawx_as_of(' + text_relationship_name + '(' + parameters + '),datetime(Time)) :- blawx_initially(' + text_relationship_name + '(' + parameters + ')), not blawx_becomes(-' + text_relationship_name + '(' + parameters + '), datetime(BetweenT)), BetweenT #< Time.\n'; code += 'blawx_during(datetime(Start),' + text_relationship_name + '(' + parameters + '),datetime(End)) :- blawx_becomes(' + text_relationship_name + '(' + parameters + '),datetime(Start)), not blawx_becomes(-' + text_relationship_name + '(' + parameters + '),datetime(BeforeEnd)), blawx_becomes(-' + text_relationship_name + '(' + parameters + '),datetime(End)), BeforeEnd #< End, Start #< End.\n'; - code += 'blawx_during(datetime(bot),' + text_relationship_name + '(' + parameters + '),datetime(End)) :- blawx_initially(' + text_relationship_name + '(' + parameters + ')), not blawx_becomes(-' + text_relationship_name + '' + parameters + '),datetime(BeforeEnd)), blawx_becomes(-' + text_relationship_name + '(' + parameters + '),datetime(End)), BeforeEnd #< End.\n'; + code += 'blawx_during(datetime(bot),' + text_relationship_name + '(' + parameters + '),datetime(End)) :- blawx_initially(' + text_relationship_name + '(' + parameters + ')), not blawx_becomes(-' + text_relationship_name + '(' + parameters + '),datetime(BeforeEnd)), blawx_becomes(-' + text_relationship_name + '(' + parameters + '),datetime(End)), BeforeEnd #< End.\n'; code += 'blawx_during(datetime(Start),' + text_relationship_name + '(' + parameters + '),datetime(eot)) :- blawx_becomes(' + text_relationship_name + '(' + parameters + '),datetime(Start)), not blawx_becomes(-' + text_relationship_name + '(' + parameters + '),datetime(AfterStart)), blawx_ultimately(' + text_relationship_name + '(' + parameters + ')), AfterStart #> Start.\n'; code += 'blawx_as_of(-' + text_relationship_name + '(' + parameters + '),datetime(Time)) :- blawx_becomes(-' + text_relationship_name + '(' + parameters + '),datetime(BeforeT)), not blawx_becomes(' + text_relationship_name + '(' + parameters + '), datetime(BetweenT)), BeforeT #< Time,BeforeT #< BetweenT, BetweenT #< Time.\n'; code += 'blawx_as_of(-' + text_relationship_name + '(' + parameters + '),datetime(Time)) :- blawx_initially(-' + text_relationship_name + '(' + parameters + ')), not blawx_becomes(' + text_relationship_name + '(' + parameters + '), datetime(BetweenT)), BetweenT #< Time.\n'; diff --git a/blawx/templates/blawx/scenario_editor.html b/blawx/templates/blawx/scenario_editor.html index 2d2bbe5b..6d2997b5 100644 --- a/blawx/templates/blawx/scenario_editor.html +++ b/blawx/templates/blawx/scenario_editor.html @@ -640,6 +640,27 @@
Response
return displayValue(model.args[0],human) + " is equal to " + displayValue(model.args[1],human) + ")"; } else if (model.functor == '\=') { return displayValue(model.args[0],human) + " is not equal to " + displayValue(model.args[1],human) + ")"; + } else if (model.functor == 'blawx_diseq') { + return displayValue(model.args[0],human) + " is not the same object as " + displayValue(model.args[1],human); + } else if (model.functor == 'blawx_comparison' ) { + if (model.args[1] == "eq") { + return displayValue(model.args[0],human) + " is equal to " + displayValue(model.args[2],human) + } + if (model.args[1] == "neq") { + return displayValue(model.args[0],human) + " is not equal to " + displayValue(model.args[2],human) + } + if (model.args[1] == "gt") { + return displayValue(model.args[0],human) + " is greater than " + displayValue(model.args[2],human) + } + if (model.args[1] == "gte") { + return displayValue(model.args[0],human) + " is greater than or equal to " + displayValue(model.args[2],human) + } + if (model.args[1] == "lt") { + return displayValue(model.args[0],human) + " is less than " + displayValue(model.args[2],human) + } + if (model.args[1] == "lte") { + return displayValue(model.args[0],human) + " is less than or equal to " + displayValue(model.args[2],human) + } } else { // this should be a real one, now predicate_name = model.functor; if (predicate_name == "datetime_diff_duration") { @@ -1456,97 +1477,102 @@
Response
ontology_element.innerHTML = '
' + JSON.stringify(ontology,null,2) + '
' // If the question is answered - if (parsed_test_response['Answers'].length) { - // Display the answer - var output_content = '
'; - var answers = parsed_test_response.Answers; - for (let i = 0; i < answers.length; i++) { - var count = i + 1; - var heading_name = "answer_" + count + "_heading"; - var collapse_name = "answer_" + count + "_collapse"; - output_content += '

'; - output_content += '

'; - output_content += '
'; - models = answers[i].Models; - output_content += '
' - output_content += '
    ' - for (var key in variables) { - output_content += '
  • ' + key + ': ' + displayValue(variables[key]) + '
  • '; - } - output_content += '
' - var attributes_output = []; - - for (let j = 0; j < models.length; j++) { - var model_count = j + 1; - var model_heading_name = "answer_" + count + "_model_" + model_count + "_heading"; - var model_collapse_name = "answer_" + count + "_model_" + model_count + "_collapse"; - output_content += '

'; - output_content += '

'; - output_content += '
'; - //for (var attribute in models[j]['Residuals']) { - // attributes_output.push(describe_constraint(models[j]['Residuals'][attribute])); - //} - var constraints_output = describe_constraints_new(models[j].Residuals,models[j].Terms); - - - //output_content += convertModelToTree(models[j].Tree, count, model_count); - getExplanationNodeIndex(models[j].Raw,i,j) - var explanations = getNodesFromModel(models[j].Raw,false,i,j); - var displayed_nodes = []; - var explanation_content = ""; - var referenced_variables = []; - for (var k = 0; k < explanations.length; k++) { - - if (!displayed_nodes.includes(explanations[k].name)) { // To eliminate duplicates caused by "proved" method. - explanation_content += explanations[k].content; - referenced_variables = referenced_variables.concat(get_variables(explanation_content)); - displayed_nodes.push(explanations[k].name); - } + output_content += '
'; + models = answers[i].Models; + output_content += '
' + output_content += '
    ' + for (var key in variables) { + output_content += '
  • ' + key + ': ' + displayValue(variables[key]) + '
  • '; } - // We could de-duplicate the lists, but I feel like at this point it would not help much. - // var constraint_output = ""; - var displayed_constraint_output = []; - if (constraints_output.length) { - for (var ao=0; ao < constraints_output.length; ao++) { - var variables_in_constraint = get_variables(constraints_output[ao]); - var any_matches = referenced_variables.filter(x => variables_in_constraint.includes(x)); - if (any_matches.length) { - displayed_constraint_output.push(constraints_output[ao]); + output_content += '
' + var attributes_output = []; + + for (let j = 0; j < models.length; j++) { + var model_count = j + 1; + var model_heading_name = "answer_" + count + "_model_" + model_count + "_heading"; + var model_collapse_name = "answer_" + count + "_model_" + model_count + "_collapse"; + output_content += '

'; + output_content += '

'; + output_content += '
'; + //for (var attribute in models[j]['Residuals']) { + // attributes_output.push(describe_constraint(models[j]['Residuals'][attribute])); + //} + var constraints_output = describe_constraints_new(models[j].Residuals, models[j].Terms); + + + //output_content += convertModelToTree(models[j].Tree, count, model_count); + getExplanationNodeIndex(models[j].Raw, i, j) + var explanations = getNodesFromModel(models[j].Raw, false, i, j); + var displayed_nodes = []; + var explanation_content = ""; + var referenced_variables = []; + for (var k = 0; k < explanations.length; k++) { + + if (!displayed_nodes.includes(explanations[k].name)) { // To eliminate duplicates caused by "proved" method. + explanation_content += explanations[k].content; + referenced_variables = referenced_variables.concat(get_variables(explanation_content)); + displayed_nodes.push(explanations[k].name); } } - } - // if (constraint_output != "") { - if (displayed_constraint_output.length) { - output_content += "Where:
    "; - for (var dc=0; dc < displayed_constraint_output.length; dc++) { - output_content += "
  • " + displayed_constraint_output[dc] + "
  • "; + // We could de-duplicate the lists, but I feel like at this point it would not help much. + // var constraint_output = ""; + var displayed_constraint_output = []; + if (constraints_output.length) { + for (var ao = 0; ao < constraints_output.length; ao++) { + var variables_in_constraint = get_variables(constraints_output[ao]); + var any_matches = referenced_variables.filter(x => variables_in_constraint.includes(x)); + if (any_matches.length) { + displayed_constraint_output.push(constraints_output[ao]); + } + } } - //output_content += constraint_output; - output_content += "
" - } - - output_content += explanation_content; - //output_content += getNodesFromModel(models[j].Raw); - // output_content += convertModelToParagraphs(models[j].Raw); - output_content += '
'; + // if (constraint_output != "") { + if (displayed_constraint_output.length) { + output_content += "Where:
    "; + for (var dc = 0; dc < displayed_constraint_output.length; dc++) { + output_content += "
  • " + displayed_constraint_output[dc] + "
  • "; + } + //output_content += constraint_output; + output_content += "
" + } + + output_content += explanation_content; + //output_content += getNodesFromModel(models[j].Raw); + // output_content += convertModelToParagraphs(models[j].Raw); + output_content += '
'; + } + output_content += '
'; } - output_content += '
'; - } - output_content += ''; - answer_element.innerHTML = output_content; + output_content += ''; + answer_element.innerHTML = output_content; - $('#nav-answers-tab').tab('show'); - draw_facts(); // So that new relevance information will be displayed in the interface. - } else { - // Indicate that there are no answers. - answer_element.innerHTML = "No answers received."; - + $('#nav-answers-tab').tab('show'); + draw_facts(); // So that new relevance information will be displayed in the interface. + } else { + // Indicate that there are no answers. + answer_element.innerHTML = "No answers received."; + + $('#nav-answers-tab').tab('show'); + } + } else { + answer_element.innerHTML = "

There was an error

" + JSON.stringify(parsed_test_response,null,2) + "
"; $('#nav-answers-tab').tab('show'); } } @@ -2184,7 +2210,7 @@
Response
} } function get_variables(string) { - const pattern = /[^\w]([A-Z_]\w*)/gm; + const pattern = /\b([A-Z_]\w*)/gm; var output = []; for(let match of string.matchAll(pattern)) { output.push(match[1]); diff --git a/blawx/views.py b/blawx/views.py index f37cee97..88e141d4 100644 --- a/blawx/views.py +++ b/blawx/views.py @@ -82,9 +82,12 @@ def ruleDocLegalTextView(request,pk,section_name): ruledoc=RuleDoc.objects.get(pk=pk) if request.user.has_perm('blawx.view_ruledoc',ruledoc): cobalt_parse = Act(ruledoc.akoma_ntoso) - target = cobalt_parse.act.find(".//*[@eId='" + section_name + "']") - return Response({'xml': lxml.etree.tostring(target), - 'text': ' '.join(target.itertext())}) + if section_name != "root": + target = cobalt_parse.act.find(".//*[@eId='" + section_name + "']") + return Response({'xml': lxml.etree.tostring(target), + 'text': ' '.join(target.itertext())}) + else: + return Response({'xml': '', 'text': ruledoc.ruledoc_name}) else: return HttpResponseForbidden()