mirror of
https://github.com/ae-utbm/sith.git
synced 2024-12-22 15:51:19 +00:00
core: add family graphs
Signed-off-by: Skia <skia@libskia.so>
This commit is contained in:
parent
47bace2057
commit
7879b6dd6b
@ -6,6 +6,8 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p><a href="{{ url("core:user_godfathers_tree_pict", user_id=profile.id) }}?family">
|
||||
{% trans %}Show family picture{% endtrans %}</a></p>
|
||||
{% if profile.godfathers.exists() %}
|
||||
<h4>{% trans %}Godfathers{% endtrans %}</h4>
|
||||
<ul>
|
||||
@ -14,6 +16,8 @@
|
||||
{{ u.get_mini_item()|safe }} </a>{{ delete_godfather(user, profile, u, True) }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p><a href="{{ url("core:user_godfathers_tree", user_id=profile.id) }}">
|
||||
{% trans %}Show ancestors tree{% endtrans %}</a></p>
|
||||
{% else %}
|
||||
<p>{% trans %}No godfathers{% endtrans %}
|
||||
{% endif %}
|
||||
@ -25,6 +29,8 @@
|
||||
{{ u.get_mini_item()|safe }} </a>{{ delete_godfather(user, profile, u, False) }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p><a href="{{ url("core:user_godfathers_tree", user_id=profile.id) }}?descent">
|
||||
{% trans %}Show descent tree{% endtrans %}</a></p>
|
||||
{% else %}
|
||||
<p>{% trans %}No godchildren{% endtrans %}
|
||||
{% endif %}
|
||||
|
54
core/templates/core/user_godfathers_tree.jinja
Normal file
54
core/templates/core/user_godfathers_tree.jinja
Normal file
@ -0,0 +1,54 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
|
||||
{% block title %}
|
||||
{% if param == "godchildren" %}
|
||||
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s godchildren{% endtrans %}
|
||||
{% else %}
|
||||
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s godfathers{% endtrans %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% macro display_members_list(user) %}
|
||||
{% if user.__getattribute__(param).exists() %}
|
||||
<ul>
|
||||
{% for u in user.__getattribute__(param).all() %}
|
||||
<li>
|
||||
<a href="{{ url("core:user_godfathers", user_id=u.id) }}">
|
||||
{{ u.get_short_name() }}
|
||||
</a>
|
||||
{% if u in members_set %}
|
||||
{% trans %}Already seen (check above){% endtrans %}
|
||||
{% else %}
|
||||
{{ members_set.add(u) or "" }}
|
||||
{{ display_members_list(u) }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% block content %}
|
||||
<p><a href="{{ url("core:user_godfathers", user_id=profile.id) }}">
|
||||
{% trans %}Back to family{% endtrans %}</a></p>
|
||||
{% if profile.__getattribute__(param).exists() %}
|
||||
{% if param == "godchildren" %}
|
||||
<p><a href="{{ url("core:user_godfathers_tree_pict", user_id=profile.id) }}?descent">
|
||||
{% trans %}Show a picture of the tree{% endtrans %}</a></p>
|
||||
<h4>{% trans u=profile.get_short_name() %}Descent tree of {{ u }}{% endtrans %}</h4>
|
||||
{% else %}
|
||||
<p><a href="{{ url("core:user_godfathers_tree_pict", user_id=profile.id) }}?ancestors">
|
||||
{% trans %}Show a picture of the tree{% endtrans %}</a></p>
|
||||
<h4>{% trans u=profile.get_short_name() %}Ancestors tree of {{ u }}{% endtrans %}</h4>
|
||||
{% endif %}
|
||||
{{ members_set.add(profile) or "" }}
|
||||
{{ display_members_list(profile) }}
|
||||
{% else %}
|
||||
{% if param == "godchildren" %}
|
||||
<p>{% trans %}No godchildren{% endtrans %}
|
||||
{% else %}
|
||||
<p>{% trans %}No godfathers{% endtrans %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
@ -61,6 +61,8 @@ urlpatterns = [
|
||||
url(r'^user/(?P<user_id>[0-9]+)/$', UserView.as_view(), name='user_profile'),
|
||||
url(r'^user/(?P<user_id>[0-9]+)/pictures$', UserPicturesView.as_view(), name='user_pictures'),
|
||||
url(r'^user/(?P<user_id>[0-9]+)/godfathers$', UserGodfathersView.as_view(), name='user_godfathers'),
|
||||
url(r'^user/(?P<user_id>[0-9]+)/godfathers/tree$', UserGodfathersTreeView.as_view(), name='user_godfathers_tree'),
|
||||
url(r'^user/(?P<user_id>[0-9]+)/godfathers/tree/pict$', UserGodfathersTreePictureView.as_view(), name='user_godfathers_tree_pict'),
|
||||
url(r'^user/(?P<user_id>[0-9]+)/godfathers/(?P<godfather_id>[0-9]+)/(?P<is_father>(True)|(False))/delete$', DeleteUserGodfathers, name='user_godfathers_delete'),
|
||||
url(r'^user/(?P<user_id>[0-9]+)/edit$', UserUpdateProfileView.as_view(), name='user_edit'),
|
||||
url(r'^user/(?P<user_id>[0-9]+)/profile_upload$', UserUploadProfilePictView.as_view(), name='user_profile_upload'),
|
||||
|
@ -28,7 +28,7 @@ from django.contrib.auth import views
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.http import Http404
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.views.generic.edit import UpdateView
|
||||
from django.views.generic import ListView, DetailView, TemplateView
|
||||
from django.forms.models import modelform_factory
|
||||
@ -233,20 +233,6 @@ class UserView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
current_tab = 'infos'
|
||||
|
||||
|
||||
def DeleteUserGodfathers(request, user_id, godfather_id, is_father):
|
||||
user = User.objects.get(id=user_id)
|
||||
if ((user == request.user) or
|
||||
request.user.is_root or
|
||||
request.user.is_board_member):
|
||||
ud = get_object_or_404(User, id=godfather_id)
|
||||
if is_father == "True":
|
||||
user.godfathers.remove(ud)
|
||||
else:
|
||||
user.godchildren.remove(ud)
|
||||
else:
|
||||
raise PermissionDenied
|
||||
return redirect('core:user_godfathers', user_id=user_id)
|
||||
|
||||
|
||||
class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
@ -274,6 +260,20 @@ class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['pictures'][album.id].append(pict_relation.picture)
|
||||
return kwargs
|
||||
|
||||
def DeleteUserGodfathers(request, user_id, godfather_id, is_father):
|
||||
user = User.objects.get(id=user_id)
|
||||
if ((user == request.user) or
|
||||
request.user.is_root or
|
||||
request.user.is_board_member):
|
||||
ud = get_object_or_404(User, id=godfather_id)
|
||||
if is_father == "True":
|
||||
user.godfathers.remove(ud)
|
||||
else:
|
||||
user.godchildren.remove(ud)
|
||||
else:
|
||||
raise PermissionDenied
|
||||
return redirect('core:user_godfathers', user_id=user_id)
|
||||
|
||||
class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's godfathers
|
||||
@ -305,6 +305,91 @@ class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['form'] = UserGodfathersForm()
|
||||
return kwargs
|
||||
|
||||
class UserGodfathersTreeView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's family tree
|
||||
"""
|
||||
model = User
|
||||
pk_url_kwarg = "user_id"
|
||||
context_object_name = "profile"
|
||||
template_name = "core/user_godfathers_tree.jinja"
|
||||
current_tab = 'godfathers'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super(UserGodfathersTreeView, self).get_context_data(**kwargs)
|
||||
if "descent" in self.request.GET:
|
||||
kwargs['param'] = "godchildren"
|
||||
else:
|
||||
kwargs['param'] = "godfathers"
|
||||
kwargs['members_set'] = set()
|
||||
return kwargs
|
||||
|
||||
class UserGodfathersTreePictureView(CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's tree as a picture
|
||||
"""
|
||||
model = User
|
||||
pk_url_kwarg = "user_id"
|
||||
|
||||
def build_complex_graph(self):
|
||||
import pygraphviz as pgv
|
||||
self.depth = int(self.request.GET.get("depth", 4))
|
||||
if self.param == "godfathers":
|
||||
self.graph = pgv.AGraph(strict=False, directed=True, rankdir="BT")
|
||||
else:
|
||||
self.graph = pgv.AGraph(strict=False, directed=True)
|
||||
family = set()
|
||||
self.level = 1
|
||||
# Since the tree isn't very deep, we can build it recursively
|
||||
def crawl_family(user):
|
||||
if self.level > self.depth: return
|
||||
self.level += 1
|
||||
for u in user.__getattribute__(self.param).all():
|
||||
self.graph.add_edge(user.get_short_name(), u.get_short_name())
|
||||
if u not in family:
|
||||
family.add(u)
|
||||
crawl_family(u)
|
||||
self.level -= 1
|
||||
self.graph.add_node(self.object.get_short_name())
|
||||
family.add(self.object)
|
||||
crawl_family(self.object)
|
||||
|
||||
def build_family_graph(self):
|
||||
import pygraphviz as pgv
|
||||
self.graph = pgv.AGraph(strict=False, directed=True)
|
||||
self.graph.add_node(self.object.get_short_name())
|
||||
for u in self.object.godfathers.all():
|
||||
self.graph.add_edge(u.get_short_name(), self.object.get_short_name())
|
||||
for u in self.object.godchildren.all():
|
||||
self.graph.add_edge(self.object.get_short_name(), u.get_short_name())
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
if "descent" in self.request.GET:
|
||||
self.param = "godchildren"
|
||||
elif "ancestors" in self.request.GET:
|
||||
self.param = "godfathers"
|
||||
else:
|
||||
self.param = "family"
|
||||
|
||||
if self.param == "family":
|
||||
self.build_family_graph()
|
||||
else:
|
||||
self.build_complex_graph()
|
||||
# Pimp the graph before display
|
||||
self.graph.node_attr['color'] = "lightblue"
|
||||
self.graph.node_attr['style'] = "filled"
|
||||
main_node = self.graph.get_node(self.object.get_short_name())
|
||||
main_node.attr['color'] = "sandybrown"
|
||||
main_node.attr['shape'] = "rect"
|
||||
if self.param == "godchildren":
|
||||
self.graph.graph_attr['label'] = _("Godchildren")
|
||||
elif self.param == "godfathers":
|
||||
self.graph.graph_attr['label'] = _("Godfathers")
|
||||
else:
|
||||
self.graph.graph_attr['label'] = _("Family")
|
||||
img = self.graph.draw(format="png", prog="dot")
|
||||
return HttpResponse(img, content_type="image/png")
|
||||
|
||||
class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
|
@ -6,7 +6,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-10-09 16:19+0200\n"
|
||||
"POT-Creation-Date: 2017-10-11 12:22+0200\n"
|
||||
"PO-Revision-Date: 2016-07-18\n"
|
||||
"Last-Translator: Skia <skia@libskia.so>\n"
|
||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||
@ -745,9 +745,10 @@ msgid ""
|
||||
"Warning: if you select <em>Account</em>, the opposite operation will be "
|
||||
"created in the target account. If you don't want that, select <em>Club</em> "
|
||||
"instead of <em>Account</em>."
|
||||
msgstr "Attention : si vous sélectionnez <em>Compte</em>, l'opération inverse sera "
|
||||
"créée dans le compte cible. Si vous ne le voulez pas, sélectionnez <em>Club</em> "
|
||||
"à la place de <em>Compte</em>."
|
||||
msgstr ""
|
||||
"Attention : si vous sélectionnez <em>Compte</em>, l'opération inverse sera "
|
||||
"créée dans le compte cible. Si vous ne le voulez pas, sélectionnez <em>Club</"
|
||||
"em> à la place de <em>Compte</em>."
|
||||
|
||||
#: accounting/templates/accounting/operation_edit.jinja:46
|
||||
msgid "Linked operation:"
|
||||
@ -760,7 +761,7 @@ msgstr "Opération liée : "
|
||||
#: core/templates/core/file_edit.jinja:8
|
||||
#: core/templates/core/macros_pages.jinja:26
|
||||
#: core/templates/core/page_prop.jinja:11
|
||||
#: core/templates/core/user_godfathers.jinja:35
|
||||
#: core/templates/core/user_godfathers.jinja:41
|
||||
#: core/templates/core/user_preferences.jinja:12
|
||||
#: core/templates/core/user_preferences.jinja:19
|
||||
#: counter/templates/counter/cash_register_summary.jinja:22
|
||||
@ -896,16 +897,12 @@ msgid "logo"
|
||||
msgstr "logo"
|
||||
|
||||
#: club/models.py:62
|
||||
#, fuzzy
|
||||
#| msgid "active"
|
||||
msgid "is active"
|
||||
msgstr "actif"
|
||||
|
||||
#: club/models.py:63
|
||||
#, fuzzy
|
||||
#| msgid "description"
|
||||
msgid "short description"
|
||||
msgstr "description"
|
||||
msgstr "description courte"
|
||||
|
||||
#: club/models.py:64 core/models.py:199
|
||||
msgid "address"
|
||||
@ -2895,26 +2892,69 @@ msgid "Change user password"
|
||||
msgstr "Changer le mot de passe"
|
||||
|
||||
#: core/templates/core/user_godfathers.jinja:5
|
||||
#: core/templates/core/user_godfathers_tree.jinja:7
|
||||
#, python-format
|
||||
msgid "%(user_name)s's godfathers"
|
||||
msgstr "Parrains de %(user_name)s"
|
||||
|
||||
#: core/templates/core/user_godfathers.jinja:10 core/views/user.py:169
|
||||
#: core/templates/core/user_godfathers.jinja:10
|
||||
msgid "Show family picture"
|
||||
msgstr "Voir une image de la famille"
|
||||
|
||||
#: core/templates/core/user_godfathers.jinja:12 core/views/user.py:169
|
||||
#: core/views/user.py:388
|
||||
msgid "Godfathers"
|
||||
msgstr "Parrains"
|
||||
|
||||
#: core/templates/core/user_godfathers.jinja:18
|
||||
#: core/templates/core/user_godfathers.jinja:20
|
||||
msgid "Show ancestors tree"
|
||||
msgstr "Voir l'arbre des ancêtres"
|
||||
|
||||
#: core/templates/core/user_godfathers.jinja:22
|
||||
#: core/templates/core/user_godfathers_tree.jinja:50
|
||||
msgid "No godfathers"
|
||||
msgstr "Pas de parrains"
|
||||
|
||||
#: core/templates/core/user_godfathers.jinja:21
|
||||
#: core/templates/core/user_godfathers.jinja:25 core/views/user.py:386
|
||||
msgid "Godchildren"
|
||||
msgstr "Fillots"
|
||||
|
||||
#: core/templates/core/user_godfathers.jinja:29
|
||||
#: core/templates/core/user_godfathers.jinja:33
|
||||
msgid "Show descent tree"
|
||||
msgstr "Voir l'arbre de la descendance"
|
||||
|
||||
#: core/templates/core/user_godfathers.jinja:35
|
||||
#: core/templates/core/user_godfathers_tree.jinja:48
|
||||
msgid "No godchildren"
|
||||
msgstr "Pas de fillots"
|
||||
|
||||
#: core/templates/core/user_godfathers_tree.jinja:5
|
||||
msgid "%(user_name)s's godchildren"
|
||||
msgstr "Fillots de %(user_name)s"
|
||||
|
||||
#: core/templates/core/user_godfathers_tree.jinja:20
|
||||
msgid "Already seen (check above)"
|
||||
msgstr "Déjà vu (voir plus haut)"
|
||||
|
||||
#: core/templates/core/user_godfathers_tree.jinja:33
|
||||
msgid "Back to family"
|
||||
msgstr "Retour à la famille"
|
||||
|
||||
#: core/templates/core/user_godfathers_tree.jinja:37
|
||||
#: core/templates/core/user_godfathers_tree.jinja:41
|
||||
msgid "Show a picture of the tree"
|
||||
msgstr "Voir une image de l'arbre"
|
||||
|
||||
#: core/templates/core/user_godfathers_tree.jinja:38
|
||||
#, python-format
|
||||
msgid "Descent tree of %(u)s"
|
||||
msgstr "Descendants de %(u)s"
|
||||
|
||||
#: core/templates/core/user_godfathers_tree.jinja:42
|
||||
#, python-format
|
||||
msgid "Ancestors tree of %(u)s"
|
||||
msgstr "Ancêtres de %(u)s"
|
||||
|
||||
#: core/templates/core/user_group.jinja:4
|
||||
#, python-format
|
||||
msgid "Edit user groups for %(user_name)s"
|
||||
@ -3181,7 +3221,11 @@ msgstr "Fillot"
|
||||
msgid "Pictures"
|
||||
msgstr "Photos"
|
||||
|
||||
#: core/views/user.py:388
|
||||
#: core/views/user.py:390
|
||||
msgid "Family"
|
||||
msgstr "Famille"
|
||||
|
||||
#: core/views/user.py:473
|
||||
msgid "User already has a profile picture"
|
||||
msgstr "L'utilisateur a déjà une photo de profil"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user