diff --git a/media/css/main.css b/media/css/main.css
index ddaedb1..aae2231 100644
--- a/media/css/main.css
+++ b/media/css/main.css
@@ -269,6 +269,27 @@ span.pager {
background: #ccc;
}
+.request {
+ border: 1px #888 solid;
+ padding: 1ex;
+}
+
+.request-state-1 {
+ background: #eee;
+}
+
+.request-state-2 {
+ background: #fda;
+}
+
+.request-state-3 {
+ background: #ffc;
+}
+
+.request-state-4 {
+ background: #cfc;
+}
+
.git-tags {
border-left: 1px #888 solid;
float: right;
diff --git a/mgmt/models.py b/mgmt/models.py
index d1b3f3a..9115d7c 100644
--- a/mgmt/models.py
+++ b/mgmt/models.py
@@ -58,6 +58,34 @@ class Bug(Object):
class Admin:
pass
+class Request(Object):
+ project = models.ForeignKey(Project)
+ status = models.SmallIntegerField()
+ class Meta:
+ ordering = ("-created",)
+ class Admin:
+ pass
+
+class Task(Object):
+ author = models.ForeignKey(User,related_name='created_tasks')
+ to = models.ForeignKey(User)
+ project = models.ForeignKey(Project)
+ bug = models.ForeignKey(Bug,null=True)
+ request = models.ForeignKey(Request,null=True)
+ status = models.SmallIntegerField()
+ class Meta:
+ ordering = ("-created",)
+ class Admin:
+ pass
+
+class Report(Object):
+ author = models.ForeignKey(User)
+ task = models.ForeignKey(Task)
+ class Meta:
+ ordering = ("-created",)
+ class Admin:
+ pass
+
class Document(Object):
project = models.ForeignKey(Project)
author = models.ForeignKey(User)
diff --git a/mgmt/rights.py b/mgmt/rights.py
index 0cd64e7..63bea5d 100644
--- a/mgmt/rights.py
+++ b/mgmt/rights.py
@@ -18,7 +18,7 @@ def can_change_bug_state(user,bug,new_state):
return False
def can(user,action,object=None,target=None):
- if action in ['comment','send bug']:
+ if action in ['comment','send bug','request']:
return user.is_authenticated()
if action == 'edit':
if object.__class__.__name__ == 'Project':
@@ -32,6 +32,9 @@ def can(user,action,object=None,target=None):
if object.__class__.__name__ == 'PrivateMsg':
if action == 'delete':
return user.id == object.reciever.id
+ if object.__class__.__name__ == 'Request':
+ if action == 'change_state':
+ return user in object.project.admins.iterator()
if object.__class__.__name__ == 'Bug':
if action == 'set_priority':
return user in object.project.admins.iterator() and not object.status.id in [1,4,5]
diff --git a/mgmt/views.py b/mgmt/views.py
index 440c006..31c09d8 100644
--- a/mgmt/views.py
+++ b/mgmt/views.py
@@ -44,16 +44,16 @@ def get_comments(request,id,type):
############
class CommentForm(forms.Form):
- title = forms.CharField(required=False)
- text = forms.CharField(widget=forms.Textarea())
+ title = forms.CharField(required=False,label=u'Тема')
+ text = forms.CharField(widget=forms.Textarea(),label=u'Текст')
class BugForm(forms.Form):
- name = forms.CharField()
- component = forms.CharField(required=False)
- your_actions = forms.CharField(widget=forms.Textarea())
- expected = forms.CharField(widget=forms.Textarea())
- unexpected = forms.CharField(widget=forms.Textarea())
- text = forms.CharField(widget=forms.Textarea())
+ name = forms.CharField(label=u'Название')
+ component = forms.CharField(required=False,label=u'Компонент')
+ your_actions = forms.CharField(widget=forms.Textarea(),label=u'Ваши действия')
+ expected = forms.CharField(widget=forms.Textarea(),label=u'Что ожидали получить')
+ unexpected = forms.CharField(widget=forms.Textarea(),label=u'Что получили')
+ text = forms.CharField(widget=forms.Textarea(),label=u'Дополнения')
class BugPriorityForm(forms.Form):
priority = forms.ChoiceField(settings.PRIORITY_CHOICES,required=False)
@@ -61,6 +61,13 @@ class BugPriorityForm(forms.Form):
class BugComplexityForm(forms.Form):
complexity = forms.ChoiceField(settings.COMPLEXITY_CHOICES,required=False)
+class RequestForm(forms.Form):
+ title = forms.CharField(label=u'Название')
+ text = forms.CharField(widget=forms.Textarea(),label=u'Текст')
+
+class RequestStateForm(forms.Form):
+ state = forms.ChoiceField(settings.REQUEST_STATES)
+
class DocForm(forms.Form):
name = forms.CharField()
text = forms.CharField(widget=forms.Textarea())
@@ -405,6 +412,57 @@ def delete_private_message(request,msg):
msg.delete()
return HttpResponseRedirect(reverse('mgmt.views.user_page',args=(request.user.username,)))
+def requests(request,pid):
+ project = Project.objects.get(pk=pid)
+ rs = project.request_set.all()
+ can_request = can(request.user,'request',project)
+ return render_it('requests.html',
+ {'project': project,
+ 'can_request': can_request,
+ 'requests': rs},
+ request)
+
+def create_request(request,pid):
+ project = Project.objects.get(pk=pid)
+ if request.method == 'POST':
+ form = RequestForm(request.POST)
+ if form.is_valid():
+ title = form.cleaned_data['title']
+ text = form.cleaned_data['text']
+ rq = Request(name=title,
+ created = datetime.now(),
+ text=text,
+ status=0,
+ project=project)
+ rq.save()
+ return HttpResponseRedirect(reverse('mgmt.views.requests',args=(pid,)))
+ else:
+ return render_it('create_request.html',
+ {'project': project,
+ 'form': form},
+ request)
+ form = RequestForm()
+ return render_it('create_request.html',
+ {'project': project,
+ 'form': form},
+ request)
+
+def one_request(request,rid):
+ rq = Request.objects.get(pk=rid)
+ if request.method=='POST':
+ new_state = request.POST['state']
+ rq.status = new_state
+ rq.save()
+ can_change_state = can(request.user,'change_state',rq)
+ if can_change_state:
+ state_form = RequestStateForm({'state': rq.status})
+ else:
+ state_form = None
+ return render_it('request.html',
+ {'request': rq,
+ 'state_form': state_form},
+ request)
+
def message_preview(request):
if request.method=='POST':
text = request.POST['data']
diff --git a/settings.py b/settings.py
index b079056..3f3a553 100644
--- a/settings.py
+++ b/settings.py
@@ -136,3 +136,9 @@ COMPLEXITY_CHOICES = [(0,u'Тривиально'),
(4,u'Сложно'),
(5,u'Очень сложно'),
(6,u'Потребует изменения архитектуры')]
+
+REQUEST_STATES = [(0,u'Не рассмотрен'),
+ (1,u'Не принят'),
+ (2,u'Принят'),
+ (3,u'В работе'),
+ (4,u'Реализован')]
diff --git a/templates/create_request.html b/templates/create_request.html
new file mode 100644
index 0000000..814d28a
--- /dev/null
+++ b/templates/create_request.html
@@ -0,0 +1,11 @@
+{% extends "base.html" %}
+{% load prj_filters %}
+
+{% block title %}Создать запрос функциональности для проекта {{project.name}}{% endblock %}
+
+{% block main %}
+<h2>Создать запрос функциональности для проекта {{project.name}}</h2>
+
+{% form form %}
+
+{% endblock %}
diff --git a/templates/project_body.html b/templates/project_body.html
index 4b11587..c44b34b 100644
--- a/templates/project_body.html
+++ b/templates/project_body.html
@@ -11,5 +11,6 @@
<p class='links'>
<a href='{% url mgmt.views.project_documents project.id %}'>Документация</a>
<a href='{% url mgmt.views.project_bugs project.id %}'>Баги</a>
+ <a href='{% url mgmt.views.requests project.id %}'>Запросы функциональности</a>
</p>
</div>
diff --git a/templates/request.html b/templates/request.html
new file mode 100644
index 0000000..832a231
--- /dev/null
+++ b/templates/request.html
@@ -0,0 +1,17 @@
+{% extends "base.html" %}
+{% load prj_filters %}
+
+{% block title %}Запрос функциональности: {{request.name}}{% endblock %}
+
+{% block main %}
+<h2>Запрос функциональности: {{request.name}}</h2>
+
+<div class='request request-state-{{request.status}}'>
+ {{request.text|markdown|safe}}
+</div>
+
+{% if state_form %}
+ {% form state_form %}
+{% endif %}
+
+{% endblock %}
diff --git a/templates/requests.html b/templates/requests.html
new file mode 100644
index 0000000..3784a8f
--- /dev/null
+++ b/templates/requests.html
@@ -0,0 +1,28 @@
+{% extends "base.html" %}
+{% load prj_filters %}
+
+{% block title %}Запросы функциональности для проекта {{project.name}}{% endblock %}
+
+{% block main %}
+<h2>Запросы функциональности для проекта {{project.name}}</h2>
+
+{% if requests %}
+ {% if can_request %}
+ <p><a href='{% url mgmt.views.create_request project.id %}'>Создать запрос</a></p>
+ {% endif %}
+ {% for request in requests %}
+ <div class='request request-state-{{request.status}}'>
+ <h3><a href='{% url mgmt.views.one_request request.id %}'>{{request.name}}</a></h3>
+ {{request.text|markdown|safe}}
+ </div>
+ {% endfor %}
+{% else %}
+ <p>Запросов для этого проекта нет.</p>
+{% endif %}
+
+{% if can_request %}
+ <p><a href='{% url mgmt.views.create_request project.id %}'>Создать запрос</a></p>
+{% endif %}
+
+{% endblock %}
+
diff --git a/urls.py b/urls.py
index e72767d..ed777f5 100644
--- a/urls.py
+++ b/urls.py
@@ -6,14 +6,17 @@ urlpatterns = patterns('',
(r'^projects/(\d+)/$', "mgmt.views.one_project"),
(r'^projects/(\d+)/bugs/$', "mgmt.views.project_bugs"),
(r'^projects/(\d+)/docs/$', "mgmt.views.project_documents"),
+ (r'^projects/(\d+)/requests/$', 'mgmt.views.requests'),
(r'^docs/(\d+)/$', "mgmt.views.one_document"),
(r'^docs/(\d+)/edit/$', "mgmt.views.edit_document"),
+ (r'^docs/create/(\d+)/$', "mgmt.views.create_document"),
(r'^bugs/state/(\d+)/$', 'mgmt.views.bugs_by_state'),
(r'^bugs/(\d+)/$', "mgmt.views.one_bug"),
(r'^bugs/(\d+)/edit/$', "mgmt.views.edit_bug"),
(r'^bugs/report/(\d+)/$', 'mgmt.views.bug_report'),
(r'^bugs/$', "mgmt.views.all_bugs"),
- (r'^create/doc/(\d+)/$', "mgmt.views.create_document"),
+ (r'^requests/(\d+)/$', 'mgmt.views.one_request'),
+ (r'^requests/create/(\d+)/$', 'mgmt.views.create_request'),
(r'^users/(\w+)/$', 'mgmt.views.user_page'),
(r'^message/(\d+)/$', 'mgmt.views.private_message'),
(r'^message/(\d+)/delete/$', 'mgmt.views.delete_private_message'),