From 3c2bf0c6b8f810fa01bf86797bd895c1e8c66dcd Mon Sep 17 00:00:00 2001 From: Gauntlet173 Date: Mon, 11 Jul 2022 11:20:04 -0600 Subject: [PATCH 1/7] add owner and ownership check, remove unused views --- CHANGELOG.md | 4 + blawx/admin.py | 8 +- blawx/fixtures/workspace/beard_tax.yaml | 1 + blawx/fixtures/workspace/bird.yaml | 1 + blawx/fixtures/workspace/mbp.yaml | 1 + blawx/fixtures/workspace/mortality.yaml | 1 + blawx/fixtures/workspace/net30.yaml | 1 + blawx/fixtures/workspace/r34.yaml | 1 + blawx/fixtures/workspace/rps.yaml | 1 + blawx/fixtures/workspace/siblings.yaml | 1 + blawx/fixtures/workspace/wills.yaml | 1 + blawx/fixtures/workspace/wills_tutorial.yaml | 1 + blawx/migrations/0020_ruledoc_owner.py | 22 + ..._content_workspacetemplate_yaml_content.py | 18 + ...ename_workspacetemplate_ruledoctemplate.py | 17 + blawx/models.py | 6 +- blawx/reasoner.py | 955 +++++++++--------- blawx/urls.py | 6 +- blawx/views.py | 138 ++- 19 files changed, 662 insertions(+), 522 deletions(-) create mode 100644 blawx/migrations/0020_ruledoc_owner.py create mode 100644 blawx/migrations/0021_rename_xml_content_workspacetemplate_yaml_content.py create mode 100644 blawx/migrations/0022_rename_workspacetemplate_ruledoctemplate.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f93af2e1..970bad37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and breaking changes will not necessarily result in changes to the main version ### Added * User registration +* Users can now only see and interact with projects they created. + +### Changed +* Ownership of demonstration projects/tutorials has been given to the demo user (this is temporary). ## [v1.3.19-alpha](https://github.com/Lexpedite/blawx/releases/tag/v1.3.19-alpha) 2022-07-07 diff --git a/blawx/admin.py b/blawx/admin.py index abfbaba3..bc17d751 100644 --- a/blawx/admin.py +++ b/blawx/admin.py @@ -1,17 +1,21 @@ from django.contrib import admin # Register your models here. -from .models import Workspace, RuleDoc, BlawxTest +from .models import Workspace, RuleDoc, BlawxTest, RuleDocTemplate class WorkspaceAdmin(admin.ModelAdmin): fields = ['ruledoc','workspace_name','xml_content','scasp_encoding'] class RuleDocAdmin(admin.ModelAdmin): - fields = ['ruledoc_name','scasp_encoding','tutorial'] + fields = ['ruledoc_name','scasp_encoding','tutorial','owner'] class BlawxTestAdmin(admin.ModelAdmin): fields = ['ruledoc','test_name','xml_content','scasp_encoding', 'tutorial'] +class RuleDocTemplateAdmin(admin.ModelAdmin): + fields = ['template_name','yaml_content'] + admin.site.register(Workspace,WorkspaceAdmin) admin.site.register(RuleDoc,RuleDocAdmin) admin.site.register(BlawxTest,BlawxTestAdmin) +admin.site.register(RuleDocTemplate,RuleDocTemplateAdmin) diff --git a/blawx/fixtures/workspace/beard_tax.yaml b/blawx/fixtures/workspace/beard_tax.yaml index 85cf4ca5..2fdd9f7e 100644 --- a/blawx/fixtures/workspace/beard_tax.yaml +++ b/blawx/fixtures/workspace/beard_tax.yaml @@ -1,6 +1,7 @@ - model: blawx.ruledoc pk: 12 fields: + owner: 3 ruledoc_name: Beard Tax Act rule_text: "Beard Tax Act\r\n\r\n1. In this Act, beard means any facial hair no shorter than 5 millimetres in\r\nlength that:\r\n (a) occurs on or below the diff --git a/blawx/fixtures/workspace/bird.yaml b/blawx/fixtures/workspace/bird.yaml index 8690773a..338e430d 100644 --- a/blawx/fixtures/workspace/bird.yaml +++ b/blawx/fixtures/workspace/bird.yaml @@ -1,6 +1,7 @@ - model: blawx.ruledoc pk: 6 fields: + owner: 3 ruledoc_name: Bird Act rule_text: "Bird Act\r\n\r\n1. A Penguin is a Bird.\r\n2. If a thing is a bird, it flies, [penguin]{unless it is a penguin}." diff --git a/blawx/fixtures/workspace/mbp.yaml b/blawx/fixtures/workspace/mbp.yaml index 6bd26a84..2104dd95 100644 --- a/blawx/fixtures/workspace/mbp.yaml +++ b/blawx/fixtures/workspace/mbp.yaml @@ -1,6 +1,7 @@ - model: blawx.ruledoc pk: 13 fields: + owner: 3 ruledoc_name: Motor Breakdown Policy rule_text: "Motor Breakdown Policy\r\n\r\nMEANING OF WORDS \r\n1. Wherever the following words and phrases appear in bold in this document, they will always diff --git a/blawx/fixtures/workspace/mortality.yaml b/blawx/fixtures/workspace/mortality.yaml index d452252f..9f41a3aa 100644 --- a/blawx/fixtures/workspace/mortality.yaml +++ b/blawx/fixtures/workspace/mortality.yaml @@ -1,6 +1,7 @@ - model: blawx.ruledoc pk: 5 fields: + owner: 3 ruledoc_name: Mortality Act rule_text: "Mortality Act\r\n\r\n1. Humans are mortal." scasp_encoding: '' diff --git a/blawx/fixtures/workspace/net30.yaml b/blawx/fixtures/workspace/net30.yaml index 4f0c78d1..8506b718 100644 --- a/blawx/fixtures/workspace/net30.yaml +++ b/blawx/fixtures/workspace/net30.yaml @@ -1,6 +1,7 @@ - model: blawx.ruledoc pk: 8 fields: + owner: 3 ruledoc_name: Net 30 rule_text: "Net Thirty Act\r\n\r\n1. The payment due date of an invoice is 30 days after the date the invoice is issued." diff --git a/blawx/fixtures/workspace/r34.yaml b/blawx/fixtures/workspace/r34.yaml index 9c08542d..b112108b 100644 --- a/blawx/fixtures/workspace/r34.yaml +++ b/blawx/fixtures/workspace/r34.yaml @@ -1,6 +1,7 @@ - model: blawx.ruledoc pk: 4 fields: + owner: 3 ruledoc_name: Rule 34 rule_text: "Legal Profession Professional Conduct Rules\r\n\r\nExecutive appointments\r\n34.\r\n \ (1) A legal practitioner must not accept any executive appointment associated diff --git a/blawx/fixtures/workspace/rps.yaml b/blawx/fixtures/workspace/rps.yaml index 2c2c14e5..aec40084 100644 --- a/blawx/fixtures/workspace/rps.yaml +++ b/blawx/fixtures/workspace/rps.yaml @@ -1,6 +1,7 @@ - model: blawx.ruledoc pk: 2 fields: + owner: 3 ruledoc_name: Rock Paper Scissors Act rule_text: "Rock Paper Scissors Act\n\nPlayers\n1. A game of rock paper scissors has two players.\n2. There are three signs:\n (a) Rock,\n (b) Paper, and\n diff --git a/blawx/fixtures/workspace/siblings.yaml b/blawx/fixtures/workspace/siblings.yaml index 4b9bd39a..d86aa0f6 100644 --- a/blawx/fixtures/workspace/siblings.yaml +++ b/blawx/fixtures/workspace/siblings.yaml @@ -1,6 +1,7 @@ - model: blawx.ruledoc pk: 11 fields: + owner: 3 ruledoc_name: Siblings Act rule_text: "Siblings Act\r\n\r\n1. A person's sibling is another person with whom the person shares a parent.\r\n2. With regard to wards of the state, parent diff --git a/blawx/fixtures/workspace/wills.yaml b/blawx/fixtures/workspace/wills.yaml index 12c89e6d..729db1ad 100644 --- a/blawx/fixtures/workspace/wills.yaml +++ b/blawx/fixtures/workspace/wills.yaml @@ -1,6 +1,7 @@ - model: blawx.ruledoc pk: 9 fields: + owner: 3 ruledoc_name: Wills Act rule_text: "Wills Act\r\n\r\n1. A person over the age of 18 may make a will.\r\n2. An active military member over the age of 14 may make a will." diff --git a/blawx/fixtures/workspace/wills_tutorial.yaml b/blawx/fixtures/workspace/wills_tutorial.yaml index 496acb45..39748b18 100644 --- a/blawx/fixtures/workspace/wills_tutorial.yaml +++ b/blawx/fixtures/workspace/wills_tutorial.yaml @@ -1,6 +1,7 @@ - model: blawx.ruledoc pk: 14 fields: + owner: 3 ruledoc_name: Wills Tutorial rule_text: "Wills Act\r\n\r\n1. A person over the age of 18 may make a will." scasp_encoding: '' diff --git a/blawx/migrations/0020_ruledoc_owner.py b/blawx/migrations/0020_ruledoc_owner.py new file mode 100644 index 00000000..372f6615 --- /dev/null +++ b/blawx/migrations/0020_ruledoc_owner.py @@ -0,0 +1,22 @@ +# Generated by Django 4.0.1 on 2022-07-08 18:25 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('blawx', '0019_blawxtest_tutorial'), + ] + + operations = [ + migrations.AddField( + model_name='ruledoc', + name='owner', + field=models.ForeignKey(default=3, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + ] diff --git a/blawx/migrations/0021_rename_xml_content_workspacetemplate_yaml_content.py b/blawx/migrations/0021_rename_xml_content_workspacetemplate_yaml_content.py new file mode 100644 index 00000000..c37d6148 --- /dev/null +++ b/blawx/migrations/0021_rename_xml_content_workspacetemplate_yaml_content.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.1 on 2022-07-08 23:00 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('blawx', '0020_ruledoc_owner'), + ] + + operations = [ + migrations.RenameField( + model_name='workspacetemplate', + old_name='xml_content', + new_name='yaml_content', + ), + ] diff --git a/blawx/migrations/0022_rename_workspacetemplate_ruledoctemplate.py b/blawx/migrations/0022_rename_workspacetemplate_ruledoctemplate.py new file mode 100644 index 00000000..08608715 --- /dev/null +++ b/blawx/migrations/0022_rename_workspacetemplate_ruledoctemplate.py @@ -0,0 +1,17 @@ +# Generated by Django 4.0.1 on 2022-07-08 23:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('blawx', '0021_rename_xml_content_workspacetemplate_yaml_content'), + ] + + operations = [ + migrations.RenameModel( + old_name='WorkspaceTemplate', + new_name='RuleDocTemplate', + ), + ] diff --git a/blawx/models.py b/blawx/models.py index a7de3100..33d025ab 100644 --- a/blawx/models.py +++ b/blawx/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.contrib.auth.models import User from .parse_an import generate_tree from cobalt.hierarchical import Act from clean.clean import generate_akn @@ -10,6 +11,7 @@ class RuleDoc(models.Model): # akoma_ntoso = models.TextField(default="",blank=True) scasp_encoding = models.TextField(default="",blank=True) tutorial = models.TextField(default="",blank=True) + owner = models.ForeignKey(User,on_delete=models.CASCADE,) def __str__(self): return self.ruledoc_name @@ -52,9 +54,9 @@ class Meta: models.UniqueConstraint(fields=['ruledoc','test_name'],name='unique_test_and_ruledoc') ] -class WorkspaceTemplate(models.Model): +class RuleDocTemplate(models.Model): template_name = models.CharField(max_length=200) - xml_content = models.TextField(default="") + yaml_content = models.TextField(default="") def __str__(self): return self.template_name diff --git a/blawx/reasoner.py b/blawx/reasoner.py index 99b5e6f2..ce105477 100644 --- a/blawx/reasoner.py +++ b/blawx/reasoner.py @@ -1,4 +1,4 @@ -from django.http import Http404 +from django.http import Http404, HttpResponseNotFound from rest_framework.decorators import api_view, permission_classes, authentication_classes from rest_framework.response import Response @@ -125,274 +125,277 @@ def json_2_scasp(element,higher_order=False): else: return str(element) -@api_view(['GET', 'POST']) -@authentication_classes([SessionAuthentication]) -@permission_classes([IsAuthenticated]) -def run_query(request,workspace,query): +# @api_view(['GET', 'POST']) +# @authentication_classes([SessionAuthentication]) +# @permission_classes([IsAuthenticated]) +# def run_query(request,workspace,query): - # Collect the rules based on the ruleset specified - if workspace == "rps": - scasp_ruleset = """ -:- use_module(library(scasp)). - -#pred player(X) :: '@(X) is a player'. -#pred participate_in(Game,Player) :: '@(Player) participated in @(Game)'. -#pred winner(Game,Player) :: '@(Player) is the winner of @(Game)'. -#pred throw(Player,Sign) :: '@(Player) threw @(Sign)'. -#pred beat(Sign,OtherSign) :: '@(Sign) beats @(OtherSign)'. -#pred game(G) :: '@(G) is a game of rock-paper-scissors'. -#pred not_same_player(X,Y) :: '@(X) and @(Y) are not the same player'. - -beat(rock,scissors). -beat(scissors,paper). -beat(paper,rock). - -not_same_player(X,Y) :- - X \= Y. - -game_has_two_different_players(Game,Player,OtherPlayer) :- - game(Game), - player(Player), - not_same_player(Player,OtherPlayer), - player(OtherPlayer), - participate_in(Game,Player), - participate_in(Game,OtherPlayer). - -winner(Game,Player) :- - game_has_two_different_players(Game,Player,OtherPlayer), - throw(Player,Sign), - throw(OtherPlayer,OtherSign), - beat(Sign,OtherSign). -""" - rulefile = tempfile.NamedTemporaryFile('w',delete=False) - rulefile.write(scasp_ruleset) - rulefile.close() - rulefilename = rulefile.name - else: - return Http404("Workspace not found") - - # Collect the query based on the query specified in the URL - if query == "winner": - scasp_query = "winner(G,P)" - else: - return Http404("Query not found") +# # Collect the rules based on the ruleset specified +# if workspace == "rps": +# scasp_ruleset = """ +# :- use_module(library(scasp)). + +# #pred player(X) :: '@(X) is a player'. +# #pred participate_in(Game,Player) :: '@(Player) participated in @(Game)'. +# #pred winner(Game,Player) :: '@(Player) is the winner of @(Game)'. +# #pred throw(Player,Sign) :: '@(Player) threw @(Sign)'. +# #pred beat(Sign,OtherSign) :: '@(Sign) beats @(OtherSign)'. +# #pred game(G) :: '@(G) is a game of rock-paper-scissors'. +# #pred not_same_player(X,Y) :: '@(X) and @(Y) are not the same player'. + +# beat(rock,scissors). +# beat(scissors,paper). +# beat(paper,rock). + +# not_same_player(X,Y) :- +# X \= Y. + +# game_has_two_different_players(Game,Player,OtherPlayer) :- +# game(Game), +# player(Player), +# not_same_player(Player,OtherPlayer), +# player(OtherPlayer), +# participate_in(Game,Player), +# participate_in(Game,OtherPlayer). + +# winner(Game,Player) :- +# game_has_two_different_players(Game,Player,OtherPlayer), +# throw(Player,Sign), +# throw(OtherPlayer,OtherSign), +# beat(Sign,OtherSign). +# """ +# rulefile = tempfile.NamedTemporaryFile('w',delete=False) +# rulefile.write(scasp_ruleset) +# rulefile.close() +# rulefilename = rulefile.name +# else: +# return Http404("Workspace not found") + +# # Collect the query based on the query specified in the URL +# if query == "winner": +# scasp_query = "winner(G,P)" +# else: +# return Http404("Query not found") - # Collect the data provided, and convert it into s(CASP) statements. - if request.method == "GET": - translated_facts = """ -game(testgame). -player(bob). -player(jane). -participate_in(testgame,bob). -participate_in(testgame,jane). -throw(bob,rock). -throw(jane,scissors).""" - - elif request.method == "POST": - translated_facts = json_2_scasp(request.get_json()) +# # Collect the data provided, and convert it into s(CASP) statements. +# if request.method == "GET": +# translated_facts = """ +# game(testgame). +# player(bob). +# player(jane). +# participate_in(testgame,bob). +# participate_in(testgame,jane). +# throw(bob,rock). +# throw(jane,scissors).""" + +# elif request.method == "POST": +# translated_facts = json_2_scasp(request.get_json()) - # Start the Prolog "thread" - with PrologMQI() as swipl: - with swipl.create_thread() as swipl_thread: +# # Start the Prolog "thread" +# with PrologMQI() as swipl: +# with swipl.create_thread() as swipl_thread: - file = open(rulefilename,'a') +# file = open(rulefilename,'a') - file.write(translated_facts) +# file.write(translated_facts) - file.close() - file = open(rulefilename,'r') - transcript = open("transcript","w") - transcript.write("Loading " + rulefilename + ", the contents of which are:\n") - transcript.write(file.read() + '\n') - # print(file.read()) - file.close() +# file.close() +# file = open(rulefilename,'r') +# transcript = open("transcript","w") +# transcript.write("Loading " + rulefilename + ", the contents of which are:\n") +# transcript.write(file.read() + '\n') +# # print(file.read()) +# file.close() - with redirect_stderr(transcript): - load_file_answer = swipl_thread.query("['" + rulefilename + "'].") - transcript.write(str(load_file_answer) + '\n') - if os.path.exists(rulefilename): - os.remove(rulefilename) - - # Execute the requested query. - transcript.write("scasp(" + scasp_query + "),scasp_embed:scasp_justification(J,[]),with_output_to(string(JOut), scasp_just_human:human_justification_tree(J,[])).\n") - with redirect_stderr(transcript): - query_answer = swipl_thread.query("scasp(" + scasp_query + "),scasp_embed:scasp_justification(J,[]),with_output_to(string(JOut), scasp_just_human:human_justification_tree(J,[])).") - transcript.write(str(query_answer) + '\n') +# with redirect_stderr(transcript): +# load_file_answer = swipl_thread.query("['" + rulefilename + "'].") +# transcript.write(str(load_file_answer) + '\n') +# if os.path.exists(rulefilename): +# os.remove(rulefilename) + +# # Execute the requested query. +# transcript.write("scasp(" + scasp_query + "),scasp_embed:scasp_justification(J,[]),with_output_to(string(JOut), scasp_just_human:human_justification_tree(J,[])).\n") +# with redirect_stderr(transcript): +# query_answer = swipl_thread.query("scasp(" + scasp_query + "),scasp_embed:scasp_justification(J,[]),with_output_to(string(JOut), scasp_just_human:human_justification_tree(J,[])).") +# transcript.write(str(query_answer) + '\n') - transcript.close() - transcript = open("transcript",'r') - transcript_output = transcript.read() - transcript.close() - os.remove('transcript') +# transcript.close() +# transcript = open("transcript",'r') +# transcript_output = transcript.read() +# transcript.close() +# os.remove('transcript') - if type(query_answer) is not list: - query_output = query_answer - else: - query_output = query_answer[0] - - # Return the results as JSON - return Response({ "answer": query_output, "transcript": transcript_output }) - -@api_view(['POST']) -@authentication_classes([SessionAuthentication]) -@permission_classes([IsAuthenticated]) -def run_ruledoc(request,pk): - translated_facts = "" - if request.data: - translated_facts = json_2_scasp(request.data) - wss = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=pk)) - ruleset = "" - for ws in wss: - ruleset += ws.scasp_encoding - query = "No Query Specified" - for line in ruleset.splitlines(): - if line.startswith("?- "): - query = line[3:-1] # remove query prompt and period. - full_query = "scasp(" + query + ",[tree(Tree)]),with_output_to(string(Human), human_justification_tree(Tree,[]))." - rulefile = tempfile.NamedTemporaryFile('w',delete=False) - rulefile.write(":- use_module(library(scasp)).\n") - rulefile.write(":- use_module(library(scasp/human)).\n") - rulefile.write(translated_facts) - rulefile.write(ruleset) - rulefile.close() - rulefilename = rulefile.name - - # Start the Prolog "thread" - try: - with PrologMQI() as swipl: - with swipl.create_thread() as swipl_thread: - - transcript = tempfile.NamedTemporaryFile('w',delete=False,prefix="transcript_") - transcript_name = transcript.name - - with redirect_stderr(transcript): - load_file_answer = swipl_thread.query("['" + rulefilename + "'].") - transcript.write(str(load_file_answer) + '\n') - if os.path.exists(rulefilename): - os.remove(rulefilename) +# if type(query_answer) is not list: +# query_output = query_answer +# else: +# query_output = query_answer[0] + +# # Return the results as JSON +# return Response({ "answer": query_output, "transcript": transcript_output }) + +# @api_view(['POST']) +# @authentication_classes([SessionAuthentication]) +# @permission_classes([IsAuthenticated]) +# def run_ruledoc(request,pk): +# translated_facts = "" +# if request.data: +# translated_facts = json_2_scasp(request.data) +# wss = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=pk)) +# ruleset = "" +# for ws in wss: +# ruleset += ws.scasp_encoding +# query = "No Query Specified" +# for line in ruleset.splitlines(): +# if line.startswith("?- "): +# query = line[3:-1] # remove query prompt and period. +# full_query = "scasp(" + query + ",[tree(Tree)]),with_output_to(string(Human), human_justification_tree(Tree,[]))." +# rulefile = tempfile.NamedTemporaryFile('w',delete=False) +# rulefile.write(":- use_module(library(scasp)).\n") +# rulefile.write(":- use_module(library(scasp/human)).\n") +# rulefile.write(translated_facts) +# rulefile.write(ruleset) +# rulefile.close() +# rulefilename = rulefile.name + +# # Start the Prolog "thread" +# try: +# with PrologMQI() as swipl: +# with swipl.create_thread() as swipl_thread: + +# transcript = tempfile.NamedTemporaryFile('w',delete=False,prefix="transcript_") +# transcript_name = transcript.name + +# with redirect_stderr(transcript): +# load_file_answer = swipl_thread.query("['" + rulefilename + "'].") +# transcript.write(str(load_file_answer) + '\n') +# if os.path.exists(rulefilename): +# os.remove(rulefilename) + +# transcript.write(full_query) +# with redirect_stderr(transcript): +# query_answer = swipl_thread.query(full_query) +# transcript.write(str(query_answer) + '\n') + +# transcript.close() +# transcript = open(transcript_name,'r') +# # transcript = open("transcript",'r') +# transcript_output = transcript.read() +# transcript.close() +# os.remove(transcript_name) +# except PrologError as err: +# return Response({ "error": "There was an error while running the code.", "transcript": err.prolog() }) +# except PrologLaunchError as err: +# query_answer = "Blawx could not load the reasoner." +# return Response({ "error": "Blawx could not load the reasoner." }) +# # Return the results as JSON +# return Response({ "answer": json.dumps(query_answer), "transcript": transcript_output }) - transcript.write(full_query) - with redirect_stderr(transcript): - query_answer = swipl_thread.query(full_query) - transcript.write(str(query_answer) + '\n') - - transcript.close() - transcript = open(transcript_name,'r') - # transcript = open("transcript",'r') - transcript_output = transcript.read() - transcript.close() - os.remove(transcript_name) - except PrologError as err: - return Response({ "error": "There was an error while running the code.", "transcript": err.prolog() }) - except PrologLaunchError as err: - query_answer = "Blawx could not load the reasoner." - return Response({ "error": "Blawx could not load the reasoner." }) - # Return the results as JSON - return Response({ "answer": json.dumps(query_answer), "transcript": transcript_output }) - - @api_view(['POST']) @authentication_classes([SessionAuthentication]) @permission_classes([IsAuthenticated]) def run_test(request,ruledoc,test_name): - translated_facts = "" - if request.data: - translated_facts = new_json_2_scasp(request.data) - wss = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=ruledoc)) - test = BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(pk=ruledoc),test_name=test_name) - ruleset = "" - for ws in wss: - ruleset += "\n\n" + ws.scasp_encoding - ruleset += "\n\n" + test.scasp_encoding - # print(ruleset) - - rulefile = tempfile.NamedTemporaryFile('w',delete=False) - rulefile.write(""" -:- use_module(library(scasp)). -:- use_module(library(scasp/human)). -:- use_module(library(scasp/output)). - -:- meta_predicate - blawxrun2(0,-). -""") - - query = "No Query Specified" - for line in test.scasp_encoding.splitlines(): - if line.startswith("?- "): - query = line[3:-1] # remove query prompt and period. - - rulefile.write(""" -blawxrun(Query, Human) :- - scasp(Query,[tree(Tree)]), - ovar_analyze_term(t(Query, Tree),[name_constraints(true)]), - with_output_to(string(Human), - human_justification_tree(Tree,[])). - term_attvars(Query, AttVars), - maplist(del_attrs, AttVars). -""") - # For Each Variable in the query -# for v in get_variables(query): -# rulefile.write("ovar_analyze_term(t(" + v + ", Tree),[name_constraints(true)]),") -# rulefile.write(""" -# with_output_to(string(Human), -# human_justification_tree(Tree,[])). -# """) - - rulefile.write(ldap_code + '\n\n') - rulefile.write(scasp_dates + '\n\n') - - - rulefile.write(translated_facts) - rulefile.write(ruleset) - rulefile.close() - rulefilename = rulefile.name - temprulefile = open(rulefilename,'r') - print(temprulefile.read()) - temprulefile.close() - - # Start the Prolog "thread" - try: - with PrologMQI() as swipl: - with swipl.create_thread() as swipl_thread: - - transcript = tempfile.NamedTemporaryFile('w',delete=False,prefix="transcript_") - transcript_name = transcript.name - - with redirect_stderr(transcript): - load_file_answer = swipl_thread.query("['" + rulefilename + "'].") - transcript.write(str(load_file_answer) + '\n') - if os.path.exists(rulefilename): - rules = open(rulefilename) - rulestext = rules.read() - transcript.write(rulestext + '\n') - rules.close() - os.remove(rulefilename) - - #transcript.write(full_query) - with redirect_stderr(transcript): - print("blawxrun(" + query + ",Human).") - query_answer = swipl_thread.query("blawxrun(" + query + ",Human).") - - transcript.write(str(query_answer) + '\n') - - transcript.close() - transcript = open(transcript_name,'r') - # transcript = open("transcript",'r') - transcript_output = transcript.read() - transcript.close() - os.remove(transcript_name) - except PrologError as err: - return Response({ "error": "There was an error while running the code.", "transcript": err.prolog() }) - except PrologLaunchError as err: - query_answer = "Blawx could not load the reasoner." - return Response({ "error": "Blawx could not load the reasoner." }) - # Return the results as JSON - if query_answer == False: - return Response({ "Answers": [], "Transcript": transcript_output }) + ruledoctest = RuleDoc.objects.filter(pk=ruledoc,owner=request.user) + if ruledoctest.exists(): + translated_facts = "" + if request.data: + translated_facts = new_json_2_scasp(request.data) + wss = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=ruledoc)) + test = BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(pk=ruledoc),test_name=test_name) + ruleset = "" + for ws in wss: + ruleset += "\n\n" + ws.scasp_encoding + ruleset += "\n\n" + test.scasp_encoding + # print(ruleset) + + rulefile = tempfile.NamedTemporaryFile('w',delete=False) + rulefile.write(""" + :- use_module(library(scasp)). + :- use_module(library(scasp/human)). + :- use_module(library(scasp/output)). + + :- meta_predicate + blawxrun2(0,-). + """) + + query = "No Query Specified" + for line in test.scasp_encoding.splitlines(): + if line.startswith("?- "): + query = line[3:-1] # remove query prompt and period. + + rulefile.write(""" + blawxrun(Query, Human) :- + scasp(Query,[tree(Tree)]), + ovar_analyze_term(t(Query, Tree),[name_constraints(true)]), + with_output_to(string(Human), + human_justification_tree(Tree,[])). + term_attvars(Query, AttVars), + maplist(del_attrs, AttVars). + """) + # For Each Variable in the query + # for v in get_variables(query): + # rulefile.write("ovar_analyze_term(t(" + v + ", Tree),[name_constraints(true)]),") + # rulefile.write(""" + # with_output_to(string(Human), + # human_justification_tree(Tree,[])). + # """) + + rulefile.write(ldap_code + '\n\n') + rulefile.write(scasp_dates + '\n\n') + + + rulefile.write(translated_facts) + rulefile.write(ruleset) + rulefile.close() + rulefilename = rulefile.name + temprulefile = open(rulefilename,'r') + print(temprulefile.read()) + temprulefile.close() + + # Start the Prolog "thread" + try: + with PrologMQI() as swipl: + with swipl.create_thread() as swipl_thread: + + transcript = tempfile.NamedTemporaryFile('w',delete=False,prefix="transcript_") + transcript_name = transcript.name + + with redirect_stderr(transcript): + load_file_answer = swipl_thread.query("['" + rulefilename + "'].") + transcript.write(str(load_file_answer) + '\n') + if os.path.exists(rulefilename): + rules = open(rulefilename) + rulestext = rules.read() + transcript.write(rulestext + '\n') + rules.close() + os.remove(rulefilename) + + #transcript.write(full_query) + with redirect_stderr(transcript): + print("blawxrun(" + query + ",Human).") + query_answer = swipl_thread.query("blawxrun(" + query + ",Human).") + + transcript.write(str(query_answer) + '\n') + + transcript.close() + transcript = open(transcript_name,'r') + # transcript = open("transcript",'r') + transcript_output = transcript.read() + transcript.close() + os.remove(transcript_name) + except PrologError as err: + return Response({ "error": "There was an error while running the code.", "transcript": err.prolog() }) + except PrologLaunchError as err: + query_answer = "Blawx could not load the reasoner." + return Response({ "error": "Blawx could not load the reasoner." }) + # Return the results as JSON + if query_answer == False: + return Response({ "Answers": [], "Transcript": transcript_output }) + else: + return Response({ "Answers": generate_answers(query_answer), "Transcript": transcript_output }) else: - return Response({ "Answers": generate_answers(query_answer), "Transcript": transcript_output }) + return HttpResponseNotFound() def get_ontology_internal(ruledoc,test_name): wss = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=ruledoc)) @@ -540,235 +543,243 @@ def get_ontology_internal(ruledoc,test_name): @authentication_classes([SessionAuthentication]) @permission_classes([IsAuthenticated]) def get_ontology(request,ruledoc,test_name): - result = get_ontology_internal(ruledoc,test_name) - return Response(result) + ruledoctest = RuleDoc.objects.filter(owner=request.user,pk=ruledoc) + if ruledoctest.exists(): + result = get_ontology_internal(ruledoc,test_name) + return Response(result) + else: + return HttpResponseNotFound() @api_view(['POST']) @authentication_classes([SessionAuthentication]) @permission_classes([IsAuthenticated]) def interview(request,ruledoc,test_name): - translated_facts = "" - if request.data: - translated_facts = new_json_2_scasp(request.data, True) #Generate answers ignoring assumptions in the submitted data - wss = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=ruledoc)) - test = BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(pk=ruledoc),test_name=test_name) - ruleset = "" - for ws in wss: - ruleset += "\n\n" + ws.scasp_encoding - ruleset += "\n\n" + test.scasp_encoding - # print(ruleset) - - rulefile = tempfile.NamedTemporaryFile('w',delete=False) - rulefile.write(""" -:- use_module(library(scasp)). -:- use_module(library(scasp/human)). -:- use_module(library(scasp/output)). - -:- meta_predicate - blawxrun2(0,-). -""") - - query = "No Query Specified" - for line in test.scasp_encoding.splitlines(): - if line.startswith("?- "): - query = line[3:-1] # remove query prompt and period. - - rulefile.write(""" -blawxrun(Query, Human) :- - scasp(Query,[tree(Tree)]), - ovar_analyze_term(t(Query, Tree),[name_constraints(true)]), - with_output_to(string(Human), - human_justification_tree(Tree,[])). - term_attvars(Query, AttVars), - maplist(del_attrs, AttVars). -""") - # For Each Variable in the query -# for v in get_variables(query): -# rulefile.write("ovar_analyze_term(t(" + v + ", Tree),[name_constraints(true)]),") -# rulefile.write(""" -# with_output_to(string(Human), -# human_justification_tree(Tree,[])). -# """) - - rulefile.write(ldap_code + '\n\n') - rulefile.write(scasp_dates + '\n\n') - - - rulefile.write(translated_facts) - rulefile.write(ruleset) - rulefile.close() - rulefilename = rulefile.name - temprulefile = open(rulefilename,'r') - print(temprulefile.read()) - temprulefile.close() - - # Start the Prolog "thread" - try: - with PrologMQI() as swipl: - with swipl.create_thread() as swipl_thread: - - transcript = tempfile.NamedTemporaryFile('w',delete=False,prefix="transcript_") - transcript_name = transcript.name - - with redirect_stderr(transcript): - load_file_answer = swipl_thread.query("['" + rulefilename + "'].") - transcript.write(str(load_file_answer) + '\n') - if os.path.exists(rulefilename): - rules = open(rulefilename) - rulestext = rules.read() - transcript.write(rulestext + '\n') - rules.close() - os.remove(rulefilename) - - #transcript.write(full_query) - with redirect_stderr(transcript): - print("blawxrun(" + query + ",Human).") - query_answer = swipl_thread.query("blawxrun(" + query + ",Human).") - - transcript.write(str(query_answer) + '\n') - - transcript.close() - transcript = open(transcript_name,'r') - # transcript = open("transcript",'r') - transcript_output = transcript.read() - transcript.close() - os.remove(transcript_name) - except PrologError as err: - return Response({ "error": "There was an error while running the code.", "transcript": err.prolog() }) - except PrologLaunchError as err: - query_answer = "Blawx could not load the reasoner." - return Response({ "error": "Blawx could not load the reasoner." }) - - # Now get the ontology information to be able to generate the relevance data - # Later, we will need to run the query again with assumptions in order to determine relevance. - # For now, we are just filling the data structure with all the categories and attributes. - # - # Old Version: - # ontology = get_ontology_internal(ruledoc,test_name) - # relevant_categories = ontology['Categories'] - # relevant_attributes = [] - # for a in ontology['Attributes']: - # relevant_attributes.append({"Attribute": a['Attribute']}) - # Effectively, we're going to start over. - translated_facts = "" - if request.data: - translated_facts = new_json_2_scasp(request.data, False) #Generate answers INCLUDING assumptions in the submitted data - wss = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=ruledoc)) - test = BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(pk=ruledoc),test_name=test_name) - ruleset = "" - for ws in wss: - ruleset += "\n\n" + ws.scasp_encoding - ruleset += "\n\n" + test.scasp_encoding - - rulefile = tempfile.NamedTemporaryFile('w',delete=False) - rulefile.write(""" -:- use_module(library(scasp)). -:- use_module(library(scasp/human)). -:- use_module(library(scasp/output)). - -:- meta_predicate - blawxrun2(0,-). -""") - - query = "No Query Specified" - for line in test.scasp_encoding.splitlines(): - if line.startswith("?- "): - query = line[3:-1] # remove query prompt and period. - - rulefile.write(""" -blawxrun(Query, Tree, Model) :- - scasp(Query,[tree(Tree),model(Model)]), - ovar_analyze_term(t(Query, Tree),[name_constraints(true)]). -""") - - rulefile.write(ldap_code + '\n\n') - rulefile.write(scasp_dates + '\n\n') - - - rulefile.write(translated_facts) - rulefile.write(ruleset) - rulefile.close() - rulefilename = rulefile.name - temprulefile = open(rulefilename,'r') - print(temprulefile.read()) - temprulefile.close() - - # Start the Prolog "thread" - try: - with PrologMQI() as swipl: - with swipl.create_thread() as swipl_thread: - - transcript = tempfile.NamedTemporaryFile('w',delete=False,prefix="transcript_") - transcript_name = transcript.name - - with redirect_stderr(transcript): - load_file_answer = swipl_thread.query("['" + rulefilename + "'].") - transcript.write(str(load_file_answer) + '\n') - if os.path.exists(rulefilename): - rules = open(rulefilename) - rulestext = rules.read() - transcript.write(rulestext + '\n') - rules.close() - os.remove(rulefilename) + ruledoctest = RuleDoc.objects.filter(owner=request.user,pk=ruledoc) + if ruledoctest.exists(): + translated_facts = "" + if request.data: + translated_facts = new_json_2_scasp(request.data, True) #Generate answers ignoring assumptions in the submitted data + wss = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=ruledoc)) + test = BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(pk=ruledoc),test_name=test_name) + ruleset = "" + for ws in wss: + ruleset += "\n\n" + ws.scasp_encoding + ruleset += "\n\n" + test.scasp_encoding + # print(ruleset) + + rulefile = tempfile.NamedTemporaryFile('w',delete=False) + rulefile.write(""" + :- use_module(library(scasp)). + :- use_module(library(scasp/human)). + :- use_module(library(scasp/output)). + + :- meta_predicate + blawxrun2(0,-). + """) + + query = "No Query Specified" + for line in test.scasp_encoding.splitlines(): + if line.startswith("?- "): + query = line[3:-1] # remove query prompt and period. + + rulefile.write(""" + blawxrun(Query, Human) :- + scasp(Query,[tree(Tree)]), + ovar_analyze_term(t(Query, Tree),[name_constraints(true)]), + with_output_to(string(Human), + human_justification_tree(Tree,[])). + term_attvars(Query, AttVars), + maplist(del_attrs, AttVars). + """) + # For Each Variable in the query + # for v in get_variables(query): + # rulefile.write("ovar_analyze_term(t(" + v + ", Tree),[name_constraints(true)]),") + # rulefile.write(""" + # with_output_to(string(Human), + # human_justification_tree(Tree,[])). + # """) + + rulefile.write(ldap_code + '\n\n') + rulefile.write(scasp_dates + '\n\n') + + + rulefile.write(translated_facts) + rulefile.write(ruleset) + rulefile.close() + rulefilename = rulefile.name + temprulefile = open(rulefilename,'r') + print(temprulefile.read()) + temprulefile.close() + + # Start the Prolog "thread" + try: + with PrologMQI() as swipl: + with swipl.create_thread() as swipl_thread: + + transcript = tempfile.NamedTemporaryFile('w',delete=False,prefix="transcript_") + transcript_name = transcript.name + + with redirect_stderr(transcript): + load_file_answer = swipl_thread.query("['" + rulefilename + "'].") + transcript.write(str(load_file_answer) + '\n') + if os.path.exists(rulefilename): + rules = open(rulefilename) + rulestext = rules.read() + transcript.write(rulestext + '\n') + rules.close() + os.remove(rulefilename) + + #transcript.write(full_query) + with redirect_stderr(transcript): + print("blawxrun(" + query + ",Human).") + query_answer = swipl_thread.query("blawxrun(" + query + ",Human).") + + transcript.write(str(query_answer) + '\n') + + transcript.close() + transcript = open(transcript_name,'r') + # transcript = open("transcript",'r') + transcript_output = transcript.read() + transcript.close() + os.remove(transcript_name) + except PrologError as err: + return Response({ "error": "There was an error while running the code.", "transcript": err.prolog() }) + except PrologLaunchError as err: + query_answer = "Blawx could not load the reasoner." + return Response({ "error": "Blawx could not load the reasoner." }) + + # Now get the ontology information to be able to generate the relevance data + # Later, we will need to run the query again with assumptions in order to determine relevance. + # For now, we are just filling the data structure with all the categories and attributes. + # + # Old Version: + # ontology = get_ontology_internal(ruledoc,test_name) + # relevant_categories = ontology['Categories'] + # relevant_attributes = [] + # for a in ontology['Attributes']: + # relevant_attributes.append({"Attribute": a['Attribute']}) + # Effectively, we're going to start over. + translated_facts = "" + if request.data: + translated_facts = new_json_2_scasp(request.data, False) #Generate answers INCLUDING assumptions in the submitted data + wss = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=ruledoc)) + test = BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(pk=ruledoc),test_name=test_name) + ruleset = "" + for ws in wss: + ruleset += "\n\n" + ws.scasp_encoding + ruleset += "\n\n" + test.scasp_encoding + + rulefile = tempfile.NamedTemporaryFile('w',delete=False) + rulefile.write(""" + :- use_module(library(scasp)). + :- use_module(library(scasp/human)). + :- use_module(library(scasp/output)). + + :- meta_predicate + blawxrun2(0,-). + """) + + query = "No Query Specified" + for line in test.scasp_encoding.splitlines(): + if line.startswith("?- "): + query = line[3:-1] # remove query prompt and period. + + rulefile.write(""" + blawxrun(Query, Tree, Model) :- + scasp(Query,[tree(Tree),model(Model)]), + ovar_analyze_term(t(Query, Tree),[name_constraints(true)]). + """) + + rulefile.write(ldap_code + '\n\n') + rulefile.write(scasp_dates + '\n\n') + + + rulefile.write(translated_facts) + rulefile.write(ruleset) + rulefile.close() + rulefilename = rulefile.name + temprulefile = open(rulefilename,'r') + print(temprulefile.read()) + temprulefile.close() + + # Start the Prolog "thread" + try: + with PrologMQI() as swipl: + with swipl.create_thread() as swipl_thread: + + transcript = tempfile.NamedTemporaryFile('w',delete=False,prefix="transcript_") + transcript_name = transcript.name + + with redirect_stderr(transcript): + load_file_answer = swipl_thread.query("['" + rulefilename + "'].") + transcript.write(str(load_file_answer) + '\n') + if os.path.exists(rulefilename): + rules = open(rulefilename) + rulestext = rules.read() + transcript.write(rulestext + '\n') + rules.close() + os.remove(rulefilename) + + #transcript.write(full_query) + with redirect_stderr(transcript): + print("blawxrun(" + query + ",Human,Model).") + relevance_query_answer = swipl_thread.query("blawxrun(" + query + ",Human, Model).") + + transcript.write(str(relevance_query_answer) + '\n') + + transcript.close() + transcript = open(transcript_name,'r') + # transcript = open("transcript",'r') + transcript_output += transcript.read() # Adding to the transcript instead of setting it. + transcript.close() + os.remove(transcript_name) + except PrologError as err: + return Response({ "error": "There was an error while running the code.", "transcript": err.prolog() }) + except PrologLaunchError as err: + query_answer = "Blawx could not load the reasoner." + return Response({ "error": "Blawx could not load the reasoner." }) + + # Okay, the relevance query is running properly, and including terms in the results. + # Now I need to generate relevant categories and relevant attributes from the contents. + # The way to do that is to go through the terms, find the ones that have been assumed. + + # The relevant categories are the categories for which there is an assumed member of a category in the results. + # It is assumed if it justified with a chs(category(term)) in the tree. The term can be a symbol or an atom. + # It makes a difference. If it is a variable, then the unground term is valid. If it is a symbol, the ground + # term is valid, but not necessarily the unground term, unless it is valid elsewhere. + # Similarly for attributes. if there exists chs(attribute(object,value)) in the tree, then it was assumed. + + # So we could start by just pulling out anything that appears inside chs, and then processing those. + assumptions = [] + useful_assumptions = [] + relevant_categories = [] + relevant_attributes= [] + relevance_answers_processed = generate_answers(relevance_query_answer, False) + for a in relevance_answers_processed: + for m in a['Models']: + assumptions.extend(find_assumptions(m['Tree'])) + # print("Found Assumptions:") + # pprint(assumptions) + for a in assumptions: + if a['functor'] == 'not' and a['args'][0]['functor'] == 'abducible$$': + pass + elif simplify_term(a) not in useful_assumptions: + useful_assumptions.append(simplify_term(a)) + for ua in useful_assumptions: + if len(ua['args']) == 1: + relevant_categories.append(ua['functor']) + else: + relevant_attributes.append({'Attribute': ua['functor'], 'Arguments': ua['args']}) - #transcript.write(full_query) - with redirect_stderr(transcript): - print("blawxrun(" + query + ",Human,Model).") - relevance_query_answer = swipl_thread.query("blawxrun(" + query + ",Human, Model).") - - transcript.write(str(relevance_query_answer) + '\n') - transcript.close() - transcript = open(transcript_name,'r') - # transcript = open("transcript",'r') - transcript_output += transcript.read() # Adding to the transcript instead of setting it. - transcript.close() - os.remove(transcript_name) - except PrologError as err: - return Response({ "error": "There was an error while running the code.", "transcript": err.prolog() }) - except PrologLaunchError as err: - query_answer = "Blawx could not load the reasoner." - return Response({ "error": "Blawx could not load the reasoner." }) - - # Okay, the relevance query is running properly, and including terms in the results. - # Now I need to generate relevant categories and relevant attributes from the contents. - # The way to do that is to go through the terms, find the ones that have been assumed. - - # The relevant categories are the categories for which there is an assumed member of a category in the results. - # It is assumed if it justified with a chs(category(term)) in the tree. The term can be a symbol or an atom. - # It makes a difference. If it is a variable, then the unground term is valid. If it is a symbol, the ground - # term is valid, but not necessarily the unground term, unless it is valid elsewhere. - # Similarly for attributes. if there exists chs(attribute(object,value)) in the tree, then it was assumed. - - # So we could start by just pulling out anything that appears inside chs, and then processing those. - assumptions = [] - useful_assumptions = [] - relevant_categories = [] - relevant_attributes= [] - relevance_answers_processed = generate_answers(relevance_query_answer, False) - for a in relevance_answers_processed: - for m in a['Models']: - assumptions.extend(find_assumptions(m['Tree'])) - # print("Found Assumptions:") - # pprint(assumptions) - for a in assumptions: - if a['functor'] == 'not' and a['args'][0]['functor'] == 'abducible$$': - pass - elif simplify_term(a) not in useful_assumptions: - useful_assumptions.append(simplify_term(a)) - for ua in useful_assumptions: - if len(ua['args']) == 1: - relevant_categories.append(ua['functor']) + + # Return the results as JSON + if query_answer == False: + return Response({ "Answers": [], "Relevant Categories": relevant_categories, "Relevant Attributes": relevant_attributes, "Transcript": transcript_output }) else: - relevant_attributes.append({'Attribute': ua['functor'], 'Arguments': ua['args']}) - - - - # Return the results as JSON - if query_answer == False: - return Response({ "Answers": [], "Relevant Categories": relevant_categories, "Relevant Attributes": relevant_attributes, "Transcript": transcript_output }) + return Response({ "Answers": generate_answers(query_answer), "Relevant Categories": relevant_categories, "Relevant Attributes": relevant_attributes, "Transcript": transcript_output }) else: - return Response({ "Answers": generate_answers(query_answer), "Relevant Categories": relevant_categories, "Relevant Attributes": relevant_attributes, "Transcript": transcript_output }) + return HttpResponseNotFound() diff --git a/blawx/urls.py b/blawx/urls.py index 213b2173..54608992 100644 --- a/blawx/urls.py +++ b/blawx/urls.py @@ -32,8 +32,8 @@ path('docs//', views.DocumentView.as_view(), name="docs_page"), path('api/', include(router.urls)), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), - path('query///', reasoner.run_query), - path('/run/', reasoner.run_ruledoc, name="runcode"), - path('example//', views.get_example), + # path('query///', reasoner.run_query), + # path('/run/', reasoner.run_ruledoc, name="runcode"), + # path('example//', views.get_example), path("register/", views.register_request, name="register"), ] diff --git a/blawx/views.py b/blawx/views.py index acdae1bc..9047d4a1 100644 --- a/blawx/views.py +++ b/blawx/views.py @@ -5,7 +5,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.forms import UserCreationForm from django.core import serializers -from django.http import FileResponse, HttpResponseRedirect, HttpResponseNotAllowed +from django.http import FileResponse, HttpResponseNotFound, HttpResponseRedirect, HttpResponseNotAllowed from django.shortcuts import render, redirect from django.contrib.auth import login from django.contrib import messages @@ -19,7 +19,7 @@ # from rest_framework import permissions from .serializers import WorkspaceSerializer, CodeUpdateRequestSerializer -from .models import Workspace, DocPage, WorkspaceTemplate, RuleDoc, BlawxTest +from .models import Workspace, DocPage, RuleDocTemplate, RuleDoc, BlawxTest from cobalt.hierarchical import Act @@ -40,12 +40,13 @@ def register_request(request): form = UserCreationForm() return render (request=request, template_name="registration/register.html", context={"register_form":form}) -class RuleDocsView(generic.ListView): +class RuleDocsView(LoginRequiredMixin, generic.ListView): template_name = 'blawx/index.html' context_object_name = 'ruledoc_list' def get_queryset(self): - return RuleDoc.objects.all() + return RuleDoc.objects.filter(owner=self.request.user) + # return RuleDoc.objects.all() # class WorkspacesView(generic.ListView): # template_name = 'blawx/index.html' @@ -61,6 +62,9 @@ class RuleDocView(LoginRequiredMixin, generic.DetailView): template_name = 'blawx/ruledoc.html' model = RuleDoc + def get_queryset(self): + return RuleDoc.objects.filter(pk=self.kwargs['pk'],owner=self.request.user) + # def get_queryset(self): # return RuleDoc.objects.all() @@ -80,7 +84,7 @@ def get_context_data(self, **kwargs): @authentication_classes([SessionAuthentication]) @permission_classes([IsAuthenticated]) def ruleDocLegalTextView(request,pk,section_name): - ruledoc=RuleDoc.objects.get(pk=pk) + ruledoc=RuleDoc.objects.get(owner=request.user,pk=pk) cobalt_parse = Act(ruledoc.akoma_ntoso) target = cobalt_parse.act.find(".//*[@eId='" + section_name + "']") return Response({'xml': lxml.etree.tostring(target), @@ -92,7 +96,7 @@ def ruleDocLegalTextView(request,pk,section_name): def ruleDocExportView(request,pk): download = tempfile.NamedTemporaryFile('w',delete=False,prefix="blawx_rule_" + str(pk) + "_") download_filename = download.name - ruledoc = RuleDoc.objects.filter(pk=pk) + ruledoc = RuleDoc.objects.filter(owner=request.user,pk=pk) download.write(serializers.serialize('yaml',ruledoc)) sections = Workspace.objects.filter(ruledoc=pk) if len(sections): @@ -118,6 +122,7 @@ def ruleDocImportView(request): new_object_list = list(new_objects) # Get the RuleDoc, remove the PK, save it, and get the PK of the saved version. new_object_list[0].pk = None + new_object_list[0].owner = request.user new_object_list[0].save() new_pk = new_object_list[0].pk @@ -135,12 +140,12 @@ class BlawxView(LoginRequiredMixin, generic.DetailView): model = RuleDoc def get_queryset(self): - return RuleDoc.objects.all() + return RuleDoc.objects.filter(owner=self.request.user) def get_context_data(self, **kwargs): context = super(BlawxView, self).get_context_data(**kwargs) - context['templates'] = WorkspaceTemplate.objects.all() # TODO I don't think this is being used. - context['workspaces'] = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=self.kwargs['pk'])) #TODO I don't think this is being used. + # context['templates'] = WorkspaceTemplate.objects.all() # TODO I don't think this is being used. + context['workspaces'] = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(owner=self.request.user,pk=self.kwargs['pk'])) return context class TestView(LoginRequiredMixin, generic.DetailView): @@ -148,14 +153,14 @@ class TestView(LoginRequiredMixin, generic.DetailView): model = BlawxTest def get_object(self): - return BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(pk=self.kwargs['pk']),test_name=self.kwargs['test_name']) + return BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(owner=self.request.user,pk=self.kwargs['pk']),test_name=self.kwargs['test_name']) class BlawxBot(LoginRequiredMixin, generic.DetailView): template_name = "blawx/bot.html" model = BlawxTest def get_object(self): - return BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(pk=self.kwargs['ruledoc']),test_name=self.kwargs['test_name']) + return BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(owner=self.request.user,pk=self.kwargs['ruledoc']),test_name=self.kwargs['test_name']) class TestCreateView(LoginRequiredMixin, CreateView): model = BlawxTest @@ -166,7 +171,7 @@ def get_success_url(self): return reverse_lazy('blawx:ruledoc', args=(self.kwargs['pk'],)) def form_valid(self, form): - form.instance.ruledoc = RuleDoc.objects.get(pk=self.kwargs['pk']) + form.instance.ruledoc = RuleDoc.objects.get(owner=self.request.user,pk=self.kwargs['pk']) return super().form_valid(form) class TestDeleteView(LoginRequiredMixin, DeleteView): @@ -174,6 +179,13 @@ class TestDeleteView(LoginRequiredMixin, DeleteView): def get_success_url(self): return reverse_lazy('blawx:ruledoc', args=(self.kwargs['pk'],)) + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + target = RuleDoc.objects.get(pk=self.kwargs['pk']) + if target.owner != self.request.user: + return redirect(self.success_url) # This is sub-optimal. + return super().post(request, *args, **kwargs) # class WorkspaceCreateView(CreateView): # model = Workspace @@ -185,10 +197,31 @@ class RuleDocCreateView(LoginRequiredMixin, CreateView): fields = ['ruledoc_name','rule_text'] success_url = reverse_lazy('blawx:ruledocs') + def form_valid(self, form): + form.instance.owner = self.request.user + return super().form_valid(form) + + class RuleDocDeleteView(LoginRequiredMixin, DeleteView): model = RuleDoc success_url = reverse_lazy('blawx:ruledocs') + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + if self.object.owner != self.request.user: + return redirect(self.success_url) + return super().post(request, *args, **kwargs) + + + # def delete(self, request, *args, **kwargs): + # target = RuleDoc.objects.get(owner=self.request.user,pk=request.pk) + # if target.exists(): + # target.delete() + # return redirect(self.success_url) + # else: + # return HttpResponseNotFound() + # class WorkspaceDeleteView(DeleteView): # model = Workspace # success_url = reverse_lazy('blawx:workspaces') @@ -221,59 +254,78 @@ class WorkspaceAPIViewSet(viewsets.ModelViewSet): serializer_class = WorkspaceSerializer permission_classes = [permissions.IsAuthenticated] + @api_view(['POST']) @authentication_classes([SessionAuthentication]) @permission_classes([IsAuthenticated]) def update_code(request,pk,workspace): - target = Workspace.objects.get(ruledoc=pk,workspace_name=workspace) - workspace_serializer = CodeUpdateRequestSerializer(data=request.data) - workspace_serializer.is_valid() - target.xml_content = workspace_serializer.validated_data.get('xml_content', target.xml_content) - target.scasp_encoding = workspace_serializer.validated_data.get('scasp_encoding', target.scasp_encoding) - target.save() - return Response({"That probably worked."}) + ruledoctest = RuleDoc.objects.get(owner=request.user,pk=pk) + if ruledoctest: + target = Workspace.objects.get(ruledoc=pk,workspace_name=workspace) + workspace_serializer = CodeUpdateRequestSerializer(data=request.data) + workspace_serializer.is_valid() + target.xml_content = workspace_serializer.validated_data.get('xml_content', target.xml_content) + target.scasp_encoding = workspace_serializer.validated_data.get('scasp_encoding', target.scasp_encoding) + target.save() + return Response({"That probably worked."}) + else: + return HttpResponseNotFound() @api_view(['POST']) @authentication_classes([SessionAuthentication]) @permission_classes([IsAuthenticated]) def get_code(request,pk,workspace): - (workspace, created) = Workspace.objects.get_or_create(ruledoc=RuleDoc.objects.get(pk=pk),workspace_name=workspace) - return Response({"xml_content": workspace.xml_content}) + ruledoctest = RuleDoc.objects.get(owner=request.user,pk=pk) + if ruledoctest: + (workspace, created) = Workspace.objects.get_or_create(ruledoc=RuleDoc.objects.get(pk=pk),workspace_name=workspace) + return Response({"xml_content": workspace.xml_content}) + else: + return HttpResponseNotFound() @api_view(['GET']) @authentication_classes([SessionAuthentication]) @permission_classes([IsAuthenticated]) def get_all_code(request,pk): - workspaces = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=pk)) - output = [] - for w in workspaces: - output.append({"name": w.workspace_name, "xml_content": w.xml_content}) - return Response(output) + ruledoctest = RuleDoc.objects.get(owner=request.user,pk=pk) + if ruledoctest: + workspaces = Workspace.objects.filter(ruledoc=RuleDoc.objects.get(pk=pk)) + output = [] + for w in workspaces: + output.append({"name": w.workspace_name, "xml_content": w.xml_content}) + return Response(output) + else: + return HttpResponseNotFound() -@api_view(['GET']) -@authentication_classes([SessionAuthentication]) -@permission_classes([IsAuthenticated]) -def get_example(request,pk): - target = WorkspaceTemplate.objects.get(pk=pk) - # template_serializer = TemplateRequestSerializer(data=request.data) - # template_serializer.is_valid() - return Response({ 'xml_content': target.xml_content }) +# @api_view(['GET']) +# @authentication_classes([SessionAuthentication]) +# @permission_classes([IsAuthenticated]) +# def get_example(request,pk): +# target = RuleDocTemplate.objects.get(pk=pk) +# return Response({ 'yaml_content': target.yaml_content }) @api_view(['POST']) @authentication_classes([SessionAuthentication]) @permission_classes([IsAuthenticated]) def update_test(request,ruledoc,test_name): - target = BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(pk=ruledoc),test_name=test_name) - workspace_serializer = CodeUpdateRequestSerializer(data=request.data) - workspace_serializer.is_valid() - target.xml_content = workspace_serializer.validated_data.get('xml_content', target.xml_content) - target.scasp_encoding = workspace_serializer.validated_data.get('scasp_encoding', target.scasp_encoding) - target.save() - return Response({"That probably worked."}) + ruledoctest = RuleDoc.objects.get(owner=request.user,pk=ruledoc) + if ruledoctest: + target = BlawxTest.objects.get(ruledoc=RuleDoc.objects.get(pk=ruledoc),test_name=test_name) + workspace_serializer = CodeUpdateRequestSerializer(data=request.data) + workspace_serializer.is_valid() + target.xml_content = workspace_serializer.validated_data.get('xml_content', target.xml_content) + target.scasp_encoding = workspace_serializer.validated_data.get('scasp_encoding', target.scasp_encoding) + target.save() + return Response({"That probably worked."}) + else: + return HttpResponseNotFound() @api_view(['POST']) @authentication_classes([SessionAuthentication]) @permission_classes([IsAuthenticated]) def get_test(request,ruledoc,test_name): - (test, created) = BlawxTest.objects.get_or_create(ruledoc=RuleDoc.objects.get(pk=ruledoc),test_name=test_name) - return Response({"xml_content": test.xml_content}) \ No newline at end of file + ruledoctest = RuleDoc.objects.get(owner=request.user,pk=ruledoc) + if ruledoctest: + (test, created) = BlawxTest.objects.get_or_create(ruledoc=RuleDoc.objects.get(pk=ruledoc),test_name=test_name) + return Response({"xml_content": test.xml_content}) + else: + return HttpResponseNotFound() \ No newline at end of file From 9dca7920b29e3fde8d7ad727901cd41aff9366d0 Mon Sep 17 00:00:00 2001 From: Gauntlet173 Date: Mon, 11 Jul 2022 12:19:46 -0600 Subject: [PATCH 2/7] move examples to new project menu, fix import --- CHANGELOG.md | 7 ++-- .../blawx/examples}/beard_tax.yaml | 0 .../blawx/examples}/bird.yaml | 0 .../blawx/examples}/mbp.yaml | 0 .../blawx/examples}/mortality.yaml | 0 .../blawx/examples}/net30.yaml | 0 .../blawx/examples}/r34.yaml | 0 .../blawx/examples}/rps.yaml | 0 .../blawx/examples}/siblings.yaml | 0 .../blawx/examples}/wills.yaml | 0 .../blawx/examples}/wills_tutorial.yaml | 0 blawx/templates/blawx/index.html | 29 +++++++++++++++- blawx/urls.py | 1 + blawx/views.py | 33 ++++++++++++++++--- 14 files changed, 63 insertions(+), 7 deletions(-) rename blawx/{fixtures/workspace => static/blawx/examples}/beard_tax.yaml (100%) rename blawx/{fixtures/workspace => static/blawx/examples}/bird.yaml (100%) rename blawx/{fixtures/workspace => static/blawx/examples}/mbp.yaml (100%) rename blawx/{fixtures/workspace => static/blawx/examples}/mortality.yaml (100%) rename blawx/{fixtures/workspace => static/blawx/examples}/net30.yaml (100%) rename blawx/{fixtures/workspace => static/blawx/examples}/r34.yaml (100%) rename blawx/{fixtures/workspace => static/blawx/examples}/rps.yaml (100%) rename blawx/{fixtures/workspace => static/blawx/examples}/siblings.yaml (100%) rename blawx/{fixtures/workspace => static/blawx/examples}/wills.yaml (100%) rename blawx/{fixtures/workspace => static/blawx/examples}/wills_tutorial.yaml (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 970bad37..e8282cc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,14 @@ and breaking changes will not necessarily result in changes to the main version ## Since Last Release ### Added -* User registration +* User registration - non-admin users can create user accounts for themselves. * Users can now only see and interact with projects they created. ### Changed -* Ownership of demonstration projects/tutorials has been given to the demo user (this is temporary). +* Tutorials and Examples are now available from the New Project button. + +### Fixed +* A bug in the project import method has been fixed. ## [v1.3.19-alpha](https://github.com/Lexpedite/blawx/releases/tag/v1.3.19-alpha) 2022-07-07 diff --git a/blawx/fixtures/workspace/beard_tax.yaml b/blawx/static/blawx/examples/beard_tax.yaml similarity index 100% rename from blawx/fixtures/workspace/beard_tax.yaml rename to blawx/static/blawx/examples/beard_tax.yaml diff --git a/blawx/fixtures/workspace/bird.yaml b/blawx/static/blawx/examples/bird.yaml similarity index 100% rename from blawx/fixtures/workspace/bird.yaml rename to blawx/static/blawx/examples/bird.yaml diff --git a/blawx/fixtures/workspace/mbp.yaml b/blawx/static/blawx/examples/mbp.yaml similarity index 100% rename from blawx/fixtures/workspace/mbp.yaml rename to blawx/static/blawx/examples/mbp.yaml diff --git a/blawx/fixtures/workspace/mortality.yaml b/blawx/static/blawx/examples/mortality.yaml similarity index 100% rename from blawx/fixtures/workspace/mortality.yaml rename to blawx/static/blawx/examples/mortality.yaml diff --git a/blawx/fixtures/workspace/net30.yaml b/blawx/static/blawx/examples/net30.yaml similarity index 100% rename from blawx/fixtures/workspace/net30.yaml rename to blawx/static/blawx/examples/net30.yaml diff --git a/blawx/fixtures/workspace/r34.yaml b/blawx/static/blawx/examples/r34.yaml similarity index 100% rename from blawx/fixtures/workspace/r34.yaml rename to blawx/static/blawx/examples/r34.yaml diff --git a/blawx/fixtures/workspace/rps.yaml b/blawx/static/blawx/examples/rps.yaml similarity index 100% rename from blawx/fixtures/workspace/rps.yaml rename to blawx/static/blawx/examples/rps.yaml diff --git a/blawx/fixtures/workspace/siblings.yaml b/blawx/static/blawx/examples/siblings.yaml similarity index 100% rename from blawx/fixtures/workspace/siblings.yaml rename to blawx/static/blawx/examples/siblings.yaml diff --git a/blawx/fixtures/workspace/wills.yaml b/blawx/static/blawx/examples/wills.yaml similarity index 100% rename from blawx/fixtures/workspace/wills.yaml rename to blawx/static/blawx/examples/wills.yaml diff --git a/blawx/fixtures/workspace/wills_tutorial.yaml b/blawx/static/blawx/examples/wills_tutorial.yaml similarity index 100% rename from blawx/fixtures/workspace/wills_tutorial.yaml rename to blawx/static/blawx/examples/wills_tutorial.yaml diff --git a/blawx/templates/blawx/index.html b/blawx/templates/blawx/index.html index 959e8345..47d36ced 100644 --- a/blawx/templates/blawx/index.html +++ b/blawx/templates/blawx/index.html @@ -2,12 +2,39 @@ {% block header_elements %}
- + {% comment %} New Rule + {% endcomment %} + + + + diff --git a/blawx/urls.py b/blawx/urls.py index 54608992..c35e0fa7 100644 --- a/blawx/urls.py +++ b/blawx/urls.py @@ -10,6 +10,7 @@ urlpatterns = [ path('', views.RuleDocsView.as_view(), name='ruledocs'), path('import/', views.ruleDocImportView, name="import"), + path('load_example//',views.exampleLoadView, name="load"), path('/', views.RuleDocView.as_view(), name='ruledoc'), path('/rule//', views.ruleDocLegalTextView, name="ruledoc_text"), path('/edit/', views.RuleDocEditView.as_view(), name='ruledoc_edit'), diff --git a/blawx/views.py b/blawx/views.py index 9047d4a1..f35037d1 100644 --- a/blawx/views.py +++ b/blawx/views.py @@ -121,20 +121,45 @@ def ruleDocImportView(request): new_object_list = list(new_objects) # Get the RuleDoc, remove the PK, save it, and get the PK of the saved version. - new_object_list[0].pk = None - new_object_list[0].owner = request.user + new_object_list[0].object.pk = None + new_object_list[0].object.owner = request.user new_object_list[0].save() - new_pk = new_object_list[0].pk + # new_pk = new_object_list[0].pk # Use the PK of the saved version to save the workspaces and tests for o in new_object_list[1:]: - o.ruledoc = new_pk + o.object.ruledoc = new_object_list[0].object o.save() # Send the user back to root. return HttpResponseRedirect('/') else: return HttpResponseNotAllowed('POST') +@authentication_classes([SessionAuthentication]) +@permission_classes([IsAuthenticated]) +def exampleLoadView(request,example_name): + # Load that file + example = open('/blawx/blawx/static/blawx/examples/' + example_name + ".yaml") + # Do the stuff in import. + new_objects = serializers.deserialize('yaml',example.read()) + # print(new_objects) + new_object_list = list(new_objects) + # print(new_object_list) + # Get the RuleDoc, remove the PK, save it, and get the PK of the saved version. + new_object_list[0].object.pk = None + new_object_list[0].object.owner = request.user + new_object_list[0].save() + # print(new_object_list[0].object) + # new_pk = new_object_list[0].object.pk + # print(new_pk) + # Use the PK of the saved version to save the workspaces and tests + for o in new_object_list[1:]: + o.object.ruledoc = new_object_list[0].object + o.save() + # Send the user back to root. + return HttpResponseRedirect('/') + + class BlawxView(LoginRequiredMixin, generic.DetailView): template_name = 'blawx/blawx.html' model = RuleDoc From f6ad239dfb9bed56b0c2cc3de173177d2b28fc83 Mon Sep 17 00:00:00 2001 From: Gauntlet173 Date: Mon, 11 Jul 2022 12:24:45 -0600 Subject: [PATCH 3/7] updating examples documentation --- blawx/fixtures/docs/examples/beards.yaml | 7 +++---- blawx/fixtures/docs/examples/bird.yaml | 5 ++--- blawx/fixtures/docs/examples/mortality.yaml | 5 ++--- blawx/fixtures/docs/examples/net30.yaml | 5 ++--- blawx/fixtures/docs/examples/r34.yaml | 5 ++--- blawx/fixtures/docs/examples/rps.yaml | 5 ++--- blawx/fixtures/docs/examples/wills.yaml | 5 ++--- blawx/fixtures/docs/examples/wills_tutorial.yaml | 5 ++--- 8 files changed, 17 insertions(+), 25 deletions(-) diff --git a/blawx/fixtures/docs/examples/beards.yaml b/blawx/fixtures/docs/examples/beards.yaml index 02763517..678968c3 100644 --- a/blawx/fixtures/docs/examples/beards.yaml +++ b/blawx/fixtures/docs/examples/beards.yaml @@ -5,11 +5,10 @@ content: | # Beard Tax Act - The Beard Tax Act example included with Blawx is [available here](/12/) (assuming it hasn't been - deleted from your system). + All the example projects are available under the New Project button. - It is an implemention inside Blawx of the Beard Tax Act rules as code demonstration task + This example is an implementation inside Blawx of the Beard Tax Act rules as code demonstration task described at [the Better Rules github page](https://github.com/BetterRules/example-rules-as-code). It includes a single test designed to be run inside BlawxBot. Open the project, click on the name - of the test, and click Bot to run the demo. \ No newline at end of file + of the test, and click "Bot" to run the demo. \ No newline at end of file diff --git a/blawx/fixtures/docs/examples/bird.yaml b/blawx/fixtures/docs/examples/bird.yaml index 3620a4d0..0bb9b124 100644 --- a/blawx/fixtures/docs/examples/bird.yaml +++ b/blawx/fixtures/docs/examples/bird.yaml @@ -5,8 +5,7 @@ content: | # Bird Act - The Bird Act example included with Blawx is [available here](/6/) (assuming it hasn't been - deleted from your system). + All the example projects are available under the New Project button. - It is used to demonstrate the use of defaults and exceptions, and references to source + The Bird Act example is used to demonstrate the use of defaults and exceptions, and references to source legislation in explanations. \ No newline at end of file diff --git a/blawx/fixtures/docs/examples/mortality.yaml b/blawx/fixtures/docs/examples/mortality.yaml index 701b0f5d..0f86ff69 100644 --- a/blawx/fixtures/docs/examples/mortality.yaml +++ b/blawx/fixtures/docs/examples/mortality.yaml @@ -5,7 +5,6 @@ content: | # Mortality Act - The Mortality Act example included with Blawx is [available here](/5/) (assuming it hasn't been - deleted from your system). + All the example projects are available under the New Project button. - It is used as a minimal example of a Blawx project. \ No newline at end of file + The Mortality Act example is used as a minimal example of a Blawx project. \ No newline at end of file diff --git a/blawx/fixtures/docs/examples/net30.yaml b/blawx/fixtures/docs/examples/net30.yaml index 0225f094..8a8f7512 100644 --- a/blawx/fixtures/docs/examples/net30.yaml +++ b/blawx/fixtures/docs/examples/net30.yaml @@ -5,7 +5,6 @@ content: | # Net 30 - The Net 30 example included with Blawx is [available here](/8/) (assuming it hasn't been - deleted from your system). + All the example projects are available under the New Project button. - It is used to demonstrate the use of dates and durations in Blawx. \ No newline at end of file + The Net 30 example is used to demonstrate the use of dates and durations in Blawx. \ No newline at end of file diff --git a/blawx/fixtures/docs/examples/r34.yaml b/blawx/fixtures/docs/examples/r34.yaml index 71cf4aa1..30701343 100644 --- a/blawx/fixtures/docs/examples/r34.yaml +++ b/blawx/fixtures/docs/examples/r34.yaml @@ -5,7 +5,6 @@ content: | # Rule 34 - The Rule 34 example included with Blawx is [available here](/4/) (assuming it hasn't been - deleted from your system). + All the example projects are available under the New Project button. - It is used to demonstrate the use of Blawx to detect drafting flaws in legislative texts. \ No newline at end of file + The Rule 34 example is used to demonstrate the use of Blawx to detect drafting flaws in legislative texts. \ No newline at end of file diff --git a/blawx/fixtures/docs/examples/rps.yaml b/blawx/fixtures/docs/examples/rps.yaml index 423ffada..7128a60f 100644 --- a/blawx/fixtures/docs/examples/rps.yaml +++ b/blawx/fixtures/docs/examples/rps.yaml @@ -5,10 +5,9 @@ content: | # Rock Paper Scissors Act - The Rock Paper Scissors Act example included with Blawx is [available here](/2/) (assuming it hasn't been - deleted from your system). + All the example projects are available under the New Project button. - It is used to demonstrate many different features of Blawx. + The Rock Paper Scissors Act example is used to demonstrate many different features of Blawx. A complete run-through of how to generate the Rule Paper Scissors example from scratch is available as [a tutorial in the Beginner's Guide](/docs/guide/rps). \ No newline at end of file diff --git a/blawx/fixtures/docs/examples/wills.yaml b/blawx/fixtures/docs/examples/wills.yaml index 03f48dc0..4e74254c 100644 --- a/blawx/fixtures/docs/examples/wills.yaml +++ b/blawx/fixtures/docs/examples/wills.yaml @@ -5,10 +5,9 @@ content: | # Wills Act - The Wills Act example included with Blawx is [available here](/9/) (assuming it hasn't been - deleted from your system). + All the example projects are available under the New Project button. - It is used to demonstrate the relevance reasoning features of the interview endpoint + The Wills Act example is used to demonstrate the relevance reasoning features of the interview endpoint for Blawx tests, as seen in BlawxBot. The rules set out that a person is entitled to make a will if they are over the age of 18, diff --git a/blawx/fixtures/docs/examples/wills_tutorial.yaml b/blawx/fixtures/docs/examples/wills_tutorial.yaml index 0b541a0e..39dfc2f5 100644 --- a/blawx/fixtures/docs/examples/wills_tutorial.yaml +++ b/blawx/fixtures/docs/examples/wills_tutorial.yaml @@ -5,10 +5,9 @@ content: | # Wills Act Tutorial - The Wills Act Tutorial included with Blawx is [available here](/14/) (assuming it hasn't been - deleted from your system). + All the tutorial projects are available under the New Project button. - It contains a single-section rule, a single test, and no code. Instead, if you click the "Tutorial" + The Wills Act Tutorial contains a single-section rule, a single test, and no code. Instead, if you click the "Tutorial" button in the button bar, it will take you through a short tutorial of how to encode your rule, test it, and deploy it using BlawxBot. From af732a57bcb44f63cdc35bdac4cc39d4f610f095 Mon Sep 17 00:00:00 2001 From: Gauntlet173 Date: Mon, 11 Jul 2022 12:27:17 -0600 Subject: [PATCH 4/7] remove demo account, update docs --- blawx/fixtures/accounts/demo.yaml | 15 -------------- blawx/fixtures/docs/components/admin.yaml | 24 +++-------------------- 2 files changed, 3 insertions(+), 36 deletions(-) delete mode 100644 blawx/fixtures/accounts/demo.yaml diff --git a/blawx/fixtures/accounts/demo.yaml b/blawx/fixtures/accounts/demo.yaml deleted file mode 100644 index 46dea5f8..00000000 --- a/blawx/fixtures/accounts/demo.yaml +++ /dev/null @@ -1,15 +0,0 @@ -- model: auth.user - pk: 3 - fields: - password: pbkdf2_sha256$320000$EYa6tD6PQmBXYtwTRPC0ea$S1xpa2WqgmNAPbHgaGKTy+6MC0zHPZT0Au0bJPx39tg= - last_login: null - is_superuser: false - username: demo - first_name: '' - last_name: '' - email: '' - is_staff: false - is_active: true - date_joined: 2022-06-27 20:30:41.275005+00:00 - groups: [] - user_permissions: [] \ No newline at end of file diff --git a/blawx/fixtures/docs/components/admin.yaml b/blawx/fixtures/docs/components/admin.yaml index b8b30bae..59bba0cd 100644 --- a/blawx/fixtures/docs/components/admin.yaml +++ b/blawx/fixtures/docs/components/admin.yaml @@ -8,27 +8,9 @@ The Blawx Administration Interface is available by navigating to `/admin` on your Blawx Server. - ## User Accounts - - The primary purpose of accessing the admin interface is currently to be able to create - user accounts. This can be done by logging into the admin interface using an - admin username and password, click on users on the left side of the screen, and clicking - "add user" on the top right of the users screen. - - Once you have created an account here, it will be possible for users to log in using that - account in the main Blawx Interface. - - Note that Blawx' user permissions currently require that the user be logged in to be able - to access anything other than the main page, the login screen, or the documentation. However, - all users have access to all functions of the server. - - More fine-grained user control will be implemented in future versions. + The admin interface can be used to view and make changes to the projects owned by anyone on the server, + and to add, change, and delete user accounts. By default, on a new installation of Blawx, there is an admin account created with the user name `admin` and the password `blawx2022`. When installing a server to which you want to - restrict access, make sure to change the admin password immediately. - - Servers will also be created with a demo account with the username `demo` and the password - `blawx2022`. This user account should be deleted in the admin interface if you are trying - to restrict access to your server. The demo account is capable of doing anything inside - the system except add and remove users and access the admin interface. \ No newline at end of file + restrict access, make sure to change the admin password immediately. \ No newline at end of file From 8fdb2eaabf8bd243f11114970ff187e88587581f Mon Sep 17 00:00:00 2001 From: Gauntlet173 Date: Mon, 11 Jul 2022 12:33:05 -0600 Subject: [PATCH 5/7] updating beginner's guide with new interface --- blawx/fixtures/docs/guide/legal_text.yaml | 6 ++++-- .../blawx/docs/images/blawx_root_screen.png | Bin 9254 -> 9254 bytes .../images/blawx_root_with_new_project.png | Bin 10869 -> 10869 bytes 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/blawx/fixtures/docs/guide/legal_text.yaml b/blawx/fixtures/docs/guide/legal_text.yaml index 70378529..93340e8a 100644 --- a/blawx/fixtures/docs/guide/legal_text.yaml +++ b/blawx/fixtures/docs/guide/legal_text.yaml @@ -48,11 +48,13 @@ ## Creating a New Project - To begin, go to [the root of your Blawx server](/), which will look something like this: + To begin, go to [the root of your Blawx server](/). You may need to create a user account if + you have not done so already. When you are logged in, the root of the server will look something like this: ![Blawx Root Screen](/static/blawx/docs/images/blawx_root_screen.png) - Once there, click "Create Rule". This will open the Project Editor screen, which will + Once there, click "New Project", and in the resulting drop-down choose "New Blank Project". + This will open the Project Editor screen, which will look like this: ![Project Editor Screen](/static/blawx/docs/images/project_editor_screen.png) diff --git a/blawx/static/blawx/docs/images/blawx_root_screen.png b/blawx/static/blawx/docs/images/blawx_root_screen.png index 4f70c82e0b576d84093ef8ab4c5635fe47959578..90c61747363934a8c52058c2f9babd86682443e2 100644 GIT binary patch literal 9254 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 VSC2SruZC`AJVVis=>KlK{{Z(o5S0J` literal 9254 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 RySF_6RYfg@O1U>-{|lwl-1Gnd diff --git a/blawx/static/blawx/docs/images/blawx_root_with_new_project.png b/blawx/static/blawx/docs/images/blawx_root_with_new_project.png index 1e4b66a33fcf42618a0fd11f76b6f2d749cb3ecd..8c1780c3d766eb725e4a6e3d2115b13b1e4537cf 100644 GIT binary patch delta 9011 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 W-AB?oV5qB*+?1NLj)u*S=zjo{@?V+& From 8fb8273f1073dfe0626e2385ab614edfae6164ee Mon Sep 17 00:00:00 2001 From: Gauntlet173 Date: Mon, 11 Jul 2022 12:41:19 -0600 Subject: [PATCH 6/7] removing unused template model --- blawx/admin.py | 8 ++++---- blawx/migrations/0023_delete_ruledoctemplate.py | 16 ++++++++++++++++ blawx/models.py | 10 +++++----- blawx/views.py | 2 +- 4 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 blawx/migrations/0023_delete_ruledoctemplate.py diff --git a/blawx/admin.py b/blawx/admin.py index bc17d751..5acff8d0 100644 --- a/blawx/admin.py +++ b/blawx/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin # Register your models here. -from .models import Workspace, RuleDoc, BlawxTest, RuleDocTemplate +from .models import Workspace, RuleDoc, BlawxTest class WorkspaceAdmin(admin.ModelAdmin): fields = ['ruledoc','workspace_name','xml_content','scasp_encoding'] @@ -12,10 +12,10 @@ class RuleDocAdmin(admin.ModelAdmin): class BlawxTestAdmin(admin.ModelAdmin): fields = ['ruledoc','test_name','xml_content','scasp_encoding', 'tutorial'] -class RuleDocTemplateAdmin(admin.ModelAdmin): - fields = ['template_name','yaml_content'] +# class RuleDocTemplateAdmin(admin.ModelAdmin): +# fields = ['template_name','yaml_content'] admin.site.register(Workspace,WorkspaceAdmin) admin.site.register(RuleDoc,RuleDocAdmin) admin.site.register(BlawxTest,BlawxTestAdmin) -admin.site.register(RuleDocTemplate,RuleDocTemplateAdmin) +# admin.site.register(RuleDocTemplate,RuleDocTemplateAdmin) diff --git a/blawx/migrations/0023_delete_ruledoctemplate.py b/blawx/migrations/0023_delete_ruledoctemplate.py new file mode 100644 index 00000000..c8f8215c --- /dev/null +++ b/blawx/migrations/0023_delete_ruledoctemplate.py @@ -0,0 +1,16 @@ +# Generated by Django 4.0.1 on 2022-07-11 18:40 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('blawx', '0022_rename_workspacetemplate_ruledoctemplate'), + ] + + operations = [ + migrations.DeleteModel( + name='RuleDocTemplate', + ), + ] diff --git a/blawx/models.py b/blawx/models.py index 33d025ab..ce513104 100644 --- a/blawx/models.py +++ b/blawx/models.py @@ -54,12 +54,12 @@ class Meta: models.UniqueConstraint(fields=['ruledoc','test_name'],name='unique_test_and_ruledoc') ] -class RuleDocTemplate(models.Model): - template_name = models.CharField(max_length=200) - yaml_content = models.TextField(default="") +# class RuleDocTemplate(models.Model): +# template_name = models.CharField(max_length=200) +# yaml_content = models.TextField(default="") - def __str__(self): - return self.template_name +# def __str__(self): +# return self.template_name class Query(models.Model): ruledoc = models.ForeignKey(Workspace, on_delete=models.CASCADE) diff --git a/blawx/views.py b/blawx/views.py index f35037d1..238513a3 100644 --- a/blawx/views.py +++ b/blawx/views.py @@ -19,7 +19,7 @@ # from rest_framework import permissions from .serializers import WorkspaceSerializer, CodeUpdateRequestSerializer -from .models import Workspace, DocPage, RuleDocTemplate, RuleDoc, BlawxTest +from .models import Workspace, DocPage, RuleDoc, BlawxTest from cobalt.hierarchical import Act From 6f7a4d2d3482438b94758b9c8306cf65f2223e03 Mon Sep 17 00:00:00 2001 From: Gauntlet173 Date: Mon, 11 Jul 2022 12:41:52 -0600 Subject: [PATCH 7/7] updating changelog --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8282cc2..49a4e2ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,12 @@ 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. -## Since Last Release +## [v1.3.20-alpha](https://github.com/Lexpedite/blawx/releases/tag/v1.3.20-alpha) 2022-07-11 + +This release adds user registration, restricts access to projects that the user created, +and allows the user to create new projects from templates and tutorials. + +These features make it possible for Blawx to be used in a multi-user environment. ### Added * User registration - non-admin users can create user accounts for themselves. @@ -15,6 +20,7 @@ and breaking changes will not necessarily result in changes to the main version ### Changed * Tutorials and Examples are now available from the New Project button. +* Updates to Beginner's Guide and Examples documentation. ### Fixed * A bug in the project import method has been fixed.