"""
categories.py:
The views functions that handle category management and editing
"""
from catedit import app
from catedit.models import Category
from catedit.resources import CategoryAPI, CategoryChangesAPI
from catedit.views.utils import check_user_status_and_repo_access, \
get_current_category_list, get_commits, \
get_issues
from catedit.utils import get_property_list
from io import StringIO
from flask import render_template, request, redirect, url_for, abort, Blueprint
from rdflib import Graph
from catedit.views.forms import CommitForm, CategoryForm
module = Blueprint('categories', __name__)
logger = app.logger
@module.route(
'/<string:repository>/workshop', methods=['GET']
)
def workshop(repository):
"""
View that has a list of all categories available. Template is
categories/workshop.html, located in src/templates/
"""
if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
abort(404)
check_user_status_and_repo_access(repository)
cat_list = get_current_category_list(repository=repository)
cat_list = sorted(cat_list, key=lambda cat: cat['cat_id'])
changeset_list = get_commits(repository, 5, 1)[0]
discussion_list = get_issues(repository, 5, 1)[0]
return render_template(
'categories/workshop.html',
cat_list=cat_list,
changeset_list=changeset_list,
discussion_list=discussion_list,
current_repository=repository
)
@module.route(
'/<string:repository>/delete-modifs-<deleted_changes_id>&redirect_to=<redirect_to>',
methods=['POST']
)
def delete_changes(repository, deleted_changes_id, redirect_to):
"""
View that handles deleting changes for a given repo. If no deleted_changes_id is
given, it will delete every change
"""
if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
abort(404)
check_user_status_and_repo_access(repository)
cat_changes_api_instance = CategoryChangesAPI()
if deleted_changes_id is None:
cat_changes_api_instance.delete(repository=repository)
else:
cat_changes_api_instance.delete(
repository=repository,
modified_cat_id=deleted_changes_id
)
return redirect(url_for('categories.'+redirect_to, repository=repository))
@module.route(
'/<string:repository>/delete-<deleted_cat_id>&redirect_to=<redirect_to>',
methods=['POST']
)
def delete_category(repository, deleted_cat_id, redirect_to):
"""
View that handles deleting a given category for a given repo
"""
if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
abort(404)
check_user_status_and_repo_access(repository)
cat_api_instance = CategoryAPI()
if deleted_cat_id is not None:
cat_api_instance.delete(
repository=repository,
deleted_cat_id=deleted_cat_id
)
return redirect(url_for('categories.'+redirect_to, repository=repository))
@module.route(
'/<string:repository>/submit', methods=['GET']
)
def submit(repository):
"""
View that handles all the visualisation of changes for a user's
session, links to the editor forms, allows the users to cancel their
own changes and submits all their changes.
Template is categories/submit.html, located in src/templates/
"""
if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
abort(404)
check_user_status_and_repo_access(repository)
commit_form = CommitForm(request.form)
cat_list = get_current_category_list(repository=repository)
original_cat_list = get_current_category_list(
repository=repository,
with_local_changes=False
)
created_cat_count = len([
created_cat for created_cat in cat_list
if created_cat["state"] == "created"
])
edited_cat_count = len([
edited_cat for edited_cat in cat_list
if edited_cat["state"] == "modified"
])
deleted_cat_count = len([
deleted_cat for deleted_cat in cat_list
if deleted_cat["state"] == "deleted"
])
return render_template('categories/submit.html',
cat_list=cat_list,
original_cat_list=original_cat_list,
original_cat_count=len(original_cat_list),
created_cat_count=created_cat_count,
edited_cat_count=edited_cat_count,
deleted_cat_count=deleted_cat_count,
commit_form=commit_form,
current_repository=repository)
@module.route(
'/<string:repository>/push_changes', methods=['POST']
)
def push_changes(repository):
"""
View that handles POST from the submit page
"""
if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
abort(404)
check_user_status_and_repo_access(repository)
cat_api_instance = CategoryAPI()
cat_changes_api_instance = CategoryChangesAPI()
commit_form = CommitForm(request.form)
if commit_form.validate_on_submit():
cat_api_instance.put(repository=repository, message=commit_form.commit_message.data)
cat_changes_api_instance.delete(repository=repository)
return redirect(url_for('categories.workshop', repository=repository))
else:
return redirect(url_for('categories.submit', repository=repository))
@module.route('/<string:repository>/editor',
defaults={'cat_id': None},
methods=['GET', 'POST'])
@module.route('/<string:repository>/editor/<string:cat_id>',
methods=['GET', 'POST'])
def editor(repository, cat_id):
"""
View that handles creation and edition of categories. Template is
categories/editor.html, located in src/templates
"""
if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
abort(404)
check_user_status_and_repo_access(repository)
cat_api_instance = CategoryAPI()
cat_changes_api_instance = CategoryChangesAPI()
specific_serialized_cat = ""
# Serialization of the category of id cat_id, either from
# CategoryChangesAPI (if it was modified) or from CategoryAPI (if it
# was not)
changes_response = None
# Changes on the category cat_id we get from CategoryChangesAPI
current_cat = None
current_cat_id = None
current_cat_properties = []
# Args for the template, if we create a new category they are not used
cat_list = []
# Category list that will be used by the template
deleted_cat_dict = {}
# Deleted categories we won't append to cat_list
modified_cat_dict = {}
# Modified categories to append to cat_list in case label changed
serialized_cat_list = []
# Existing categories we get from CategoryAPI
cat_changes = {}
# Changes list we get from CategoryChangesAPI
if cat_id is not None:
changes_response = cat_changes_api_instance.get(
repository=repository,
modified_cat_id=cat_id
)
if changes_response[0]["type"] is "deleted":
abort(404)
elif changes_response[0]["type"] is "modified":
specific_serialized_cat = changes_response[0] \
["category"] \
[cat_id]
elif changes_response[0]["type"] is "untouched":
category_api_response = cat_api_instance.get(
repository=repository,
cat_id=cat_id
)
if category_api_response == 404:
abort(404)
else:
specific_serialized_cat = category_api_response[0]
#TODO: vérifier encoding - logger.debug(specific_serialized_cat)
cat_rdf_graph = Graph()
cat_rdf_graph.parse(source=StringIO(specific_serialized_cat),
format='turtle')
current_cat = Category(graph=cat_rdf_graph)
current_cat_id = current_cat.cat_id
current_cat_properties = current_cat.properties
serialized_cat_list = cat_api_instance.get(repository)[0]
cat_changes = cat_changes_api_instance.get(repository)[0]
deleted_cat_dict = cat_changes["deleted_categories"]
modified_cat_dict = cat_changes["modified_categories"]
#TODO: vérifier encoding - logger.debug(changes_response)
for modified_cat_name in modified_cat_dict.keys():
modified_cat_rdf_graph = Graph()
modified_cat_rdf_graph.parse(
source=StringIO(modified_cat_dict[modified_cat_name]),
format='turtle'
)
modified_cat = Category(graph=modified_cat_rdf_graph)
cat_list.append({"cat_label": modified_cat.label,
"cat_description": modified_cat.description,
"cat_id": modified_cat.cat_id,
"cat_properties": modified_cat.properties})
for serialized_cat in serialized_cat_list:
cat_rdf_graph = Graph()
cat_rdf_graph.parse(source=StringIO(serialized_cat),
format='turtle')
cat = Category(graph=cat_rdf_graph)
if cat.cat_id not in deleted_cat_dict.keys() and \
cat.cat_id not in modified_cat_dict.keys():
cat_list.append({"cat_label": cat.label,
"cat_description": cat.description,
"cat_id": cat.cat_id,
"cat_properties": cat.properties})
cat_form = CategoryForm(request.form)
if request.method == "GET":
"""
GET Method means we will display the editor
"""
if current_cat is not None:
cat_form.label.data = current_cat.label
cat_form.description.data = current_cat.description
for (cat_predicate, cat_object) in current_cat_properties:
can_append = True
if get_property_list()[repository][cat_predicate.toPython()]["object_type"] == "uriref-category":
if "#" in cat_object:
namespace, object_id = cat_object.split("#", 1)
else:
can_append = False
else:
object_id = cat_object
if can_append:
cat_form.properties.append_entry({
"property_predicate": cat_predicate.toPython(),
"property_object": object_id
})
# list of category that will be used in property editor, list of dict
# {"cat_id": cat.cat_id,
# "cat_label": cat.label,
# "cat_description": cat.description,
# "cat_properties": cat.properties}
return render_template('categories/editor.html',
cat_id=current_cat_id,
cat_properties=current_cat_properties,
form=cat_form,
cat_list=cat_list,
deleted_cat_list=list(deleted_cat_dict.keys()),
current_repository=repository)
elif request.method == "POST":
"""
POST Method means we will compute the form data, call the relevant
"POST" and "PUT" method on CategoryAPI, and return to a display
page (workshop for now, ideally the one from whence we came)
"""
if cat_form.validate_on_submit():
cat_data = {}
cat_data["label"] = cat_form.label.data
cat_data["description"] = cat_form.description.data
cat_data["properties"] = [
(cat_property["property_predicate"],
cat_property["property_object"])
for cat_property in cat_form.properties.data
]
if cat_id is not None:
#TODO: vérifier encoding - logger.debug(str(cat_data))
cat_api_instance.put(
repository=repository,
cat_id=cat_id,
cat_data=cat_data
)
else:
#TODO: vérifier encoding - logger.debug(str(cat_data))
cat_api_instance.post(
repository=repository,
cat_data=cat_data
)
return redirect(url_for('categories.workshop', repository=repository))
else:
# if form doesn't validate we don't want to delete whatever
# changes the user did
return render_template('categories/editor.html',
cat_id=cat_id,
cat_properties=current_cat_properties,
form=cat_form,
cat_list=cat_list,
deleted_cat_list=list(
deleted_cat_dict.keys()
),
current_repository=repository)