# HG changeset patch # User Nicolas DURAND # Date 1423844541 -3600 # Node ID dac9b1248e0f714ae826f72936a98329899c8fad # Parent 1fee953e2d9d89a69f2a2145dde8e60356e3d22a V0.1.4: Implemented github social functions into CatEdit: refactored template and views hierarchy, names, views submodule, created macro to generate category tables + small esthetic changes (buttons to display categories/tables switch state, possibility to hide tables) + views modules have been converted into Flask Blueprints diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/__init__.py --- a/src/catedit/__init__.py Fri Jan 30 12:41:40 2015 +0100 +++ b/src/catedit/__init__.py Fri Feb 13 17:22:21 2015 +0100 @@ -48,6 +48,16 @@ ) github = GitHub(app) +@github.access_token_getter +def token_getter(): + """ + Utility function for github-flask module to get user token when + making authenticated requests + """ + if session.get("user_logged", None): + # logger.debug("I made an authentified request") + return session["user_code"] + # Api api = Api(app) @@ -56,10 +66,11 @@ app.config["CURRENT_VERSION"] = CURRENT_VERSION # Views and APIs -from catedit.views import cat_editor, cat_recap, github_login,\ - github_callback, logout - +from catedit.views import home, categories, social from catedit.resources import CategoryAPI, CategoryChangesAPI +app.register_blueprint(home.module) +app.register_blueprint(categories.module) +app.register_blueprint(social.module) api.add_resource(CategoryAPI, diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/persistence.py --- a/src/catedit/persistence.py Fri Jan 30 12:41:40 2015 +0100 +++ b/src/catedit/persistence.py Fri Feb 13 17:22:21 2015 +0100 @@ -450,14 +450,14 @@ """ file_content="" try: - filedict = github.get("repos/" - + app.config["PERSISTENCE_CONFIG"] - ["REPOSITORY_OWNER"] + "/" - + self.repository - + "/contents/" - + app.config["PERSISTENCE_CONFIG"] - ["CATEGORIES_PATH"] - + kwargs["name"]) + filedict = github.get( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + self.repository + + "/contents/" + + app.config["PERSISTENCE_CONFIG"]["CATEGORIES_PATH"] + + kwargs["name"] + ) file_content = str(b64decode(filedict["content"]), "utf-8") except GitHubError as ghe: logger.error("Github Error trying to get file: "+kwargs["name"]) @@ -484,13 +484,13 @@ """ filenames_list = [] try: - files_in_repo = github.get("repos/" - + app.config["PERSISTENCE_CONFIG"] - ["REPOSITORY_OWNER"] + "/" - + self.repository - + "/contents/" - + app.config["PERSISTENCE_CONFIG"] - ["CATEGORIES_PATH"]) + files_in_repo = github.get( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + self.repository + + "/contents/" + + app.config["PERSISTENCE_CONFIG"]["CATEGORIES_PATH"] + "?per_page=100" + ) filenames_list = [github_file["name"] for github_file in files_in_repo] # logger.debug(filenames_list) @@ -505,14 +505,14 @@ file_content_list = [] for filename in filenames_list: try: - filedict = github.get("repos/" - + app.config["PERSISTENCE_CONFIG"] - ["REPOSITORY_OWNER"] + "/" - + self.repository - + "/contents/" - + app.config["PERSISTENCE_CONFIG"] - ["CATEGORIES_PATH"] - + filename) + filedict = github.get( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + self.repository + + "/contents/" + + app.config["PERSISTENCE_CONFIG"]["CATEGORIES_PATH"] + + filename + ) file_content_list.append(str(b64decode(filedict["content"]), "utf-8")) except GitHubError as ghe: diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/resources.py --- a/src/catedit/resources.py Fri Jan 30 12:41:40 2015 +0100 +++ b/src/catedit/resources.py Fri Feb 13 17:22:21 2015 +0100 @@ -45,17 +45,17 @@ if cat_id is not None: cat = cat_manager_instance.load_category(cat_id) if cat != None: - return cat.cat_graph \ - .serialize(format='turtle') \ - .decode("utf-8"), \ - 200 + return cat.cat_graph.serialize( + format='turtle' + ).decode("utf-8"), 200 else: return 404 else: response = [] for cat in cat_manager_instance.list_categories(): - response.append(cat.cat_graph.serialize(format='turtle') - .decode("utf-8")) + response.append( + cat.cat_graph.serialize(format='turtle').decode("utf-8") + ) return response, 200 # update category cat_id @@ -86,17 +86,26 @@ args = cat_parser.parse_args() if cat_id is None: if cat_manager_instance.persistence.session_compliant is True: - logger.debug("Submitting - deleted categories are:" - + str(session.get("deleted_categories", {}) - .get(repository, {})) - + " and modified categories are:" - + str(session.get("modified_categories", {}) - .get(repository, {}))) + logger.debug( + "Submitting - deleted categories are:" + + str( + session.get( + "deleted_categories", {} + ).get(repository, {}) + ) + " and modified categories are:" + + str( + session.get( + "modified_categories", {} + ).get(repository, {}) + ) + ) cat_manager_instance.save_changes( - deleted_cat_dict=session.get("deleted_categories", {}) - .get(repository, {}), - modified_cat_dict=session.get("modified_categories", {}) - .get(repository, {}), + deleted_cat_dict=session.get( + "deleted_categories", {} + ).get(repository, {}), + modified_cat_dict=session.get( + "modified_categories", {} + ).get(repository, {}), message=args["commit_message"] ) session["deleted_categories"]["local"] = {} @@ -105,13 +114,14 @@ return 204 else: # is the edition occuring on an already modified category? - if cat_id in session.get("modified_categories", {}) \ - .get(repository, {}).keys(): + if (cat_id in session.get( + "modified_categories", {} + ).get(repository, {}).keys()): cat_graph = Graph() cat_graph.parse( - source=StringIO(session["modified_categories"] - [repository] - [cat_id]), + source=StringIO( + session["modified_categories"][repository][cat_id] + ), format="turtle" ) cat = Category(graph=cat_graph) @@ -213,18 +223,21 @@ ) cat_list = cat_manager_instance.list_categories() # first we edit what was already modified before the deletion - logger.debug(session.get("modified_categories", {}) - .get(repository, {})) - if deleted_cat_id in session["deleted_categories"] \ - [repository].keys(): + logger.debug( + session.get("modified_categories", {}).get(repository, {}) + ) + if ( + deleted_cat_id in + session["deleted_categories"][repository].keys() + ): for element in session.get("modified_categories", {}) \ .get(repository, {}).keys(): logger.debug(str(element)) modified_cat_graph = Graph() modified_cat_graph.parse( - source=StringIO(session["modified_categories"] - [repository] - [element]), + source=StringIO( + session["modified_categories"][repository][element] + ), format="turtle" ) modified_cat = Category(graph=modified_cat_graph) @@ -233,12 +246,16 @@ new_property_list = [] for (predicate, obj) in modified_cat.properties: if not ( - app.config["PROPERTY_LIST"] - [predicate] - ["object_type"] == "uriref-category" - and - (obj == (app.config["CATEGORY_NAMESPACE"] + - deleted_cat_id)) + app.config["PROPERTY_LIST"] + [predicate] + ["object_type"] + == "uriref-category" + and ( + obj == ( + app.config["CATEGORY_NAMESPACE"] + + deleted_cat_id + ) + ) ): new_property_list.append((predicate, obj)) diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/static/css/style.css --- a/src/catedit/static/css/style.css Fri Jan 30 12:41:40 2015 +0100 +++ b/src/catedit/static/css/style.css Fri Feb 13 17:22:21 2015 +0100 @@ -1,11 +1,3 @@ -.property-row{ - margin-top: 4px; - margin-bottom: 4px; -} - -.property-delete-button{ - margin-left: 4px; -} .visible{ display:block; diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/catbase.html --- a/src/catedit/templates/catbase.html Fri Jan 30 12:41:40 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ - - - - {% block head %} - - - - {% block title %}{% endblock title %} - - - {% endblock head %} - - - -
- {% block page_content %} - {% endblock page_content %} -
- - diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/cateditor.html --- a/src/catedit/templates/cateditor.html Fri Jan 30 12:41:40 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,145 +0,0 @@ -{% extends "catbase.html" %} -{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} - {% set readonly="readonly" %} -{% else %} - {% set readonly=False %} -{% endif %} -{% block title %} {{ current_repository}}: Editeur {% endblock title %} -{% block head %} - {{ super() }} -{% endblock head %} -{% block navbar_items %} - {{ super() }} - {% if session.get("user_logged", None) %} -
  • >
  • -
  • Atelier
  • -
  • >
  • -
  • Editeur
  • - {% endif %} -{% endblock navbar_items%} -{% block repo_list %} - {{ super() }} -{% endblock repo_list %} -{% block page_content %} -

    CatEdit - {{current_repository}}

    -{% if session["user_logged"] and not session["user_can_edit"][current_repository] %} - -{% endif %} -{% if not session["user_logged"] %} - {% endif %} -{% if form.label.errors or form.description.errors %} -
    - - - Erreur: - - Vous n'avez pas rempli certains champs obligatoires. -
    -{% endif %} -

    {% if cat_id: %} Edition : Catégorie existante{% else %}Création : Nouvelle catégorie{% endif %}

    - {% if readonly %}
    {% endif %} -
    - {{ form.hidden_tag() }} - {% if form.label.errors %} - {% set label_placeholder="Champ obligatoire" %} - {% endif %} - {% if form.description.errors %} - {% set description_placeholder="Champ obligatoire" %} - {% endif %} - {{ form.label.label }}
    {{ form.label(size=40, class="form-control", readonly=readonly, placeholder=label_placeholder) }}
    - {{ form.description.label }}
    {{ form.description(size=150, class="form-control", readonly=readonly, placeholder=description_placeholder) }}
    - -
    - - - - - -
    - -
    -
    -
    -
    - - - {% set property_count=0 %} - {% for property in form.properties %} - - - {% if config["PROPERTY_LIST"][property.property_predicate.data]["object_type"]=="uriref-category" %} - {% for cat in cat_list %} - {% if property.property_object.data == cat.cat_id %} - {% if cat.cat_id not in deleted_cat_list %} - {{ property.property_predicate() }} - {{ property.property_object() }} - - - - {% endif %} - {% endif %} - {% endfor %} - {% else %} - {{ property.property_predicate() }} - {{ property.property_object() }} - - - - {% endif %} - {% set property_count=property_count+1 %} - - {% endfor %} - -
    - {{ config["PROPERTY_LIST"][property.property_predicate.data]["descriptive_label_fr"] }} - - {{ cat.cat_label }} - - - - {{ config["PROPERTY_LIST"][property.property_predicate.data]["descriptive_label_fr"] }} - - {% if config["PROPERTY_LIST"][property.property_predicate.data]["object_type"]=="uriref-link" %} - {{ property.property_object.data }} - {% else %} - {{ property.property_object.data }} - {% endif %} - - -
    -
    -
    -

    - - Annuler -
    - {% if readonly %}
    {% endif %} - - {% if cat_id %} - - {% endif %} -{% endblock page_content %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/categories/editor.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/templates/categories/editor.html Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,146 @@ +{% extends "layout.html" %} +{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} + {% set readonly="readonly" %} +{% else %} + {% set readonly=False %} +{% endif %} +{% block title %} {{ current_repository}}: Editeur {% endblock title %} +{% block head %} + {{ super() }} +{% endblock head %} +{% block navbar_items %} + {{ super() }} + {% if session.get("user_logged", None) %} +
  • >
  • +
  • Atelier
  • +
  • >
  • +
  • Editeur
  • + {% endif %} +{% endblock navbar_items%} +{% block repo_list %} + {{ super() }} +{% endblock repo_list %} +{% block page_content %} +

    CatEdit - {{current_repository}}

    +{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} + +{% endif %} +{% if not session["user_logged"] %} + {% endif %} +{% if form.label.errors or form.description.errors %} +
    + + + Erreur: + + Vous n'avez pas rempli certains champs obligatoires. +
    + +{% endif %} +

    {% if cat_id: %} Edition : Catégorie existante{% else %}Création : Nouvelle catégorie{% endif %}

    + {% if readonly %}
    {% endif %} +
    + {{ form.hidden_tag() }} + {% if form.label.errors %} + {% set label_placeholder="Champ obligatoire" %} + {% endif %} + {% if form.description.errors %} + {% set description_placeholder="Champ obligatoire" %} + {% endif %} + {{ form.label.label }}
    {{ form.label(size=40, class="form-control", readonly=readonly, placeholder=label_placeholder) }}
    + {{ form.description.label }}
    {{ form.description(size=150, class="form-control", readonly=readonly, placeholder=description_placeholder) }}
    + +
    + + + + + +
    + +
    +
    +
    +
    + + + {% set property_count=0 %} + {% for property in form.properties %} + + + {% if config["PROPERTY_LIST"][property.property_predicate.data]["object_type"]=="uriref-category" %} + {% for cat in cat_list %} + {% if property.property_object.data == cat.cat_id %} + {% if cat.cat_id not in deleted_cat_list %} + {{ property.property_predicate() }} + {{ property.property_object() }} + + + + {% endif %} + {% endif %} + {% endfor %} + {% else %} + {{ property.property_predicate() }} + {{ property.property_object() }} + + + + {% endif %} + {% set property_count=property_count+1 %} + + {% endfor %} + +
    + {{ config["PROPERTY_LIST"][property.property_predicate.data]["descriptive_label_fr"] }} + + {{ cat.cat_label }} + + + + {{ config["PROPERTY_LIST"][property.property_predicate.data]["descriptive_label_fr"] }} + + {% if config["PROPERTY_LIST"][property.property_predicate.data]["object_type"]=="uriref-link" %} + {{ property.property_object.data }} + {% else %} + {{ property.property_object.data }} + {% endif %} + + +
    +
    +
    +

    + + Annuler +
    + {% if readonly %}
    {% endif %} + + {% if cat_id %} + + {% endif %} +{% endblock page_content %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/categories/submit.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/templates/categories/submit.html Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,156 @@ +{% extends "layout.html" %} +{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} + {% set readonly="readonly" %} +{% else %} + {% set readonly=False %} +{% endif %} +{% block title %}{{current_repository}}: Soumission{% endblock title %} +{% block head %} + {{ super() }} + + +{% endblock head %} +{% block navbar_items %} + {{ super() }} + {% if session.get("user_logged", None) %} +
  • >
  • +
  • Atelier
  • +
  • >
  • +
  • Soumission
  • + {% endif %} +{% endblock navbar_items %} +{% block repo_list %} + {{ super() }} +{% endblock repo_list %} +{% block page_content %} +

    CatEdit - {{current_repository}}

    +

    Catégories existantes + +

    +
    + + + + + + + + + {% if not session["user_logged"] %} + + + + {% else %} + {% if original_cat_count == 0 %} + + + + {% else %} + {% import "macros.html" as macros %} + {{ macros.category_table(original_cat_list, current_repository, state_list=["original"], target="categories.submit") }} + {% endif %} + {% endif %} + +
    Nom de la catégorieDescription de la catégorie
    + + Attention: + Veuillez vous identifier pour visualiser les catégories +
    Aucune catégorie n'a été créée pour l'instant. {% if not readonly %}Créer une catégorie{% endif %}
    +
    + +

    Mes modifications

    + + + + + + + + + {% if not session["user_logged"] %} + + + + {% else %} + + + + {% if created_cat_count == 0 %} + + + + {% else %} + {% import "macros.html" as macros %} + {{ macros.category_table(cat_list, current_repository, state_list=["created"], target="categories.submit") }} + {% endif %} + + + + {% if edited_cat_count == 0 %} + + + + {% else %} + {% import "macros.html" as macros %} + {{ macros.category_table(cat_list, current_repository, state_list=["modified"], target="categories.submit") }} + {% endif %} + + + + + {% if deleted_cat_count == 0 %} + + + + {% else %} + {% import "macros.html" as macros %} + {{ macros.category_table(cat_list, current_repository, state_list=["deleted"], target="categories.submit") }} + {% endif %} + {% endif %} + +
    Nom de la catégorieDescription de la catégorie
    + + Attention: + Veuillez vous identifier pour visualiser les modifications +
    + Catégories ajoutées +
    Aucune catégorie n'a été ajoutée pour l'instant.
    + Catégories modifiées +
    Aucune catégorie n'a été modifiée pour l'instant.
    + Catégories supprimées +
    Aucune catégorie n'a été supprimée pour l'instant.
    +

    Soumettre mes changements

    +
    +
    +
    + {{ commit_form.hidden_tag() }} +
    + {{ commit_form.commit_message.label }} + {{ commit_form.commit_message(size=40, class="form-control", readonly=readonly) }} +
    + + Retour +
    +

    +
    +{% endblock page_content %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/categories/workshop.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/templates/categories/workshop.html Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,101 @@ +{% extends "layout.html" %} +{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} + {% set readonly="readonly" %} +{% else %} + {% set readonly=False %} +{% endif %} +{% block title %}{{current_repository}}: Atelier{% endblock title %} +{% block head %} + {{ super() }} + + +{% endblock head %} +{% block navbar_items %} + {{ super() }} + {% if session.get("user_logged", None) %} +
  • >
  • +
  • Atelier
  • + {% endif %} +{% endblock navbar_items %} +{% block repo_list %} + {{ super() }} +{% endblock repo_list %} +{% block page_content %} +

    CatEdit - {{current_repository}}

    +

    Créer une catégorie :

    + {% if session["user_logged"] and not session["user_can_edit"] %} + + {% endif %} +

    Mes catégories + +

    +
    + + + + + + + + + {% if not session["user_logged"] %} + + + + {% else %} + {% if cat_list|length == 0 %} + + + + {% else %} + {% import "macros.html" as macros %} + {{ macros.category_table(cat_list, current_repository, target="categories.workshop") }} + {% endif %} + {% endif %} + +
    Nom de la catégorieDescription de la catégorie
    + + Attention: + Veuillez vous identifier pour visualiser les catégories +
    Aucune catégorie n'a été créée pour l'instant. {% if not readonly %}Créer une catégorie{% endif %}
    +
    + {% if session.get("user_logged") %} +
    + +

    Annuler tous mes changements actuels : + + + +

    +
    +

    Soumettre mes changements actuels : +

    + {% endif %} +

    Discussions sur cet ensemble de catégories :

    +{% endblock page_content %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/catindex.html --- a/src/catedit/templates/catindex.html Fri Jan 30 12:41:40 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -{% extends "catbase.html" %} -{% if not session["user_logged"] %} - {% set readonly="readonly" %} -{% else %} - {% set readonly=False %} -{% endif %} -{% block title %}Page d'accueil{% endblock title %} -{% block head %} - {{ super() }} -{% endblock head %} -{% block navbar_items %} -
  • Page d'accueil
  • -{% endblock navbar_items %} -{% block repo_list %} - - {{ super() }} -{% endblock repo_list %} -{% block page_content %} -

    CatEdit: Editeur de Catégories

    -

    Mode d'emploi

    -
    -

    - CatEdit est un outil permettant de créer et d'éditer des catégories. - Vous aurez besoin d'un compte Github pour utiliser CatEdit: - s'inscrire sur Github -

    -

    - Une fois authentifié, choisissez un ensemble de catégorie dans la liste se trouvant - dans la barre de navigation pour être redirigé vers l'atelier correspondant. - Vous pourrez y trouver la liste des catégories existantes pour cet ensemble, - présentée dans un tableau qui initialement ne comprend que les catégories de l'état courant. -

    -

    - L'état courant de l'ensemble de catégorie est la base à partir de laquelle - vous pourrez créer et éditer des catégories. Une catégorie consiste en un label, unique - et non-vide, une description, unique et non-vide et un nombre variable de propriétés. -

    -

    La liste de catégories

    -

    - A chaque fois que vous faites des modifications, elles sont stockées - de manière temporaire tant que vous restez authentifié. La liste de catégories - dans l'Atelier est mise à jour au fil de vos modifications selon un code couleur - vous permettant de visualiser vos changements. -

    - -

    -

    - Pour que vos modifications soient permanentes et qu'elles deviennent le nouvel état courant - de l'ensemble de catégories, vous devez soumettre vos modifications en suivant le lien dans l'Atelier. - Vous trouverez une page vous présentant l'état initial d'une part et vos changements d'autre part. -

    -

    - Vous devez obligatoirement renseigner une explication de vos changements avant soumission. -

    -

    Interface de discussion

    -

    - Chaque soumission de modifications ouvrira sur GitHub un fil de discussion ayant pour titre le message - de soumission. Vous pouvez accéder à la liste de changements via un lien dans l'Atelier. Cela vous - redirigera vers la page GitHub présentant tous les changements, et vous pouvez accéder à la discussion - d'un changement donné en cliquant sur son titre. -

    -

    - Il est aussi possible de créer des discussions générales sur un ensemble de catégories. Un lien est disponible - dans l'atelier vers la page "Issues" de l'ensemble de catégories sur GitHub, où vous pouvez commenter et créer - de nouvelles "Issues" pour alimenter la discussion sur l'ensemble de catégories considéré. -

    -

    -
    -{% endblock page_content %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/catlogin.html --- a/src/catedit/templates/catlogin.html Fri Jan 30 12:41:40 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -{% extends "catbase.html" %} -{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} - {% set readonly="readonly" %} -{% else %} - {% set readonly=False %} -{% endif %} -{% block title %} Catedit: Login {% endblock title %} -{% block head %} - {{ super() }} -{% endblock head %} -{% block navbar_items %} - {{ super() }} -{% endblock navbar_items%} -{% block repo_list %} - {{ super() }} -{% endblock repo_list %} -{% block page_content %} -

    CatEdit - Authentification

    -{% if form.user_login.errors or form.user_password.errors %} -
    - - - Erreur: - - Vous n'avez pas rempli certains champs obligatoires. -
    -{% endif %} -
    -

    -
    - Veuillez entrez votre nom d'utilisateur Github et mot de passe Github. -

    -
    -
    - {% if form.user_login.errors %} - {% set login_placeholder="Champ obligatoire" %} - {% endif %} - {{ form.hidden_tag() }} - {{form.user_login.label}} - {{form.user_login(class="form-control", id="user_login", placeholder=login_placeholder)}} - {{form.user_password.label}} - {{form.user_password(class="form-control", id="user_password")}} -
    - -
    -{% if form.user_login.data and not(form.user_login.errors or form.user_password.errors) %} -
    -
    -

    - Il semble que vous utilisez CatEdit pour la première fois. Veuillez cliquer - sur le lien suivant pour vous authentifier sur Github afin de pouvoir utiliser CatEdit. -

    -

    - Si ça n'est pas la première fois que vous utilisez CatEdit, vérifiez que vous n'avez pas entré - un mauvais nom d'utilisateur/mot de passe. Note: Si vous souhaitez changer d'utilisateur, - n'oubliez pas auparavant de vous déconnecter de l'utilisateur courant sur Github.

    -

    -
    - - -
    -
    -{% endif %} -
    -{% endblock page_content%} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/catmodifs.html --- a/src/catedit/templates/catmodifs.html Fri Jan 30 12:41:40 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,329 +0,0 @@ -{% extends "catbase.html" %} -{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} - {% set readonly="readonly" %} -{% else %} - {% set readonly=False %} -{% endif %} -{% block title %}{{current_repository}}: Soumission{% endblock title %} -{% block head %} - {{ super() }} - - -{% endblock head %} -{% block navbar_items %} - {{ super() }} - {% if session.get("user_logged", None) %} -
  • >
  • -
  • Atelier
  • -
  • >
  • -
  • Soumission
  • - {% endif %} -{% endblock navbar_items %} -{% block repo_list %} - {{ super() }} -{% endblock repo_list %} -{% block page_content %} -

    CatEdit - {{current_repository}}

    -

    Catégories existantes

    - - - - - - - - - {% if not session["user_logged"] %} - - - - {% else %} - {% if existing_cat_list|length == 0 %} - - - - {% else %} - {% for cat in existing_cat_list %} - - - - - {% if cat.cat_id not in deleted_cat_namelist and cat.cat_id not in modified_cat_namelist %} - - - {% else %} - - {% endif %} - - - - - {% endfor %} - {% endif %} - {% endif %} - -
    Nom de la catégorieDescription de la catégorie
    - - Attention: - Veuillez vous identifier pour visualiser les catégories -
    Aucune catégorie n'a été créée pour l'instant. {% if not readonly %}Créer une catégorie{% endif %}
    {{ cat.cat_label }}{{ cat.cat_description}} - - - - - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    - {% if cat.cat_properties|length == 0 %}
    Aucune autre propriété
    - {% else %} - {% for (predicate, object) in cat.cat_properties %} -
    {{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}
    -
    - {% if config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-category" %} - {% for cat in existing_cat_list %} - {% if object == config["CATEGORY_NAMESPACE"]+cat.cat_id %} - {{ cat.cat_label }} - {% endif %} - {% endfor %} - {% elif config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-link" %} - {{ object }} - {% else %} - {{ object }} - {% endif %} -
    - {% endfor %} - {% endif %} -
    -
    -
    - -

    Mes modifications

    - - - - - - - - - {% if not session["user_logged"] %} - - - - {% else %} - - - - {% if created_cat_list|length == 0 %} - - - - {% else %} - {% for cat in created_cat_list %} - - - - - - - - - {% endfor %} - {% endif %} - - - - {% if modified_cat_list|length == 0 %} - - - - {% else %} - {% for cat in modified_cat_list %} - - - - - - - - - {% endfor %} - {% endif %} - - - - - {% if deleted_cat_namelist|length == 0 %} - - - - {% else %} - {% for deleted_cat in deleted_cat_namelist %} - {% for existing_cat in existing_cat_list %} - {% if existing_cat.cat_id == deleted_cat %} - - - - - - {% endif %} - {% endfor %} - {% endfor %} - {% endif %} - {% endif %} - -
    Nom de la catégorieDescription de la catégorie
    - - Attention: - Veuillez vous identifier pour visualiser les modifications -
    - Catégories ajoutées -
    Aucune catégorie n'a été ajoutée pour l'instant.
    {{ cat.cat_label }}{{ cat.cat_description}} - - - -
    -
    -
    - {% if cat.cat_properties|length == 0 %}
    Aucune autre propriété
    - {% else %} - {% for (predicate, object) in cat.cat_properties %} -
    {{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}
    -
    - {% if config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-category" %} - {% for cat in total_cat_list %} - {% if object == config["CATEGORY_NAMESPACE"]+cat.cat_id %} - {{ cat.cat_label }} - {% endif %} - {% endfor %} - {% elif config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-link" %} - {{ object }} - {% else %} - {{ object }} - {% endif %} -
    - {% endfor %} - {% endif %} -
    -
    -
    -
    - -
    -
    -
    - Vous allez supprimer les changements faits sur cette catégorie. -
    - -
    -
    -
    -
    -
    - Catégories modifiées -
    Aucune catégorie n'a été modifiée pour l'instant.
    {{ cat.cat_label }}{{ cat.cat_description}} - - - -
    -
    -
    - {% if cat.cat_properties|length == 0 %}
    Aucune autre propriété
    - {% else %} - {% for (predicate, object) in cat.cat_properties %} -
    {{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}
    -
    - {% if config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-category" %} - {% for cat in total_cat_list %} - {% if object == config["CATEGORY_NAMESPACE"]+cat.cat_id %} - {{ cat.cat_label }} - {% endif %} - {% endfor %} - {% elif config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-link" %} - {{ object }} - {% else %} - {{ object }} - {% endif %} -
    - {% endfor %} - {% endif %} -
    -
    -
    -
    - -
    -
    -
    - Vous allez supprimer les changements faits sur cette catégorie. -
    - -
    -
    -
    -
    -
    - Catégories supprimées -
    Aucune catégorie n'a été supprimée pour l'instant.
    {{ existing_cat.cat_label }}Cette catégorie va être supprimée quand vous soumettrez vos modifications. -
    - -
    - -
    -
    -
    -

    Soumettre mes changements

    -
    -
    -
    - {{ commit_form.hidden_tag() }} -
    - {{ commit_form.commit_message.label }} - {{ commit_form.commit_message(size=40, class="form-control", readonly=readonly) }} -
    - - Retour -
    -

    -
    -{% endblock page_content %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/catrecap.html --- a/src/catedit/templates/catrecap.html Fri Jan 30 12:41:40 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -{% extends "catbase.html" %} -{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} - {% set readonly="readonly" %} -{% else %} - {% set readonly=False %} -{% endif %} -{% block title %}{{current_repository}}: Atelier{% endblock title %} -{% block head %} - {{ super() }} - - -{% endblock head %} -{% block navbar_items %} - {{ super() }} - {% if session.get("user_logged", None) %} -
  • >
  • -
  • Atelier
  • - {% endif %} -{% endblock navbar_items %} -{% block repo_list %} - {{ super() }} -{% endblock repo_list %} -{% block page_content %} -

    CatEdit - {{current_repository}}

    -

    Créer une catégorie :

    - {% if session["user_logged"] and not session["user_can_edit"] %} - - {% endif %} -

    Mes catégories

    - - - - - - - - - {% if not session["user_logged"] %} - - - - {% else %} - {% if cat_list|length == 0 %} - - - - {% else %} - {% for cat in cat_list %} - - - - - {% if (cat.state != "deleted") %} - - {% endif %} - {% if (cat.state == "untouched") %} - - {% elif (cat.state == "created" or cat.state == "modified") %} - - {% else %} - - {% endif %} - - - - - {% endfor %} - {% endif %} - {% endif %} - -
    Nom de la catégorieDescription de la catégorie
    - - Attention: - Veuillez vous identifier pour visualiser les catégories -
    Aucune catégorie n'a été créée pour l'instant. {% if not readonly %}Créer une catégorie{% endif %}
    {{ cat.cat_label }}{{ cat.cat_description}} - - - - - - - - -
    - - - - -
    -
    -
    - - - - -
    -
    -
    - - - - -
    -
    -
    -
    - {% if cat.cat_properties|length == 0 %}
    Aucune autre propriété
    - {% else %} - {% for (predicate, object) in cat.cat_properties %} -
    {{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}
    -
    - {% if config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-category" %} - {% for cat in cat_list %} - {% if object == config["CATEGORY_NAMESPACE"]+cat.cat_id %} - {{ cat.cat_label }} - {% endif %} - {% endfor %} - {% elif config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-link" %} - {{ object }} - {% else %} - {{ object }} - {% endif %} -
    - {% endfor %} - {% endif %} -
    -
    -
    - {% if session.get("user_logged") %} -
    - -

    Annuler tous mes changements actuels : - - - -

    -
    -

    Soumettre mes changements actuels : -

    - {% endif %} -

    Consulter l'historique des modifications :

    -

    Consulter les discussions générales sur cet ensemble de catégorie (Issues) :

    -{% endblock page_content %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/home/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/templates/home/index.html Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,86 @@ +{% extends "layout.html" %} +{% if not session["user_logged"] %} + {% set readonly="readonly" %} +{% else %} + {% set readonly=False %} +{% endif %} +{% block title %}Page d'accueil{% endblock title %} +{% block head %} + {{ super() }} +{% endblock head %} +{% block navbar_items %} +
  • Page d'accueil
  • +{% endblock navbar_items %} +{% block repo_list %} + + {{ super() }} +{% endblock repo_list %} +{% block page_content %} +

    CatEdit: Editeur de Catégories

    +

    Mode d'emploi

    +
    +

    + CatEdit est un outil permettant de créer et d'éditer des catégories. + Vous aurez besoin d'un compte Github pour utiliser CatEdit: + s'inscrire sur Github +

    +

    + Une fois authentifié, choisissez un ensemble de catégorie dans la liste se trouvant + dans la barre de navigation pour être redirigé vers l'atelier correspondant. + Vous pourrez y trouver la liste des catégories existantes pour cet ensemble, + présentée dans un tableau qui initialement ne comprend que les catégories de l'état courant. +

    +

    + L'état courant de l'ensemble de catégorie est la base à partir de laquelle + vous pourrez créer et éditer des catégories. Une catégorie consiste en un label, unique + et non-vide, une description, unique et non-vide et un nombre variable de propriétés. +

    +

    La liste de catégories

    +

    + A chaque fois que vous faites des modifications, elles sont stockées + de manière temporaire tant que vous restez authentifié. La liste de catégories + dans l'Atelier est mise à jour au fil de vos modifications selon un code couleur + vous permettant de visualiser vos changements. +

    + +

    +

    + Pour que vos modifications soient permanentes et qu'elles deviennent le nouvel état courant + de l'ensemble de catégories, vous devez soumettre vos modifications en suivant le lien dans l'Atelier. + Vous trouverez une page vous présentant l'état initial d'une part et vos changements d'autre part. +

    +

    + Vous devez obligatoirement renseigner une explication de vos changements avant soumission. +

    +

    Interface de discussion

    +

    + Chaque soumission de modifications ouvrira sur GitHub un fil de discussion ayant pour titre le message + de soumission. Vous pouvez accéder à la liste de changements via un lien dans l'Atelier. Cela vous + redirigera vers la page GitHub présentant tous les changements, et vous pouvez accéder à la discussion + d'un changement donné en cliquant sur son titre. +

    +

    + Il est aussi possible de créer des discussions générales sur un ensemble de catégories. Un lien est disponible + dans l'atelier vers la page "Issues" de l'ensemble de catégories sur GitHub, où vous pouvez commenter et créer + de nouvelles "Issues" pour alimenter la discussion sur l'ensemble de catégories considéré. +

    +

    +
    +{% endblock page_content %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/home/login.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/templates/home/login.html Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,67 @@ +{% extends "layout.html" %} +{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} + {% set readonly="readonly" %} +{% else %} + {% set readonly=False %} +{% endif %} +{% block title %} Catedit: Login {% endblock title %} +{% block head %} + {{ super() }} +{% endblock head %} +{% block navbar_items %} + {{ super() }} +
  • >
  • +
  • Authentification
  • +{% endblock navbar_items%} +{% block repo_list %} + {{ super() }} +{% endblock repo_list %} +{% block page_content %} +

    CatEdit - Authentification

    +{% if form.user_login.errors or form.user_password.errors %} +
    + + + Erreur: + + Vous n'avez pas rempli certains champs obligatoires. +
    +{% endif %} +
    +

    +
    + Veuillez entrez votre nom d'utilisateur Github et mot de passe Github. +

    +
    +
    + {% if form.user_login.errors %} + {% set login_placeholder="Champ obligatoire" %} + {% endif %} + {{ form.hidden_tag() }} + {{form.user_login.label}} + {{form.user_login(class="form-control", id="user_login", placeholder=login_placeholder)}} + {{form.user_password.label}} + {{form.user_password(class="form-control", id="user_password")}} +
    + +
    +{% if form.user_login.data and not(form.user_login.errors or form.user_password.errors) %} +
    +
    +

    + Il semble que vous utilisez CatEdit pour la première fois. Veuillez cliquer + sur le lien suivant pour vous authentifier sur Github afin de pouvoir utiliser CatEdit. +

    +

    + Si ça n'est pas la première fois que vous utilisez CatEdit, vérifiez que vous n'avez pas entré + un mauvais nom d'utilisateur/mot de passe. Note: Si vous souhaitez changer d'utilisateur, + n'oubliez pas auparavant de vous déconnecter de l'utilisateur courant sur Github.

    +

    +
    + + +
    +
    +{% endif %} +
    +{% endblock page_content%} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/layout.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/templates/layout.html Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,61 @@ + + + + {% block head %} + + + + {% block title %}{% endblock title %} + + + {% endblock head %} + + + +
    + {% block page_content %} + {% endblock page_content %} +
    + + diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/macros.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/templates/macros.html Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,104 @@ +{% macro category_table(cat_list, current_repository, state_list=["mixed"], target="", interactive=True) -%} + {% for cat in cat_list %} + {% if state_list == ["mixed"] or (cat.state in state_list) %} + + {{ cat.cat_label }} + {{ cat.cat_description}} + + + + + + {% if interactive %} + {% if (cat.state != "deleted") %} + + + + + + {% endif %} + {% if (cat.state == "untouched") or (cat.state == "original") %} + + + + + + {% elif (cat.state == "created" or cat.state == "modified") %} + + + + + + {% else %} + +
    + + + + +
    + + {% endif %} + {% endif %} + + + +
    +
    + {% if cat.cat_properties|length == 0 %}
    Aucune autre propriété
    + {% else %} + {% for (predicate, object) in cat.cat_properties %} +
    {{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}
    +
    + {% if config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-category" %} + {% for cat in cat_list %} + {% if object == config["CATEGORY_NAMESPACE"]+cat.cat_id %} + {{ cat.cat_label }} + {% endif %} + {% endfor %} + {% elif config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-link" %} + {{ object }} + {% else %} + {{ object }} + {% endif %} +
    + {% endfor %} + {% endif %} +
    +
    + {% if interactive %} + {% if cat.state != "deleted" %} +
    +
    + + + + + +
    +
    + {% endif %} + {% endif %} + + + {% endif %} + {% endfor %} +{%- endmacro %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/social/changeset.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/templates/social/changeset.html Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,74 @@ +{% extends "social/comment_thread_layout.html" %} +{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} + {% set readonly="readonly" %} +{% else %} + {% set readonly=False %} +{% endif %} +{% block title %}{{super()}}{% endblock title %} +{% block head %} + {{super()}} + + +{% endblock head%} +{% block navbar_items %} + {{super()}} +
  • >
  • +
  • Discussion: changements
  • +{% endblock navbar_items %} +{% block additional_content %} +

    Etat de l'ensemble des catégories pour ces modifications + + + +

    +
    + + + + + + + + + {% if not session["user_logged"] %} + + + + {% else %} + {% if cat_list|length == 0 %} + + + + {% else %} + {% import "macros.html" as macros %} + {{ macros.category_table(cat_list, current_repository, state_list=["original"], interactive=False) }} + {% endif %} + {% endif %} + +
    Nom de la catégorieDescription de la catégorie
    + + Attention: + Veuillez vous identifier pour visualiser les catégories +
    Aucune catégorie n'existait suite à ces changements. {% if not readonly %}Créer une catégorie{% endif %}
    +
    +{% endblock additional_content %} +{% block comment_posting_target %}{{url_for("social.changeset", changeset_id=changeset_id, repository=current_repository)}}{% endblock %} +{% block page_content %} + {{super()}} +{% endblock page_content %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/social/comment_thread_layout.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/templates/social/comment_thread_layout.html Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,76 @@ +{% extends "layout.html" %} +{% block title %}{{ current_repository }}: Discussion{% endblock title %} +{% block head %} + {{ super ()}} +{% endblock head %} +{% block navbar_items %} + {{ super() }} + {% if session.get("user_logged", None) %} +
  • >
  • +
  • Atelier
  • +
  • >
  • +
  • Social
  • + {% endif %} +{% endblock navbar_items %} +{% block page_content %} +

    CatEdit - {{current_repository}}

    +{% block additional_content %} +{% endblock additional_content %} +

    Discussion

    + + +{% if comment_form.comment_field.errors %} +
    + + + Erreur: + + Votre commentaire est vide. +
    +{% endif %} + + + + + + + + + + {% if comments["comment_list"]|length == 0 %} + + + + {% else %} + {% for comment in comments["comment_list"] %} + + + + + + {% endfor %} + {% endif %} + + + + + +
    {{comments["author"]}} + {{comments["opening_date"]}} + Titre: {{comments["title"]}} +
    + {{comments["opening_post"]}}
    Aucun commentaire n'a été posté pour le moment
    {{comment["author"]}} {{comment["date"]}} {{comment["body"]}}
    + {{ comment_form.comment_field.label }} + +
    +
    + {{ comment_form.hidden_tag() }} +
    + {{ comment_form.comment_field(class="form-control", readonly=readonly) }} +
    + + Retour +
    +

    +
    +{% endblock page_content %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/social/discussion.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/templates/social/discussion.html Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,48 @@ +{% extends "social/comment_thread_layout.html" %} +{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} + {% set readonly="readonly" %} +{% else %} + {% set readonly=False %} +{% endif %} +{% block title %}{{super()}}{% endblock title %} +{% block head %} + {{super()}} +{% endblock head%} +{% block navbar_items %} + {{super()}} +
  • >
  • +
  • Discussion
  • +{% endblock navbar_items %} +{% block comment_posting_target %}{{url_for("social.discussion", discussion_id=discussion_id, repository=current_repository)}}{% endblock %} +{% block page_content %} + {% if discussion_id == "new" %} +

    CatEdit - {{current_repository}}

    +

    Nouvelle discussion

    + {% if comment_form.comment_field.errors or comment_form.discussion_title.errors %} +
    + + + Erreur: + + Vous devez renseigner un titre pour votre discussion. +
    + {% endif %} +
    +
    + {{ comment_form.hidden_tag() }} +
    +
    + {{ comment_form.discussion_title.label }} + {{ comment_form.discussion_title(class="form-control", readonly=readonly) }} +
    + {{ comment_form.comment_field.label }} + {{ comment_form.comment_field(class="form-control", readonly=readonly) }} +
    + + Retour +
    +
    + {% else %} + {{super()}} + {% endif %} +{% endblock page_content %} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/templates/social/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/templates/social/index.html Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,99 @@ +{% extends "layout.html" %} +{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %} + {% set readonly="readonly" %} +{% else %} + {% set readonly=False %} +{% endif %} +{% block title %} {{ current_repository}}: Espaces de discussion {% endblock title %} +{% block head %} + {{ super() }} +{% endblock head %} +{% block navbar_items %} + {{ super() }} + {% if session.get("user_logged", None) %} +
  • >
  • +
  • Atelier
  • +
  • >
  • +
  • Social
  • + {% endif %} +{% endblock navbar_items %} +{% block repo_list %} + {{ super() }} +{% endblock repo_list %} +{% block page_content %} +

    CatEdit - {{current_repository}}

    + {% if readonly %} + + {% endif %} +

    Historique des modifications

    + + + + + + + + + + + {% for changeset in changeset_list %} + + + + + + + + {% endfor %} + +
    AuteurDateMessage de soumissionCommentaires
    {{changeset["author"]}}{{changeset["date"]}}{{changeset["title"]}}{{changeset["comment_count"]}} + + +
    +

    Discussions générales

    +

    Créer une nouvelle discussion : + + + +

    + + + + + + + + + + + + {% if not discussion_list %} + + + + {% else %} + {% for discussion in discussion_list %} + + + + + + + + + {% endfor %} + {% endif %} + +
    AuteurDateTitreCommentairesDernier commentaire
    + Il n'y a pas encore de discussion pour cette ensemble de catégories. Créer une discussion +
    {{discussion["author"]}}{{discussion["opening_date"]}}{{discussion["title"]}}{{discussion["comment_count"]}}{{discussion["last_updated"]}} + + +
    +{% endblock page_content%} diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/version.py --- a/src/catedit/version.py Fri Jan 30 12:41:40 2015 +0100 +++ b/src/catedit/version.py Fri Feb 13 17:22:21 2015 +0100 @@ -6,7 +6,7 @@ __all__ = ["VERSION", "get_version", "CURRENT_VERSION"] -VERSION = (0, 1, 3) +VERSION = (0, 1, 4) def get_version(): """ diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/views.py --- a/src/catedit/views.py Fri Jan 30 12:41:40 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,717 +0,0 @@ -""" -views.py: -The views functions that handle the front-end of the application -""" - -from catedit import app, github, cache -from catedit.models import Category -from requests import get -from requests.auth import HTTPBasicAuth -import catedit.persistence -from flask import render_template, request, redirect, url_for, session, abort -from flask.ext.github import GitHubError -from flask_wtf import Form -from catedit.resources import CategoryAPI, CategoryChangesAPI -from wtforms import StringField, TextAreaField, FormField, PasswordField, \ - FieldList, HiddenField -from wtforms.validators import DataRequired, Optional, AnyOf -from rdflib import Graph -from io import StringIO - -logger = app.logger - -@app.route('/', methods=['GET']) -@app.route('/index', methods=['GET']) -def cat_index(): - """ - View for index page (for now it's only a readme text so no computing - is required) - """ - return render_template("catindex.html") - - -@app.route('//catrecap', - defaults={'deleted_cat_id': None, 'deleted_modifs_id': None}, - methods=['GET', 'POST']) -@app.route('//catrecap/delete-modifs-', - defaults={'deleted_cat_id': None}, - methods=['POST']) -@app.route('//catrecap/delete-', - defaults={'deleted_modifs_id': None}, - methods=['POST']) -def cat_recap(repository, deleted_cat_id, deleted_modifs_id): - """ - View that has a list of all categories available. Template is - catrecap.html, located in src/templates/ - - Note: it also handles category deletion from the same page. - """ - if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]: - abort(404) - - cat_api_instance = CategoryAPI() - cat_changes_api_instance = CategoryChangesAPI() - - if request.method == "POST": - if (session.get("user_logged", None) is not None and - session.get("user_can_edit", False) is not False): - if deleted_modifs_id is None and deleted_cat_id is None: - cat_changes_api_instance.delete(repository=repository) - elif deleted_modifs_id is not None: - logger.debug("Changes for category " - + deleted_modifs_id - + " will be deleted.") - cat_changes_api_instance.delete( - repository=repository, - modified_cat_id=deleted_modifs_id - ) - # We identify if we want to delete a category - elif deleted_cat_id is not None: - logger.debug("Category " - + deleted_cat_id - + " will be deleted.") - cat_api_instance.delete( - repository=repository, - deleted_cat_id=deleted_cat_id - ) - return redirect(url_for('cat_recap', repository=repository)) - elif request.method == "GET": - - deleted_cat_dict = {} - modified_cat_dict = {} - serialized_cat_list = [] - if session.get("user_logged", None) is not None: - serialized_cat_list = cat_api_instance.get(repository=repository) \ - [0] - cat_changes = cat_changes_api_instance.get(repository=repository) \ - [0] - modified_cat_dict = cat_changes["modified_categories"] - deleted_cat_dict = cat_changes["deleted_categories"] - # logger.debug(serialized_cat_list) - cat_list = [] - original_cat_list = [] - for serialized_cat in serialized_cat_list: - cat_rdf_graph = Graph() - cat_rdf_graph.parse(source=StringIO(serialized_cat), - format='turtle') - original_cat_list.append(Category(graph=cat_rdf_graph)) - - edited_cat_list = [] - for modified_cat_name in modified_cat_dict.keys(): - new_cat_rdf_graph = Graph() - new_cat_rdf_graph.parse( - source=StringIO( - modified_cat_dict[modified_cat_name] - ), - format='turtle' - ) - edited_cat_list.append(Category(graph=new_cat_rdf_graph)) - # first we find the untouched, edited and deleted categories - cat_state = "" - for category in original_cat_list: - if category.cat_id not in modified_cat_dict.keys(): - if category.cat_id in deleted_cat_dict.keys(): - cat_state = "deleted" - else: - cat_state = "untouched" - - cat_list.append({"cat_label": category.label, - "cat_description": category.description, - "cat_id": category.cat_id, - "cat_properties": category.properties, - "state": cat_state}) - - # now we must find the not yet submitted categories that were created - cat_state = "" - logger.debug("Edited cat list: " - + str([cat.label for cat in edited_cat_list]) - + " - Original cat list: " - + str([cat.label for cat in original_cat_list])) - for category in edited_cat_list: - if category.cat_id not in [cat.cat_id for - cat in original_cat_list]: - cat_state = "created" - else: - cat_state = "modified" - cat_list.append({"cat_label": category.label, - "cat_description": category.description, - "cat_id": category.cat_id, - "cat_properties": category.properties, - "state": cat_state}) - - # logger.debug(c.properties) - cat_list = sorted(cat_list, key=lambda cat: cat['cat_id']) - return render_template('catrecap.html', - cat_list=cat_list, - current_repository=repository) - -class CommitForm(Form): - """ - Custom form class for commiting changes - """ - commit_message = StringField( - "Message de soumission (obligatoire)", - validators=[DataRequired()] - ) - - -@app.route('//catmodifs/delete-modifs-', - defaults={'deleted_cat_id': None}, - methods=['POST']) -@app.route('//catmodifs/delete-', - defaults={'deleted_modifs_id': None}, - methods=['POST']) -@app.route('//catmodifs', - defaults={'deleted_cat_id': None, 'deleted_modifs_id': None}, - methods=['GET', 'POST']) -def cat_modifs(repository, deleted_cat_id, deleted_modifs_id): - """ - 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. - """ - if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]: - abort(404) - - logger.debug(repository) - - changes_list = [] - - existing_cat_list = [] - modified_cat_list = [] - created_cat_list = [] - total_cat_list = [] - - deleted_cat_namelist = [] - modified_cat_namelist = [] - - cat_api_instance = CategoryAPI() - cat_changes_api_instance = CategoryChangesAPI() - - if deleted_cat_id is None and deleted_modifs_id is None: - - commit_form = CommitForm(request.form) - - # if it's a GET with no delete_cat_id or deleted_modifs_id, then we'll - # display the changes - if request.method == "GET": - if session.get("user_logged", None) is not None: - serialized_cat_list = cat_api_instance.get( - repository=repository - )[0] - changes_list = cat_changes_api_instance.get( - repository=repository - )[0] - - cat_data = dict({}) - - # Creating existing cat list - 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) - - cat_data = { - "cat_label": cat.label, - "cat_description": cat.description, - "cat_id": cat.cat_id, - "cat_properties": cat.properties - } - - existing_cat_list.append(cat_data) - total_cat_list.append(cat_data) - cat_data = dict({}) - - # Creating modified and created cat lists - for modified_cat_name in \ - changes_list.get("modified_categories", {}).keys(): - modified_cat_rdf_graph = Graph() - modified_cat_rdf_graph.parse( - source=StringIO( - changes_list["modified_categories"] - [modified_cat_name] - ), - format='turtle' - ) - modified_cat = Category(graph=modified_cat_rdf_graph) - - cat_data = { - "cat_label": modified_cat.label, - "cat_description": modified_cat.description, - "cat_id": modified_cat.cat_id, - "cat_properties": modified_cat.properties - } - if modified_cat.cat_id in \ - [existing_cat["cat_id"] - for existing_cat in existing_cat_list]: - modified_cat_list.append(cat_data) - else: - created_cat_list.append(cat_data) - total_cat_list.append(cat_data) - cat_data = dict({}) - - # Creating deleted cat list - deleted_cat_namelist = list( - changes_list["deleted_categories"].keys() - ) - modified_cat_namelist = list( - changes_list["modified_categories"].keys() - ) - logger.debug("total:"+str(total_cat_list)) - logger.debug("existing:"+str(existing_cat_list)) - logger.debug("created:"+str(created_cat_list)) - logger.debug("modified:"+str(modified_cat_list)) - logger.debug("modified names:"+str(modified_cat_namelist)) - logger.debug("deleted names:"+str(deleted_cat_namelist)) - - return render_template('catmodifs.html', - total_cat_list=total_cat_list, - existing_cat_list=existing_cat_list, - created_cat_list=created_cat_list, - modified_cat_list=modified_cat_list, - modified_cat_namelist=modified_cat_namelist, - deleted_cat_namelist=deleted_cat_namelist, - commit_form=commit_form, - current_repository=repository) - # If it's a POST with no delete_cat_id and delete_modifs_id, then we - # will submit all the stored changes - elif request.method == "POST": - if commit_form.validate_on_submit(): - cat_api_instance.put(repository=repository) - cat_changes_api_instance.delete(repository=repository) - return redirect(url_for('cat_recap', repository=repository)) - - # One of the ids is not None (either deleting a category or a modification) - else: - # We only do that if we have a POST - if request.method == "POST": - # We identify if we want to delete a change - if (session.get("user_logged", None) is not None) \ - and \ - (session.get("user_can_edit", False) is not False): - if deleted_modifs_id is not None: - cat_changes_api_instance.delete( - repository=repository, - modified_cat_id=deleted_modifs_id - ) - # We identify if we want to delete a category - elif deleted_cat_id is not None: - cat_api_instance.delete( - repository=repository, - deleted_cat_id=deleted_cat_id - ) - return redirect(url_for('cat_modifs', repository=repository)) - - - -class PropertyForm(Form): - """ - Form of a given property, each one is a couple of hidden fields that - can be Javascript-generated in the template - """ - property_predicate = HiddenField( - validators=[AnyOf(values=app.config["PROPERTY_LIST"].keys())] - ) - property_object = HiddenField( - validators=[DataRequired()] - ) - - -class CategoryForm(Form): - """ - Custom form class for creating a category with the absolute minimal - attributes (label and description) - """ - label = StringField( - "Nom de la categorie (obligatoire)", - validators=[DataRequired()] - ) - description = TextAreaField( - "Description de la categorie (obligatoire)", - validators=[DataRequired()] - ) - properties = FieldList(FormField(PropertyForm), validators=[Optional()]) - - -@app.route('//cateditor', - methods=['GET', 'POST']) -@app.route('//cateditor/', - methods=['GET', 'POST']) -def cat_editor(repository, cat_id=None): - """ - View that handles creation and edition of categories. Template is - cateditor.html, located in src/templates - """ - if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]: - abort(404) - - 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: - if session.get("user_logged", None) 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": - specific_serialized_cat = cat_api_instance.get( - repository=repository, - cat_id=cat_id - )[0] - 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 - logger.debug(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"] - 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: - if app.config["PROPERTY_LIST"] \ - [cat_predicate] \ - ["object_type"] == "uriref-category": - namespace, object_id = cat_object.split("#",1) - else: - object_id = cat_object - cat_form.properties.append_entry({ - "property_predicate": cat_predicate, - "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} - logger.debug("category list that can be linked :" + str(cat_list)) - logger.debug( - "deleted categories list that can't be linked :" - + str(list(deleted_cat_dict.keys())) - ) - return render_template('cateditor.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 (catrecap for now, ideally the one from whence we came) - """ - if (session.get("user_logged", None) is not None and - session.get("user_can_edit", False) is not False): - - 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: - logger.debug(str(cat_data)) - cat_api_instance.put( - repository=repository, - cat_id=cat_id, - cat_data=cat_data - ) - else: - logger.debug(str(cat_data)) - cat_api_instance.post( - repository=repository, - cat_data=cat_data - ) - return redirect(url_for('cat_recap', repository=repository)) - else: - # if form doesn't validate we don't want to delete whatever - # changes the user did - return render_template('cateditor.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) - - # If user wasn't logged or couldn't edit but somehow submitted a POST - else: - return redirect(url_for('cat_index')) - - -class LoginForm(Form): - """ - Custom form class for commiting changes - """ - user_login = StringField( - "Nom d'utilisateur Github", - validators=[DataRequired()] - ) - user_password = PasswordField( - "Mot de passe Github", - validators=[DataRequired()] - ) - - -@app.route('/catedit-login', methods=["GET", "POST"]) -def github_login(): - """ - Function that manages authentication (Github), login - - Note: If Persistence is set to PersistenceToFile (categories stored - in local files, used for debugging), creates a mock user named - "FileEditUser" - """ - if not session.get("user_logged", False): - session["modified_categories"] = { - repo: {} for repo in app.config["PERSISTENCE_CONFIG"] - ["REPOSITORY_LIST"] - } - session["deleted_categories"] = { - repo: {} for repo in app.config["PERSISTENCE_CONFIG"] - ["REPOSITORY_LIST"] - } - if app.config["PERSISTENCE_CONFIG"]["METHOD"] == "PersistenceToGithub": - login_form = LoginForm(request.form) - if request.method == "POST": - if login_form.validate_on_submit(): - # We'll try to get the auth token for given username - attempted_login = login_form.user_login.data - try: - auth_response = get( - "https://api.github.com/" - + "authorizations", - auth=HTTPBasicAuth( - login_form.user_login.data, - login_form.user_password.data - ) - ) - for auth in auth_response.json(): - if auth["app"]["client_id"] \ - == app.config["GITHUB_CLIENT_ID"]: - session["user_code"] = auth["token"] - session["user_logged"] = True - except: - logger.debug( - "Error requesting authorizations for" - + " user. Either the user is new to catedit, or " - + "entered a wrong username/password" - ) - logger.debug( - "user token found by request: " - + str(session.get("user_code", None)) - ) - if session.get("user_code", None) == None: - # We didn't get it, so we direct the user to the login page - # with a link to github oauth system - return render_template( - 'catlogin.html', - form = login_form - ) - else: - # we did get it, so we redirect to callback function - # to wrap up user auth - return redirect(url_for('github_callback')) - else: - # form didn't validate, so we send it back to user - return render_template( - 'catlogin.html', - form = login_form - ) - elif request.method == "GET": - # We'll render the login form - return render_template( - 'catlogin.html', - form=login_form, - ) - elif app.config["PERSISTENCE_CONFIG"]["METHOD"] == "PersistenceToFile": - session["user_logged"] = True - session["user_can_edit"] = {} - session["user_can_edit"]["local"] = True - session["user_login"] = "FileEditUser" - return redirect(url_for('cat_index')) - else: - return redirect(url_for('cat_index')) - -@app.route('/catedit-login-confirm', methods=["GET", "POST"]) -def github_login_confirm(): - """ - Function called if the user is new or revoked the auth token - """ - if not session.get("user_logged", False): - if request.method == "POST": - return github.authorize( - scope="repo", - redirect_uri=url_for('github_callback', _external=True) - ) - else: - return redirect(url_for('cat_index')) - -@app.route('/catedit-github-callback') -@github.authorized_handler -def github_callback(oauth_code): - """ - Function that handles callback from Github after succesful login - """ - session.permanent = False - if session.get("user_code", None) == None: - # That means we got here using github callback and not the login form - session["user_code"] = oauth_code - logger.debug(session["user_code"]) - session["user_logged"] = True - session["user_login"] = "auth-error" - try: - logger.debug( - "after login: " - + str(github.get("rate_limit")["resources"]) - ) - session["user_login"] = github.get("user")["login"] - except GitHubError as ghe: - logger.error( - "GitHubError trying to get the user login" - ) - logger.error(ghe.response.text) - try: - repo_list = [] - repo_list = github.get("user/repos") - logger.debug(str(github.get("rate_limit")["resources"])) - for repo in repo_list: - logger.debug(repo["name"]) - user_repos_name = [repo["name"] for repo in repo_list] - logger.debug( - str(user_repos_name) + " " - + str(app.config["PERSISTENCE_CONFIG"] - ["REPOSITORY_LIST"]) - ) - session["user_repositories"] = list( - set(user_repos_name).intersection( - app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"] - ) - ) - session["user_can_edit"] = {} - for repo in session["user_repositories"]: - if repo in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]: - session["user_can_edit"][repo] = True - logger.debug(session["user_can_edit"]) - except GitHubError as ghe: - logger.error( - "GitHubError trying to get the list of repository for user " - + session["user_login"] - ) - logger.error(ghe.response.text) - return redirect(url_for('cat_index')) - - -@github.access_token_getter -def token_getter(): - """ - Utility function for github-flask module to get user token when - making authenticated requests - """ - if session.get("user_logged", None): - # logger.debug("I made an authentified request") - return session["user_code"] - - -@app.route('/catedit-logout') -def logout(): - """ - Function that manages authentication (Github), logout - - Note: if you want to switch github users, you will have to logout of - Github, else when logging back in, github will send the app the - same oauth code - """ - session["user_logged"] = None - session["user_code"] = None - session["user_login"] = None - session["user_can_edit"] = None - session["modified_categories"] = { - repo: {} for repo in app.config["PERSISTENCE_CONFIG"] - ["REPOSITORY_LIST"] - } - session["deleted_categories"] = { - repo: {} for repo in app.config["PERSISTENCE_CONFIG"] - ["REPOSITORY_LIST"] - } - return redirect(url_for('cat_index')) diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/views/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/views/__init__.py Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,1 @@ +__all__ = ["home", "categories", "social"] diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/views/categories.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/views/categories.py Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,494 @@ +""" +categories.py: +The views functions that handle category management and editing +""" + +from catedit import app +from catedit.models import Category +from views.utils import check_user_status +from flask import render_template, request, redirect, url_for, session, \ + abort, Blueprint +from flask_wtf import Form +from catedit.resources import CategoryAPI, CategoryChangesAPI +from wtforms import StringField, TextAreaField, FormField, \ + FieldList, HiddenField +from wtforms.validators import DataRequired, Optional, AnyOf +from rdflib import Graph +from io import StringIO + +module = Blueprint('categories', __name__) +logger = app.logger + +def make_category_list(repository, without_changes=False): + """ + Shortcut function that generates the list of category to use for + view templates. + Each category is a dict with the following format: + { + "cat_label": category label, + "cat_description": category description, + "cat_id": category id, + "cat_properties": category properties, + "state": category state (one of {"untouched", "created", + "edited", "deleted"}) + } + """ + cat_api_instance = CategoryAPI() + cat_changes_api_instance = CategoryChangesAPI() + + deleted_cat_dict = {} + modified_cat_dict = {} + serialized_cat_list = [] + if session.get("user_logged", None) is not None: + serialized_cat_list = cat_api_instance.get(repository=repository) \ + [0] + cat_changes = cat_changes_api_instance.get(repository=repository) \ + [0] + modified_cat_dict = cat_changes["modified_categories"] + deleted_cat_dict = cat_changes["deleted_categories"] + # logger.debug(serialized_cat_list) + cat_list = [] + original_cat_list = [] + for serialized_cat in serialized_cat_list: + cat_rdf_graph = Graph() + cat_rdf_graph.parse(source=StringIO(serialized_cat), + format='turtle') + original_cat_list.append(Category(graph=cat_rdf_graph)) + + if without_changes: + for category in original_cat_list: + cat_list.append( + { + "cat_label": category.label, + "cat_description": category.description, + "cat_id": category.cat_id, + "cat_properties": category.properties, + "state": "original" + } + ) + else: + edited_cat_list = [] + for modified_cat_name in modified_cat_dict.keys(): + new_cat_rdf_graph = Graph() + new_cat_rdf_graph.parse( + source=StringIO( + modified_cat_dict[modified_cat_name] + ), + format='turtle' + ) + edited_cat_list.append(Category(graph=new_cat_rdf_graph)) + # first we find the untouched, edited and deleted categories + cat_state = "" + for category in original_cat_list: + if category.cat_id not in modified_cat_dict.keys(): + if category.cat_id in deleted_cat_dict.keys(): + cat_state = "deleted" + else: + cat_state = "untouched" + + cat_list.append( + { + "cat_label": category.label, + "cat_description": category.description, + "cat_id": category.cat_id, + "cat_properties": category.properties, + "state": cat_state + } + ) + + # now we must find the not yet submitted categories that were created + cat_state = "" + logger.debug("Edited cat list: " + + str([cat.label for cat in edited_cat_list]) + + " - Original cat list: " + + str([cat.label for cat in original_cat_list])) + for category in edited_cat_list: + if category.cat_id not in [cat.cat_id for + cat in original_cat_list]: + cat_state = "created" + else: + cat_state = "modified" + cat_list.append({"cat_label": category.label, + "cat_description": category.description, + "cat_id": category.cat_id, + "cat_properties": category.properties, + "state": cat_state}) + return cat_list + +@module.route('//workshop', + defaults={'deleted_cat_id': None, 'deleted_changes_id': None}, + methods=['GET', 'POST']) +@module.route('//workshop/delete-modifs-', + defaults={'deleted_cat_id': None}, + methods=['POST']) +@module.route('//workshop/delete-', + defaults={'deleted_changes_id': None}, + methods=['POST']) +def workshop(repository, deleted_cat_id, deleted_changes_id): + """ + View that has a list of all categories available. Template is + categories/workshop.html, located in src/templates/ + + Note: it also handles category deletion from the same page. + """ + if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]: + abort(404) + + check_user_status(repository) + + cat_api_instance = CategoryAPI() + cat_changes_api_instance = CategoryChangesAPI() + + if request.method == "POST": + if deleted_changes_id is None and deleted_cat_id is None: + cat_changes_api_instance.delete(repository=repository) + elif deleted_changes_id is not None: + logger.debug("Changes for category " + + deleted_changes_id + + " will be deleted.") + cat_changes_api_instance.delete( + repository=repository, + modified_cat_id=deleted_changes_id + ) + # We identify if we want to delete a category + elif deleted_cat_id is not None: + logger.debug("Category " + + deleted_cat_id + + " will be deleted.") + cat_api_instance.delete( + repository=repository, + deleted_cat_id=deleted_cat_id + ) + return redirect(url_for('categories.workshop', repository=repository)) + elif request.method == "GET": + + cat_list = make_category_list(repository=repository) + # logger.debug(c.properties) + cat_list = sorted(cat_list, key=lambda cat: cat['cat_id']) + return render_template('categories/workshop.html', + cat_list=cat_list, + current_repository=repository) + + +class CommitForm(Form): + """ + Custom form class for commiting changes + """ + commit_message = StringField( + "Message de soumission (obligatoire)", + validators=[DataRequired()] + ) + + +@module.route('//' + + 'submit/' + + 'delete-changes-', + defaults={'deleted_cat_id': None}, + methods=['POST']) +@module.route('//submit/delete-', + defaults={'deleted_changes_id': None}, + methods=['POST']) +@module.route('//submit', + defaults={'deleted_cat_id': None, 'deleted_changes_id': None}, + methods=['GET', 'POST']) +def submit(repository, deleted_cat_id, deleted_changes_id): + """ + 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(repository) + + cat_api_instance = CategoryAPI() + cat_changes_api_instance = CategoryChangesAPI() + + logger.debug( + "submit page for " + repository + ", method is " + request.method + ) + + if deleted_cat_id is None and deleted_changes_id is None: + + commit_form = CommitForm(request.form) + + # If it's a POST with no delete_cat_id and delete_modifs_id, then we + # will submit all the stored changes + if request.method == "POST": + if commit_form.validate_on_submit(): + cat_api_instance.put(repository=repository) + cat_changes_api_instance.delete(repository=repository) + return redirect(url_for('categories.workshop', repository=repository)) + + # if it's a GET with no delete_cat_id or deleted_changes_id, then we'll + # display the changes + elif request.method == "GET": + cat_list = make_category_list(repository=repository) + original_cat_list = make_category_list( + repository=repository, + without_changes=True + ) + + 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) + + # One of the ids is not None (either deleting a category or a modification) + else: + # We only do that if we have a POST + if (request.method == "POST"): + # We identify if we want to delete a change + if deleted_changes_id is not None: + cat_changes_api_instance.delete( + repository=repository, + modified_cat_id=deleted_changes_id + ) + # We identify if we want to delete a category + elif deleted_cat_id is not None: + cat_api_instance.delete( + repository=repository, + deleted_cat_id=deleted_cat_id + ) + return redirect( + url_for('categories.submit', repository=repository) + ) + else: + return redirect( + url_for('home.index') + ) + + + +class PropertyForm(Form): + """ + Form of a given property, each one is a couple of hidden fields that + can be Javascript-generated in the template + """ + property_predicate = HiddenField( + validators=[AnyOf(values=app.config["PROPERTY_LIST"].keys())] + ) + property_object = HiddenField( + validators=[DataRequired()] + ) + + +class CategoryForm(Form): + """ + Custom form class for creating a category with the absolute minimal + attributes (label and description) + """ + label = StringField( + "Nom de la categorie (obligatoire)", + validators=[DataRequired()] + ) + description = TextAreaField( + "Description de la categorie (obligatoire)", + validators=[DataRequired()] + ) + properties = FieldList(FormField(PropertyForm), validators=[Optional()]) + + +@module.route('//editor', + defaults={'cat_id': None}, + methods=['GET', 'POST']) +@module.route('//editor/', + 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(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 = cat_api_response[0] + 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 + logger.debug(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"] + 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: + if app.config["PROPERTY_LIST"] \ + [cat_predicate] \ + ["object_type"] == "uriref-category": + namespace, object_id = cat_object.split("#",1) + else: + object_id = cat_object + cat_form.properties.append_entry({ + "property_predicate": cat_predicate, + "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} + logger.debug("category list that can be linked :" + str(cat_list)) + logger.debug( + "deleted categories list that can't be linked :" + + str(list(deleted_cat_dict.keys())) + ) + 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: + logger.debug(str(cat_data)) + cat_api_instance.put( + repository=repository, + cat_id=cat_id, + cat_data=cat_data + ) + else: + 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) diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/views/home.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/views/home.py Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,214 @@ +""" +home.py: +The views functions that handle authentication and index pages +""" + +from catedit import app, github +from views.utils import check_user_status +from requests import get +from requests.auth import HTTPBasicAuth +from flask import render_template, request, redirect, url_for, \ + session, Blueprint +from flask.ext.github import GitHubError +from flask_wtf import Form +from wtforms import StringField, PasswordField +from wtforms.validators import DataRequired + +module = Blueprint('home', __name__) +logger = app.logger + + +@module.route('/', methods=['GET']) +@module.route('/index', methods=['GET']) +def index(): + """ + View for index page (for now it's only a readme text so no computing + is required) + """ + return render_template("home/index.html") + + +class LoginForm(Form): + """ + Custom form class for commiting changes + """ + user_login = StringField( + "Nom d'utilisateur Github", + validators=[DataRequired()] + ) + user_password = PasswordField( + "Mot de passe Github", + validators=[DataRequired()] + ) + + +@module.route('/catedit-login', methods=["GET", "POST"]) +def login(): + """ + Function that manages authentication (Github), login + + Note: If Persistence is set to PersistenceToFile (categories stored + in local files, used for debugging), creates a mock user named + "FileEditUser" + """ + if not session.get("user_logged", False): + session["modified_categories"] = { + repo: {} for repo in app.config["PERSISTENCE_CONFIG"] + ["REPOSITORY_LIST"] + } + session["deleted_categories"] = { + repo: {} for repo in app.config["PERSISTENCE_CONFIG"] + ["REPOSITORY_LIST"] + } + if app.config["PERSISTENCE_CONFIG"]["METHOD"] == "PersistenceToGithub": + login_form = LoginForm(request.form) + if request.method == "GET": + # We'll render the login form + return render_template( + "home/login.html", + form=login_form, + ) + elif request.method == "POST": + if login_form.validate_on_submit(): + # We'll try to get the auth token for given username + try: + auth_response = get( + "https://api.github.com/" + + "authorizations", + auth=HTTPBasicAuth( + login_form.user_login.data, + login_form.user_password.data + ) + ) + for auth in auth_response.json(): + if auth["app"]["client_id"] \ + == app.config["GITHUB_CLIENT_ID"]: + session["user_code"] = auth["token"] + session["user_logged"] = True + except: + logger.debug( + "Error requesting authorizations for" + + " user. Either the user is new to catedit, or " + + "entered a wrong username/password" + ) + logger.debug(str(github.get("rate_limit")["resources"])) + logger.debug( + "user token found by request: " + + str(session.get("user_code", None)) + ) + if session.get("user_code", None) == None: + # We didn't get it, so we direct the user to the login page + # with a link to github oauth system + return render_template( + "home/login.html", + form=login_form + ) + else: + # we did get it, so we redirect to callback function + # to wrap up user auth + return redirect(url_for('home.login_callback')) + else: + # form didn't validate, so we send it back to user + return render_template( + "home/login.html", + form = login_form + ) + elif app.config["PERSISTENCE_CONFIG"]["METHOD"] == "PersistenceToFile": + session["user_logged"] = True + session["user_can_edit"] = {} + session["user_can_edit"]["local"] = True + session["user_login"] = "FileEditUser" + return redirect(url_for('home.index')) + else: + return redirect(url_for('home.index')) + +@module.route('/catedit-login-confirm', methods=["GET", "POST"]) +def login_confirm(): + """ + Function called if the user is new or revoked the auth token + """ + if not session.get("user_logged", False): + if request.method == "POST": + return github.authorize( + scope="repo", + redirect_uri=url_for('home.login_callback', _external=True) + ) + else: + return redirect(url_for('home.index')) + +@module.route('/catedit-callback') +@github.authorized_handler +def login_callback(oauth_code): + """ + Function that handles callback from Github after succesful login + """ + session.permanent = False + if session.get("user_code", None) == None: + # That means we got here using github callback and not the login form + session["user_code"] = oauth_code + logger.debug(session["user_code"]) + session["user_logged"] = True + session["user_login"] = "auth-error" + try: + logger.debug( + "after login: " + + str(github.get("rate_limit")["resources"]) + ) + session["user_login"] = github.get("user")["login"] + except GitHubError as ghe: + logger.error( + "GitHubError trying to get the user login" + ) + logger.error(ghe.response.text) + try: + repo_list = [] + repo_list = github.get("user/repos") + logger.debug(str(github.get("rate_limit")["resources"])) + for repo in repo_list: + logger.debug(repo["name"]) + user_repos_name = [repo["name"] for repo in repo_list] + logger.debug( + str(user_repos_name) + " " + + str(app.config["PERSISTENCE_CONFIG"] + ["REPOSITORY_LIST"]) + ) + session["user_repositories"] = list( + set(user_repos_name).intersection( + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"] + ) + ) + session["user_can_edit"] = {} + for repo in session["user_repositories"]: + if repo in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]: + session["user_can_edit"][repo] = True + logger.debug(session["user_can_edit"]) + except GitHubError as ghe: + logger.error( + "GitHubError trying to get the list of repository for user " + + session["user_login"] + ) + logger.error(ghe.response.text) + return redirect(url_for('home.index')) + +@module.route('/catedit-logout') +def logout(): + """ + Function that manages authentication (Github), logout + + Note: if you want to switch github users, you will have to logout of + Github, else when logging back in, github will send the app the + same oauth code + """ + session["user_logged"] = None + session["user_code"] = None + session["user_login"] = None + session["user_can_edit"] = None + session["modified_categories"] = { + repo: {} for repo in app.config["PERSISTENCE_CONFIG"] + ["REPOSITORY_LIST"] + } + session["deleted_categories"] = { + repo: {} for repo in app.config["PERSISTENCE_CONFIG"] + ["REPOSITORY_LIST"] + } + return redirect(url_for('home.index')) diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/views/social.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/views/social.py Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,184 @@ +""" +social.py: +The views functions that handle comment display and posting +""" + +from catedit import app, github, cache +from views.utils import check_user_status, get_comments, \ + post_comment, get_commits, get_issues, \ + convert_github_date, get_category_list +from flask import render_template, request, redirect, url_for, \ + session, Blueprint +from flask.ext.github import GitHubError +from flask_wtf import Form +from wtforms import TextAreaField, StringField +from wtforms.validators import DataRequired + +module = Blueprint('social', __name__) +logger = app.logger + +@module.route("//social", + methods=["GET"]) +def index(repository): + """ + View that displays every changeset and thread in general discussion + and links to every thread + """ + if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]: + abort(404) + + check_user_status(repository) + + issues_data = [] + commits_data = [] + + changeset_list = get_commits(repository) + discussion_list = get_issues(repository) + + return render_template( + "social/index.html", + current_repository=repository, + discussion_list=discussion_list, + changeset_list=changeset_list + ) + +class CommentForm(Form): + """ + Custom form class for commiting changes + """ + comment_field = TextAreaField( + "Poster un commentaire: ", + validators=[DataRequired()] + ) + + +@module.route("//changesets/", + methods=["GET", "POST"]) +def changeset(repository, changeset_id): + """ + View that displays a snapshot of the repository as it was for a given + changeset, and the related discussion to this changeset. Allows + users to comment on it. + + For re-building the old category set we use the tree and blob data: + 1) first we get the commit of id changeset_id + 2) we get the tree associated with this commit + 3) we get all the blobs referenced by this tree + 4) we build categories from the blobs contents + --> Will probably be incorporated into a function later on + + """ + if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]: + abort(404) + + check_user_status(repository) + + comment_form = CommentForm() + comments_list = get_comments({ + "repository": repository, + "type": "commits", + "id": changeset_id + }) + cat_list = get_category_list(repository, changeset_id) + + if request.method == "GET": + return render_template( + "social/changeset.html", + cat_list=cat_list, + comments=comments_list, + changeset_id = changeset_id, + comment_form = comment_form, + current_repository=repository + ) + elif request.method == "POST": + if comment_form.validate_on_submit(): + comment_data = { + "repository": repository, + "type": "commits", + "thread_id": changeset_id, + "comment_body": comment_form.comment_field.data + } + return_id = post_comment(comment_data) + return redirect(url_for( + "social.changeset", + repository=repository, + changeset_id = return_id + )) + else : + return render_template( + "social/changeset.html", + cat_list=cat_list, + comments=comments_list, + changeset_id = changeset_id, + comment_form = comment_form, + current_repository=repository + ) + +class NewDiscussionForm(Form): + """ + Custom form class for commiting changes + """ + discussion_title = StringField( + "Titre de la discussion: ", + validators=[DataRequired()] + ) + comment_field = TextAreaField( + "Commentaire d'ouverture (facultatif): " + ) + +@module.route("//discussions/", + methods=["GET", "POST"]) +def discussion(repository, discussion_id): + """ + View that displays the discussion of a given id and allows users + to post comments on it. + """ + if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]: + abort(404) + + check_user_status(repository) + + comment_form = None + if discussion_id == "new": + comment_form = NewDiscussionForm() + comments_list = [] + else: + comment_form = CommentForm() + comments_list = get_comments({ + "repository": repository, + "type": "issues", + "id": discussion_id + }) + + if request.method == "GET": + return render_template( + "social/discussion.html", + comments=comments_list, + comment_form=comment_form, + current_repository=repository, + discussion_id=discussion_id + ) + elif request.method == "POST": + if comment_form.validate_on_submit(): + comment_data = { + "repository": repository, + "type": "issues", + "thread_id": discussion_id, + "comment_body": comment_form.comment_field.data + } + if discussion_id == "new": + comment_data["thread_title"] = comment_form.discussion_title.data + return_id = post_comment(comment_data) + return redirect(url_for( + "social.discussion", + repository=repository, + discussion_id=return_id + )) + else: + return render_template( + "social/discussion.html", + comments=comments_list, + comment_form=comment_form, + current_repository=repository, + discussion_id=discussion_id + ) diff -r 1fee953e2d9d -r dac9b1248e0f src/catedit/views/utils.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/catedit/views/utils.py Fri Feb 13 17:22:21 2015 +0100 @@ -0,0 +1,365 @@ +""" +utils.py: +Module that groups utility functions that are used by views, partly because +most of them do requests to the Github API and as such must be cached +""" + +from catedit import app, github, cache +from catedit.models import Category +from flask import redirect, url_for, session, abort +from flask.ext.github import GitHubError +from datetime import datetime +from rdflib import Graph +from base64 import b64decode +from io import StringIO + +logger = app.logger + +def check_user_status(repository): + """ + Function to call at the beginning of every view that would require + authentication and editing privilege to work properly (basically + everything save login and index page) + """ + if not session.get("user_logged", None): + return redirect(url_for("home.index")) + if not session.get("user_can_edit", {}).get(repository, False): + return redirect(url_for("home.index")) + +@cache.memoize(timeout=3600) +def get_comments(request_data): + """ + Function that takes a dict with the following two values: + * type: either "issues" or "commits" + * id: the id of the issue of commit to get comments from + then builds a comment list with each comment being a dict with the + following format: + { + "author": author of the comment + "body": body of the comment + "date": date of the comment format dd/mm/yy hh:mm + } + """ + github_comments_data = [] + + if request_data["type"] == "commits": + commits_list = get_commits(request_data["repository"]) + if request_data["id"] not in [commit["id"] for commit in commits_list]: + abort(404) + elif request_data["type"] == "issues": + issues_list = get_issues(request_data["repository"]) + if request_data["id"] not in [issue["id"] for issue in issues_list]: + abort(404) + + try: + github_comments_data = github.get( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + request_data["repository"] + "/" + + request_data["type"] + "/" + + request_data["id"] + "/comments?per_page=100" + ) + + except GitHubError as ghe: + logger.error( + "Error trying to get comments with following data: " + + str(request_data) + ) + logger.error(ghe.response.text) + + comment_list = [] + for comment in github_comments_data: + comment_dict = { + "author": comment["user"]["login"], + "body": comment["body"], + "date": convert_github_date( + comment["created_at"] + ) + } + comment_list.append(comment_dict) + + discussion_data = {} + try: + discussion_data = github.get( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + request_data["repository"] + "/" + + request_data["type"] + "/" + + request_data["id"] + ) + except GitHubError as ghe: + logger.error( + "Error trying to get the or issue of id " + request_data["id"] + ) + logger.error( + "endpoint: " + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + request_data["repository"] + "/" + + request_data["type"] + "/" + + request_data["id"] + ) + logger.error(ghe.response.text) + + thread_author = "" + thread_opening_date = "" + thread_title = "" + thread_opening_post = "" + + if request_data["type"] == "commits": + thread_author = discussion_data.get("author",{}).get("login", "") + thread_opening_date = convert_github_date( + discussion_data.get( + "commit", + {} + ).get( + "author", + {} + ).get("date","") + ) + thread_title = discussion_data.get("commit",{}).get("message","") + elif request_data["type"] == "issues": + thread_author = discussion_data.get("user",{}).get("login", "") + thread_opening_date = convert_github_date( + discussion_data.get("created_at", "0001-01-01T00:00:00Z") + ) + thread_title = discussion_data.get("title", "") + thread_opening_post = discussion_data.get("body", "") + + logger.debug(str(github.get("rate_limit")["resources"])) + thread_dict = { + "author": thread_author, + "title": thread_title, + "opening_date": thread_opening_date, + "comment_list": comment_list, + "opening_post": thread_opening_post + } + return thread_dict + + +def post_comment(request_data): + """ + Function that posts a given comment to github. + + * repository is the repository to post in + * type is either "issues" or "commits" + * thread_id is the id of the issue or the commit to comment + * thread_title is the title of the new issue, if we're posting a new + issue + * comment_body is the content of the comment + """ + comment_data = { + "body": request_data["comment_body"] + } + return_id = "" + if request_data["thread_id"] != "new": + try: + github_response = github.post( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + request_data["repository"] + "/" + + request_data["type"] + "/" + + request_data["thread_id"] + + "/comments", + data=comment_data + ) + return_id = request_data["thread_id"] + except GitHubError as ghe: + logger.error( + "Error posting comment with following data: " + + str(request_data) + ) + logger.error(ghe.response.text) + else: + # We're posting a new issue + comment_data["title"] = request_data["thread_title"] + try: + github_response = github.post( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + request_data["repository"] + "/" + + request_data["type"], + data=comment_data + ) + return_id = str(github_response["number"]) + except GitHubError as ghe: + logger.error( + "Error posting new issue with following data: " + + str(request_data) + ) + logger.error(ghe.response.text) + cache.clear() + return return_id + +@cache.memoize(timeout=3600) +def get_commits(repository): + """ + Fuction that get the list of commits for a given repository. Returns a + list of dict with the format: + { + id : commit id + title : commit message + author : commit author + comment_count : commit comments count + } + """ + commits_data = [] + try: + commits_data = github.get( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + repository + + "/commits?per_page=100" + ) + except GitHubError as ghe: + logger.error("Error getting commits for repo " + repository) + logger.error(ghe.response.text) + changeset_list = [ + { + "id": commit["sha"], + "title": commit["commit"]["message"], + "date": convert_github_date( + commit["commit"]["committer"]["date"], + ), + "author": commit["commit"]["committer"]["name"], + "comment_count": commit["commit"]["comment_count"], + } + for commit in commits_data + ] + + logger.debug(str(github.get("rate_limit")["resources"])) + return changeset_list + +@cache.memoize(timeout=3600) +def get_issues(repository): + """ + Fuction that get the list of issues for a given repository. Returns a + list of dict with the format: + { + id: issue id + title: issue title + author: issue author + opening_date: issue opening date + last_updated: last update date + comment_count: comments count + } + """ + issues_data = [] + try: + issues_data = github.get( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + repository + + "/issues?per_page=100") + except GitHubError as ghe: + logger.error("Error getting issues for repo " + repository) + logger.error(ghe.response.text) + + discussion_list = [ + { + "id": str(issue["number"]), + "title": issue["title"], + "author": issue["user"]["login"], + "opening_date": convert_github_date(issue["created_at"]), + "last_updated": convert_github_date(issue["updated_at"]), + "comment_count": issue["comments"], + } + for issue in issues_data + ] + + logger.debug(str(github.get("rate_limit")["resources"])) + return discussion_list + +@cache.memoize(timeout=3600) +def get_category_list(repository, changeset_id): + """ + Get the category list as it was following the changeset of + id changeset_id + """ + commits_list = get_commits(repository) + if changeset_id not in [commit["id"] for commit in commits_list]: + abort(404) + + # First step + commit_data = {} + try: + commit_data = github.get( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + repository + "/commits/" + + changeset_id + ) + except GitHubError as ghe: + logger.error("Error trying to get the commit of id " + changeset_id) + logger.error(ghe.response.text) + + logger.debug(str(github.get("rate_limit")["resources"])) + + tree_sha = commit_data.get("commit", {}).get("tree", {}).get("sha", "") + + # Second step + tree_data = {} + try: + tree_data = github.get( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/" + + repository + "/git/trees/" + + commit_data["commit"]["tree"]["sha"] + + "?recursive=1" + ) + except GitHubError as ghe: + logger.error("Error trying to get the tree of sha " + tree_sha) + logger.error(ghe.response.text) + + logger.debug(str(github.get("rate_limit")["resources"])) + logger.debug(tree_data) + + # Third step and fourth step + cat_list = [] + for blob in tree_data.get("tree", []): + if app.config["PERSISTENCE_CONFIG"]["CATEGORIES_PATH"] in blob["path"]: + blob_data = {} + try: + blob_data = github.get( + "repos/" + + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + + "/" + repository + "/git/blobs/" + + blob["sha"] + ) + except GitHubError as ghe: + logger.error( + "Error trying to get the blob of sha " + blob["sha"] + ) + logger.error(ghe.response.text) + + cat_graph = Graph() + cat_graph.parse( + source=StringIO( + str(b64decode(blob_data["content"]), "utf-8") + ), + format="turtle" + ) + category = Category(graph=cat_graph) + cat_list.append( + { + "cat_label": category.label, + "cat_description": category.description, + "cat_id": category.cat_id, + "cat_properties": category.properties, + "state": "original" + } + ) + + logger.debug(str(github.get("rate_limit")["resources"])) + return cat_list + +def convert_github_date(date): + """ + Function that converts github date format to a + "dd/mm/yyyy à hh:mm" format + """ + return datetime.strptime( + date, + "%Y-%m-%dT%H:%M:%SZ" + ).strftime( + "%d/%m/%Y à %H:%M" + ) diff -r 1fee953e2d9d -r dac9b1248e0f virtualenv/requirements.txt --- a/virtualenv/requirements.txt Fri Jan 30 12:41:40 2015 +0100 +++ b/virtualenv/requirements.txt Fri Feb 13 17:22:21 2015 +0100 @@ -12,6 +12,7 @@ isodate==0.5.1 itsdangerous==0.24 pep8==1.5.7 +pylint pyparsing==2.0.3 python-slugify==0.1.0 pytz==2014.10