Browse Source

Ajout des types NEW et unplanned aux stories

omassot 6 years ago
parent
commit
7698841698

+ 3 - 2
main/forms.py

@@ -48,7 +48,7 @@ class StoryForm(forms.ModelForm):
     class Meta:
         model = Story
         widgets = {'author': forms.HiddenInput()}
-        fields = ('epic', 'author', 'name', 'weight', 'description', 'assignees', 'sprints')
+        fields = ('epic', 'author', 'name', 'weight', 'story_type', 'description', 'assignees', 'sprints')
         
     def __init__(self, *args, **kwargs):
         super(StoryForm, self).__init__(*args, **kwargs)
@@ -56,6 +56,7 @@ class StoryForm(forms.ModelForm):
         self.fields['assignees'].required = False
         self.fields['sprints'].required = False
         self.fields['weight'].required = False
+        self.fields['story_type'].required = False
     
     def save(self, *args, **kwargs):
         if self.cleaned_data['weight'] == '':
@@ -96,5 +97,5 @@ class NewSprintForm(forms.ModelForm):
 class SprintForm(forms.ModelForm):
     class Meta:
         model = Sprint
-        fields = ('retro','unplanned_weight')
+        fields = ('retro',)
     retro = MartorFormField(label="Rétrospective")

+ 7 - 4
main/models.py

@@ -134,7 +134,6 @@ class Sprint(BaseModel):
     date_end = models.DateField()
     closed = models.BooleanField(default=False, verbose_name="Terminé")
     retro = MartorField(blank=True, default="", verbose_name="Bilan / Rétrospective")
-    unplanned_weight = models.IntegerField(default=0)
         
     def __str__(self):
         return "Sprint #{} ({:%d/%m/%Y} > {:%d/%m/%Y})".format(self.number, self.date_start, self.date_end)
@@ -165,6 +164,10 @@ class Sprint(BaseModel):
             row = cursor.fetchone()
         return row[0] if row[0] else 0
 
+    def unplanned(self):
+        total =  self.stories.filter(story_type=2).aggregate(Sum('weight'))['weight__sum']
+        return total if total is not None else 0
+
     @classmethod
     def current(cls):
         """ the current sprint is the first non-closed sprint """
@@ -196,7 +199,8 @@ class Story(BaseModel):
         ordering = ('closed', '-updated')
         
     WEIGHTS = ((None, '------'), (1, 1),(2, 2),(3, 3),(5, 5),(8, 8),(13, 13),(21, 21))
-        
+    STORY_TYPE = ((0, 'Standard'), (1, 'NEW'), (2, 'Non Planifiée'))
+    
     epic = models.ForeignKey(Epic, 
                              on_delete=models.PROTECT, 
                              null=True, blank=True,
@@ -204,6 +208,7 @@ class Story(BaseModel):
                              verbose_name="Epic")
     name = models.CharField(max_length=200, default="", verbose_name="Nom")
     weight = models.IntegerField(null=True, choices=WEIGHTS, verbose_name="Poids")
+    story_type = models.IntegerField(default=0, choices=STORY_TYPE, verbose_name="Type")
     description = MartorField(blank=True, default="", verbose_name="Description")
     closed = models.BooleanField(default=False, verbose_name="Clôturée")
     author = models.ForeignKey(User, 
@@ -247,5 +252,3 @@ class Story(BaseModel):
     def reopen(self):
         self.closed = False
         self.save()
-
-

+ 20 - 0
main/static/css/custom.css

@@ -414,6 +414,26 @@ select[multiple] option {
 	border-radius: 4px;
 }
 
+.new-story {
+	color: #006622;
+	height: 22px;
+	font-size: 12px;
+	font-weight: 600;
+	padding: 0 8px;
+	border: solid 1px #006622;
+	border-radius: 4px;
+}
+
+.unplanned-story {
+	color: 	#860000;
+	height: 22px;
+	font-size: 12px;
+	font-weight: 600;
+	padding: 0 8px;
+	border: solid 1px #860000;
+	border-radius: 4px;
+}
+
 /* Epic details */
 
 .story-closed {

+ 2 - 1
main/templates/reports/report_sprints.html

@@ -36,6 +36,7 @@
 					<b>Stories programmées: <a href="{% url 'story_index' %}?sprint={{ sprint.id }}">{{ sprint.nb_stories }}</a> </b>
 					<b>Vélocité prévue: {{ sprint.planned_velocity }} </b>
 					<b>Vélocité réelle: {{ sprint.real_velocity }} </b>
+					<b>Non-planifié: {{ sprint.unplanned }} </b>
 				</div>
 				
 				{% if sprint.retro %}
@@ -80,7 +81,7 @@ var myChart = new Chart(ctx, {
         },
         {
             label: 'Charge non-prévue',
-            data: [{% for sprint in sprints|dictsort:"id" %}{% if sprint.closed %}{{ sprint.unplanned_weight }},{% endif %}{% endfor %}],
+            data: [{% for sprint in sprints|dictsort:"id" %}{% if sprint.closed %}{{ sprint.unplanned }},{% endif %}{% endfor %}],
             backgroundColor: [
                 'rgba(255, 255, 255, 0.2)'
             ],

+ 0 - 1
main/templates/sprint_end.html

@@ -60,7 +60,6 @@
 			
 			{{ form.retro }}
 			
-			<div><p>Charge non-planifiée:</p> {{ form.unplanned_weight }} </div>
 	    	<div class="flex-row flex-end" style="margin-top: 10px;">
 	    		<input type="submit" value="Enregistrer et clôre le sprint">
 	    	</div>

+ 6 - 0
main/templates/story_form.html

@@ -88,6 +88,12 @@
 		    </div>
 		    {{ form.sprints }}
 		</p>
+			
+		<p>
+		    {{ form.story_type.errors }}
+		    {{ form.story_type.label_tag }}
+		    {{ form.story_type }}
+		</p>
 		
 		<div class="flex-row flex-end" style="margin-top: 10px;">
 			<span style="margin-right: 1em;">

+ 8 - 8
main/templates/story_index.html

@@ -37,14 +37,6 @@
 				{% endfor %}
 			</select>
 		
-			<label>Auteur: </label>
-			<select data-filter="author">
-				<option value="" selected="selected">Tous</option>
-				{% for user in users %}
-					<option value="{{ user.id }}">{{ user.first_name }} {{ user.last_name }}</option>
-				{% endfor %}
-			</select>
-		
 			<label>Assignées: </label>
 			<select data-filter="assignee">
 				<option value="" selected="selected">Tous</option>
@@ -52,6 +44,14 @@
 					<option value="{{ user.id }}">{{ user.first_name }} {{ user.last_name }}</option>
 				{% endfor %}
 			</select>
+			
+			<label>Type: </label>
+			<select data-filter="story-type">
+				<option value="" selected="selected">Tous</option>
+				<option value="0">Standard</option>
+				<option value="1">NEW</option>
+				<option value="2">Non-Planifiée</option>
+			</select>
 		</div>
 	
 		{% if stories %}

+ 2 - 0
main/templates/story_li.html

@@ -16,6 +16,8 @@
 	    	<div>  {# partie droite #}
 	       		<div class="flex-row controls">
 	       			{% if story.running %}<span class="running">En cours</span>{% endif %}
+	       			{% if story.story_type == 1 %}<span class="new-story">NEW</span>{% endif %}
+	       			{% if story.story_type == 2 %}<span class="unplanned-story">Non-Planifiée</span>{% endif %}
 					{% if story.weight %}<span>{% include 'weight_svg.html' with weight=story.weight h=20 %}</span>{% endif %}
 					{# <a href="#" class="disabled annotation"><i class="fa fa-comment"></i> 0</a> #}
 	       		</div>

+ 2 - 3
main/views.py

@@ -120,7 +120,6 @@ def sprint_end(request):
     
     if request.method == 'POST':
         current_sprint.retro = request.POST["retro"]
-        current_sprint.unplanned_weight = request.POST["unplanned_weight"]
         current_sprint.closed = True
         current_sprint.save()
         notify_sprint_end(current_sprint, next_sprint, request.user)
@@ -145,10 +144,10 @@ def story_index(request):
             stories = stories.filter(sprints=None)
         else:
             stories = stories.filter(sprints__id=filters['sprint'])
-    if 'author' in filters and filters['author']:
-        stories = stories.filter(author_id=filters['author'])
     if 'assignee' in filters and filters['assignee']:
         stories = stories.filter(assignees__id=filters['assignee'])
+    if 'story-type' in filters and filters['story-type']:
+        stories = stories.filter(story_type=filters['story-type'])
     
     count = stories.count()
     total_weight = stories.aggregate(Sum('weight'))['weight__sum']