Skip to content

Commit

Permalink
Mise à jour du process pour créer des statistis de développement (#1669)
Browse files Browse the repository at this point in the history
  • Loading branch information
kolok authored Dec 2, 2024
1 parent 908e37b commit 7186171
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 15 deletions.
3 changes: 1 addition & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ Description plus détaillée de l'intention, l'approche ou de l'implémentation

- [ ] Bug fix
- [ ] Nouvelle fonctionnalité
- [ ] Mise à jour de données / DAG
- [ ] Les changements nécessitent une mise à jour de documentation
- [ ] Mise à jour de la documentation
- [ ] Refactoring de code (explication à retrouver dans la description)

## Auto-review
Expand Down
64 changes: 52 additions & 12 deletions DEVELOPPEUR.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ DUMP_FILE=</path/to/dump/file>
pg_restore -d "${DB_URL}" --clean --no-acl --no-owner --no-privileges "${DUMP_FILE}"
```

## MOCK
## MOCK CERBERE

En cas de non disponibilité de CERBERE en recette, il est possible de bouchonner l'authentification via CERBERE en définisant en variable d'environnement l'id de l'utilisateur à authentifier (à récupérer en base de données directement):

Expand All @@ -284,19 +284,59 @@ MOCK_CERBERE_USER_ID=

Si cette variable est définie, alors l'utilisateur est directement considéré comme authentifié et est utilisé pour récupérer les habilitations fournies par le SIAP.

## Catégoriser les PR
## Statistiques de développement

On catégorise chaque PR en utilisant les labels suivants :
Pour suivre le travail et les performances de développement de l'équipe APiLos, On extrait régulièrement des statistiques en inspectant les releases et PR github.
Les indicateurs sont extraits et agrégés par mois :

- bug
- enhancement
- documentation
- technical
- dependencies
* nb version majeur
* nb version mineur
* nb version de patch
* nb d'évolutions livrées
* nb de corrections livrées
* nb de mises à jour de dépendances
* nb de mises à jour technique
* nb de mises à jour de documentation
* nb d'escalades (demande de correction faite par l'équipe support suite à des retours des utilisateurs)
* nb de régressions (ça arrive :) )

Inspiré des labels proposés par défaut par Github
Ces statistiques sont basées sur l'interprétation des numéros de release qui utilise la convention `semantic versionning` (vx.y.z, x majeur, y mineur, z patch) et sur l'inspection des tags des PR de chaque release : cela est possible car on utilise la fonction `squash and merge` de github lors de l'intégration de la PR sur la branche principale `main`, on a un commit par PR sur la branche main.

On ajoute 2 labels en plus de cette catégorisation :
### Catégoriser les PR

- escalation : quand la PR vient de notre processus d'escalade avec l'équipe Support
- regression : Pour le suivi des regressions
Pour que le script d'extraction marche correctement, il est nécessaire de catégoriser les PR en les taguant avec les labels comme suit:

* bug
* enhancement
* documentation
* technical
* dependencies (tags déposé automatiquement par dépendadot lorsqu'il ouvre une PR)

Cette catégorisation est inspirée des labels proposés par défaut par Github

On ajoute aussi 2 labels en plus de cette catégorisation lorsque c'est approprié :

* escalation : quand la PR vient de notre processus d'escalade avec l'équipe Support
* regression : Pour le suivi des regressions

### Processus d'extraction des statistiques de developpement

Créer un token GitHub :

* Accéder à https://github.com
* Accéder au menu de votre profile
* Cliquer sur le menu `Settings`
* Cliquer sur le menu `Developper settings`
* Cliquer sur le menu `Personal access token` > `Tokens (classic)`
* Cliquer sur le bouton `Generate new token` > `Generate new token (classic)`
* Selectionner les options `public_repo`, `read:project`, `repo:status`, `repo_deployment`
* Créer le token et copier le

Lancer le script d'extraction des statistiques:

```sh
export GITHUB_TOKEN=<GITHUB_TOKEN>
python manage.py get_delivery_statistics --token $GITHUB_TOKEN --output DEV_STATISTICS.csv
```

Les statistiques de développement par mois sont disponibles dans le fichier `DEV_STATISTICS.csv`
169 changes: 169 additions & 0 deletions conventions/tests/views/test_preview_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import io
from uuid import UUID

import pytest
from django.core.files.storage import default_storage
from django.core.management import call_command
from django.urls import reverse
from pytest_django.asserts import assertTemplateUsed

from conventions.models.choices import ConventionStatut
from conventions.tests.factories import ConventionFactory
from users.tests.factories import GroupFactory, RoleFactory, UserFactory
from users.type_models import TypeRole


@pytest.fixture(scope="session")
def django_db_setup(django_db_blocker):
with django_db_blocker.unblock():
call_command("loaddata", "auth.json")


@pytest.fixture
def convention():
return ConventionFactory(uuid=UUID("00000000-0000-0000-0000-000000000000"))


@pytest.fixture
def logged_in_user(client, convention):
user = UserFactory()
role = RoleFactory(
user=user,
administration=convention.programme.administration,
typologie=TypeRole.INSTRUCTEUR.label,
group=GroupFactory(name="instructeur"),
)
client.force_login(user)

session = client.session
session["role"] = {"id": role.id}
session.save()

return user


@pytest.mark.django_db
@pytest.mark.parametrize(
"statut, expected_template, expected_content",
[
(ConventionStatut.SIGNEE.label, "test-signed-file.pdf", b"Test PDF signes"),
(ConventionStatut.RESILIEE.label, "test-signed-file.pdf", b"Test PDF signes"),
(ConventionStatut.DENONCEE.label, "test-signed-file.pdf", b"Test PDF signes"),
(ConventionStatut.ANNULEE.label, "test-signed-file.pdf", b"Test PDF signes"),
(
ConventionStatut.INSTRUCTION.label,
"00000000-0000-0000-0000-000000000000.pdf",
b"Test PDF en instruction",
),
(
ConventionStatut.CORRECTION.label,
"00000000-0000-0000-0000-000000000000.pdf",
b"Test PDF en instruction",
),
(
ConventionStatut.A_SIGNER.label,
"00000000-0000-0000-0000-000000000000.pdf",
b"Test PDF en instruction",
),
],
)
def test_display_pdf(
client, convention, logged_in_user, statut, expected_template, expected_content
):
convention.statut = statut
convention.nom_fichier_signe = "test-signed-file.pdf"
convention.save()

convention_path = "conventions/00000000-0000-0000-0000-000000000000/convention_docs"
default_storage.save(
f"{convention_path}/{convention.nom_fichier_signe}",
io.BytesIO(b"Test PDF signes"),
)
default_storage.save(
f"{convention_path}/00000000-0000-0000-0000-000000000000.pdf",
io.BytesIO(b"Test PDF en instruction"),
)

url = reverse("conventions:display_pdf", args=[convention.uuid])
response = client.get(url)

assert response.status_code == 200
assert f'inline; filename="{expected_template}"' in response["Content-Disposition"]
content = b"".join(response.streaming_content)
assert content == expected_content

default_storage.delete(f"{convention_path}/{convention.nom_fichier_signe}")
default_storage.delete(f"{convention_path}/00000000-0000-0000-000000000000.pdf")


@pytest.mark.django_db
def test_display_pdf_fallback(client, convention, logged_in_user):
convention.uuid = UUID("00000000-0000-0000-0000-000000000001")
convention.statut = ConventionStatut.INSTRUCTION.label
convention.save()

convention_path = "conventions/00000000-0000-0000-0000-000000000001/convention_docs"
default_storage.save(
f"{convention_path}/00000000-0000-0000-0000-000000000001.docx",
io.BytesIO(b"Test DOCX"),
)

url = reverse("conventions:display_pdf", args=[convention.uuid])
response = client.get(url)

assert response.status_code == 200
assert (
'inline; filename="00000000-0000-0000-0000-000000000001.docx"'
in response["Content-Disposition"]
)
content = b"".join(response.streaming_content)
assert content == b"Test DOCX"

default_storage.save(
f"{convention_path}/00000000-0000-0000-0000-000000000001.pdf",
io.BytesIO(b"Test PDF"),
)

url = reverse("conventions:display_pdf", args=[convention.uuid])
response = client.get(url)

assert response.status_code == 200
assert (
'inline; filename="00000000-0000-0000-0000-000000000001.pdf"'
in response["Content-Disposition"]
)
content = b"".join(response.streaming_content)
assert content == b"Test PDF"

default_storage.delete(
f"{convention_path}/00000000-0000-0000-0000-000000000001.pdf"
)
default_storage.delete(
f"{convention_path}/00000000-0000-0000-0000-000000000001.docx"
)


@pytest.mark.django_db
@pytest.mark.parametrize(
"statut",
[
(ConventionStatut.SIGNEE.label),
(ConventionStatut.RESILIEE.label),
(ConventionStatut.DENONCEE.label),
(ConventionStatut.ANNULEE.label),
(ConventionStatut.INSTRUCTION.label),
(ConventionStatut.CORRECTION.label),
(ConventionStatut.A_SIGNER.label),
],
)
def test_display_pdf_no_file(client, convention, logged_in_user, statut):
convention.uuid = UUID("00000000-0000-0000-0000-000000000002")
convention.statut = statut
convention.nom_fichier_signe = ""
convention.save()

url = reverse("conventions:display_pdf", args=[convention.uuid])
response = client.get(url)

assert response.status_code == 200
assertTemplateUsed(response, "conventions/no_convention_document.html")
1 change: 0 additions & 1 deletion conventions/views/conventions.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,6 @@ def display_pdf(request, convention_uuid):
ConventionStatut.RESILIEE.label,
ConventionStatut.DENONCEE.label,
ConventionStatut.ANNULEE.label,
ConventionStatut.A_SIGNER.label,
]
and convention.nom_fichier_signe
and default_storage.exists(f"{convention_path}/{convention.nom_fichier_signe}")
Expand Down

0 comments on commit 7186171

Please sign in to comment.