equipment main views

This commit is contained in:
Qwann 2018-08-20 13:17:10 +02:00
parent df0d5b4351
commit fd4567b531
27 changed files with 747 additions and 42 deletions

View file

@ -3,6 +3,7 @@ from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import Group
from event.models import Activity, EventSpecificMixin
from django.db.models import Q
from .fields import IdField
@ -20,20 +21,30 @@ class EquipmentCategory(models.Model):
blank=True,
null=True,
default=None,
related_name="children",
help_text=_("merci de ne pas faire de référence cyclique"),
verbose_name=_("parent"),
)
def has_parent(self, cat):
current = self
for k in range(100):
if current is None:
return False
if current == cat:
return True
current = current.parent
def full_name(self):
if self.parent is None:
return "-"
current = self
res = ""
for k in range(100):
if current.parent is None:
break
res = "/{current}{old}".format(
current=current.name,
old=res)
if current.parent is None:
break
current = current.parent
return res
@ -62,6 +73,14 @@ class EquipmentCategory(models.Model):
return super().save(*args, **kwargs)
class EquipmentQuerySet(models.QuerySet):
def in_category(self, cat):
filtre = Q(id__lt=0)
childs_id = [ c.id for c in EquipmentCategory.objects.all() if c.has_parent(cat) ]
for pk in childs_id:
filtre |= Q(category__id=pk)
return self.filter(filtre)
class Equipment(EventSpecificMixin, models.Model):
name = models.CharField(
@ -80,11 +99,13 @@ class Equipment(EventSpecificMixin, models.Model):
)
owner = models.ForeignKey(
Group,
verbose_name=_("propriétaire"),
blank=True,
null=True,
)
category = models.ForeignKey(
EquipmentCategory,
verbose_name=_("catégorie"),
on_delete=models.PROTECT,
)
@ -96,8 +117,21 @@ class Equipment(EventSpecificMixin, models.Model):
_("dernière modification"),
auto_now=True,
)
objects = EquipmentQuerySet.as_manager()
def is_in_category(self, cat):
current = self.category
for k in range(100):
if current is None:
return False
if current == cat:
return True
current = current.parent
def ids_aviable(self):
if self.stock is None:
return []
res = list(map(lambda x: x+1, range(self.stock)))
for lost in self.losts.all():
res = [ x
@ -116,7 +150,8 @@ class Equipment(EventSpecificMixin, models.Model):
return res
def stock_aviable(self):
return len(self.ids_available())
aviable = self.ids_aviable()
return len(aviable)
def stock_lost(self):
return len(self.ids_lost())
@ -165,10 +200,12 @@ class EquipmentAttributeValue(models.Model):
Equipment,
on_delete=models.CASCADE,
related_name="attributes",
verbose_name=_("matériel"),
help_text=_("Matériel concerné par le defaut"),
)
attribute = models.ForeignKey(
EquipmentAttribute,
verbose_name=_("attribut"),
on_delete=models.CASCADE,
)
value = models.CharField(
@ -186,7 +223,7 @@ class EquipmentAttributeValue(models.Model):
class EquipmentAttribution(models.Model):
equipment = models.ForeignKey(Equipment)
equipment = models.ForeignKey(Equipment, verbose_name=_("matériel"))
activity = models.ForeignKey(Activity)
amount = models.BigIntegerField(_("quantité attribuée"))
remarks = models.TextField(_("remarques concernant l'attribution"))
@ -211,6 +248,7 @@ class EquipmentDefault(models.Model):
remark = models.TextField(_("remarque sur le défaut"))
equipment = models.ForeignKey(
Equipment,
verbose_name=_("matériel"),
on_delete=models.CASCADE,
related_name="remarks",
help_text=_("Matériel concerné par le defaut"),
@ -235,6 +273,7 @@ class EquipmentLost(models.Model):
)
equipment = models.ForeignKey(
Equipment,
verbose_name=_("matériel"),
on_delete=models.CASCADE,
related_name="losts",
help_text=_("Matériel concerné par la perte"),
@ -249,6 +288,7 @@ class EquipmentRevision(models.Model):
)
equipment = models.ForeignKey(
Equipment,
verbose_name=_("matériel"),
on_delete=models.CASCADE,
related_name="revisions",
help_text=_("Matériel concerné par les révisions"),

108
equipment/tables.py Normal file
View file

@ -0,0 +1,108 @@
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import Group
from django.db.models import Q
from django.http.request import QueryDict
import django_filters
from django_filters.widgets import LinkWidget
from django_tables2.utils import A
import django_tables2 as tables
from .models import Equipment, EquipmentCategory
class EquipmentFilter(django_filters.FilterSet):
owner = django_filters.ModelChoiceFilter(
field_name='owner',
queryset=Group.objects.all(),
widget=LinkWidget(),
)
category = django_filters.ModelChoiceFilter(
field_name='category',
queryset=EquipmentCategory.objects.all(),
widget=LinkWidget(),
method='filter_category',
)
def filter_category(self, queryset, name, value):
return queryset.in_category(value)
def get_categories(self, qs):
"""
rend les catégories qui servent à filtrer les Equipments de qs
ie les catégories des equipments et tous leurs parents
"""
filtre = Q(id__lt=0)
for eq in qs:
current = eq.category
for k in range(100):
if current is None:
break
filtre |= Q(id=current.id)
current = current.parent
return EquipmentCategory.objects.filter(filtre)
def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
# On que les requêtes vides rendent quelque chose
if data is None:
data = QueryDict('category&owner')
super().__init__(data=data, queryset=queryset,
request=request, prefix=prefix)
if self.queryset is not None:
for filter_ in self.filters.values():
if filter_.queryset.model == Group:
own_ids = [eq.owner.id for eq in self.queryset]
filtre = Q(id__lt=0)
for own_id in own_ids:
filtre |= Q(id=own_id)
filter_.queryset = Group.objects.filter(filtre)
if filter_.queryset.model == EquipmentCategory:
filter_.queryset = self.get_categories(self.queryset)
class Meta:
model = Equipment
fields = ['category', 'owner']
class AbstractEquipmentTable(tables.Table):
stock_aviable_p = tables.Column(
accessor=A('stock_aviable_p'),
orderable=False, # TODO le rendre ordorable
verbose_name=_("Quantité disponible"),
)
full_category_p = tables.Column(
accessor=A('full_category_p'),
order_by=('category'),
verbose_name=_("Catégorie"),
)
name = tables.LinkColumn(
'equipment:detail',
args=[A('pk')],
verbose_name=_("Matériel"),
)
admin = tables.LinkColumn(
'admin:equipment_equipment_change',
attrs={
'a': {'class': 'glyphicon glyphicon-cog'}
},
text="",
orderable=False,
args=[A('pk')],
verbose_name=_(""),
)
def before_render(self, request):
if (request.user.is_staff and
request.user.has_perm('equipment_change_equipment')):
self.columns.show('admin')
else:
self.columns.hide('admin')
class EquipmentTable(AbstractEquipmentTable):
class Meta:
model = Equipment
template_name = 'equipment/tables/bootstrap-responsive.html'
fields = ['name', 'stock', 'owner', ]
sequence = ['admin', 'name', 'stock', 'stock_aviable_p',
'full_category_p', 'owner', ]

View file

@ -0,0 +1,13 @@
{% extends "shared/base.html" %}
{% load i18n staticfiles %}
{% block title %}{% trans "Matériel" %}{% endblock %}
{% block content %}
<h2>{{ equipment.name }}<a class="pull-right glyphicon glyphicon-cog" href="{% url 'admin:equipment_equipment_change' equipment.id %}"></a></h2>
{% endblock %}
{% block aside %}
Coucou :)
{% endblock %}

View file

@ -0,0 +1,44 @@
{% extends "shared/base.html" %}
{% load i18n staticfiles %}
{% block title %}{% trans "Matériel" %}{% endblock %}
{% block content %}
<h1>{% trans "Inventaire" %}</h1>
<a href="{% url 'equipment:list' %}" class="module">
<span class="glyphicon glyphicon-list-alt"></span>
{% trans "Tout le matériel" %}
</a>
<h2>{% trans "Liste par Propriétaire" %}</h2>
<div class="module-list">
{% for owner in owners %}
<a href="{% url 'equipment:list_by_owner' owner.id %}" class="module">
<span class="glyphicon glyphicon-user"></span>
{{ owner.name }}
</a>
{% endfor %}
</div>
<h2>{% trans "Liste par Catégorie" %}</h2>
<div class="tree">
<ul>
{% for node in root_cat %}
{% include "equipment/tree_cat.html" %}
{% endfor %}
</ul>
</div>
{% endblock %}
{% block aside %}
<div class="heading">
{{ nb_type }} <span class="sub">{% trans "référence" %}{{ nb_type|pluralize}}</span>
</div>
<div class="heading separator">
{{ stock }} <span class="sub">{% trans "item" %}{{ stock|pluralize}}</span>
</div>
<div class="heading inverted small">
{{ categories.count }} <span class="sub">{% trans "catégorie" %}{{ categories.count|pluralize}}</span>
</div>
<div class="heading inverted small ">
{{ owners.count }} <span class="sub">{% trans "propriéaire" %}{{ owners.count|pluralize}}</span>
</div>
{% endblock %}

View file

@ -0,0 +1,30 @@
{% extends "shared/base.html" %}
{% load render_table from django_tables2 %}
{% load bootstrap3 %}
{% load i18n staticfiles %}
{% block title %}{% trans "Matériel" %}{% endblock %}
{% block content %}
<h1>Inventaire</h1>
{% if subtitle %}
<h2>{{ subtitle }}</h2>
{% endif %}
{% render_table table %}
{% endblock %}
{% block aside %}
<div class="heading">
{{ nb_type }} <span class="sub">{% trans "référence" %}{{ nb_type|pluralize}}</span>
</div>
<div class="heading separator">
{{ stock }} <span class="sub">{% trans "item" %}{{ stock|pluralize}}</span>
</div>
{% if filter %}
<div class="text inverted">
<form id="filter_form" action="" method="get" class="form form-inline">
{% bootstrap_form filter.form layout='horizontal' size='lg' %}
</form>
</div>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,16 @@
{% extends 'equipment/tables/bootstrap.html' %}
{% block table-wrapper %}
<div class="table-container table-responsive">
{% block table %}
{{ block.super }}
{% endblock table %}
{% if table.page and table.paginator.num_pages > 1 %}
{% block pagination %}
{{ block.super }}
{% endblock pagination %}
{% endif %}
</div>
{% endblock table-wrapper %}

View file

@ -0,0 +1,103 @@
{% load django_tables2 %}
{% load i18n %}
{% block table-wrapper %}
<div class="table-container">
{% block table %}
<table {% render_attrs table.attrs class="table table-striped" %}>
{% block table.thead %}
{% if table.show_header %}
<thead>
<tr>
{% for column in table.columns %}
<th {{ column.attrs.th.as_html }}>
{% if column.orderable %}
<a href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a>
{% else %}
{{ column.header }}
{% endif %}
</th>
{% endfor %}
</tr>
</thead>
{% endif %}
{% endblock table.thead %}
{% block table.tbody %}
<tbody>
{% for row in table.paginated_rows %}
{% block table.tbody.row %}
<tr {{ row.attrs.as_html }}>
{% for column, cell in row.items %}
<td {{ column.attrs.td.as_html }}>{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}</td>
{% endfor %}
</tr>
{% endblock table.tbody.row %}
{% empty %}
{% if table.empty_text %}
{% block table.tbody.empty_text %}
<tr><td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
{% endblock table.tbody.empty_text %}
{% endif %}
{% endfor %}
</tbody>
{% endblock table.tbody %}
{% block table.tfoot %}
{% if table.has_footer %}
<tfoot>
<tr>
{% for column in table.columns %}
<td {{ column.attrs.tf.as_html }}>{{ column.footer }}</td>
{% endfor %}
</tr>
</tfoot>
{% endif %}
{% endblock table.tfoot %}
</table>
{% endblock table %}
{% if table.page and table.paginator.num_pages > 1 %}
{% block pagination %}
<nav aria-label="Table navigation">
<ul class="pagination">
{% if table.page.has_previous %}
{% block pagination.previous %}
<li class="previous">
<a href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}">
<span aria-hidden="true">&laquo;</span>
{% trans 'previous' %}
</a>
</li>
{% endblock pagination.previous %}
{% endif %}
{% if table.page.has_previous or table.page.has_next %}
{% block pagination.range %}
{% for p in table.page|table_page_range:table.paginator %}
<li {% if p == table.page.number %}class="active"{% endif %}>
{% if p == '...' %}
<a href="#">{{ p }}</a>
{% else %}
<a href="{% querystring table.prefixed_page_field=p %}">
{{ p }}
</a>
{% endif %}
</li>
{% endfor %}
{% endblock pagination.range %}
{% endif %}
{% if table.page.has_next %}
{% block pagination.next %}
<li class="next">
<a href="{% querystring table.prefixed_page_field=table.page.next_page_number %}">
{% trans 'next' %}
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% endblock pagination.next %}
{% endif %}
</ul>
</nav>
{% endblock pagination %}
{% endif %}
</div>
{% endblock table-wrapper %}

View file

@ -0,0 +1,11 @@
<li> <a href="{% url "equipment:list_by_category" node.id %}"><span class="category_node">{{node.name}}</span></a>
{%if node.children %}
<ul>
{%for ch in node.children.all %}
{%with node=ch template_name="equipment/tree_cat.html" %}
{%include template_name%}
{%endwith%}
{%endfor%}
</ul>
{%endif%}
</li>

11
equipment/urls.py Normal file
View file

@ -0,0 +1,11 @@
from django.conf.urls import url
from .views import EquipmentList, EquipmentView, EquipmentListByCategory, EquipmentListByOwner, EquipmentHome
app_name = 'equipment'
urlpatterns = [
url(r'^$', EquipmentHome.as_view(), name='home'),
url(r'^all/$', EquipmentList.as_view(), name='list'),
url(r'^(?P<pk>[0-9]+)/$', EquipmentView.as_view(), name='detail'),
url(r'^c/(?P<pk>[0-9]+)/$', EquipmentListByCategory.as_view(), name='list_by_category'),
url(r'^o/(?P<pk>[0-9]+)/$', EquipmentListByOwner.as_view(), name='list_by_owner'),
]

97
equipment/views.py Normal file
View file

@ -0,0 +1,97 @@
from .models import Equipment, EquipmentCategory
from django.contrib.auth.models import Group
from django.db.models import Sum
from django.views.generic import DetailView, ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from django_filters.views import FilterView
from django_tables2.views import SingleTableMixin
from .tables import EquipmentTable, EquipmentFilter
class EquipmentHome(LoginRequiredMixin, ListView):
template_name = 'equipment/home.html'
context_object_name = 'categories'
model = EquipmentCategory
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# TODO remplacer par les vrais owners
context['owners'] = Group.objects.all()
categories = (EquipmentCategory.objects.order_by('name')
.prefetch_related('children'))
context['root_cat'] = categories.filter(parent=None)
queryset = Equipment.objects.all()
context['stock'] = queryset.aggregate(Sum('stock'))['stock__sum']
context['nb_type'] = queryset.count()
return context
class EquipmentListAbstract(LoginRequiredMixin, SingleTableMixin,FilterView):
table_class = EquipmentTable
filterset_class = EquipmentFilter
template_name = 'equipment/list.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['stock'] = self.queryset.aggregate(Sum('stock'))['stock__sum']
context['nb_type'] = self.queryset.count()
return context
class EquipmentList(EquipmentListAbstract):
def get_queryset(self):
self.queryset = Equipment.objects.all()
return self.queryset
class EquipmentListByCategory(EquipmentListAbstract):
def get_category(self):
try:
pk = self.kwargs.get('pk')
except KeyError:
raise AttributeError(
"View %s must be called with an object "
"pk in the URLconf." % self.__class__.__name__
)
return EquipmentCategory.objects.get(id=pk)
def get_queryset(self):
cat = self.get_category()
self.queryset = Equipment.objects.all().in_category(cat)
return self.queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
cat = self.get_category()
context['subtitle'] = "Dans {cat}".format(cat=cat.full_name())
return context
class EquipmentListByOwner(EquipmentListAbstract):
def get_owner(self):
try:
pk = self.kwargs.get('pk')
except KeyError:
raise AttributeError(
"View %s must be called with an object "
"pk in the URLconf." % self.__class__.__name__
)
return Group.objects.get(id=pk)
def get_queryset(self):
owner = self.get_owner()
self.queryset = Equipment.objects.filter(owner=owner)
return self.queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
owner = self.get_owner()
context['subtitle'] = "Matériel de {owner}".format(owner=owner)
return context
class EquipmentView(DetailView):
model = Equipment
template_name = 'equipment/detail.html'

View file

@ -60,6 +60,9 @@ INSTALLED_APPS = [
'bootstrapform',
'widget_tweaks',
'taggit',
'django_tables2',
'django_filters',
'bootstrap3',
'allauth_ens',
'allauth',
@ -189,8 +192,8 @@ CAS_EMAIL_FORMAT = "%s@clipper.ens.fr"
CAS_FORCE_CHANGE_USERNAME_CASE = "lower"
CAS_VERSION = 'CAS_2_SAML_1_0'
LOGIN_URL = reverse_lazy('users:login')
LOGOUT_URL = reverse_lazy('users:logout')
LOGIN_URL = reverse_lazy('account_login')
LOGOUT_URL = reverse_lazy('account_logout')
LOGIN_REDIRECT_URL = reverse_lazy('shared:home')
ACCOUNT_HOME_URL = reverse_lazy('shared:home')
ACCOUNT_DETAILS_URL = reverse_lazy('shared:home')

View file

@ -18,6 +18,7 @@ urlpatterns = [
# Admin
url(r'^admin/', admin_site.urls),
# Apps
url(r'^equipment/', include('equipment.urls')),
url(r'^event/', include('event.urls')),
url(r'^user/', include('users.urls')),
# REST

View file

@ -1,9 +1,12 @@
asgi-redis==1.3.0
asgiref==1.1.1
django-bootstrap3==10.0.1
channels==1.1.5
Django==1.11.11
django-allauth==0.36.0
django-allauth-cas==1.0.0b2
django-tables2==2.0.0a2
django-filter==2.0.0
#git+https://git.eleves.ens.fr/cof-geek/django-allauth-ens@6e77b31e0dfed7659776d
git+https://git.eleves.ens.fr/cof-geek/django-allauth-ens@30a072b7db1fc2d4652284227a3e7e876b14c915
django-bootstrap-form==3.2.1

View file

@ -108,3 +108,14 @@
@include button-variant-2modes($btn-font-color, $btn-bg-base, $btn-bg-special, $btn-border);
}
form#filter_form {
.form-group {
padding-right:20px;
}
ul.form-control {
background-color: transparent;
border: none;
box-shadow: none;
}
}

View file

@ -158,17 +158,49 @@
color: #FF6969;
background-color: white; }
form#filter_form .form-group {
padding-right: 20px; }
form#filter_form ul.form-control {
background-color: transparent;
border: none;
box-shadow: none; }
.tree {
font-size: large; }
.tree ul, .tree li {
position: relative; }
.tree ul {
list-style: none;
padding-left: 32px; }
.tree li::before,
.tree li::after {
content: "";
position: absolute;
left: -12px; }
.tree li::before {
border-top: 3px solid #FFC282;
top: 9px;
width: 8px;
height: 0; }
.tree li::after {
border-left: 3px solid #FFC282;
height: 100%;
width: 0px;
top: 2px; }
.tree ul > li:last-child::after {
height: 8px; }
/* MISE EN FORME GÉNÉRALE */
html {
height: 100%;
background-color: #FFF5EB; }
background-color: #DCEAED; }
body {
font-family: "Saira Semi Condensed", sans-serif;
font-size: medium; }
#principal {
background-color: #FFF5EB; }
background-color: #DCEAED; }
/*MAIN*/
main {
@ -182,13 +214,13 @@ main {
main h1 {
border-bottom: 7px solid #FFB363;
padding-bottom: 5px;
font-family: "Saira", sans-serif;
font-family: "Capriola", sans-serif;
font-weight: 600; }
main h2 {
border-bottom: 2px solid #FF6969;
color: #FF6969;
padding-bottom: 5px;
font-family: "Saira", sans-serif;
font-family: "Capriola", sans-serif;
font-weight: 600; }
/*ASIDE*/
@ -196,7 +228,7 @@ aside {
background-color: #FFC282;
color: white;
margin-top: 0px;
padding: 15px; }
padding: 0px !important; }
aside a {
color: #FF9191; }
aside a:hover, aside a:active, aside a:focus {
@ -204,6 +236,30 @@ aside {
aside code {
color: white;
background-color: #FFB363; }
aside .heading {
padding: 8px 15px;
font-size: 32px;
line-height: 1.3;
text-align: center; }
aside .heading.inverted {
background-color: #FFF5EB;
color: black; }
aside .heading.small {
font-size: 25px; }
aside .heading.small .sub {
font-size: 0.7em;
font-weight: normal; }
aside .heading .sub {
font-size: 0.7em;
font-weight: normal; }
aside .heading.separator {
border-bottom-color: #FF9191;
border-bottom-style: solid; }
aside .text {
padding: 15px; }
aside .text.inverted {
background-color: #FFF5EB;
color: black; }
@media (min-width: 768px) {
main {
@ -224,6 +280,25 @@ div.tag-list {
code {
font-size: small; }
.module-list {
display: flex;
align-items: stretch;
flex-wrap: wrap; }
a.module {
background-color: #FFB363;
color: #FFF5EB;
padding: 20px 40px;
margin: 5px;
border-bottom-color: #FF9191;
border-bottom-style: solid;
font-size: large;
display: block; }
a.module:hover, a.module:active, a.module:focus {
color: #FFF5EB;
background-color: #FFC282;
text-decoration: none; }
/* COULD BE USERFULL LATER
.row-eq-height {

View file

@ -1,7 +1,7 @@
{
"version": 3,
"mappings": "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",
"sources": ["global.scss","variables.scss","messages.scss","header.scss","forms.scss"],
"mappings": ";AAAA;iBACiB;ACDjB,YAAY;AAoBZ,WAAW;ACpBX,aAAc;EACV,KAAK,EAAa,OAAO;EACzB,gBAAgB,EAAE,OAAO;EACzB,YAAY,EAAM,OAAO;;AAE7B,gBAAiB;EACb,KAAK,EAAa,OAAO;EACzB,gBAAgB,EAAE,OAAO;EACzB,YAAY,EAAM,OAAO;;AAE7B,gBAAiB;EACb,KAAK,EAAa,OAAO;EACzB,gBAAgB,EAAE,OAAO;EACzB,YAAY,EAAM,OAAO;;AAE7B,cAAe;EACX,KAAK,EAAa,OAAO;EACzB,gBAAgB,EAAE,OAAO;EACzB,YAAY,EAAM,OAAO;;AAE7B,MAAO;EACH,aAAa,EAAE,CAAC;;ACrBpB,YAAY;AAEZ,cAAe;EACb,WAAW,EAAS,IAAI;EACxB,cAAc,EAAM,IAAI;EACxB,gBAAgB,EFYL,OAAgB;EEX3B,KAAK,EFDa,OAAO;;AEK3B,eAAgB;EACd,gBAAgB,EFML,OAAgB;EEL3B,gBAAgB,EAAG,WAAW;EAC9B,YAAY,EAAM,IAAI;EAEhB,qIAEoB;IAChB,KAAK,EAAE,IAAI;IACX,gBAAgB,EFHX,OAAgB;;AEQjC,gBAAiB;EACb,UAAU,EAAE,qBAAsB;EAClC,OAAO,EAAG,GAAG;EAEb,iBAAiB;EACjB,gBAAgB,EFbH,OAAgB;EEc7B,YAAY,EAAE,IAAI;EAClB,WAAW,EAAE,KAAK;EAClB,YAAY,EAAE,KAAK;EAEnB,yBAA0B;IAV9B,gBAAiB;MAWT,gBAAgB,EAAI,WAAW;MAC/B,YAAY,EAAE,GAAG;MACjB,WAAW,EAAE,GAAG;MAChB,YAAY,EAAE,GAAG;;AAKzB,WAAY;EACR,KAAK,EAAE,IAAI;EACX,yBAA0B;IAF9B,WAAY;MAGJ,KAAK,EAAG,KAAK;MACb,KAAK,EAAE,IAAI;;AAInB,eAAgB;EACZ,WAAW;EAWX,WAAW;EAYX,WAAW;EArBP,uGAEQ;IACJ,KAAK,EAAE,KAAK;IACZ,WAAW,EFrCX,0BAA0B;IEsC1B,SAAS,EAAE,QAAQ;IACnB,aAAa,EAAE,iBAA0B;EAK7C,0GAEQ;IACJ,gBAAgB,EF/DR,OAAO;IEgEf,YAAY,EFhEJ,OAAO;EEkEnB,wCAAU;IACN,gBAAgB,EFjEP,OAAO;EEuEhB,4HAEQ;IACJ,WAAW,EF3DjB,oBAAoB;IE4Dd,SAAS,EAAE,KAAK;IAChB,KAAK,EF9ED,OAAO;IE+EX,UAAU,EAAE,WAAW;IACvB,yBAA0B;MAP9B,4HAEQ;QAMA,KAAK,EF/EJ,OAAO;;AGmG5B,YAAa;EA3CT;;;;;;;;;;;;;;;;;;;KAmBG;EACH,KAAK,EHrEQ,KAAK;EGsElB,gBAAgB,EHnEP,OAAgB;EGoEzB,YAAY,EHpEH,OAAgB;EGWzB;;;;;;;;;;;;;;;;;;;;KAoBG;EAxCH,0DAEQ;IA8EJ,KAAK,EH1EI,KAAK;IG2Ed,gBAAgB,EHzEP,OAAgB;IG0EzB,YAAY,EHzEP,OAAgB;EGhBzB,wCACS;IAsFL,KAAK,EH1EI,KAAK;IG2Ed,gBAAgB,EHzEP,OAAgB;IG0EzB,YAAY,EHzEP,OAAgB;IGTzB,gKAEQ;MA8EJ,KAAK,EH1EI,KAAK;MG2Ed,gBAAgB,EHzEP,OAAgB;MG0EzB,YAAY,EHzEP,OAAgB;EGOzB,oCAA0B;IAgEtB,KAAK,EH1EI,KAAK;IG2Ed,gBAAgB,EHzEP,OAAgB;IG0EzB,YAAY,EHzEP,OAAgB;IGTzB,kIAEQ;MA8EJ,KAAK,EH1EI,KAAK;MG2Ed,gBAAgB,EHzEP,OAAgB;MG0EzB,YAAY,EHzEP,OAAgB;EGTzB,oSAEQ;IAqFA,gBAAgB,EH9Ef,OAAgB;IG+EjB,YAAY,EH/EX,OAAgB;EGmFzB,mBAAO;IACH,KAAK,EHpFA,OAAgB;IGqFrB,gBAAgB,EHxFP,KAAK;;AGiGlB,4BAAY;EACR,aAAa,EAAC,IAAI;AAGtB,gCAAgB;EACZ,gBAAgB,EAAE,WAAW;EAC7B,MAAM,EAAE,IAAI;EACZ,UAAU,EAAE,IAAI;;ACtHxB,KAAM;EACF,SAAS,EAAC,KAAK;EACf,kBAAO;IACH,QAAQ,EAAE,QAAQ;EAGtB,QAAG;IACC,UAAU,EAAE,IAAI;IAChB,YAAY,EAAE,IAAI;EAGtB;iBACU;IACN,OAAO,EAAE,EAAE;IACX,QAAQ,EAAE,QAAQ;IAClB,IAAI,EAAE,KAAK;EAEf,gBAAW;IACP,UAAU,EAAE,iBAA4B;IACxC,GAAG,EAAE,GAAG;IACR,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,CAAC;EAEb,eAAU;IACN,WAAW,EAAE,iBAA4B;IACzC,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,GAAG;IACV,GAAG,EAAE,GAAG;EAEZ,+BAA0B;IACtB,MAAM,EAAE,GAAG;;ALtBnB,4BAA4B;AAC5B,IAAK;EACD,MAAM,EAAI,IAAI;EACd,gBAAgB,ECAA,OAAO;;ADG3B,IAAK;EACD,WAAW,ECSF,kCAAkC;EDR3C,SAAS,EAAE,MAAM;;AAGrB,UAAW;EACP,gBAAgB,ECTA,OAAO;;ADY3B,QAAQ;AACR,IAAK;EACD,gBAAgB,EAAC,KAAK;EACtB,UAAU,EAAC,GAAG;EACd,OAAO,EAAE,IAAI;EAEb,MAAE;IACE,KAAK,ECbA,OAAgB;IDerB,yCAEQ;MACJ,KAAK,EClBJ,OAAgB;EDqBzB,OAAG;IACC,aAAa,EAAK,iBAA4B;IAC9C,cAAc,EAAI,GAAG;IACrB,WAAW,EClBR,sBAAsB;IDmBzB,WAAW,EAAE,GAAG;EAEpB,OAAG;IACC,aAAa,EAAK,iBAA0B;IAC5C,KAAK,EC7BA,OAAgB;ID8BrB,cAAc,EAAI,GAAG;IACrB,WAAW,ECzBR,sBAAsB;ID0BzB,WAAW,EAAE,GAAG;;AAIxB,SAAS;AACT,KAAM;EACF,gBAAgB,ECrCF,OAAkB;EDsChC,KAAK,EAAE,KAAK;EACZ,UAAU,EAAC,GAAG;EACd,OAAO,EAAE,cAAa;EAEtB,OAAE;IACE,KAAK,EC7CI,OAAgB;ID+CzB,4CAEQ;MACJ,KAAK,EClDA,OAAgB;EDqD7B,UAAK;IACD,KAAK,EAAE,KAAK;IACZ,gBAAgB,EClEJ,OAAO;EDoEvB,cAAS;IACL,OAAO,EAAE,QAAQ;IACxB,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,GAAG;IACT,UAAU,EAAC,MAAM;IAEjB,uBAAW;MACP,gBAAgB,ECzEP,OAAO;MD0EhB,KAAK,EAAC,KAAK;IAGf,oBAAQ;MACJ,SAAS,EAAE,IAAI;MAEf,yBAAK;QACD,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,MAAM;IAI3B,mBAAK;MACD,SAAS,EAAE,KAAK;MAChB,WAAW,EAAE,MAAM;IAGvB,wBAAY;MACR,mBAAmB,ECnFd,OAAgB;MDoFrB,mBAAmB,EAAE,KAAK;EAGlC,WAAM;IACF,OAAO,EAAE,IAAI;IAEb,oBAAW;MACP,gBAAgB,ECpGP,OAAO;MDqGhB,KAAK,EAAC,KAAK;;AAMvB,yBAA0B;EACtB,IAAK;IACD,UAAU,EAAC,IAAI;;EAEnB,KAAM;IACF,UAAU,EAAC,IAAI;AAIvB,EAAG;EACD,UAAU,EAAG,iBAA6B;;AAI5C,SAAU;EACN,YAAY,EAAE,GAAG;EACjB,aAAa,EAAE,GAAG;;AAGtB,YAAa;EACT,UAAU,EAAE,IAAI;;AAGpB,IAAK;EACD,SAAS,EAAE,KAAK;;AAGpB,YAAa;EACT,OAAO,EAAE,IAAI;EACb,WAAW,EAAE,OAAO;EACpB,SAAS,EAAE,IAAI;;AAGnB,QAAS;EACL,gBAAgB,EC/IA,OAAO;EDgJvB,KAAK,EC9IY,OAAO;ED+IxB,OAAO,EAAE,SAAS;EAClB,MAAM,EAAE,GAAG;EACX,mBAAmB,ECxIN,OAAgB;EDyI7B,mBAAmB,EAAE,KAAK;EAC1B,SAAS,EAAE,KAAK;EAChB,OAAO,EAAE,KAAK;EAEd,+CAEQ;IACJ,KAAK,ECzJQ,OAAO;ID0JpB,gBAAgB,EC/IN,OAAkB;IDgJ5B,eAAe,EAAE,IAAI;;AAI7B;;;;;;;;EAQE",
"sources": ["global.scss","variables.scss","messages.scss","header.scss","forms.scss","tree.scss"],
"names": [],
"file": "global.css"
}

View file

@ -4,11 +4,12 @@
@import 'messages';
@import 'header';
@import 'forms';
@import 'tree';
/* MISE EN FORME GÉNÉRALE */
html {
height : 100% ;
background-color: $second_white_color;
background-color: $third_white_color;
}
body {
@ -17,7 +18,7 @@ body {
}
#principal {
background-color: $second_white_color;
background-color: $third_white_color;
}
/*MAIN*/
@ -55,7 +56,7 @@ aside {
background-color:$second_soft_color;
color: white;
margin-top:0px;
padding: 15px;
padding: 0px!important;
a {
color: $main_soft_color;
@ -70,6 +71,45 @@ aside {
color: white;
background-color: $second_bold_color;
}
.heading {
padding: 8px 15px;
font-size: 32px;
line-height: 1.3;
text-align:center;
&.inverted {
background-color:$second_white_color;
color:black;
}
&.small {
font-size: 25px;
.sub {
font-size: 0.7em;
font-weight: normal;
}
}
.sub {
font-size: 0.7em;
font-weight: normal;
}
&.separator {
border-bottom-color: $main_soft_color;
border-bottom-style: solid;
}
}
.text {
padding: 15px;
&.inverted {
background-color:$second_white_color;
color:black;
}
}
}
@media (min-width: 768px) {
@ -98,6 +138,32 @@ div.tag-list {
code {
font-size: small;
}
.module-list {
display: flex;
align-items: stretch;
flex-wrap: wrap;
}
a.module {
background-color: $second_bold_color;
color: $second_white_color;
padding: 20px 40px;
margin: 5px;
border-bottom-color: $main_soft_color;
border-bottom-style: solid;
font-size: large;
display: block;
&:hover,
&:active,
&:focus {
color: $second_white_color;
background-color: $second_soft_color;
text-decoration: none;
}
}
/* COULD BE USERFULL LATER
.row-eq-height {

View file

@ -0,0 +1,40 @@
.tree {
font-size:large;
ul, li {
position: relative;
}
ul {
list-style: none;
padding-left: 32px;
}
li::before,
li::after {
content: "";
position: absolute;
left: -12px;
}
li::before {
border-top: 3px solid $second_soft_color;
top: 9px;
width: 8px;
height: 0;
}
li::after {
border-left: 3px solid $second_soft_color;
height: 100%;
width: 0px;
top: 2px;
}
ul > li:last-child::after {
height: 8px;
}
.category_node {
}
}

View file

@ -7,6 +7,11 @@ $second_bold_color: #FFB363;
$second_soft_color: #FFC282;
$second_white_color: #FFF5EB;
$third_bold_color: #48B0C7;
$third_soft_color: #8FD4E3;
$third_white_color: #DCEAED;
$btn-font-color: white;
$btn-bg-base: $main_bold_color;
$btn-bg-special: $main_soft_color;
@ -16,5 +21,5 @@ $underline-brand: $second_soft_color;
/* FONTS */
$font_brand:'Lily Script One', cursive;
$font_nav:'Work Sans', cursive;
$font_bold:'Saira', sans-serif;
$font_bold:'Capriola', sans-serif;
$font_normal:'Saira Semi Condensed', sans-serif;

View file

@ -14,7 +14,7 @@
{# Fonts #}
<link href="{% static "fonts/glyphicons-halflings-regular.ttf" %}">
<link href="https://fonts.googleapis.com/css?family=Lily+Script+One|Saira+Semi+Condensed|Saira:600|Work+Sans" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Lily+Script+One|Saira+Semi+Condensed|Capriola|Work+Sans" rel="stylesheet">
{# Global stylesheets #}
<link href="{% static "css/bootstrap.min.css" %}" rel="stylesheet">
@ -57,10 +57,10 @@
<div class="row">
{% block real_content %}
<main class="col-sm-8">
<main class="col-lg-9">
{% block content %}{% endblock %}
</main>
<aside class="col-sm-4">
<aside class="col-lg-3">
{{ message_title }}
{% block aside %}{% endblock %}
</aside>

View file

@ -4,32 +4,60 @@
{% block page_title %}{% trans "Accueil" %}{% endblock %}
{% block content %}
<h2>Outil en construction</h2>
<p>Toutes les fonctionnalités ne sont pas encore implémentées et le site est encore en version de test. Pour le moment les fonctionnalités qui fonctionnent sont :
<ul>
<li>La gestion du matériel (uniquement via l'interface d'administration)</li>
<li>Connexion via CAS</li>
</ul>
</p>
<img src="{% static "img/poulpe.png"%}" class="img-fluid" alt="Responsive image" style="width: 100%; height: auto;">
<p>Dans la liste des choses qui sont partiellement ou totalement à implémenter :
<ul>
<li>Création/gestion d'évènements</li>
<li>Affichage d'un agenda</li>
<li>Gestion des permanences</li>
<li>Affectation du matériel à des évènements</li>
<li>Gestion de matériel spéfique à un évènement</li>
<li>Gestion des salles</li>
<li>Tags sur les activités</li>
<li>...</li>
</ul>
</p>
<h1>Bienvenue sur Poulpe !</h1>
<h2>Modules</h2>
<div class="module-list">
<a href="{% url 'equipment:home' %}" class="module">
<span class="glyphicon glyphicon-list-alt"></span>
Inventaire
</a>
<a href="{% url 'equipment:home' %}" class="module">
<span class="glyphicon glyphicon-calendar"></span>
Évènements
</a>
{% if user.is_staff %}
<a href="{% url 'admin:index' %}" class="module">
<span class="glyphicon glyphicon-cog"></span>
Administration
</a>
{% endif %}
</div>
<h2>Outil en construction</h2>
<div class="container-fluid">
<div class="row">
<div class="col-md-6">
<p>Toutes les fonctionnalités ne sont pas encore implémentées et le site est encore en version de test. Pour le moment les fonctionnalités qui fonctionnent sont :
<ul>
<li>La gestion du matériel (uniquement via l'interface d'administration)</li>
<li>Connexion via CAS</li>
</ul>
</p>
<p>Dans la liste des choses qui sont partiellement ou totalement à implémenter :
<ul>
<li>Création/gestion d'évènements</li>
<li>Affichage d'un agenda</li>
<li>Gestion des permanences</li>
<li>Affectation du matériel à des évènements</li>
<li>Gestion de matériel spéfique à un évènement</li>
<li>Gestion des salles</li>
<li>Tags sur les activités</li>
<li>...</li>
</ul>
</div>
<div class="col-md-6">
</p>
<img src="{% static "img/poulpe.png"%}" class="img-fluid" alt="Responsive image" style="width: 100%; height: auto;">
</div>
</div>
</div>
{% endblock %}
{% block aside %}
<div class="text">
<h2>Envie de nous donner un coup de main ?</h2>
<p>À terme le but de cet outil est de fournir une interface ergnonomique et efficace pour l'organisation d'évènements, petits ou grands, en permettant notament de proposer des taches et/ou des permances auquel les utilisat⋅rice⋅eur⋅s pourront ensuite s'inscrire.<p>
<p>Le projet est essentiellement codé en <code>Django</code>, un framework <code>python</code> pour le web, mais nous avons aussi besoin de mains pour écrire du <code>JavaScript</code>, <code>HTML</code>, <code>CSS</code> ou même pour réaliser des illustrations ! Nous organisons de temps en temps des sessions aucours desquelles il est possible d'apprendre à utiliser ces outils même sans avoir de connaissances préalables. Si le projet t'intéresse et que tu veux y participer, n'hésite pas à nous envoyer un mail à <code>cof-geek (at) ens (point) fr</code> :)</p>
<p>Tu peux retrouver tous nos projets sur le <a href="https://git.eleves.ens.fr/cof-geek/">serveur git de l'École</a>, ainsi qu'une présentation plus détaillée de <code>COF-Geek</code> en <a href="https://cof.ens.fr/cof-geek/">suivant ce lien</a>.</p>
</div>
{% endblock %}