omassot 7 年之前
父节点
当前提交
a85f2b551d
共有 5 个文件被更改,包括 95 次插入8 次删除
  1. 19 1
      main/forms.py
  2. 5 4
      main/models.py
  3. 38 0
      main/templates/sprint_new.html
  4. 1 0
      main/urls.py
  5. 32 3
      main/views.py

+ 19 - 1
main/forms.py

@@ -8,7 +8,7 @@ from django.contrib.auth.models import User
 from martor.fields import MartorFormField
 
 from main.models import Story, Epic, Comment, Sprint
-
+from django.forms.fields import DateField
 
 class RegisterForm(UserCreationForm):
                             
@@ -75,6 +75,24 @@ class CommentForm(forms.ModelForm):
         if not self.prefix:
             self.prefix = str(self.instance.id) if self.instance and self.instance.id else "new"
  
+class NewSprintForm(forms.ModelForm):
+    class Meta:
+        model = Sprint
+        widgets = {'number': forms.HiddenInput()}
+        fields = ('date_start','date_end','number')
+        
+    date_start = DateField(label='Date de début', widget=forms.TextInput(attrs={'class':'datepicker'}))
+    date_end = DateField(label='Date de fin', widget=forms.TextInput(attrs={'class':'datepicker'}))
+
+    def clean(self):
+        cleaned_data = super().clean()
+        
+        if not cleaned_data.get("date_end") > cleaned_data.get("date_start"):
+            raise forms.ValidationError("La date de fin doit être postérieure à la date de début.")
+
+        if not cleaned_data.get("date_start") >= Sprint.current().date_end:
+            raise forms.ValidationError("La date de début doit être postérieure à la date de fin du sprint en cours.")
+
 class SprintForm(forms.ModelForm):
     class Meta:
         model = Sprint

+ 5 - 4
main/models.py

@@ -128,14 +128,15 @@ class Sprint(BaseModel):
     class Meta:
         verbose_name = "sprint"
         verbose_name_plural = "sprints"
-        ordering = ('-date_start', )
+        ordering = ('-number', )
+    number = models.IntegerField(default=0)
     date_start = models.DateField()
     date_end = models.DateField()
     closed = models.BooleanField(default=False, verbose_name="Terminé")
     retro = MartorField(blank=True, default="", verbose_name="Bilan / Rétrospective")
         
     def __str__(self):
-        return "Sprint #{} ({:%d/%m/%Y} > {:%d/%m/%Y})".format(self.id, self.date_start, self.date_end)
+        return "Sprint #{} ({:%d/%m/%Y} > {:%d/%m/%Y})".format(self.number, self.date_start, self.date_end)
     
     def running(self):
         return self.date_start <= datetime.date.today() <= self.date_end and not self.closed
@@ -167,14 +168,14 @@ class Sprint(BaseModel):
     def current(cls):
         """ the current sprint is the first non-closed sprint """
         try:
-            return Sprint.objects.filter(closed = False).order_by('date_start')[0]
+            return Sprint.objects.filter(closed = False).order_by('number')[0]
         except IndexError:
             return None
 
     @classmethod
     def next(cls):
         try:
-            return Sprint.objects.filter(closed = False).order_by('date_start')[1]
+            return Sprint.objects.filter(closed = False).order_by('number')[1]
         except IndexError:
             return None
 

+ 38 - 0
main/templates/sprint_new.html

@@ -0,0 +1,38 @@
+{% extends '_layout.html' %}
+
+{% block title %}
+	Nouveau Sprint
+{% endblock %}
+
+
+{% block breadcrumb %}
+	<li><a href="{% url 'index' %}">Accueil</a></li>
+	<li><a>Nouveau Sprint</a></li>
+{% endblock %}
+
+{% block main %}
+	<header>
+		<div class="flex-row">
+			<h2 class="flex-extend">Nouveau sprint</h2>
+			<span>
+			</span>
+		</div>
+	</header>
+
+	<form action="." method="post" enctype="multipart/form-data">
+		{% csrf_token %}
+		
+		{{ form.as_p }}
+	
+		<div class="flex-row flex-end" style="margin-top: 10px;">
+			<span style="margin-right: 1em;">
+				<a class="button alt" href="{% url 'index' %}">Annuler</a>
+			</span>
+	    	<div class="flex-row flex-end">
+	    		<input type="submit" value="Enregistrer">
+	    	</div>
+		</div>
+	
+	</form>
+
+{% endblock %}

+ 1 - 0
main/urls.py

@@ -15,6 +15,7 @@ urlpatterns = [
     path(r'change_password/', views.change_password, name='change_password'),
     path('edition/', views.backlog_editor, name='backlog_editor'),
     path('sprintend/', views.sprint_end, name='sprint_end'),
+    path('sprintnew/', views.sprint_new, name='sprint_new'),
     path('stories/', views.story_index, name='story_index'),
     path('stories/<int:story_id>', views.story_details, name='story_details'),
     path('stories/create/', views.story_create, name='story_create'),

+ 32 - 3
main/views.py

@@ -1,4 +1,4 @@
-from datetime import datetime
+from datetime import datetime, timedelta
 
 from django.contrib.auth import logout, login, update_session_auth_hash
 from django.contrib.auth.decorators import login_required
@@ -13,7 +13,7 @@ from notifications.models import Notification
 from notifications.signals import notify
 
 from main.forms import StoryForm, EpicForm, RegisterForm, ProfileForm, \
-    CommentForm, SprintForm
+    CommentForm, SprintForm, NewSprintForm
 from main.models import Story, Epic, Sprint, Comment, Project
 
 
@@ -72,11 +72,40 @@ def backlog_editor(request):
     
     return render(request, 'backlog_editor.html', {'epics': epics, 'closed': closed})
 
+def sprint_new(request):
+    if request.method == 'POST':
+        form = NewSprintForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return redirect("sprint_end")
+    else:
+        sprint = Sprint()
+        
+        current_sprint = Sprint.current()
+        sprint.number = current_sprint.number + 1
+        
+        new_start = current_sprint.date_end + timedelta(days=1)
+        while new_start.weekday() >= 5:
+            new_start = new_start + timedelta(days=1)
+        
+        new_end = new_start + timedelta(days=13)
+        while new_end.weekday() >= 5:
+            new_end = new_end - timedelta(days=1)
+        
+        sprint.date_start = new_start.strftime('%d/%m/%Y')
+        sprint.date_end = new_end.strftime('%d/%m/%Y')
+        
+        form = NewSprintForm(instance=sprint)
+    return render(request, 'sprint_new.html', {'form': form })
+
 def sprint_end(request):
     
     current_sprint = Sprint.current()
     next_sprint = Sprint.next()
     
+    if not next_sprint:
+        return redirect("sprint_new")
+    
     if request.method == 'POST':
         current_sprint.retro = request.POST["retro"]
         current_sprint.closed = True
@@ -314,7 +343,7 @@ def report_activity(request):
         epics_activity[epic]["sixmonths"] = 0
         
     current = True
-    for sprint in Sprint.objects.filter(date_end__lt = datetime.today()).order_by('-date_start')[:12]:
+    for sprint in Sprint.objects.filter(date_end__lt = datetime.datetime.today()).order_by('-date_start')[:12]:
         for story in sprint.stories.all():
             if not story.epic:
                 continue