Candidature: Stockage du CV dans un objet de la base de donnée [GEN-2443] - #6087
Conversation
francoisfreitag
left a comment
There was a problem hiding this comment.
Ça m’a l’air très bien parti.
e7f04e6 to
7e85902
Compare
|
C'est bon, la première étape est prête. Prochaine PR: lier toutes les candidatures existantes aux File correspondants. |
francoisfreitag
left a comment
There was a problem hiding this comment.
Très content de voir du progrès sur ce sujet
|
|
||
| new_key = str(pathlib.Path(self.key).with_stem(str(uuid.uuid4()))) | ||
| copy_file(self.key, new_key) | ||
| return File.objects.create(key=new_key) |
There was a problem hiding this comment.
| return File.objects.create(key=new_key) | |
| return type(self).objects.create(key=new_key) |
There was a problem hiding this comment.
Je suis parti sur self.__class__ parce qu'il me semble que c'est plus la façon de faire de Django, mais je me trompe peut être.
| mocker.patch( | ||
| "itou.files.models.uuid.uuid4", | ||
| return_value=uuid.UUID("22222222-2222-2222-2222-222222222222"), | ||
| ) |
There was a problem hiding this comment.
On peut même faire sans mock, non ?
On vérifie juste que new_file.key != key. On pourrait aller jusqu’à définir content puis comparer le contenu de new_file avec content.
There was a problem hiding this comment.
Je veux vérifier que seul le stem change (qu'on garde l'extension et le "resume/" au début)
Je pourrais utiliser pathlib, ou une regex, mais je trouvais ça aussi simple de mocker (comme on le fait dans le parcours de candidature)
There was a problem hiding this comment.
Pas du tout convaincu par le mock d'un symbole de la stdlib mais seulement celui importé dans un module précis 😨.
Surtout que foncièrement on s'en fiche de tester que le stem change, ce qu'on veux vraiment tester c'est que .copy() crée un nouveau File() et ne réutilise pas le fichier d'origine donc que key change.
There was a problem hiding this comment.
Pour moi, c'est important qu'on s'assure que le fichier est différent (key différent) mais aussi que l'extension et le repertoire est le même.
Sinon, on pourrait juste renommer le fichier en uuid.uuid4() et les personnes qui ne téléchargeraient aurait un fichier "illisible" car sans extension.
J'ai donc mis une regex
Je me note de nettoyer tous les mock similaires quand j'aurai retiré le champ resume_link
| hiring_end_at = factory.LazyFunction(lambda: datetime.now(UTC).date() + relativedelta(years=2)) | ||
| resume_link = "https://server.com/rockie-balboa.pdf" | ||
| resume = factory.SubFactory(FileFactory) | ||
| resume_link = factory.LazyAttribute(lambda o: default_storage.url(o.resume.key)) |
There was a problem hiding this comment.
Je me rends compte qu'il faut storages["public"] ici, je corrige
| expected_message = f"Le 15/07/2024 à 11h52, Pierre DUPONT a écrit :\n\n{job_application.message}" | ||
| assert response.context["form"].initial["message"] == expected_message | ||
|
|
||
| mocker.patch("itou.files.models.copy_file", return_value=None) |
There was a problem hiding this comment.
Pourquoi mocker ? On a un S3 fonctionnel.
There was a problem hiding this comment.
la factory ne crée pas le fichier sur le s3 ^^'
Je vais ajouter un post_generation pour le faire et éviter le mock
There was a problem hiding this comment.
Bon, la copie via l'api s3 fait des trucs bizarre (la key est préfixée par un uuid, ce qui n'est pas le cas en prod, et c'est donc probablement lié à une séparation entre tests)
J'ai donc modifié le code pour se baser sur l'api storages de Django (on doit donc télécharger le fichier pour le saver à nouveau sous un autre nom).
Et j'ai du modifier test_copy car c'est pareil : en écrivant le fichier avec le client s3, impossible d'y accéder via storages.
There was a problem hiding this comment.
Ah, c'est à cause de storage_prefix_per_test...
Vu le faible nombre de copie qu'on a à faire, je pense que je vais laisser l'utilisation de storages plutôt que du s3_client.
| # CHeck back_url | ||
| assertContains(response, transfer_step_2_url) | ||
|
|
||
| mocker.patch("itou.files.models.copy_file", return_value=None) |
There was a problem hiding this comment.
Idem, on doit pouvoir faire sans mock. (et ce faisant, on doit pouvoir supprimer le test spécifique test_copy 🤷)
There was a problem hiding this comment.
j'ai pu virer le mock, mais je propose de laisser test_copy qui s'assure que le contenu est le même
7e85902 to
3bf6838
Compare
vincentporte
left a comment
There was a problem hiding this comment.
j'ai l'impression qu'il me manque une pièce du puzzle. La gestion des fichiers du S3 me semble plus simple sur la commu, sans trop savoir d'où ça vient.
| if create and extracted: | ||
| public_storage = storages["public"] | ||
| public_storage.save(self.resume.key, extracted) | ||
|
|
There was a problem hiding this comment.
Que se passe-t-il qd un test utilise la factory sans le param with_file ?
resume contient la FK du File lié mais sans objet dans le S3 ?
There was a problem hiding this comment.
Oui, c'est bien ça
À part quand on doit copier le fichier, on n'a pas besoin d'y accéder
There was a problem hiding this comment.
Mais est-ce qu'on a besoin d'avoir un resume par défaut ?
Les tests explosent comment dans ce cas là ? 😁
There was a problem hiding this comment.
Je ne sais pas, je veux bien regarder quand on aura basculé sur resume (il va y avoir beaucoup de tests à adapter)
| import pathlib | ||
| import uuid | ||
|
|
||
| from django.core.files.storage import default_storage |
There was a problem hiding this comment.
La commu utilise from storages.backends.s3boto3 import S3Boto3Storage, j'ai le souvenir qu'il n'y avait pas besoin d'utiliser default_storage.save() ni de déclarer public_storages puis public_storages.save().
Vous rappelez-vous des raisons du choix de default_storage ?
There was a problem hiding this comment.
Dans les settings il y a :
STORAGES = {
"default": {
"BACKEND": "storages.backends.s3.S3Storage",
},
"public": {
"BACKEND": "itou.utils.storage.s3.PublicStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
},
}
Sachant que le PublicStorage, c'est un S3Storage avec querystring_auth = False
Du coup on permet à django de gérer les fichiers sans avoir besoin de tout faire à la main.
Ça nous permet aussi de "tricher" en ajoutant un sous répertoire dans le bucket pour compartimenter les fichiers des tests grâce à cette fixture
from storages.backends.s3boto3 import S3Boto3Storage est un alias sur storages.backends.s3.S3Storage donc on utilise exactement la même chose que la communauté en dessous.
3bf6838 to
ad7281e
Compare
rsebille
left a comment
There was a problem hiding this comment.
A priori j'avais pas envoyé, j'espère n'avoir rien oublié ou perdu :/
| mocker.patch( | ||
| "itou.files.models.uuid.uuid4", | ||
| return_value=uuid.UUID("22222222-2222-2222-2222-222222222222"), | ||
| ) |
There was a problem hiding this comment.
Pas du tout convaincu par le mock d'un symbole de la stdlib mais seulement celui importé dans un module précis 😨.
Surtout que foncièrement on s'en fiche de tester que le stem change, ce qu'on veux vraiment tester c'est que .copy() crée un nouveau File() et ne réutilise pas le fichier d'origine donc que key change.
| if create and extracted: | ||
| public_storage = storages["public"] | ||
| public_storage.save(self.resume.key, extracted) | ||
|
|
There was a problem hiding this comment.
Mais est-ce qu'on a besoin d'avoir un resume par défaut ?
Les tests explosent comment dans ce cas là ? 😁
f1cddfc to
4ab16d5
Compare
4ab16d5 to
44f8d45
Compare
🤔 Pourquoi ?
Cela nous permettra a terme de mieux gérer les fichiers du S3
Le lien entre candidature et File est un OneToOneField pour s'assurer qu'on n'utilise pas le même fichier sur plusieurs candidatures.
🍰 Comment ?
🚨 À vérifier
🏝️ Comment tester ?
💻 Captures d'écran