Merge branch 'pep8' into 'master'

Pep8

See merge request !81
This commit is contained in:
Skia 2017-06-13 11:39:38 +02:00
commit fbcf525378
64 changed files with 1756 additions and 1474 deletions

View File

@ -35,5 +35,3 @@ admin.site.register(SimplifiedAccountingType)
admin.site.register(Operation)
admin.site.register(Label)
admin.site.register(Company)

View File

@ -25,7 +25,6 @@
from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError
from django.core import validators
from django.db.models import Count
from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
@ -37,6 +36,7 @@ from decimal import Decimal
from core.models import User, SithFile
from club.models import Club
class CurrencyField(models.DecimalField):
"""
This is a custom database field used for currency
@ -56,6 +56,7 @@ class CurrencyField(models.DecimalField):
# Accounting classes
class Company(models.Model):
name = models.CharField(_('name'), max_length=60)
street = models.CharField(_('street'), max_length=60, blank=True)
@ -104,6 +105,7 @@ class Company(models.Model):
def __str__(self):
return self.name
class BankAccount(models.Model):
name = models.CharField(_('name'), max_length=30)
iban = models.CharField(_('iban'), max_length=255, blank=True)
@ -131,6 +133,7 @@ class BankAccount(models.Model):
def __str__(self):
return self.name
class ClubAccount(models.Model):
name = models.CharField(_('name'), max_length=30)
club = models.ForeignKey(Club, related_name="club_account", verbose_name=_("club"))
@ -244,6 +247,7 @@ class GeneralJournal(models.Model):
self.amount -= o.amount
self.save()
class Operation(models.Model):
"""
An operation is a line in the journal, a debit or a credit
@ -352,6 +356,7 @@ class Operation(models.Model):
self.amount, self.date, self.accounting_type, self.done,
)
class AccountingType(models.Model):
"""
Class describing the accounting types.
@ -385,6 +390,7 @@ class AccountingType(models.Model):
def __str__(self):
return self.code + " - " + self.get_movement_type_display() + " - " + self.label
class SimplifiedAccountingType(models.Model):
"""
Class describing the simplified accounting types.
@ -410,6 +416,7 @@ class SimplifiedAccountingType(models.Model):
def __str__(self):
return self.get_movement_type_display() + " - " + self.accounting_type.code + " - " + self.label
class Label(models.Model):
"""Label allow a club to sort its operations"""
name = models.CharField(_('label'), max_length=64)
@ -432,4 +439,3 @@ class Label(models.Model):
def can_be_viewed_by(self, user):
return self.club_account.can_be_viewed_by(user)

View File

@ -22,15 +22,12 @@
#
#
from django.test import Client, TestCase
from django.test import TestCase
from django.core.urlresolvers import reverse
from django.contrib.auth.models import Group
from django.core.management import call_command
from django.conf import settings
from datetime import date, datetime
from datetime import date
from core.models import User
from counter.models import Counter
from accounting.models import GeneralJournal, Operation, Label, AccountingType, SimplifiedAccountingType
@ -72,6 +69,7 @@ class RefoundAccountTest(TestCase):
self.assertFalse(response_post.status_code == 403)
self.assertTrue(self.skia.customer.amount == 0)
class JournalTest(TestCase):
def setUp(self):
call_command("populate")
@ -91,6 +89,7 @@ class JournalTest(TestCase):
self.assertTrue(response_get.status_code == 403)
self.assertFalse('<td>M\xc3\xa9thode de paiement</td>' in str(response_get.content))
class OperationTest(TestCase):
def setUp(self):
call_command("populate")
@ -137,7 +136,7 @@ class OperationTest(TestCase):
def test_bad_new_operation(self):
self.client.login(username='comptable', password='plop')
at = AccountingType.objects.filter(code = '604').first()
AccountingType.objects.filter(code='604').first()
response = self.client.post(reverse('accounting:op_new',
args=[self.journal.id]),
{'amount': 30,
@ -216,7 +215,6 @@ class OperationTest(TestCase):
response_get = self.client.get(reverse("accounting:journal_person_statement", args=[self.journal.id]))
self.assertTrue("S&#39; Kia</a></td>\\n \\n <td>3.00</td>" in str(response_get.content))
def test_accounting_statement(self):
self.client.login(username='comptable', password='plop')
response_get = self.client.get(reverse("accounting:journal_accounting_statement", args=[self.journal.id]))

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from accounting.views import *
@ -71,5 +71,3 @@ urlpatterns = [
# User account
url(r'^refound/account$', RefoundAccountView.as_view(), name='refound_account'),
]

View File

@ -22,25 +22,21 @@
#
#
from django.views.generic import ListView, DetailView, RedirectView
from django.views.generic import ListView, DetailView
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView
from django.shortcuts import render
from django.core.urlresolvers import reverse_lazy, reverse
from django.utils.translation import ugettext_lazy as _
from django.forms.models import modelform_factory
from django.core.exceptions import PermissionDenied, ValidationError
from django.forms import HiddenInput, TextInput
from django.forms import HiddenInput
from django.db import transaction
from django.db.models import Sum
from django.conf import settings
from django import forms
from django.http import HttpResponseRedirect, HttpResponse
from django.utils.translation import ugettext as _
from django.conf import settings
from django.http import HttpResponse
import collections
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
from ajax_select.fields import AutoCompleteSelectField
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
from core.views.forms import SelectFile, SelectDate
@ -49,6 +45,7 @@ from counter.models import Counter, Selling, Product
# Main accounting view
class BankAccountListView(CanViewMixin, ListView):
"""
A list view for the admins
@ -57,6 +54,7 @@ class BankAccountListView(CanViewMixin, ListView):
template_name = 'accounting/bank_account_list.jinja'
ordering = ['name']
# Simplified accounting types
class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
@ -66,6 +64,7 @@ class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
model = SimplifiedAccountingType
template_name = 'accounting/simplifiedaccountingtype_list.jinja'
class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
@ -75,6 +74,7 @@ class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
fields = ['label', 'accounting_type']
template_name = 'core/edit.jinja'
class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
"""
Create an accounting type (for the admins)
@ -83,6 +83,7 @@ class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
fields = ['label', 'accounting_type']
template_name = 'core/create.jinja'
# Accounting types
class AccountingTypeListView(CanViewMixin, ListView):
@ -92,6 +93,7 @@ class AccountingTypeListView(CanViewMixin, ListView):
model = AccountingType
template_name = 'accounting/accountingtype_list.jinja'
class AccountingTypeEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
@ -101,6 +103,7 @@ class AccountingTypeEditView(CanViewMixin, UpdateView):
fields = ['code', 'label', 'movement_type']
template_name = 'core/edit.jinja'
class AccountingTypeCreateView(CanCreateMixin, CreateView):
"""
Create an accounting type (for the admins)
@ -109,6 +112,7 @@ class AccountingTypeCreateView(CanCreateMixin, CreateView):
fields = ['code', 'label', 'movement_type']
template_name = 'core/create.jinja'
# BankAccount views
class BankAccountEditView(CanViewMixin, UpdateView):
@ -120,6 +124,7 @@ class BankAccountEditView(CanViewMixin, UpdateView):
fields = ['name', 'iban', 'number', 'club']
template_name = 'core/edit.jinja'
class BankAccountDetailView(CanViewMixin, DetailView):
"""
A detail view, listing every club account
@ -128,6 +133,7 @@ class BankAccountDetailView(CanViewMixin, DetailView):
pk_url_kwarg = "b_account_id"
template_name = 'accounting/bank_account_details.jinja'
class BankAccountCreateView(CanCreateMixin, CreateView):
"""
Create a bank account (for the admins)
@ -136,6 +142,7 @@ class BankAccountCreateView(CanCreateMixin, CreateView):
fields = ['name', 'club', 'iban', 'number']
template_name = 'core/create.jinja'
class BankAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
"""
Delete a bank account (for the admins)
@ -145,6 +152,7 @@ class BankAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('accounting:bank_list')
# ClubAccount views
class ClubAccountEditView(CanViewMixin, UpdateView):
@ -156,6 +164,7 @@ class ClubAccountEditView(CanViewMixin, UpdateView):
fields = ['name', 'club', 'bank_account']
template_name = 'core/edit.jinja'
class ClubAccountDetailView(CanViewMixin, DetailView):
"""
A detail view, listing every journal
@ -164,6 +173,7 @@ class ClubAccountDetailView(CanViewMixin, DetailView):
pk_url_kwarg = "c_account_id"
template_name = 'accounting/club_account_details.jinja'
class ClubAccountCreateView(CanCreateMixin, CreateView):
"""
Create a club account (for the admins)
@ -180,6 +190,7 @@ class ClubAccountCreateView(CanCreateMixin, CreateView):
ret['bank_account'] = obj.id
return ret
class ClubAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
"""
Delete a club account (for the admins)
@ -189,6 +200,7 @@ class ClubAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('accounting:bank_list')
# Journal views
class JournalTabsMixin(TabedViewMixin):
@ -219,6 +231,7 @@ class JournalTabsMixin(TabedViewMixin):
})
return tab_list
class JournalCreateView(CanCreateMixin, CreateView):
"""
Create a general journal
@ -236,6 +249,7 @@ class JournalCreateView(CanCreateMixin, CreateView):
ret['club_account'] = obj.id
return ret
class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
"""
A detail view, listing every operation
@ -245,6 +259,7 @@ class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
template_name = 'accounting/journal_details.jinja'
current_tab = 'journal'
class JournalEditView(CanEditMixin, UpdateView):
"""
Update a general journal
@ -254,6 +269,7 @@ class JournalEditView(CanEditMixin, UpdateView):
fields = ['name', 'start_date', 'end_date', 'club_account', 'closed']
template_name = 'core/edit.jinja'
class JournalDeleteView(CanEditPropMixin, DeleteView):
"""
Delete a club account (for the admins)
@ -270,6 +286,7 @@ class JournalDeleteView(CanEditPropMixin, DeleteView):
else:
raise PermissionDenied
# Operation views
class OperationForm(forms.ModelForm):
@ -350,6 +367,7 @@ class OperationForm(forms.ModelForm):
self.save()
return ret
class OperationCreateView(CanCreateMixin, CreateView):
"""
Create an operation
@ -376,6 +394,7 @@ class OperationCreateView(CanCreateMixin, CreateView):
kwargs['object'] = self.journal
return kwargs
class OperationEditView(CanEditMixin, UpdateView):
"""
An edit view, working as detail for the moment
@ -391,6 +410,7 @@ class OperationEditView(CanEditMixin, UpdateView):
kwargs['object'] = self.object.journal
return kwargs
class OperationPDFView(CanViewMixin, DetailView):
"""
Display the PDF of a given operation
@ -402,11 +422,10 @@ class OperationPDFView(CanViewMixin, DetailView):
def get(self, request, *args, **kwargs):
from reportlab.pdfgen import canvas
from reportlab.lib.units import cm
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
from reportlab.platypus import Table, TableStyle
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter
from reportlab.lib.utils import ImageReader
from reportlab.graphics.shapes import Drawing
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfmetrics
@ -419,7 +438,6 @@ class OperationPDFView(CanViewMixin, DetailView):
num = self.object.number
date = self.object.date
mode = self.object.mode
cheque_number = self.object.cheque_number
club_name = self.object.journal.club_account.name
ti = self.object.journal.name
op_label = self.object.label
@ -455,7 +473,7 @@ class OperationPDFView(CanViewMixin, DetailView):
p.drawString(90, height - 100, _("Financial proof: ") + "OP%010d" % (id_op)) # Justificatif du libellé
p.drawString(90, height - 130, _("Club: %(club_name)s") % ({"club_name": club_name}))
p.drawString(90, height - 160, _("Label: %(op_label)s") % {"op_label": op_label if op_label != None else ""})
p.drawString(90, height - 160, _("Label: %(op_label)s") % {"op_label": op_label if op_label is not None else ""})
p.drawString(90, height - 190, _("Date: %(date)s") % {"date": date})
data = []
@ -516,6 +534,7 @@ class OperationPDFView(CanViewMixin, DetailView):
p.save()
return response
class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
"""
Display a statement sorted by labels
@ -532,8 +551,10 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
for sat in [None] + list(SimplifiedAccountingType.objects.order_by('label').all()):
sum = queryset.filter(accounting_type__movement_type=movement_type,
simpleaccounting_type=sat).aggregate(amount_sum=Sum('amount'))['amount_sum']
if sat: sat = sat.label
else: sat = ""
if sat:
sat = sat.label
else:
sat = ""
if sum:
total_sum += sum
statement[sat] = sum
@ -566,6 +587,7 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
kwargs['statement'] = self.big_statement()
return kwargs
class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
"""
Calculate a dictionary with operation target and sum of operations
@ -598,6 +620,7 @@ class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
kwargs['total_debit'] = self.total("DEBIT")
return kwargs
class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView):
"""
Calculate a dictionary with operation type and sum of operations
@ -624,10 +647,12 @@ class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView)
# Company views
class CompanyListView(CanViewMixin, ListView):
model = Company
template_name = 'accounting/co_list.jinja'
class CompanyCreateView(CanCreateMixin, CreateView):
"""
Create a company
@ -648,6 +673,7 @@ class CompanyEditView(CanCreateMixin, UpdateView):
template_name = 'core/edit.jinja'
success_url = reverse_lazy('accounting:co_list')
# Label views
class LabelListView(CanViewMixin, DetailView):
@ -655,6 +681,7 @@ class LabelListView(CanViewMixin, DetailView):
pk_url_kwarg = "clubaccount_id"
template_name = 'accounting/label_list.jinja'
class LabelCreateView(CanCreateMixin, CreateView): # FIXME we need to check the rights before creating the object
model = Label
form_class = modelform_factory(Label, fields=['name', 'club_account'], widgets={
@ -670,12 +697,14 @@ class LabelCreateView(CanCreateMixin, CreateView): # FIXME we need to check the
ret['club_account'] = obj.id
return ret
class LabelEditView(CanEditMixin, UpdateView):
model = Label
pk_url_kwarg = "label_id"
fields = ['name']
template_name = 'core/edit.jinja'
class LabelDeleteView(CanEditMixin, DeleteView):
model = Label
pk_url_kwarg = "label_id"
@ -684,9 +713,11 @@ class LabelDeleteView(CanEditMixin, DeleteView):
def get_success_url(self):
return self.object.get_absolute_url()
class CloseCustomerAccountForm(forms.Form):
user = AutoCompleteSelectField('users', label=_('Refound this account'), help_text=None, required=True)
class RefoundAccountView(FormView):
"""
Create a selling with the same amount than the current user money

View File

@ -29,4 +29,3 @@ from club.models import Club, Membership
admin.site.register(Club)
admin.site.register(Membership)

View File

@ -27,12 +27,13 @@ from django.core import validators
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from django.db import IntegrityError, transaction
from django.db import transaction
from django.core.urlresolvers import reverse
from django.utils import timezone
from core.models import User, MetaGroup, Group, SithFile
# Create your models here.
class Club(models.Model):
@ -153,6 +154,7 @@ class Club(models.Model):
return sub.is_subscribed
_memberships = {}
def get_membership_for(self, user):
"""
Returns the current membership the given user
@ -168,6 +170,7 @@ class Club(models.Model):
Club._memberships[self.id][user.id] = m
return m
class Membership(models.Model):
"""
The Membership class makes the connection between User and Clubs
@ -216,4 +219,3 @@ class Membership(models.Model):
def get_absolute_url(self):
return reverse('club:club_members', kwargs={'club_id': self.club.id})

View File

@ -31,6 +31,7 @@ from club.models import Club
# Create your tests here.
class ClubTest(TestCase):
def setUp(self):
call_command("populate")
@ -103,4 +104,3 @@ class ClubTest(TestCase):
"role": 10})
self.assertTrue(response.status_code == 200)
self.assertTrue("<li>Vous n&#39;avez pas la permission de faire cela</li>" in str(response.content))

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from club.views import *
@ -40,4 +40,3 @@ urlpatterns = [
url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='tools'),
url(r'^membership/(?P<membership_id>[0-9]+)/set_old$', MembershipSetOldView.as_view(), name='membership_set_old'),
]

View File

@ -23,27 +23,21 @@
#
from django import forms
from django.shortcuts import render
from django.views.generic import ListView, DetailView, TemplateView
from django.views.generic.edit import UpdateView, CreateView
from django.forms import CheckboxSelectMultiple
from django.core.exceptions import ValidationError
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext as _t
from django.conf import settings
from ajax_select.fields import AutoCompleteSelectField
from datetime import timedelta
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin
from core.views.forms import SelectDate, SelectSingle, SelectDateTime
from core.views.forms import SelectDate, SelectDateTime
from club.models import Club, Membership
from core.models import User
from sith.settings import SITH_MAXIMUM_FREE_ROLE, SITH_MAIN_BOARD_GROUP
from counter.models import Product, Selling, Counter
from sith.settings import SITH_MAXIMUM_FREE_ROLE
from counter.models import Selling, Counter
class ClubTabsMixin(TabedViewMixin):
def get_tabs_title(self):
@ -91,6 +85,7 @@ class ClubTabsMixin(TabedViewMixin):
})
return tab_list
class ClubListView(ListView):
"""
List the Clubs
@ -98,6 +93,7 @@ class ClubListView(ListView):
model = Club
template_name = 'club/club_list.jinja'
class ClubView(ClubTabsMixin, DetailView):
"""
Front page of a Club
@ -107,6 +103,7 @@ class ClubView(ClubTabsMixin, DetailView):
template_name = 'club/club_detail.jinja'
current_tab = "infos"
class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
"""
Tools page of a Club
@ -116,12 +113,14 @@ class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
template_name = 'club/club_tools.jinja'
current_tab = "tools"
class ClubMemberForm(forms.ModelForm):
"""
Form handling the members of a club
"""
error_css_class = 'error'
required_css_class = 'required'
class Meta:
model = Membership
fields = ['user', 'role', 'start_date', 'description']
@ -134,9 +133,10 @@ class ClubMemberForm(forms.ModelForm):
"""
Overloaded to return the club, and not to a Membership object that has no view
"""
ret = super(ClubMemberForm, self).save(*args, **kwargs)
super(ClubMemberForm, self).save(*args, **kwargs)
return self.instance.club
class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
"""
View of a club's members
@ -182,6 +182,7 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
else:
return self.form_invalid(form)
class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
"""
Old members of a club
@ -191,11 +192,13 @@ class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
template_name = 'club/club_old_members.jinja'
current_tab = "elderlies"
class SellingsFormBase(forms.Form):
begin_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Begin date"), required=False, widget=SelectDateTime)
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), required=False, widget=SelectDateTime)
counter = forms.ModelChoiceField(Counter.objects.order_by('name').all(), label=_("Counter"), required=False)
class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
"""
Sellings of a club
@ -235,6 +238,7 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
kwargs['form'] = form
return kwargs
class ClubSellingCSVView(ClubSellingView):
"""
Generate sellings in csv for a given period
@ -258,21 +262,25 @@ class ClubSellingCSVView(ClubSellingView):
row = [o.date, o.counter]
if o.seller:
row.append(o.seller.get_display_name())
else: row.append('')
else:
row.append('')
if o.customer:
row.append(o.customer.user.get_display_name())
else: row.append('')
else:
row.append('')
row = row + [o.label, o.quantity, o.quantity * o.unit_price,
o.get_payment_method_display()]
if o.product:
row.append(o.product.selling_price)
row.append(o.product.purchase_price)
row.append(o.product.selling_price - o.product.purchase_price)
else: row = row + ['', '', '']
else:
row = row + ['', '', '']
writer.writerow(row)
return response
class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
"""
Edit a Club's main informations (for the club's members)
@ -283,6 +291,7 @@ class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
template_name = 'core/edit.jinja'
current_tab = "edit"
class ClubEditPropView(ClubTabsMixin, CanEditPropMixin, UpdateView):
"""
Edit the properties of a Club object (for the Sith admins)
@ -293,6 +302,7 @@ class ClubEditPropView(ClubTabsMixin, CanEditPropMixin, UpdateView):
template_name = 'core/edit.jinja'
current_tab = "props"
class ClubCreateView(CanEditPropMixin, CreateView):
"""
Create a club (for the Sith admin)
@ -302,6 +312,7 @@ class ClubCreateView(CanEditPropMixin, CreateView):
fields = ['name', 'unix_name', 'parent']
template_name = 'core/edit.jinja'
class MembershipSetOldView(CanEditMixin, DetailView):
"""
Set a membership as beeing old
@ -319,6 +330,7 @@ class MembershipSetOldView(CanEditMixin, DetailView):
self.object = self.get_object()
return HttpResponseRedirect(reverse('club:club_members', args=self.args, kwargs={'club_id': self.object.club.id}))
class ClubStatView(TemplateView):
template_name = "club/stats.jinja"

View File

@ -30,5 +30,3 @@ admin.site.register(Sith)
admin.site.register(News)
admin.site.register(Weekmail)

View File

@ -25,15 +25,14 @@
from django.shortcuts import render
from django.db import models, transaction
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse_lazy, reverse
from django.core.urlresolvers import reverse
from django.conf import settings
from django.contrib.staticfiles.templatetags.staticfiles import static
from django.core.mail import EmailMultiAlternatives
from django.core.exceptions import ValidationError
from core.models import User, Preferences
from club.models import Club
import os
class Sith(models.Model):
"""A one instance class storing all the modifiable infos"""
@ -48,6 +47,7 @@ class Sith(models.Model):
def __str__(self):
return "⛩ Sith ⛩"
NEWS_TYPES = [
('NOTICE', _('Notice')),
('EVENT', _('Event')),
@ -55,6 +55,7 @@ NEWS_TYPES = [
('CALL', _('Call')),
]
class News(models.Model):
"""The news class"""
title = models.CharField(_("title"), max_length=64)
@ -81,6 +82,7 @@ class News(models.Model):
def __str__(self):
return "%s: %s" % (self.type, self.title)
class NewsDate(models.Model):
"""
A date class, useful for weekly events, or for events that just have no date
@ -95,6 +97,7 @@ class NewsDate(models.Model):
def __str__(self):
return "%s: %s - %s" % (self.news.title, self.start_date, self.end_date)
class Weekmail(models.Model):
"""
The weekmail class
@ -144,6 +147,7 @@ class Weekmail(models.Model):
def is_owned_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
class WeekmailArticle(models.Model):
weekmail = models.ForeignKey(Weekmail, related_name="articles", verbose_name=_("weekmail"), null=True)
title = models.CharField(_("title"), max_length=64)

View File

@ -28,7 +28,7 @@ from django.core.urlresolvers import reverse
from django.core.management import call_command
from core.models import User, RealGroup
from com.models import Sith
class ComTest(TestCase):
def setUp(self):
@ -56,4 +56,3 @@ class ComTest(TestCase):
r = self.client.get(reverse("core:index"))
self.assertTrue(r.status_code == 200)
self.assertTrue("""<div id="info_box">\\n <div class="markdown"><h3>INFO: <strong>Caaaataaaapuuuulte!!!!</strong></h3>""" in str(r.content))

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from com.views import *

View File

@ -22,9 +22,9 @@
#
#
from django.shortcuts import render, redirect, get_object_or_404
from django.shortcuts import redirect, get_object_or_404
from django.http import HttpResponseRedirect
from django.views.generic import ListView, DetailView, RedirectView
from django.views.generic import ListView, DetailView
from django.views.generic.edit import UpdateView, CreateView, DeleteView
from django.views.generic.detail import SingleObjectMixin
from django.utils.translation import ugettext_lazy as _
@ -49,6 +49,7 @@ from club.models import Club
sith = Sith.objects.first
class ComTabsMixin(TabedViewMixin):
def get_tabs_title(self):
return _("Communication administration")
@ -82,6 +83,7 @@ class ComTabsMixin(TabedViewMixin):
})
return tab_list
class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
model = Sith
template_name = 'core/edit.jinja'
@ -89,21 +91,25 @@ class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
def get_object(self, queryset=None):
return Sith.objects.first()
class AlertMsgEditView(ComEditView):
fields = ['alert_msg']
current_tab = "alert"
success_url = reverse_lazy('com:alert_edit')
class InfoMsgEditView(ComEditView):
fields = ['info_msg']
current_tab = "info"
success_url = reverse_lazy('com:info_edit')
class IndexEditView(ComEditView):
fields = ['index_page']
current_tab = "index"
success_url = reverse_lazy('com:index_edit')
class WeekmailDestinationEditView(ComEditView):
fields = ['weekmail_destinations']
current_tab = "weekmail_destinations"
@ -111,6 +117,7 @@ class WeekmailDestinationEditView(ComEditView):
# News
class NewsForm(forms.ModelForm):
class Meta:
model = News
@ -155,6 +162,7 @@ class NewsForm(forms.ModelForm):
end_date += timedelta(days=7)
return ret
class NewsEditView(CanEditMixin, UpdateView):
model = News
form_class = NewsForm
@ -165,15 +173,17 @@ class NewsEditView(CanEditMixin, UpdateView):
init = {}
try:
init['start_date'] = self.object.dates.order_by('id').first().start_date.strftime('%Y-%m-%d %H:%M:%S')
except: pass
except:
pass
try:
init['end_date'] = self.object.dates.order_by('id').first().end_date.strftime('%Y-%m-%d %H:%M:%S')
except: pass
except:
pass
return init
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid() and not 'preview' in request.POST.keys():
if form.is_valid() and 'preview' not in request.POST.keys():
return self.form_valid(form)
else:
return self.form_invalid(form)
@ -192,6 +202,7 @@ class NewsEditView(CanEditMixin, UpdateView):
Notification(user=u, url=reverse("com:news_detail", kwargs={'news_id': self.object.id}), type="NEWS_MODERATION").save()
return super(NewsEditView, self).form_valid(form)
class NewsCreateView(CanCreateMixin, CreateView):
model = News
form_class = NewsForm
@ -201,12 +212,13 @@ class NewsCreateView(CanCreateMixin, CreateView):
init = {'author': self.request.user}
try:
init['club'] = Club.objects.filter(id=self.request.GET['club']).first()
except: pass
except:
pass
return init
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid() and not 'preview' in request.POST.keys():
if form.is_valid() and 'preview' not in request.POST.keys():
return self.form_valid(form)
else:
self.object = form.instance
@ -224,6 +236,7 @@ class NewsCreateView(CanCreateMixin, CreateView):
Notification(user=u, url=reverse("com:news_detail", kwargs={'news_id': self.object.id}), type="NEWS_MODERATION").save()
return super(NewsCreateView, self).form_valid(form)
class NewsModerateView(CanEditMixin, SingleObjectMixin):
model = News
pk_url_kwarg = 'news_id'
@ -240,11 +253,13 @@ class NewsModerateView(CanEditMixin, SingleObjectMixin):
return redirect(self.request.GET['next'])
return redirect('com:news_admin_list')
class NewsAdminListView(CanEditMixin, ListView):
model = News
template_name = 'com/news_admin_list.jinja'
queryset = News.objects.filter(dates__end_date__gte=timezone.now()).distinct().order_by('id')
class NewsListView(CanViewMixin, ListView):
model = News
template_name = 'com/news_list.jinja'
@ -255,6 +270,7 @@ class NewsListView(CanViewMixin, ListView):
kwargs['timedelta'] = timedelta
return kwargs
class NewsDetailView(CanViewMixin, DetailView):
model = News
template_name = 'com/news_detail.jinja'
@ -262,6 +278,7 @@ class NewsDetailView(CanViewMixin, DetailView):
# Weekmail
class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
model = Weekmail
template_name = 'com/weekmail_preview.jinja'
@ -274,7 +291,8 @@ class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
if request.POST['send'] == "validate":
self.object.send()
return HttpResponseRedirect(reverse('com:weekmail') + "?qn_weekmail_send_success")
except: pass
except:
pass
return super(WeekmailEditView, self).get(request, *args, **kwargs)
def get_object(self, queryset=None):
@ -286,6 +304,7 @@ class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
kwargs['weekmail_rendered'] = self.object.render_html()
return kwargs
class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
model = Weekmail
template_name = 'com/weekmail.jinja'
@ -341,6 +360,7 @@ class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateVi
kwargs['orphans'] = WeekmailArticle.objects.filter(weekmail=None)
return kwargs
class WeekmailArticleEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
"""Edit an article"""
model = WeekmailArticle
@ -351,6 +371,7 @@ class WeekmailArticleEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, U
quick_notif_url_arg = "qn_weekmail_article_edit"
current_tab = "weekmail"
class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
"""Post an article"""
model = WeekmailArticle
@ -363,7 +384,8 @@ class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
init = {}
try:
init['club'] = Club.objects.filter(id=self.request.GET['club']).first()
except: pass
except:
pass
return init
def post(self, request, *args, **kwargs):
@ -385,14 +407,10 @@ class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
form.instance.author = self.request.user
return super(WeekmailArticleCreateView, self).form_valid(form)
class WeekmailArticleDeleteView(CanEditPropMixin, DeleteView):
"""Delete an article"""
model = WeekmailArticle
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('com:weekmail')
pk_url_kwarg = "article_id"

View File

@ -33,6 +33,7 @@ admin.site.unregister(AuthGroup)
admin.site.register(RealGroup)
admin.site.register(Page)
@admin.register(SithFile)
class SithFileAdmin(admin.ModelAdmin):
form = make_ajax_form(SithFile, {

View File

@ -23,9 +23,9 @@
#
from django.apps import AppConfig
from django.dispatch import receiver
from django.core.signals import request_started
class SithConfig(AppConfig):
name = 'core'
verbose_name = "Core app of the Sith"
@ -48,4 +48,3 @@ class SithConfig(AppConfig):
request_started.connect(clear_cached_groups, weak=False, dispatch_uid="clear_cached_groups")
request_started.connect(clear_cached_memberships, weak=False, dispatch_uid="clear_cached_memberships")
# TODO: there may be a need to add more cache clearing

View File

@ -31,16 +31,19 @@ from club.models import Club
from counter.models import Product, Counter
from accounting.models import ClubAccount, Company
def check_token(request):
return ('counter_token' in request.session.keys() and
request.session['counter_token'] and
Counter.objects.filter(token=request.session['counter_token']).exists())
class RightManagedLookupChannel(LookupChannel):
def check_auth(self, request):
if not request.user.was_subscribed and not check_token(request):
raise PermissionDenied
@register('users')
class UsersLookup(RightManagedLookupChannel):
model = User
@ -54,6 +57,7 @@ class UsersLookup(RightManagedLookupChannel):
def format_item_display(self, item):
return item.get_display_name()
@register('groups')
class GroupsLookup(RightManagedLookupChannel):
model = Group
@ -67,6 +71,7 @@ class GroupsLookup(RightManagedLookupChannel):
def format_item_display(self, item):
return item.name
@register('clubs')
class ClubLookup(RightManagedLookupChannel):
model = Club
@ -80,6 +85,7 @@ class ClubLookup(RightManagedLookupChannel):
def format_item_display(self, item):
return item.name
@register('counters')
class CountersLookup(RightManagedLookupChannel):
model = Counter
@ -90,6 +96,7 @@ class CountersLookup(RightManagedLookupChannel):
def format_item_display(self, item):
return item.name
@register('products')
class ProductsLookup(RightManagedLookupChannel):
model = Product
@ -101,6 +108,7 @@ class ProductsLookup(RightManagedLookupChannel):
def format_item_display(self, item):
return "%s (%s)" % (item.name, item.code)
@register('files')
class SithFileLookup(RightManagedLookupChannel):
model = SithFile
@ -108,6 +116,7 @@ class SithFileLookup(RightManagedLookupChannel):
def get_query(self, q, request):
return self.model.objects.filter(name__icontains=q)[:50]
@register('club_accounts')
class ClubAccountLookup(RightManagedLookupChannel):
model = ClubAccount
@ -118,6 +127,7 @@ class ClubAccountLookup(RightManagedLookupChannel):
def format_item_display(self, item):
return item.name
@register('companies')
class CompaniesLookup(RightManagedLookupChannel):
model = Company

View File

@ -21,4 +21,3 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

View File

@ -44,7 +44,6 @@ class Command(BaseCommand):
args['precision'] = settings.SASS_PRECISION
return sass.compile(**args)
def is_compilable(self, file, ext_list):
path, ext = os.path.splitext(file)
return ext in ext_list

View File

@ -26,7 +26,7 @@ import os
from datetime import date, datetime
from io import StringIO, BytesIO
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import BaseCommand
from django.core.management import call_command
from django.conf import settings
from django.db import connection
@ -42,7 +42,7 @@ from subscription.models import Subscription
from counter.models import Customer, ProductType, Product, Counter
from com.models import Sith, Weekmail
from election.models import Election, Role, Candidature, ElectionList
from forum.models import Forum, ForumMessage, ForumTopic
from forum.models import Forum, ForumTopic
class Command(BaseCommand):
@ -258,7 +258,7 @@ Welcome to the wiki page!
PageRev(page=p, title="README", author=skia, content=rm.read()).save()
# Subscription
## Root
# Root
s = Subscription(member=User.objects.filter(pk=root.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
s.subscription_start = s.compute_start()
@ -266,7 +266,7 @@ Welcome to the wiki page!
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
start=s.subscription_start)
s.save()
## Skia
# Skia
s = Subscription(member=User.objects.filter(pk=skia.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
s.subscription_start = s.compute_start()
@ -274,7 +274,7 @@ Welcome to the wiki page!
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
start=s.subscription_start)
s.save()
## Counter admin
# Counter admin
s = Subscription(member=User.objects.filter(pk=counter.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
s.subscription_start = s.compute_start()
@ -282,7 +282,7 @@ Welcome to the wiki page!
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
start=s.subscription_start)
s.save()
## Comptable
# Comptable
s = Subscription(member=User.objects.filter(pk=comptable.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
s.subscription_start = s.compute_start()
@ -290,7 +290,7 @@ Welcome to the wiki page!
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
start=s.subscription_start)
s.save()
## Richard
# Richard
s = Subscription(member=User.objects.filter(pk=r.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
s.subscription_start = s.compute_start()
@ -298,7 +298,7 @@ Welcome to the wiki page!
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
start=s.subscription_start)
s.save()
## User
# User
s = Subscription(member=User.objects.filter(pk=subscriber.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
s.subscription_start = s.compute_start()
@ -306,7 +306,7 @@ Welcome to the wiki page!
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
start=s.subscription_start)
s.save()
## Old subscriber
# Old subscriber
s = Subscription(member=User.objects.filter(pk=old_subscriber.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
s.subscription_start = s.compute_start(datetime(year=2012, month=9, day=4))
@ -456,7 +456,7 @@ Welcome to the wiki page!
krophil_profile.save()
krophil.profile_pict = krophil_profile
krophil.save()
## Adding subscription for sli
# Adding subscription for sli
s = Subscription(member=User.objects.filter(pk=sli.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
s.subscription_start = s.compute_start()
@ -464,7 +464,7 @@ Welcome to the wiki page!
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
start=s.subscription_start)
s.save()
## Adding subscription for Krophil
# Adding subscription for Krophil
s = Subscription(member=User.objects.filter(pk=krophil.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
s.subscription_start = s.compute_start()
@ -519,4 +519,3 @@ Welcome to the wiki page!
various.save()
Forum(name="Promos", description="Réservé aux Promos", parent=various).save()
ForumTopic(forum=hall)

View File

@ -23,9 +23,8 @@
#
import os
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import BaseCommand
from django.core.management import call_command
from django.conf import settings
class Command(BaseCommand):

View File

@ -24,7 +24,7 @@
import re
from mistune import Renderer, InlineGrammar, InlineLexer, Markdown, escape, escape_link
from django.core.urlresolvers import reverse_lazy, reverse
from django.core.urlresolvers import reverse
class SithRenderer(Renderer):
@ -54,13 +54,16 @@ class SithRenderer(Renderer):
src = original_src
else:
width = m.group(1)
if not width.endswith('%'): width += "px"
if not width.endswith('%'):
width += "px"
style = "width: %s; " % width
try:
height = m.group(3)
if not height.endswith('%'): height += "px"
if not height.endswith('%'):
height += "px"
style += "height: %s; " % height
except: pass
except:
pass
else:
params = None
src = original_src
@ -77,6 +80,7 @@ class SithRenderer(Renderer):
return '%s />' % html
return '%s>' % html
class SithInlineGrammar(InlineGrammar):
double_emphasis = re.compile(
r'^\*{2}([\s\S]+?)\*{2}(?!\*)' # **word**
@ -94,6 +98,7 @@ class SithInlineGrammar(InlineGrammar):
r'^<sub>([\s\S]+?)</sub>' # <sub>text</sub>
)
class SithInlineLexer(InlineLexer):
grammar_class = SithInlineGrammar
@ -166,7 +171,8 @@ class SithInlineLexer(InlineLexer):
match = page.search(link)
page = match.group(1) or ""
link = reverse('core:page', kwargs={'page_name': page})
except: pass
except:
pass
try: # Add file:// support for links
file_link = re.compile(
r'^file://(\d*)/?(\S*)?' # file://4000/download
@ -175,9 +181,11 @@ class SithInlineLexer(InlineLexer):
id = match.group(1)
suffix = match.group(2) or ""
link = reverse('core:file_detail', kwargs={'file_id': id}) + suffix
except: pass
except:
pass
return super(SithInlineLexer, self)._process_link(m, link, title)
renderer = SithRenderer(escape=True)
inline = SithInlineLexer(renderer)
@ -222,4 +230,3 @@ Petit *test* _sur_ ^une^ **seule** ^ligne pour voir^
"""
print(markdown(text))

View File

@ -52,4 +52,3 @@ class AuthenticationMiddleware(DjangoAuthenticationMiddleware):
"'account.middleware.AuthenticationMiddleware'."
)
request.user = SimpleLazyObject(lambda: get_cached_user(request))

View File

@ -44,14 +44,17 @@ from datetime import datetime, timedelta, date
import unicodedata
class RealGroupManager(AuthGroupManager):
def get_queryset(self):
return super(RealGroupManager, self).get_queryset().filter(is_meta=False)
class MetaGroupManager(AuthGroupManager):
def get_queryset(self):
return super(MetaGroupManager, self).get_queryset().filter(is_meta=True)
class Group(AuthGroup):
is_meta = models.BooleanField(
_('meta group status'),
@ -69,8 +72,10 @@ class Group(AuthGroup):
"""
return reverse('core:group_list')
class MetaGroup(Group):
objects = MetaGroupManager()
class Meta:
proxy = True
@ -78,11 +83,14 @@ class MetaGroup(Group):
super(MetaGroup, self).__init__(*args, **kwargs)
self.is_meta = True
class RealGroup(Group):
objects = RealGroupManager()
class Meta:
proxy = True
def validate_promo(value):
start_year = settings.SITH_SCHOOL_START_YEAR
delta = (date.today() + timedelta(days=180)).year - start_year
@ -92,6 +100,7 @@ def validate_promo(value):
params={'value': value, 'end': delta},
)
class User(AbstractBaseUser):
"""
Defines the base user class, useable in every app
@ -226,6 +235,7 @@ class User(AbstractBaseUser):
_club_memberships = {}
_group_names = {}
_group_ids = {}
def is_in_group(self, group_name):
"""If the user is in the group passed in argument (as string or by id)"""
group_id = 0
@ -401,7 +411,7 @@ class User(AbstractBaseUser):
Returns the generated username
"""
def remove_accents(data):
return ''.join(x for x in unicodedata.normalize('NFKD', data) if \
return ''.join(x for x in unicodedata.normalize('NFKD', data) if
unicodedata.category(x)[0] == 'L').lower()
user_name = remove_accents(self.first_name[0] + self.last_name).encode('ascii', 'ignore').decode('utf-8')
un_set = [u.username for u in User.objects.all()]
@ -489,6 +499,7 @@ class User(AbstractBaseUser):
infos.save()
return infos
class AnonymousUser(AuthAnonymousUser):
def __init__(self, request):
super(AnonymousUser, self).__init__()
@ -557,6 +568,7 @@ class AnonymousUser(AuthAnonymousUser):
def get_display_name(self):
return _("Visitor")
class Preferences(models.Model):
user = models.OneToOneField(User, related_name="preferences")
receive_weekmail = models.BooleanField(
@ -576,15 +588,19 @@ class Preferences(models.Model):
def get_absolute_url(self):
return self.user.get_absolute_url()
def get_directory(instance, filename):
return '.{0}/{1}'.format(instance.get_parent_path(), filename)
def get_compressed_directory(instance, filename):
return '.{0}/compressed/{1}'.format(instance.get_parent_path(), filename)
def get_thumbnail_directory(instance, filename):
return '.{0}/thumbnail/{1}'.format(instance.get_parent_path(), filename)
class SithFile(models.Model):
name = models.CharField(_('file name'), max_length=256, blank=False)
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True)
@ -763,18 +779,22 @@ class SithFile(models.Model):
def __str__(self):
return self.get_parent_path() + "/" + self.name
class LockError(Exception):
"""There was a lock error on the object"""
pass
class AlreadyLocked(LockError):
"""The object is already locked"""
pass
class NotLocked(LockError):
"""The object is not locked"""
pass
class Page(models.Model):
"""
The page class to build a Wiki
@ -792,8 +812,7 @@ class Page(models.Model):
r'^[A-z.+-]+$',
_('Enter a valid page name. This value may contain only '
'unaccented letters, numbers ' 'and ./+/-/_ characters.')
),
],
), ],
blank=False)
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True, on_delete=models.SET_NULL)
# Attention: this field may not be valid until you call save(). It's made for fast query, but don't rely on it when
@ -854,7 +873,8 @@ class Page(models.Model):
Performs some needed actions before and after saving a page in database
"""
locked = kwargs.pop('force_lock', False)
if not locked: locked = self.is_locked()
if not locked:
locked = self.is_locked()
if not locked:
raise NotLocked("The page is not locked and thus can not be saved")
self.full_clean()
@ -1003,6 +1023,7 @@ class PageRev(models.Model):
# Don't forget to unlock, otherwise, people will have to wait for the page's timeout
self.page.unset_lock()
class Notification(models.Model):
user = models.ForeignKey(User, related_name='notifications')
url = models.CharField(_("url"), max_length=255)
@ -1015,4 +1036,3 @@ class Notification(models.Model):
if self.param:
return self.get_type_display() % self.param
return self.get_type_display()

View File

@ -27,17 +27,18 @@ from django import template
from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe
from core.scss.processor import ScssProcessor
from django.utils.html import escape
from core.markdown import markdown as md
register = template.Library()
@register.filter(is_safe=False)
@stringfilter
def markdown(text):
return mark_safe("<div class=\"markdown\">%s</div>" % md(text))
@register.filter()
@stringfilter
def datetime_format_python_to_PHP(python_format_string):
@ -51,6 +52,7 @@ def datetime_format_python_to_PHP(python_format_string):
php_format_string = php_format_string.replace(py, php)
return php_format_string
@register.simple_tag()
def scss(path):
"""

View File

@ -24,7 +24,6 @@
from django.test import Client, TestCase
from django.core.urlresolvers import reverse
from django.contrib.auth.models import Group
from django.core.management import call_command
from core.models import User, Group, Page
@ -34,6 +33,7 @@ to run these tests :
python3 manage.py test
"""
class UserRegistrationTest(TestCase):
def setUp(self):
try:
@ -185,6 +185,7 @@ class UserRegistrationTest(TestCase):
self.assertTrue(response.status_code == 200)
self.assertTrue("""<p>Votre nom d\\'utilisateur et votre mot de passe ne correspondent pas. Merci de r\\xc3\\xa9essayer.</p>""" in str(response.content))
class PageHandlingTest(TestCase):
def setUp(self):
try:
@ -260,7 +261,7 @@ class PageHandlingTest(TestCase):
'name': 'guy',
'owner_group': '1',
})
r = self.client.post(reverse('core:page_edit', kwargs={'page_name': 'guy'}), {
self.client.post(reverse('core:page_edit', kwargs={'page_name': 'guy'}), {
'title': 'Bibou',
'content':
'''Guy *bibou*
@ -285,6 +286,7 @@ http://git.an
# - changing a page's parent --> check that page's children's full_name
# - changing the different groups of the page
class FileHandlingTest(TestCase):
def setUp(self):
try:
@ -310,4 +312,3 @@ class FileHandlingTest(TestCase):
response = self.client.get(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}))
self.assertTrue(response.status_code == 200)
self.assertTrue("ls</a>" in str(response.content))

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from core.views import *

View File

@ -27,9 +27,10 @@ import re
# Image utils
from io import BytesIO
from datetime import datetime, timezone, date
from datetime import date
from PIL import Image, ExifTags
from PIL import ExifTags
# from exceptions import IOError
import PIL
from django.conf import settings
@ -59,6 +60,7 @@ def get_start_of_semester(d=date.today()):
else:
return start2
def get_semester(d=date.today()):
start = get_start_of_semester(d)
if start.month <= 6:
@ -66,6 +68,7 @@ def get_semester(d=date.today()):
else:
return "A" + str(start.year)[-2:]
def scale_dimension(width, height, long_edge):
if width > height:
ratio = long_edge * 1. / width
@ -73,6 +76,7 @@ def scale_dimension(width, height, long_edge):
ratio = long_edge * 1. / height
return int(width * ratio), int(height * ratio)
def resize_image(im, edge, format):
(w, h) = im.size
(width, height) = scale_dimension(w, h, long_edge=edge)
@ -85,9 +89,11 @@ def resize_image(im, edge, format):
im.save(fp=content, format=format.upper(), quality=90, optimize=True, progressive=True)
return ContentFile(content.getvalue())
def exif_auto_rotate(image):
for orientation in ExifTags.TAGS.keys():
if ExifTags.TAGS[orientation]=='Orientation' : break
if ExifTags.TAGS[orientation] == 'Orientation':
break
exif = dict(image._getexif().items())
if exif[orientation] == 3:
@ -99,6 +105,7 @@ def exif_auto_rotate(image):
return image
def doku_to_markdown(text):
"""This is a quite correct doku translator"""
text = re.sub(r'([^:]|^)\/\/(.*?)\/\/', r'*\2*', text) # Italic (prevents protocol:// conflict)
@ -164,12 +171,14 @@ def doku_to_markdown(text):
quote_level -= 1
final_newline = True
new_text.append("> " * final_quote_level + line) # Finally append the line
if final_newline: new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
if final_newline:
new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
else:
new_text.append(line)
return "\n".join(new_text)
def bbcode_to_markdown(text):
"""This is a very basic BBcode translator"""
text = re.sub(r'\[b\](.*?)\[\/b\]', r'**\1**', text, flags=re.DOTALL) # Bold
@ -205,9 +214,9 @@ def bbcode_to_markdown(text):
quote_level -= 1
final_newline = True
new_text.append("> " * final_quote_level + line) # Finally append the line
if final_newline: new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
if final_newline:
new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
else:
new_text.append(line)
return "\n".join(new_text)

View File

@ -23,29 +23,28 @@
#
# This file contains all the views that concern the page model
from django.shortcuts import render, redirect, get_object_or_404
from django.shortcuts import redirect
from django.views.generic import ListView, DetailView, TemplateView
from django.views.generic.edit import UpdateView, CreateView, FormMixin, DeleteView
from django.views.generic.edit import UpdateView, FormMixin, DeleteView
from django.views.generic.detail import SingleObjectMixin
from django.contrib.auth.decorators import login_required, permission_required
from django.forms.models import modelform_factory
from django.forms import CheckboxSelectMultiple
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.http import HttpResponse
from django.core.servers.basehttp import FileWrapper
from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
from django.core.exceptions import PermissionDenied
from django import forms
import os
from ajax_select import make_ajax_form, make_ajax_field
from ajax_select import make_ajax_field
from core.models import SithFile, RealGroup, Notification
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, can_view, not_found
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, can_view, not_found
from counter.models import Counter
def send_file(request, file_id, file_class=SithFile, file_attr="file"):
"""
Send a file through Django without loading the whole file into
@ -70,6 +69,7 @@ def send_file(request, file_id, file_class=SithFile, file_attr="file"):
response['Content-Disposition'] = ('inline; filename="%s"' % f.name).encode('utf-8')
return response
class AddFilesForm(forms.Form):
folder_name = forms.CharField(label=_("Add a new folder"), max_length=30, required=False)
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}), label=_("Files"),
@ -100,6 +100,7 @@ class AddFilesForm(forms.Form):
if not u.notifications.filter(type="FILE_MODERATION", viewed=False).exists():
Notification(user=u, url=reverse("core:file_moderation"), type="FILE_MODERATION").save()
class FileListView(ListView):
template_name = 'core/file_list.jinja'
context_object_name = "file_list"
@ -114,6 +115,7 @@ class FileListView(ListView):
kwargs['popup'] = 'popup'
return kwargs
class FileEditView(CanEditMixin, UpdateView):
model = SithFile
pk_url_kwarg = "file_id"
@ -138,6 +140,7 @@ class FileEditView(CanEditMixin, UpdateView):
kwargs['popup'] = 'popup'
return kwargs
class FileEditPropForm(forms.ModelForm):
class Meta:
model = SithFile
@ -147,6 +150,7 @@ class FileEditPropForm(forms.ModelForm):
view_groups = make_ajax_field(SithFile, 'view_groups', 'groups', help_text="", label=_("view group"))
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
class FileEditPropView(CanEditPropMixin, UpdateView):
model = SithFile
pk_url_kwarg = "file_id"
@ -175,6 +179,7 @@ class FileEditPropView(CanEditPropMixin, UpdateView):
kwargs['popup'] = 'popup'
return kwargs
class FileView(CanViewMixin, DetailView, FormMixin):
"""This class handle the upload of new files into a folder"""
model = SithFile
@ -237,6 +242,7 @@ class FileView(CanViewMixin, DetailView, FormMixin):
kwargs['clipboard'] = SithFile.objects.filter(id__in=self.request.session['clipboard'])
return kwargs
class FileDeleteView(CanEditPropMixin, DeleteView):
model = SithFile
pk_url_kwarg = "file_id"
@ -258,6 +264,7 @@ class FileDeleteView(CanEditPropMixin, DeleteView):
kwargs['popup'] = 'popup'
return kwargs
class FileModerationView(TemplateView):
template_name = "core/file_moderation.jinja"
@ -266,6 +273,7 @@ class FileModerationView(TemplateView):
kwargs['files'] = SithFile.objects.filter(is_moderated=False)[:100]
return kwargs
class FileModerateView(CanEditPropMixin, SingleObjectMixin):
model = SithFile
pk_url_kwarg = "file_id"
@ -278,4 +286,3 @@ class FileModerateView(CanEditPropMixin, SingleObjectMixin):
if 'next' in self.request.GET.keys():
return redirect(self.request.GET['next'])
return redirect('core:file_moderation')

View File

@ -22,22 +22,24 @@
#
#
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, UserChangeForm
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django import forms
from django.conf import settings
from django.db import transaction
from django.core.exceptions import ValidationError
from django.contrib.auth import logout, login, authenticate
from django.forms import CheckboxSelectMultiple, Select, DateInput, TextInput, DateTimeInput, Textarea
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
from ajax_select.fields import AutoCompleteSelectField
import logging
import re
from core.models import User, Page, RealGroup, SithFile
from core.models import User, Page, SithFile
from core.utils import resize_image
from io import BytesIO
from PIL import Image
# Widgets
@ -50,6 +52,7 @@ class SelectSingle(Select):
attrs = {'class': "select_single"}
return super(SelectSingle, self).render(name, value, attrs)
class SelectMultiple(Select):
def render(self, name, value, attrs=None):
if attrs:
@ -58,6 +61,7 @@ class SelectMultiple(Select):
attrs = {'class': "select_multiple"}
return super(SelectMultiple, self).render(name, value, attrs)
class SelectDateTime(DateTimeInput):
def render(self, name, value, attrs=None):
if attrs:
@ -66,6 +70,7 @@ class SelectDateTime(DateTimeInput):
attrs = {'class': "select_datetime"}
return super(SelectDateTime, self).render(name, value, attrs)
class SelectDate(DateInput):
def render(self, name, value, attrs=None):
if attrs:
@ -74,6 +79,7 @@ class SelectDate(DateInput):
attrs = {'class': "select_date"}
return super(SelectDate, self).render(name, value, attrs)
class MarkdownInput(Textarea):
def render(self, name, value, attrs=None):
output = '<p><a href="%(syntax_url)s">%(help_text)s</a></p>'\
@ -84,6 +90,7 @@ class MarkdownInput(Textarea):
}
return output
class SelectFile(TextInput):
def render(self, name, value, attrs=None):
if attrs:
@ -98,6 +105,7 @@ class SelectFile(TextInput):
output += '<span name="' + name + '" class="choose_file_button">' + ugettext("Choose file") + '</span>'
return output
class SelectUser(TextInput):
def render(self, name, value, attrs=None):
if attrs:
@ -114,6 +122,7 @@ class SelectUser(TextInput):
# Forms
class LoginForm(AuthenticationForm):
def __init__(self, *arg, **kwargs):
if 'data' in kwargs.keys():
@ -128,14 +137,17 @@ class LoginForm(AuthenticationForm):
else:
user = User.objects.filter(username=data['username']).first()
data['username'] = user.username
except: pass
except:
pass
kwargs['data'] = data
super(LoginForm, self).__init__(*arg, **kwargs)
self.fields['username'].label = _("Username, email, or account number")
class RegisteringForm(UserCreationForm):
error_css_class = 'error'
required_css_class = 'required'
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
@ -148,9 +160,6 @@ class RegisteringForm(UserCreationForm):
user.save()
return user
from core.utils import resize_image
from io import BytesIO
from PIL import Image
class UserProfileForm(forms.ModelForm):
"""
@ -223,9 +232,11 @@ class UserProfileForm(forms.ModelForm):
{'file_name': f, 'msg': _("Bad image format, only jpeg, png, and gif are accepted")})
self._post_clean()
class UserPropForm(forms.ModelForm):
error_css_class = 'error'
required_css_class = 'required'
class Meta:
model = User
fields = ['groups']
@ -236,13 +247,16 @@ class UserPropForm(forms.ModelForm):
'groups': CheckboxSelectMultiple,
}
class UserGodfathersForm(forms.Form):
type = forms.ChoiceField(choices=[('godfather', _("Godfather")), ('godchild', _("Godchild"))], label=_("Add"))
user = AutoCompleteSelectField('users', required=True, label=_("Select user"), help_text=None)
class PagePropForm(forms.ModelForm):
error_css_class = 'error'
required_css_class = 'required'
class Meta:
model = Page
fields = ['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ]
@ -255,4 +269,3 @@ class PagePropForm(forms.ModelForm):
super(PagePropForm, self).__init__(*arg, **kwargs)
self.fields['edit_groups'].required = False
self.fields['view_groups'].required = False

View File

@ -29,6 +29,7 @@ from django.core.urlresolvers import reverse_lazy
from core.models import RealGroup
from core.views import CanEditMixin
class GroupListView(CanEditMixin, ListView):
"""
Displays the group list
@ -36,17 +37,20 @@ class GroupListView(CanEditMixin, ListView):
model = RealGroup
template_name = "core/group_list.jinja"
class GroupEditView(CanEditMixin, UpdateView):
model = RealGroup
pk_url_kwarg = "group_id"
template_name = "core/group_edit.jinja"
fields = ['name', 'description']
class GroupCreateView(CanEditMixin, CreateView):
model = RealGroup
template_name = "core/group_edit.jinja"
fields = ['name', 'description']
class GroupDeleteView(CanEditMixin, DeleteView):
model = RealGroup
pk_url_kwarg = "group_id"

View File

@ -23,23 +23,22 @@
#
# This file contains all the views that concern the page model
from django.shortcuts import render, redirect, get_object_or_404
from django.core.urlresolvers import reverse_lazy
from django.views.generic import ListView, DetailView
from django.views.generic.edit import UpdateView, CreateView, DeleteView
from django.contrib.auth.decorators import login_required, permission_required
from django.utils.decorators import method_decorator
from django.forms.models import modelform_factory
from django.forms import CheckboxSelectMultiple, modelform_factory
from django.forms import CheckboxSelectMultiple
from core.models import Page, PageRev, LockError
from core.views.forms import PagePropForm, MarkdownInput
from core.views.forms import MarkdownInput
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
class PageListView(CanViewMixin, ListView):
model = Page
template_name = 'core/page_list.jinja'
class PageView(CanViewMixin, DetailView):
model = Page
template_name = 'core/page_detail.jinja'
@ -54,6 +53,7 @@ class PageView(CanViewMixin, DetailView):
context['new_page'] = self.kwargs['page_name']
return context
class PageHistView(CanViewMixin, DetailView):
model = Page
template_name = 'core/page_hist.jinja'
@ -62,6 +62,7 @@ class PageHistView(CanViewMixin, DetailView):
self.page = Page.get_page_by_full_name(self.kwargs['page_name'])
return self.page
class PageRevView(CanViewMixin, DetailView):
model = Page
template_name = 'core/page_detail.jinja'
@ -84,6 +85,7 @@ class PageRevView(CanViewMixin, DetailView):
context['new_page'] = self.kwargs['page_name']
return context
class PageCreateView(CanCreateMixin, CreateView):
model = Page
form_class = modelform_factory(Page,
@ -115,6 +117,7 @@ class PageCreateView(CanCreateMixin, CreateView):
ret = super(PageCreateView, self).form_valid(form)
return ret
class PagePropView(CanEditPropMixin, UpdateView):
model = Page
form_class = modelform_factory(Page,
@ -145,6 +148,7 @@ class PagePropView(CanEditPropMixin, UpdateView):
raise e
return self.page
class PageEditView(CanEditMixin, UpdateView):
model = PageRev
form_class = modelform_factory(model=PageRev, fields=['title', 'content', ], widgets={'content': MarkdownInput})

View File

@ -22,17 +22,13 @@
#
#
from django.shortcuts import render, redirect, get_object_or_404
from django.db import models
from django.shortcuts import render, redirect
from django.http import JsonResponse
from django.core import serializers
from django.db.models import Q
from django.contrib.auth.decorators import login_required
from django.views.generic import ListView, TemplateView
import os
import json
from itertools import chain
from haystack.query import SearchQuerySet
@ -40,9 +36,11 @@ from core.models import User, Notification
from core.utils import doku_to_markdown, bbcode_to_markdown
from club.models import Club
def index(request, context=None):
return render(request, "core/index.jinja")
class NotificationList(ListView):
model = Notification
template_name = "core/notification_list.jinja"
@ -52,6 +50,7 @@ class NotificationList(ListView):
self.request.user.notifications.update(viewed=True)
return self.request.user.notifications.order_by('-id')[:20]
def notification(request, notif_id):
notif = Notification.objects.filter(id=notif_id).first()
if notif:
@ -60,10 +59,12 @@ def notification(request, notif_id):
return redirect(notif.url)
return redirect("/")
def search_user(query, as_json=False):
res = SearchQuerySet().models(User).filter(text=query).filter_or(text__contains=query)[:20]
return [r.object for r in res]
def search_club(query, as_json=False):
clubs = []
if query:
@ -75,6 +76,7 @@ def search_club(query, as_json=False):
clubs = list(clubs)
return clubs
@login_required
def search_view(request):
result = {
@ -83,6 +85,7 @@ def search_view(request):
}
return render(request, "core/search.jinja", context={'result': result})
@login_required
def search_user_json(request):
result = {
@ -90,6 +93,7 @@ def search_user_json(request):
}
return JsonResponse(result)
@login_required
def search_json(request):
result = {
@ -98,6 +102,7 @@ def search_json(request):
}
return JsonResponse(result)
class ToMarkdownView(TemplateView):
template_name = "core/to_markdown.jinja"
@ -119,4 +124,3 @@ class ToMarkdownView(TemplateView):
kwargs['text'] = ""
kwargs['text_md'] = ""
return kwargs

View File

@ -24,30 +24,29 @@
# This file contains all the views that concern the user model
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth import logout as auth_logout, views
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, ObjectDoesNotExist, ValidationError
from django.core.exceptions import PermissionDenied, ValidationError
from django.http import Http404
from django.views.generic.edit import UpdateView
from django.views.generic import ListView, DetailView, TemplateView, DeleteView
from django.views.generic import ListView, DetailView, TemplateView
from django.forms.models import modelform_factory
from django.forms import CheckboxSelectMultiple
from django.template.response import TemplateResponse
from django.conf import settings
from django.views.generic.dates import YearMixin, MonthMixin
from django.utils import timezone
from datetime import timedelta, datetime, date
from datetime import timedelta, date
import logging
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, QuickNotifMixin
from core.views.forms import RegisteringForm, UserPropForm, UserProfileForm, LoginForm, UserGodfathersForm
from core.views.forms import RegisteringForm, UserProfileForm, LoginForm, UserGodfathersForm
from core.models import User, SithFile, Preferences
from club.models import Club
from subscription.models import Subscription
from trombi.views import UserTrombiForm
def login(request):
"""
The login view
@ -56,24 +55,28 @@ def login(request):
"""
return views.login(request, template_name="core/login.jinja", authentication_form=LoginForm)
def logout(request):
"""
The logout view
"""
return views.logout_then_login(request)
def password_change(request):
"""
Allows a user to change its password
"""
return views.password_change(request, template_name="core/password_change.jinja", post_change_redirect=reverse("core:password_change_done"))
def password_change_done(request):
"""
Allows a user to change its password
"""
return views.password_change_done(request, template_name="core/password_change_done.jinja")
def password_root_change(request, user_id):
"""
Allows a root user to change someone's password
@ -92,6 +95,7 @@ def password_root_change(request, user_id):
form = views.SetPasswordForm(user=user)
return TemplateResponse(request, "core/password_change.jinja", {'form': form, 'target': user})
def password_reset(request):
"""
Allows someone to enter an email adresse for resetting password
@ -102,12 +106,14 @@ def password_reset(request):
post_reset_redirect="core:password_reset_done",
)
def password_reset_done(request):
"""
Confirm that the reset email has been sent
"""
return views.password_reset_done(request, template_name="core/password_reset_done.jinja")
def password_reset_confirm(request, uidb64=None, token=None):
"""
Provide a reset password formular
@ -117,6 +123,7 @@ def password_reset_confirm(request, uidb64=None, token=None):
template_name="core/password_reset_confirm.jinja",
)
def password_reset_complete(request):
"""
Confirm the password has sucessfully been reset
@ -125,6 +132,7 @@ def password_reset_complete(request):
template_name="core/password_reset_complete.jinja",
)
def register(request):
context = {}
if request.method == 'POST':
@ -143,6 +151,7 @@ def register(request):
context['form'] = form.as_p()
return render(request, "core/register.jinja", context)
class UserTabsMixin(TabedViewMixin):
def get_tabs_title(self):
return self.object.get_display_name()
@ -208,9 +217,11 @@ class UserTabsMixin(TabedViewMixin):
'slug': 'account',
'name': _("Account") + " (%s €)" % self.object.customer.amount,
})
except: pass
except:
pass
return tab_list
class UserView(UserTabsMixin, CanViewMixin, DetailView):
"""
Display a user's profile
@ -236,6 +247,7 @@ def DeleteUserGodfathers(request, user_id, godfather_id, is_father):
raise PermissionDenied
return redirect('core:user_godfathers', user_id=user_id)
class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
"""
Display a user's pictures
@ -246,6 +258,7 @@ class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
template_name = "core/user_pictures.jinja"
current_tab = 'pictures'
class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
"""
Display a user's godfathers
@ -277,6 +290,7 @@ class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
kwargs['form'] = UserGodfathersForm()
return kwargs
class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
"""
Display a user's stats
@ -303,7 +317,7 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
def get_context_data(self, **kwargs):
kwargs = super(UserStatsView, self).get_context_data(**kwargs)
from counter.models import Counter, Product, Selling
from counter.models import Counter
from django.db.models import Sum
foyer = Counter.objects.filter(name="Foyer").first()
mde = Counter.objects.filter(name="MDE").first()
@ -323,6 +337,7 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
product_sum=Sum('quantity')).exclude(product_sum=None).order_by('-product_sum').all()[:10]
return kwargs
class UserMiniView(CanViewMixin, DetailView):
"""
Display a user's profile
@ -332,6 +347,7 @@ class UserMiniView(CanViewMixin, DetailView):
context_object_name = "profile"
template_name = "core/user_mini.jinja"
class UserListView(ListView, CanEditPropMixin):
"""
Displays the user list
@ -339,6 +355,7 @@ class UserListView(ListView, CanEditPropMixin):
model = User
template_name = "core/user_list.jinja"
class UserUploadProfilePictView(CanEditMixin, DetailView):
"""
Handle the upload of the profile picture taken with webcam in navigator
@ -367,6 +384,7 @@ class UserUploadProfilePictView(CanEditMixin, DetailView):
self.object.save()
return redirect("core:user_edit", user_id=self.object.id)
class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
"""
Edit a user's profile
@ -412,6 +430,7 @@ class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
kwargs['form'] = self.form
return kwargs
class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
"""
Display the user's club(s)
@ -422,6 +441,7 @@ class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
template_name = "core/user_clubs.jinja"
current_tab = "clubs"
class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
"""
Edit a user's preferences
@ -453,6 +473,7 @@ class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
kwargs['trombi_form'] = UserTrombiForm()
return kwargs
class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
"""
Edit a user's groups
@ -465,6 +486,7 @@ class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
context_object_name = "profile"
current_tab = "groups"
class UserToolsView(QuickNotifMixin, UserTabsMixin, TemplateView):
"""
Displays the logged user's tools
@ -481,6 +503,7 @@ class UserToolsView(QuickNotifMixin, UserTabsMixin, TemplateView):
kwargs['object'] = self.request.user
return kwargs
class UserAccountBase(UserTabsMixin, DetailView):
"""
Base class for UserAccount
@ -498,6 +521,7 @@ class UserAccountBase(UserTabsMixin, DetailView):
return res
raise PermissionDenied
class UserAccountView(UserAccountBase):
"""
Display a user's account
@ -551,6 +575,7 @@ class UserAccountView(UserAccountBase):
print(repr(e))
return kwargs
class UserAccountDetailView(UserAccountBase, YearMixin, MonthMixin):
"""
Display a user's account for month
@ -568,4 +593,3 @@ class UserAccountDetailView(UserAccountBase, YearMixin, MonthMixin):
pass
kwargs['tab'] = "account"
return kwargs

View File

@ -36,4 +36,3 @@ admin.site.register(Selling)
admin.site.register(Permanency)
admin.site.register(CashRegisterSummary)
admin.site.register(Eticket)

View File

@ -22,13 +22,12 @@
#
#
from django.db import models, DataError
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.conf import settings
from django.core.urlresolvers import reverse
from django.forms import ValidationError
from django.contrib.sites.shortcuts import get_current_site
from django.utils.functional import cached_property
from datetime import timedelta, date
@ -43,6 +42,7 @@ from accounting.models import CurrencyField
from core.models import Group, User, Notification
from subscription.models import Subscription
class Customer(models.Model):
"""
This class extends a user to make a customer. It adds some basic customers informations, such as the accound ID, and
@ -118,6 +118,7 @@ class ProductType(models.Model):
def get_absolute_url(self):
return reverse('counter:producttype_list')
class Product(models.Model):
"""
This describes a product, with all its related informations
@ -156,6 +157,7 @@ class Product(models.Model):
def get_absolute_url(self):
return reverse('counter:product_list')
class Counter(models.Model):
name = models.CharField(_('name'), max_length=30)
club = models.ForeignKey(Club, related_name="counters", verbose_name=_("club"))
@ -265,6 +267,7 @@ class Counter(models.Model):
"""
return [b.id for b in self.get_barmen_list()]
class Refilling(models.Model):
"""
Handle the refilling
@ -309,6 +312,7 @@ class Refilling(models.Model):
).save()
super(Refilling, self).save(*args, **kwargs)
class Selling(models.Model):
"""
Handle the sellings
@ -414,7 +418,8 @@ class Selling(models.Model):
try:
if self.product.eticket:
self.send_mail_customer()
except: pass
except:
pass
Notification(
user=self.customer.user,
url=reverse('core:user_account_detail',
@ -424,6 +429,7 @@ class Selling(models.Model):
).save()
super(Selling, self).save(*args, **kwargs)
class Permanency(models.Model):
"""
This class aims at storing a traceability of who was barman where and when
@ -444,6 +450,7 @@ class Permanency(models.Model):
self.end.strftime("%Y-%m-%d %H:%M:%S") if self.end else "",
)
class CashRegisterSummary(models.Model):
user = models.ForeignKey(User, related_name="cash_summaries", verbose_name=_("user"))
counter = models.ForeignKey(Counter, related_name="cash_summaries", verbose_name=_("counter"))
@ -515,6 +522,7 @@ class CashRegisterSummary(models.Model):
def get_absolute_url(self):
return reverse('counter:cash_summary_list')
class CashRegisterSummaryItem(models.Model):
cash_summary = models.ForeignKey(CashRegisterSummary, related_name="items", verbose_name=_("cash summary"))
value = CurrencyField(_("value"))
@ -524,6 +532,7 @@ class CashRegisterSummaryItem(models.Model):
class Meta:
verbose_name = _("cash register summary item")
class Eticket(models.Model):
"""
Eticket can be linked to a product an allows PDF generation
@ -552,6 +561,6 @@ class Eticket(models.Model):
return user.is_in_group(settings.SITH_GROUP_COUNTER_ADMIN_ID)
def get_hash(self, string):
import hashlib, hmac
import hashlib
import hmac
return hmac.new(bytes(self.secret, 'utf-8'), bytes(string, 'utf-8'), hashlib.sha1).hexdigest()

View File

@ -24,7 +24,6 @@
import re
from pprint import pprint
from django.test import TestCase
from django.core.urlresolvers import reverse
from django.core.management import call_command

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from counter.views import *

View File

@ -22,7 +22,7 @@
#
#
from django.shortcuts import render, get_object_or_404
from django.shortcuts import get_object_or_404
from django.http import Http404
from django.core.exceptions import PermissionDenied
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
@ -31,7 +31,6 @@ from django.views.generic.edit import UpdateView, CreateView, DeleteView, Proces
from django.forms.models import modelform_factory
from django.forms import CheckboxSelectMultiple
from django.core.urlresolvers import reverse_lazy, reverse
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect, HttpResponse
from django.utils import timezone
from django import forms
@ -43,16 +42,17 @@ import re
import pytz
from datetime import date, timedelta, datetime
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
from ajax_select import make_ajax_form, make_ajax_field
from ajax_select import make_ajax_field
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
from core.views.forms import SelectUser, LoginForm, SelectDate, SelectDateTime
from core.views import CanViewMixin, TabedViewMixin
from core.views.forms import LoginForm, SelectDate, SelectDateTime
from core.models import User
from subscription.models import Subscription
from counter.models import Counter, Customer, Product, Selling, Refilling, ProductType, \
CashRegisterSummary, CashRegisterSummaryItem, Eticket, Permanency
from accounting.models import CurrencyField
class CounterAdminMixin(View):
"""
This view is made to protect counter admin section
@ -72,13 +72,13 @@ class CounterAdminMixin(View):
return True
return False
def dispatch(self, request, *args, **kwargs):
if not (request.user.is_root or self._test_group(request.user)
or self._test_club(request.user)):
raise PermissionDenied
return super(CounterAdminMixin, self).dispatch(request, *args, **kwargs)
class GetUserForm(forms.Form):
"""
The Form class aims at providing a valid user_id field in its cleaned data, in order to pass it to some view,
@ -107,20 +107,24 @@ class GetUserForm(forms.Form):
cleaned_data['user'] = cus.user
return cleaned_data
class RefillForm(forms.ModelForm):
error_css_class = 'error'
required_css_class = 'required'
amount = forms.FloatField(min_value=0, widget=forms.NumberInput(attrs={'class': 'focus'}))
class Meta:
model = Refilling
fields = ['amount', 'payment_method', 'bank']
class CounterTabsMixin(TabedViewMixin):
def get_tabs_title(self):
if hasattr(self.object, 'stock_owner'):
return self.object.stock_owner.counter
else:
return self.object
def get_list_of_tabs(self):
tab_list = []
tab_list.append({
@ -149,9 +153,11 @@ class CounterTabsMixin(TabedViewMixin):
'slug': 'take_items_from_stock',
'name': _("Take items from stock"),
})
except: pass # The counter just have no stock
except:
pass # The counter just have no stock
return tab_list
class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, FormMixin):
"""
The public (barman) view
@ -210,6 +216,7 @@ class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, F
def get_success_url(self):
return reverse_lazy('counter:click', args=self.args, kwargs=self.kwargs)
class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
"""
The click view
@ -470,6 +477,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
kwargs['categories'] = ProductType.objects.all()
return kwargs
class CounterLogin(RedirectView):
"""
Handle the login of a barman
@ -477,6 +485,7 @@ class CounterLogin(RedirectView):
Logged barmen are stored in the Permanency model
"""
permanent = False
def post(self, request, *args, **kwargs):
"""
Register the logged user as barman for this counter
@ -501,8 +510,10 @@ class CounterLogin(RedirectView):
def get_redirect_url(self, *args, **kwargs):
return reverse_lazy('counter:details', args=args, kwargs=kwargs) + "?" + '&'.join(self.errors)
class CounterLogout(RedirectView):
permanent = False
def post(self, request, *args, **kwargs):
"""
Unregister the user from the barman
@ -515,7 +526,8 @@ class CounterLogout(RedirectView):
def get_redirect_url(self, *args, **kwargs):
return reverse_lazy('counter:details', args=args, kwargs=kwargs)
## Counter admin views
# Counter admin views
class CounterAdminTabsMixin(TabedViewMixin):
tabs_title = _("Counter administration")
@ -562,6 +574,7 @@ class CounterAdminTabsMixin(TabedViewMixin):
},
]
class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
"""
A list view for the admins
@ -570,6 +583,7 @@ class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
template_name = 'counter/counter_list.jinja'
current_tab = "counters"
class CounterEditForm(forms.ModelForm):
class Meta:
model = Counter
@ -577,6 +591,7 @@ class CounterEditForm(forms.ModelForm):
sellers = make_ajax_field(Counter, 'sellers', 'users', help_text="")
products = make_ajax_field(Counter, 'products', 'products', help_text="")
class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""
Edit a counter's main informations (for the counter's manager)
@ -595,6 +610,7 @@ class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
def get_success_url(self):
return reverse_lazy('counter:admin', kwargs={'counter_id': self.object.id})
class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""
Edit a counter's main informations (for the counter's admin)
@ -605,6 +621,7 @@ class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
template_name = 'core/edit.jinja'
current_tab = "counters"
class CounterCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
"""
Create a counter (for the admins)
@ -615,6 +632,7 @@ class CounterCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
template_name = 'core/create.jinja'
current_tab = "counters"
class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
"""
Delete a counter (for the admins)
@ -627,6 +645,7 @@ class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
# Product management
class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
"""
A list view for the admins
@ -635,6 +654,7 @@ class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
template_name = 'counter/producttype_list.jinja'
current_tab = "product_types"
class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
"""
A create view for the admins
@ -644,6 +664,7 @@ class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView
template_name = 'core/create.jinja'
current_tab = "products"
class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""
An edit view for the admins
@ -654,6 +675,7 @@ class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
pk_url_kwarg = "type_id"
current_tab = "products"
class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
"""
A list view for the admins
@ -664,6 +686,7 @@ class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView
ordering = ['name']
current_tab = "archive"
class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
"""
A list view for the admins
@ -674,6 +697,7 @@ class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
ordering = ['name']
current_tab = "products"
class ProductEditForm(forms.ModelForm):
class Meta:
model = Product
@ -702,6 +726,7 @@ class ProductEditForm(forms.ModelForm):
c.save()
return ret
class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
"""
A create view for the admins
@ -711,6 +736,7 @@ class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
template_name = 'core/create.jinja'
current_tab = "products"
class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""
An edit view for the admins
@ -721,6 +747,7 @@ class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
template_name = 'core/edit.jinja'
current_tab = "products"
class RefillingDeleteView(DeleteView):
"""
Delete a refilling (for the admins)
@ -745,6 +772,7 @@ class RefillingDeleteView(DeleteView):
return super(RefillingDeleteView, self).dispatch(request, *args, **kwargs)
raise PermissionDenied
class SellingDeleteView(DeleteView):
"""
Delete a selling (for the admins)
@ -771,6 +799,7 @@ class SellingDeleteView(DeleteView):
# Cash register summaries
class CashRegisterSummaryForm(forms.Form):
"""
Provide the cash summary form
@ -839,30 +868,46 @@ class CashRegisterSummaryForm(forms.Form):
summary.save()
summary.items.all().delete()
# Cash
if cd['ten_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.1, quantity=cd['ten_cents']).save()
if cd['twenty_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.2, quantity=cd['twenty_cents']).save()
if cd['fifty_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.5, quantity=cd['fifty_cents']).save()
if cd['one_euro']: CashRegisterSummaryItem(cash_summary=summary, value=1, quantity=cd['one_euro']).save()
if cd['two_euros']: CashRegisterSummaryItem(cash_summary=summary, value=2, quantity=cd['two_euros']).save()
if cd['five_euros']: CashRegisterSummaryItem(cash_summary=summary, value=5, quantity=cd['five_euros']).save()
if cd['ten_euros']: CashRegisterSummaryItem(cash_summary=summary, value=10, quantity=cd['ten_euros']).save()
if cd['twenty_euros']: CashRegisterSummaryItem(cash_summary=summary, value=20, quantity=cd['twenty_euros']).save()
if cd['fifty_euros']: CashRegisterSummaryItem(cash_summary=summary, value=50, quantity=cd['fifty_euros']).save()
if cd['hundred_euros']: CashRegisterSummaryItem(cash_summary=summary, value=100, quantity=cd['hundred_euros']).save()
if cd['ten_cents']:
CashRegisterSummaryItem(cash_summary=summary, value=0.1, quantity=cd['ten_cents']).save()
if cd['twenty_cents']:
CashRegisterSummaryItem(cash_summary=summary, value=0.2, quantity=cd['twenty_cents']).save()
if cd['fifty_cents']:
CashRegisterSummaryItem(cash_summary=summary, value=0.5, quantity=cd['fifty_cents']).save()
if cd['one_euro']:
CashRegisterSummaryItem(cash_summary=summary, value=1, quantity=cd['one_euro']).save()
if cd['two_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=2, quantity=cd['two_euros']).save()
if cd['five_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=5, quantity=cd['five_euros']).save()
if cd['ten_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=10, quantity=cd['ten_euros']).save()
if cd['twenty_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=20, quantity=cd['twenty_euros']).save()
if cd['fifty_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=50, quantity=cd['fifty_euros']).save()
if cd['hundred_euros']:
CashRegisterSummaryItem(cash_summary=summary, value=100, quantity=cd['hundred_euros']).save()
# Checks
if cd['check_1_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_1_value'],
if cd['check_1_quantity']:
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_1_value'],
quantity=cd['check_1_quantity'], check=True).save()
if cd['check_2_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_2_value'],
if cd['check_2_quantity']:
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_2_value'],
quantity=cd['check_2_quantity'], check=True).save()
if cd['check_3_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_3_value'],
if cd['check_3_quantity']:
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_3_value'],
quantity=cd['check_3_quantity'], check=True).save()
if cd['check_4_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_4_value'],
if cd['check_4_quantity']:
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_4_value'],
quantity=cd['check_4_quantity'], check=True).save()
if cd['check_5_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_5_value'],
if cd['check_5_quantity']:
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_5_value'],
quantity=cd['check_5_quantity'], check=True).save()
if summary.items.count() < 1:
summary.delete()
class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
"""
Provide the last operations to allow barmen to delete them
@ -891,6 +936,7 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
kwargs['last_sellings'] = self.object.sellings.filter(date__gte=threshold).order_by('-id')[:20]
return kwargs
class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
"""
Provide the cash summary form
@ -933,6 +979,7 @@ class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
kwargs['form'] = self.form
return kwargs
class CounterActivityView(DetailView):
"""
Show the bar activity
@ -941,6 +988,7 @@ class CounterActivityView(DetailView):
pk_url_kwarg = "counter_id"
template_name = 'counter/activity.jinja'
class CounterStatView(DetailView, CounterAdminMixin):
"""
Show the bar stats
@ -1003,6 +1051,7 @@ class CounterStatView(DetailView, CounterAdminMixin):
return super(CanEditMixin, self).dispatch(request, *args, **kwargs)
raise PermissionDenied
class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""Edit cash summaries"""
model = CashRegisterSummary
@ -1015,10 +1064,12 @@ class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView)
def get_success_url(self):
return reverse('counter:cash_summary_list')
class CashSummaryFormBase(forms.Form):
begin_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Begin date"), required=False, widget=SelectDateTime)
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), required=False, widget=SelectDateTime)
class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
"""Display a list of cash summaries"""
model = CashRegisterSummary
@ -1056,6 +1107,7 @@ class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
kwargs['refilling_sums'][c.name] = sum([s.amount for s in refillings.all()])
return kwargs
class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
template_name = 'counter/invoices_call.jinja'
current_tab = 'invoices_call'
@ -1087,6 +1139,7 @@ class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
)).exclude(selling_sum=None).order_by('-selling_sum')
return kwargs
class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
"""
A list view for the admins
@ -1096,6 +1149,7 @@ class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
ordering = ['id']
current_tab = "etickets"
class EticketForm(forms.ModelForm):
class Meta:
model = Eticket
@ -1105,6 +1159,7 @@ class EticketForm(forms.ModelForm):
}
product = AutoCompleteSelectField('products', show_help_text=False, label=_("Product"), required=True)
class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
"""
Create an eticket
@ -1114,6 +1169,7 @@ class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
form_class = EticketForm
current_tab = "etickets"
class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
"""
Edit an eticket
@ -1124,6 +1180,7 @@ class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
pk_url_kwarg = "eticket_id"
current_tab = "etickets"
class EticketPDFView(CanViewMixin, DetailView):
"""
Display the PDF of an eticket
@ -1195,4 +1252,3 @@ class EticketPDFView(CanViewMixin, DetailView):
p.showPage()
p.save()
return response

View File

@ -27,9 +27,9 @@ from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from accounting.models import CurrencyField
from counter.models import Counter, Product, Customer, Selling, Refilling
from counter.models import Counter, Product, Selling, Refilling
from core.models import User
from subscription.models import Subscription
class Basket(models.Model):
"""
@ -64,6 +64,7 @@ class Basket(models.Model):
def __str__(self):
return "%s's basket (%d items)" % (self.user, self.items.all().count())
class Invoice(models.Model):
"""
Invoices are generated once the payment has been validated
@ -120,6 +121,7 @@ class Invoice(models.Model):
self.validated = True
self.save()
class AbstractBaseItem(models.Model):
product_id = models.IntegerField(_('product id'))
product_name = models.CharField(_('product name'), max_length=255)
@ -133,8 +135,10 @@ class AbstractBaseItem(models.Model):
def __str__(self):
return "Item: %s (%s) x%d" % (self.product_name, self.product_unit_price, self.quantity)
class BasketItem(AbstractBaseItem):
basket = models.ForeignKey(Basket, related_name='items', verbose_name=_('basket'))
class InvoiceItem(AbstractBaseItem):
invoice = models.ForeignKey(Invoice, related_name='items', verbose_name=_('invoice'))

View File

@ -34,7 +34,8 @@ from django.core.management import call_command
from django.conf import settings
from core.models import User
from counter.models import Customer, ProductType, Product, Counter, Refilling
from counter.models import Product, Counter, Refilling
class EbouticTest(TestCase):
def setUp(self):
@ -93,7 +94,6 @@ class EbouticTest(TestCase):
" <td>Barbar</td>\\n <td>1</td>\\n <td>1.70 \\xe2\\x82\\xac</td>\\n"
" <td>Compte utilisateur</td>" in str(response.content))
def test_buy_simple_product_with_credit_card(self):
self.client.login(username='subscriber', password='plop')
response = self.client.post(reverse("eboutic:main"), {
@ -171,6 +171,3 @@ class EbouticTest(TestCase):
" <td>15.00 \\xe2\\x82\\xac</td>" in str(response.content))
response = self.client.get(reverse("core:user_profile", kwargs={"user_id": self.old_subscriber.id}))
self.assertTrue("Cotisant jusqu\\'au" in str(response.content))

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from eboutic.views import *
@ -33,6 +33,3 @@ urlpatterns = [
url(r'^pay$', EbouticPayWithSith.as_view(), name='pay_with_sith'),
url(r'^et_autoanswer$', EtransactionAutoAnswer.as_view(), name='etransation_autoanswer'),
]

View File

@ -24,23 +24,21 @@
from collections import OrderedDict
from datetime import datetime
import pytz
import hmac
import base64
from OpenSSL import crypto
from django.shortcuts import render
from django.core.urlresolvers import reverse_lazy
from django.views.generic import TemplateView, View
from django.http import HttpResponse, HttpResponseRedirect
from django.core.exceptions import SuspiciousOperation
from django.shortcuts import render
from django.db import transaction, DataError
from django.utils.translation import ugettext as _
from django.conf import settings
from counter.models import Customer, Counter, ProductType, Selling
from eboutic.models import Basket, Invoice, BasketItem, InvoiceItem
from eboutic.models import Basket, Invoice, InvoiceItem
class EbouticMain(TemplateView):
template_name = 'eboutic/eboutic_main.jinja'
@ -77,7 +75,6 @@ class EbouticMain(TemplateView):
self.del_product(request)
return self.render_to_response(self.get_context_data(**kwargs))
def add_product(self, request):
""" Add a product to the basket """
try:
@ -108,6 +105,7 @@ class EbouticMain(TemplateView):
kwargs['categories'] = kwargs['categories'].exclude(id=settings.SITH_PRODUCTTYPE_SUBSCRIPTION)
return kwargs
class EbouticCommand(TemplateView):
template_name = 'eboutic/eboutic_makecommand.jinja'
@ -150,6 +148,7 @@ class EbouticCommand(TemplateView):
"sha512").hexdigest().upper()
return kwargs
class EbouticPayWithSith(TemplateView):
template_name = 'eboutic/eboutic_payment_result.jinja'
@ -189,6 +188,7 @@ class EbouticPayWithSith(TemplateView):
kwargs['not_enough'] = True
return self.render_to_response(self.get_context_data(**kwargs))
class EtransactionAutoAnswer(View):
def get(self, request, *args, **kwargs):
if (not 'Amount' in request.GET.keys() or
@ -225,4 +225,3 @@ class EtransactionAutoAnswer(View):
return HttpResponse()
else:
return HttpResponse("Payment failed with error: " + request.GET['Error'], status=400)

View File

@ -23,11 +23,9 @@
#
from django.db import models
from django.core import validators
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from django.db import IntegrityError, transaction
from django.core.urlresolvers import reverse
from django.utils import timezone
from django.utils.functional import cached_property
@ -35,9 +33,10 @@ from django.utils.functional import cached_property
from datetime import datetime
import pytz
from core.models import User, MetaGroup, Group, SithFile
from core.models import User, Group
from club.models import Club
class Forum(models.Model):
"""
The Forum class, made as a tree to allow nice tidy organization
@ -118,10 +117,12 @@ class Forum(models.Model):
def is_owned_by(self, user):
if user.is_in_group(settings.SITH_GROUP_FORUM_ADMIN_ID):
return True
try: m = Forum._club_memberships[self.id][user.id]
try:
m = Forum._club_memberships[self.id][user.id]
except:
m = self.owner_club.get_membership_for(user)
try: Forum._club_memberships[self.id][user.id] = m
try:
Forum._club_memberships[self.id][user.id] = m
except:
Forum._club_memberships[self.id] = {}
Forum._club_memberships[self.id][user.id] = m
@ -178,6 +179,7 @@ class Forum(models.Model):
l += c.get_children_list()
return l
class ForumTopic(models.Model):
forum = models.ForeignKey(Forum, related_name='topics')
author = models.ForeignKey(User, related_name='forum_topics')
@ -225,6 +227,7 @@ class ForumTopic(models.Model):
def title(self):
return self._title
class ForumMessage(models.Model):
"""
"A ForumMessage object represents a message in the forum" -- Cpt. Obvious
@ -282,10 +285,11 @@ class ForumMessage(models.Model):
return int(self.topic.messages.filter(id__lt=self.id).count() / settings.SITH_FORUM_PAGE_LENGTH) + 1
def mark_as_read(self, user):
try: # Need the try/except because of AnonymousUser
try: # Need the try/except because of AnonymousUser
if not self.is_read(user):
self.readers.add(user)
except: pass
except:
pass
def is_read(self, user):
return (self.date < user.forum_infos.last_read_date) or (user in self.readers.all())
@ -296,12 +300,14 @@ class ForumMessage(models.Model):
return meta.action == "DELETE"
return False
MESSAGE_META_ACTIONS = [
('EDIT', _("Message edited by")),
('DELETE', _("Message deleted by")),
('UNDELETE', _("Message undeleted by")),
]
class ForumMessageMeta(models.Model):
user = models.ForeignKey(User, related_name="forum_message_metas")
message = models.ForeignKey(ForumMessage, related_name="metas")
@ -326,4 +332,3 @@ class ForumUserInfo(models.Model):
def __str__(self):
return str(self.user)

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from forum.views import *

View File

@ -22,30 +22,30 @@
#
#
from django.shortcuts import render, get_object_or_404
from django.shortcuts import get_object_or_404
from django.views.generic import ListView, DetailView, RedirectView
from django.views.generic.edit import UpdateView, CreateView, DeleteView
from django.views.generic.detail import SingleObjectMixin
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse, reverse_lazy
from django.core.urlresolvers import reverse_lazy
from django.utils import timezone
from django.conf import settings
from django import forms
from django.db import models
from django.core.exceptions import PermissionDenied
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from ajax_select import make_ajax_form, make_ajax_field
from ajax_select import make_ajax_field
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
from core.views.forms import MarkdownInput
from core.models import Page
from forum.models import Forum, ForumMessage, ForumTopic, ForumMessageMeta
class ForumMainView(ListView):
queryset = Forum.objects.filter(parent=None).prefetch_related("children___last_message__author", "children___last_message__topic")
template_name = "forum/main.jinja"
class ForumMarkAllAsRead(RedirectView):
permanent = False
url = reverse_lazy('forum:last_unread')
@ -57,9 +57,11 @@ class ForumMarkAllAsRead(RedirectView):
fi.save()
for m in request.user.read_messages.filter(date__lt=fi.last_read_date):
m.readers.remove(request.user) # Clean up to keep table low in data
except: pass
except:
pass
return super(ForumMarkAllAsRead, self).get(request, *args, **kwargs)
class ForumLastUnread(ListView):
model = ForumTopic
template_name = "forum/last_unread.jinja"
@ -73,6 +75,7 @@ class ForumLastUnread(ListView):
.prefetch_related('forum__edit_groups')
return topic_list
class ForumForm(forms.ModelForm):
class Meta:
model = Forum
@ -80,6 +83,7 @@ class ForumForm(forms.ModelForm):
edit_groups = make_ajax_field(Forum, 'edit_groups', 'groups', help_text="")
view_groups = make_ajax_field(Forum, 'view_groups', 'groups', help_text="")
class ForumCreateView(CanCreateMixin, CreateView):
model = Forum
form_class = ForumForm
@ -93,12 +97,15 @@ class ForumCreateView(CanCreateMixin, CreateView):
init['owner_club'] = parent.owner_club
init['edit_groups'] = parent.edit_groups.all()
init['view_groups'] = parent.view_groups.all()
except: pass
except:
pass
return init
class ForumEditForm(ForumForm):
recursive = forms.BooleanField(label=_("Apply rights and club owner recursively"), required=False)
class ForumEditView(CanEditPropMixin, UpdateView):
model = Forum
pk_url_kwarg = "forum_id"
@ -112,12 +119,14 @@ class ForumEditView(CanEditPropMixin, UpdateView):
self.object.apply_rights_recursively()
return ret
class ForumDeleteView(CanEditPropMixin, DeleteView):
model = Forum
pk_url_kwarg = "forum_id"
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy('forum:main')
class ForumDetailView(CanViewMixin, DetailView):
model = Forum
template_name = "forum/forum.jinja"
@ -139,6 +148,7 @@ class ForumDetailView(CanViewMixin, DetailView):
kwargs["topics"] = paginator.page(paginator.num_pages)
return kwargs
class TopicForm(forms.ModelForm):
class Meta:
model = ForumMessage
@ -148,6 +158,7 @@ class TopicForm(forms.ModelForm):
}
title = forms.CharField(required=True, label=_("Title"))
class ForumTopicCreateView(CanCreateMixin, CreateView):
model = ForumMessage
form_class = TopicForm
@ -166,12 +177,14 @@ class ForumTopicCreateView(CanCreateMixin, CreateView):
form.instance.author = self.request.user
return super(ForumTopicCreateView, self).form_valid(form)
class ForumTopicEditView(CanEditMixin, UpdateView):
model = ForumTopic
fields = ['forum']
pk_url_kwarg = "topic_id"
template_name = "core/edit.jinja"
class ForumTopicDetailView(CanViewMixin, DetailView):
model = ForumTopic
pk_url_kwarg = "topic_id"
@ -186,7 +199,7 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
kwargs['first_unread_message_id'] = msg.id
except:
kwargs['first_unread_message_id'] = float("inf")
paginator = Paginator(self.object.messages.select_related('author__avatar_pict')\
paginator = Paginator(self.object.messages.select_related('author__avatar_pict')
.prefetch_related('topic__forum__edit_groups', 'readers').order_by('date'),
settings.SITH_FORUM_PAGE_LENGTH)
page = self.request.GET.get('page')
@ -198,6 +211,7 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
kwargs["msgs"] = paginator.page(paginator.num_pages)
return kwargs
class ForumMessageView(SingleObjectMixin, RedirectView):
model = ForumMessage
pk_url_kwarg = "message_id"
@ -207,6 +221,7 @@ class ForumMessageView(SingleObjectMixin, RedirectView):
self.object = self.get_object()
return self.object.get_url()
class ForumMessageEditView(CanEditMixin, UpdateView):
model = ForumMessage
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message', ], widgets={'message': MarkdownInput})
@ -222,6 +237,7 @@ class ForumMessageEditView(CanEditMixin, UpdateView):
kwargs['topic'] = self.object.topic
return kwargs
class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
model = ForumMessage
pk_url_kwarg = "message_id"
@ -233,6 +249,7 @@ class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
ForumMessageMeta(message=self.object, user=self.request.user, action="DELETE").save()
return self.object.get_absolute_url()
class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
model = ForumMessage
pk_url_kwarg = "message_id"
@ -244,6 +261,7 @@ class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
ForumMessageMeta(message=self.object, user=self.request.user, action="UNDELETE").save()
return self.object.get_absolute_url()
class ForumMessageCreateView(CanCreateMixin, CreateView):
model = ForumMessage
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message', ], widgets={'message': MarkdownInput})
@ -277,4 +295,3 @@ class ForumMessageCreateView(CanCreateMixin, CreateView):
kwargs = super(ForumMessageCreateView, self).get_context_data(**kwargs)
kwargs['topic'] = self.topic
return kwargs

View File

@ -27,12 +27,13 @@ from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django.core.urlresolvers import reverse
from counter.models import Counter, Product
from counter.models import Counter
from core.models import User
from club.models import Club
# Create your models here.
class Launderette(models.Model):
name = models.CharField(_('name'), max_length=30)
counter = models.OneToOneField(Counter, verbose_name=_('counter'), related_name='launderette')
@ -78,6 +79,7 @@ class Launderette(models.Model):
def token_list(self):
return [t.id for t in self.get_token_list()]
class Machine(models.Model):
name = models.CharField(_('name'), max_length=30)
launderette = models.ForeignKey(Launderette, related_name='machines', verbose_name=_('launderette'))
@ -103,6 +105,7 @@ class Machine(models.Model):
def get_absolute_url(self):
return reverse('launderette:launderette_admin', kwargs={"launderette_id": self.launderette.id})
class Token(models.Model):
name = models.CharField(_('name'), max_length=5)
launderette = models.ForeignKey(Launderette, related_name='tokens', verbose_name=_('launderette'))
@ -140,6 +143,7 @@ class Token(models.Model):
else:
return False
class Slot(models.Model):
start_date = models.DateTimeField(_('start date'))
type = models.CharField(_('type'), max_length=10, choices=settings.SITH_LAUNDERETTE_MACHINE_TYPES)
@ -157,5 +161,3 @@ class Slot(models.Model):
def __str__(self):
return "User: %s - Date: %s - Type: %s - Machine: %s - Token: %s" % (self.user, self.start_date, self.get_type_display(),
self.machine.name, self.token)

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from launderette.views import *

View File

@ -26,11 +26,8 @@ from datetime import datetime, timedelta
from collections import OrderedDict
import pytz
from django.shortcuts import render
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
from django.views.generic import ListView, DetailView, TemplateView
from django.views.generic.edit import UpdateView, CreateView, DeleteView, BaseFormView
from django.forms.models import modelform_factory
from django.forms import CheckboxSelectMultiple
from django.utils.translation import ugettext as _
from django.utils import dateparse, timezone
from django.core.urlresolvers import reverse_lazy
@ -38,7 +35,6 @@ from django.conf import settings
from django.db import transaction, DataError
from django import forms
from django.template import defaultfilters
from django.utils import formats
from core.models import Page, User
from club.models import Club
@ -49,6 +45,7 @@ from counter.views import GetUserForm
# For users
class LaunderetteMainView(TemplateView):
"""Main presentation view"""
template_name = 'launderette/launderette_main.jinja'
@ -59,11 +56,13 @@ class LaunderetteMainView(TemplateView):
kwargs['page'] = Page.objects.filter(name='launderette').first()
return kwargs
class LaunderetteBookMainView(CanViewMixin, ListView):
"""Choose which launderette to book"""
model = Launderette
template_name = 'launderette/launderette_book_choose.jinja'
class LaunderetteBookView(CanViewMixin, DetailView):
"""Display the launderette schedule"""
model = Launderette
@ -100,7 +99,8 @@ class LaunderetteBookView(CanViewMixin, DetailView):
return super(LaunderetteBookView, self).get(request, *args, **kwargs)
def check_slot(self, type, date=None):
if date is None: date = self.date
if date is None:
date = self.date
for m in self.object.machines.filter(is_working=True, type=type).all():
slot = Slot.objects.filter(start_date=date, machine=m).first()
if slot is None:
@ -137,6 +137,7 @@ class LaunderetteBookView(CanViewMixin, DetailView):
kwargs['planning'][date].append(None)
return kwargs
class SlotDeleteView(CanEditPropMixin, DeleteView):
"""Delete a slot"""
model = Slot
@ -154,6 +155,7 @@ class LaunderetteListView(CanEditPropMixin, ListView):
model = Launderette
template_name = 'launderette/launderette_list.jinja'
class LaunderetteEditView(CanEditPropMixin, UpdateView):
"""Edit a launderette"""
model = Launderette
@ -161,6 +163,7 @@ class LaunderetteEditView(CanEditPropMixin, UpdateView):
fields = ['name']
template_name = 'core/edit.jinja'
class LaunderetteCreateView(CanCreateMixin, CreateView):
"""Create a new launderette"""
model = Launderette
@ -174,6 +177,7 @@ class LaunderetteCreateView(CanCreateMixin, CreateView):
form.instance.counter = c
return super(LaunderetteCreateView, self).form_valid(form)
class ManageTokenForm(forms.Form):
action = forms.ChoiceField(choices=[("BACK", _("Back")), ("ADD", _("Add")), ("DEL", _("Delete"))], initial="BACK",
label=_("Action"), widget=forms.RadioSelect)
@ -210,6 +214,7 @@ class ManageTokenForm(forms.Form):
except:
self.add_error(None, _("Token %(token_name)s does not exists") % {'token_name': t})
class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
"""The admin page of the launderette"""
model = Launderette
@ -253,6 +258,7 @@ class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
def get_success_url(self):
return reverse_lazy('launderette:launderette_admin', args=self.args, kwargs=self.kwargs)
class GetLaunderetteUserForm(GetUserForm):
def clean(self):
cleaned_data = super(GetLaunderetteUserForm, self).clean()
@ -261,6 +267,7 @@ class GetLaunderetteUserForm(GetUserForm):
raise forms.ValidationError(_("User has booked no slot"))
return cleaned_data
class LaunderetteMainClickView(CanEditMixin, BaseFormView, DetailView):
"""The click page of the launderette"""
model = Launderette
@ -301,6 +308,7 @@ class LaunderetteMainClickView(CanEditMixin, BaseFormView, DetailView):
def get_success_url(self):
return reverse_lazy('launderette:click', args=self.args, kwargs=self.kwargs)
class ClickTokenForm(forms.BaseForm):
def clean(self):
with transaction.atomic():
@ -332,6 +340,7 @@ class ClickTokenForm(forms.BaseForm):
self.last_basket['last_total'] = str(total)
return self.cleaned_data
class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
"""The click page of the launderette"""
model = Launderette
@ -401,7 +410,6 @@ class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
return reverse_lazy('launderette:main_click', args=self.args, kwargs=self.kwargs)
class MachineEditView(CanEditPropMixin, UpdateView):
"""Edit a machine"""
model = Machine
@ -409,6 +417,7 @@ class MachineEditView(CanEditPropMixin, UpdateView):
fields = ['name', 'launderette', 'type', 'is_working']
template_name = 'core/edit.jinja'
class MachineDeleteView(CanEditPropMixin, DeleteView):
"""Edit a machine"""
model = Machine
@ -416,6 +425,7 @@ class MachineDeleteView(CanEditPropMixin, DeleteView):
template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('launderette:launderette_list')
class MachineCreateView(CanCreateMixin, CreateView):
"""Create a new machine"""
model = Machine
@ -429,6 +439,3 @@ class MachineCreateView(CanCreateMixin, CreateView):
if obj is not None:
ret['launderette'] = obj.id
return ret

View File

@ -30,5 +30,3 @@ from sas.models import *
admin.site.register(Album)
# admin.site.register(Picture)
admin.site.register(PeoplePictureRelation)

View File

@ -23,11 +23,9 @@
#
from django.db import models
from django.core.urlresolvers import reverse_lazy, reverse
from django.conf import settings
from django.core.urlresolvers import reverse
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.core.files.base import ContentFile
from PIL import Image
from io import BytesIO
@ -36,6 +34,7 @@ import os
from core.models import SithFile, User
from core.utils import resize_image, exif_auto_rotate
class Picture(SithFile):
class Meta:
proxy = True
@ -73,7 +72,8 @@ class Picture(SithFile):
im = Image.open(BytesIO(self.file.read()))
try:
im = exif_auto_rotate(im)
except: pass
except:
pass
file = resize_image(im, max(im.size), self.mime_type.split('/')[-1])
thumb = resize_image(im, 200, self.mime_type.split('/')[-1])
compressed = resize_image(im, 1200, self.mime_type.split('/')[-1])
@ -113,6 +113,7 @@ class Picture(SithFile):
else:
return Picture.objects.filter(id__lt=self.id, is_moderated=False, is_in_sas=True).order_by('-id').first()
class Album(SithFile):
class Meta:
proxy = True
@ -148,6 +149,7 @@ class Album(SithFile):
self.file.name = self.name + '/thumb.jpg'
self.save()
class PeoplePictureRelation(models.Model):
"""
The PeoplePictureRelation class makes the connection between User and Picture

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from sas.views import *
@ -40,4 +40,3 @@ urlpatterns = [
# url(r'^album/new$', AlbumCreateView.as_view(), name='album_new'),
# url(r'^(?P<club_id>[0-9]+)/$', ClubView.as_view(), name='club_view'),
]

View File

@ -22,31 +22,27 @@
#
#
from django.shortcuts import render, redirect
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import redirect
from django.http import HttpResponse
from django.core.urlresolvers import reverse_lazy, reverse
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
from django.views.generic.edit import UpdateView, CreateView, DeleteView, ProcessFormView, FormMixin, FormView
from core.views.forms import SelectDate
from django.views.generic import DetailView, TemplateView
from django.views.generic.edit import UpdateView, FormMixin, FormView
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.conf import settings
from django.forms.models import modelform_factory
from django import forms
from django.core.exceptions import PermissionDenied
from ajax_select import make_ajax_form, make_ajax_field
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
from ajax_select import make_ajax_field
from ajax_select.fields import AutoCompleteSelectMultipleField
from io import BytesIO
from PIL import Image
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
from core.views.forms import SelectUser, LoginForm, SelectDate, SelectDateTime
from core.views import CanViewMixin, CanEditMixin
from core.views.files import send_file, FileView
from core.models import SithFile, User, Notification, RealGroup
from sas.models import Picture, Album, PeoplePictureRelation
class SASForm(forms.Form):
album_name = forms.CharField(label=_("Add a new album"), max_length=30, required=False)
images = forms.ImageField(widget=forms.ClearableFileInput(attrs={'multiple': True}), label=_("Upload images"),
@ -80,6 +76,7 @@ class SASForm(forms.Form):
if not u.notifications.filter(type="SAS_MODERATION", viewed=False).exists():
Notification(user=u, url=reverse("sas:moderation"), type="SAS_MODERATION").save()
class RelationForm(forms.ModelForm):
class Meta:
model = PeoplePictureRelation
@ -87,6 +84,7 @@ class RelationForm(forms.ModelForm):
widgets = {'picture': forms.HiddenInput}
users = AutoCompleteSelectMultipleField('users', show_help_text=False, help_text="", label=_("Add user"), required=False)
class SASMainView(FormView):
form_class = SASForm
template_name = "sas/main.jinja"
@ -112,6 +110,7 @@ class SASMainView(FormView):
kwargs['latest'] = SithFile.objects.filter(is_in_sas=True, is_folder=True, is_moderated=True).order_by('-id')[:5]
return kwargs
class PictureView(CanViewMixin, DetailView, FormMixin):
model = Picture
form_class = RelationForm
@ -132,8 +131,9 @@ class PictureView(CanViewMixin, DetailView, FormMixin):
try:
user = User.objects.filter(id=int(request.GET['remove_user'])).first()
if user.id == request.user.id or request.user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID):
r = PeoplePictureRelation.objects.filter(user=user, picture=self.object).delete()
except: pass
PeoplePictureRelation.objects.filter(user=user, picture=self.object).delete()
except:
pass
if 'ask_removal' in request.GET.keys():
self.object.is_moderated = False
self.object.asked_for_removal = True
@ -165,15 +165,19 @@ class PictureView(CanViewMixin, DetailView, FormMixin):
def get_success_url(self):
return reverse('sas:picture', kwargs={'picture_id': self.object.id})
def send_pict(request, picture_id):
return send_file(request, picture_id, Picture)
def send_compressed(request, picture_id):
return send_file(request, picture_id, Picture, "compressed")
def send_thumb(request, picture_id):
return send_file(request, picture_id, Picture, "thumbnail")
class AlbumUploadView(CanViewMixin, DetailView, FormMixin):
model = Album
form_class = SASForm
@ -239,6 +243,7 @@ class AlbumView(CanViewMixin, DetailView, FormMixin):
# Admin views
class ModerationView(TemplateView):
template_name = "sas/moderation.jinja"
@ -257,7 +262,8 @@ class ModerationView(TemplateView):
a.save()
elif 'delete' in request.POST.keys():
a.delete()
except: pass
except:
pass
return super(ModerationView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
@ -268,12 +274,14 @@ class ModerationView(TemplateView):
kwargs['albums'] = Album.objects.filter(id__in=kwargs['pictures'].values('parent').distinct('parent'))
return kwargs
class PictureEditForm(forms.ModelForm):
class Meta:
model = Picture
fields = ['name', 'parent']
parent = make_ajax_field(Picture, 'parent', 'files', help_text="")
class AlbumEditForm(forms.ModelForm):
class Meta:
model = Album
@ -283,12 +291,14 @@ class AlbumEditForm(forms.ModelForm):
edit_groups = make_ajax_field(Album, 'edit_groups', 'groups', help_text="")
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
class PictureEditView(CanEditMixin, UpdateView):
model = Picture
form_class = PictureEditForm
template_name = 'core/edit.jinja'
pk_url_kwarg = "picture_id"
class AlbumEditView(CanEditMixin, UpdateView):
model = Album
form_class = AlbumEditForm
@ -300,4 +310,3 @@ class AlbumEditView(CanEditMixin, UpdateView):
if form.cleaned_data['recursive']:
self.object.apply_rights_recursively(True)
return ret

View File

@ -24,6 +24,7 @@
from debug_toolbar.panels.templates import TemplatesPanel as BaseTemplatesPanel
class TemplatesPanel(BaseTemplatesPanel):
def generate_stats(self, *args):
template = self.templates[0]['template']

View File

@ -80,4 +80,3 @@ if settings.DEBUG:
urlpatterns += [
url(r'^__debug__/', include(debug_toolbar.urls)),
]

View File

@ -26,6 +26,4 @@ from django.contrib import admin
from subscription.models import Subscription
admin.site.register(Subscription)

View File

@ -35,15 +35,16 @@ from core.models import User
from core.utils import get_start_of_semester
def validate_type(value):
if value not in settings.SITH_SUBSCRIPTIONS.keys():
raise ValidationError(_('Bad subscription type'))
def validate_payment(value):
if value not in settings.SITH_SUBSCRIPTION_PAYMENT_METHOD:
raise ValidationError(_('Bad payment method'))
class Subscription(models.Model):
member = models.ForeignKey(User, related_name='subscriptions')
subscription_type = models.CharField(_('subscription type'),
@ -134,7 +135,6 @@ class Subscription(models.Model):
else:
return 'No user - ' + str(self.pk)
@staticmethod
def compute_start(d=date.today(), duration=1):
"""
@ -171,17 +171,21 @@ class Subscription(models.Model):
return start.replace(day=1, month=(start.month + 6 * duration) % 12 + 1,
year=start.year + int(duration / 2) + (1 if start.month > 6 and duration % 2 == 1 else 0))
def can_be_edited_by(self, user):
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_root
def is_valid_now(self):
return self.subscription_start <= date.today() and date.today() <= self.subscription_end
def guy_test(date, duration=4):
print(str(date) + " - " + str(duration) + " -> " + str(Subscription.compute_start(date, duration)))
def bibou_test(duration, date=date.today()):
print(str(date) + " - " + str(duration) + " -> " + str(Subscription.compute_end(duration, Subscription.compute_start(date, duration))))
def guy():
guy_test(date(2015, 7, 11))
guy_test(date(2015, 8, 11))
@ -219,5 +223,6 @@ def guy():
bibou_test(3)
bibou_test(4)
if __name__ == "__main__":
guy()

View File

@ -31,6 +31,3 @@ urlpatterns = [
url(r'^$', NewSubscription.as_view(), name='subscription'),
url(r'stats', SubscriptionsStatsView.as_view(), name='stats'),
]

View File

@ -26,16 +26,13 @@ from django.views.generic.edit import CreateView, FormView
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.urlresolvers import reverse_lazy
from django.db import IntegrityError
from django import forms
from django.forms import Select
from django.conf import settings
from ajax_select.fields import AutoCompleteSelectField
import random
from subscription.models import Subscription
from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin
from core.views.forms import SelectDateTime
from core.models import User
@ -102,6 +99,7 @@ class SubscriptionForm(forms.ModelForm):
raise ValidationError(_("You must either choose an existing user or create a new one properly"))
return cleaned_data
class NewSubscription(CreateView):
template_name = 'subscription/subscription.jinja'
form_class = SubscriptionForm

View File

@ -34,15 +34,18 @@ from core.models import User
from core.utils import get_start_of_semester, get_semester
from club.models import Club
class TrombiManager(models.Manager):
def get_queryset(self):
return super(TrombiManager, self).get_queryset()
class AvailableTrombiManager(models.Manager):
def get_queryset(self):
return super(AvailableTrombiManager,
self).get_queryset().filter(subscription_deadline__gte=date.today())
class Trombi(models.Model):
"""
This is the main class, the Trombi itself.
@ -81,6 +84,7 @@ class Trombi(models.Model):
def can_be_viewed_by(self, user):
return user.id in [u.user.id for u in self.users.all()]
class TrombiUser(models.Model):
"""
This class is only here to avoid cross references between the core, club,
@ -120,6 +124,7 @@ class TrombiUser(models.Model):
end=end_date,
).save()
class TrombiComment(models.Model):
"""
This represent a comment given by someone to someone else in the same Trombi
@ -135,6 +140,7 @@ class TrombiComment(models.Model):
return False
return user.id == self.author.user.id or user.can_edit(self.author.trombi)
class TrombiClubMembership(models.Model):
"""
This represent a membership to a club
@ -156,4 +162,3 @@ class TrombiClubMembership(models.Model):
def get_absolute_url(self):
return reverse('trombi:profile')

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from trombi.views import *
@ -43,4 +43,3 @@ urlpatterns = [
url(r'^membership/(?P<membership_id>[0-9]+)/edit$', UserTrombiEditMembershipView.as_view(), name='edit_membership'),
url(r'^membership/(?P<membership_id>[0-9]+)/delete$', UserTrombiDeleteMembershipView.as_view(), name='delete_membership'),
]

View File

@ -23,10 +23,10 @@
#
from django.http import Http404
from django.shortcuts import render, get_object_or_404, redirect
from django.shortcuts import get_object_or_404, redirect
from django.core.urlresolvers import reverse_lazy, reverse
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView, SingleObjectMixin
from django.views.generic import DetailView, RedirectView, TemplateView
from django.views.generic.edit import UpdateView, CreateView, DeleteView
from django.utils.translation import ugettext_lazy as _
from django import forms
from django.conf import settings
@ -35,11 +35,12 @@ from django.forms.models import modelform_factory
from datetime import date
from trombi.models import Trombi, TrombiUser, TrombiComment, TrombiClubMembership
from core.views.forms import SelectFile, SelectDate
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin, QuickNotifMixin
from core.views.forms import SelectDate
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, QuickNotifMixin
from core.models import User
from club.models import Club
class TrombiTabsMixin(TabedViewMixin):
def get_tabs_title(self):
return _("Trombi")
@ -69,9 +70,11 @@ class TrombiTabsMixin(TabedViewMixin):
'slug': 'admin_tools',
'name': _("Admin tools"),
})
except: pass
except:
pass
return tab_list
class TrombiForm(forms.ModelForm):
class Meta:
model = Trombi
@ -81,6 +84,7 @@ class TrombiForm(forms.ModelForm):
'comments_deadline': SelectDate,
}
class TrombiCreateView(CanCreateMixin, CreateView):
"""
Create a trombi for a club
@ -102,6 +106,7 @@ class TrombiCreateView(CanCreateMixin, CreateView):
else:
return self.form_invalid(form)
class TrombiEditView(CanEditPropMixin, TrombiTabsMixin, UpdateView):
model = Trombi
form_class = TrombiForm
@ -112,12 +117,14 @@ class TrombiEditView(CanEditPropMixin, TrombiTabsMixin, UpdateView):
def get_success_url(self):
return super(TrombiEditView, self).get_success_url() + "?qn_success"
class TrombiDetailView(CanEditMixin, QuickNotifMixin, TrombiTabsMixin, DetailView):
model = Trombi
template_name = 'trombi/detail.jinja'
pk_url_kwarg = 'trombi_id'
current_tab = "admin_tools"
class TrombiDeleteUserView(CanEditPropMixin, TrombiTabsMixin, DeleteView):
model = TrombiUser
pk_url_kwarg = 'user_id'
@ -127,6 +134,7 @@ class TrombiDeleteUserView(CanEditPropMixin, TrombiTabsMixin, DeleteView):
def get_success_url(self):
return reverse('trombi:detail', kwargs={'trombi_id': self.object.trombi.id}) + "?qn_success"
class TrombiModerateCommentsView(CanEditPropMixin, QuickNotifMixin, TrombiTabsMixin, DetailView):
model = Trombi
template_name = 'trombi/comment_moderation.jinja'
@ -139,10 +147,12 @@ class TrombiModerateCommentsView(CanEditPropMixin, QuickNotifMixin, TrombiTabsMi
author__trombi__id=self.object.id).exclude(target__user__id=self.request.user.id)
return kwargs
class TrombiModerateForm(forms.Form):
reason = forms.CharField(help_text=_("Explain why you rejected the comment"))
action = forms.CharField(initial="delete", widget=forms.widgets.HiddenInput)
class TrombiModerateCommentView(DetailView):
model = TrombiComment
template_name = 'core/edit.jinja'
@ -179,23 +189,26 @@ class TrombiModerateCommentView(DetailView):
return redirect(reverse('trombi:moderate_comments', kwargs={'trombi_id': self.object.author.trombi.id}) + "?qn_success")
raise Http404
def get_context_data(self, **kwargs):
kwargs = super(TrombiModerateCommentView, self).get_context_data(**kwargs)
kwargs['form'] = TrombiModerateForm()
return kwargs
# User side
class TrombiModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return _("%(name)s (deadline: %(date)s)") % {'name': str(obj), 'date': str(obj.subscription_deadline)}
class UserTrombiForm(forms.Form):
trombi = TrombiModelChoiceField(Trombi.availables.all(), required=False, label=_("Select trombi"),
help_text=_("This allows you to subscribe to a Trombi. "
"Be aware that you can subscribe only once, so don't play with that, "
"or you will expose yourself to the admins' wrath!"))
class UserTrombiToolsView(QuickNotifMixin, TrombiTabsMixin, TemplateView):
"""
Display a user's trombi tools
@ -222,6 +235,7 @@ class UserTrombiToolsView(QuickNotifMixin, TrombiTabsMixin, TemplateView):
kwargs['date'] = date
return kwargs
class UserTrombiEditPicturesView(TrombiTabsMixin, UpdateView):
model = TrombiUser
fields = ['profile_pict', 'scrub_pict']
@ -234,6 +248,7 @@ class UserTrombiEditPicturesView(TrombiTabsMixin, UpdateView):
def get_success_url(self):
return reverse('trombi:user_tools') + "?qn_success"
class UserTrombiEditProfileView(QuickNotifMixin, TrombiTabsMixin, UpdateView):
model = User
form_class = modelform_factory(User,
@ -253,6 +268,7 @@ class UserTrombiEditProfileView(QuickNotifMixin, TrombiTabsMixin, UpdateView):
def get_success_url(self):
return reverse('trombi:user_tools') + "?qn_success"
class UserTrombiResetClubMembershipsView(RedirectView):
permanent = False
@ -264,6 +280,7 @@ class UserTrombiResetClubMembershipsView(RedirectView):
def get_success_url(self):
return reverse('trombi:profile') + "?qn_success"
class UserTrombiDeleteMembershipView(TrombiTabsMixin, CanEditMixin, DeleteView):
model = TrombiClubMembership
pk_url_kwarg = "membership_id"
@ -274,6 +291,7 @@ class UserTrombiDeleteMembershipView(TrombiTabsMixin, CanEditMixin, DeleteView):
def get_success_url(self):
return super(UserTrombiDeleteMembershipView, self).get_success_url() + "?qn_success"
class UserTrombiEditMembershipView(CanEditMixin, TrombiTabsMixin, UpdateView):
model = TrombiClubMembership
pk_url_kwarg = "membership_id"
@ -300,6 +318,7 @@ class UserTrombiProfileView(TrombiTabsMixin, DetailView):
raise Http404()
return super(UserTrombiProfileView, self).get(request, *args, **kwargs)
class TrombiCommentFormView():
"""
Create/edit a trombi comment
@ -335,6 +354,7 @@ class TrombiCommentFormView():
kwargs['target'] = self.object.target
return kwargs
class TrombiCommentCreateView(TrombiCommentFormView, CreateView):
def form_valid(self, form):
target = get_object_or_404(TrombiUser, id=self.kwargs['user_id'])
@ -342,11 +362,10 @@ class TrombiCommentCreateView(TrombiCommentFormView, CreateView):
form.instance.target = target
return super(TrombiCommentCreateView, self).form_valid(form)
class TrombiCommentEditView(TrombiCommentFormView, CanViewMixin, UpdateView):
pk_url_kwarg = "comment_id"
def form_valid(self, form):
form.instance.is_moderated = False
return super(TrombiCommentEditView, self).form_valid(form)