-
Notifications
You must be signed in to change notification settings - Fork 165
Fusion de zds-antispam dans zds-site #6720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
…és" dans la sidebar des forums. Fix zestedesavoir#6467 - Vérifiez que les liens s'affichent bien dans la sidebar. - Testez que les liens redirigent vers les bonnes pages. - Assurez-vous que le comportement est correct sur mobile et desktop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merci pour cette PR ! Voici quelques remarques qui me viennent après une première lecture, n'hésites pas à argumenter si tu as un avis différent ;)
Remarques concernant tous les fichiers :
- Pour une meilleure lisibilité, il est recommandé de grouper les lignes d'importation (dans l'ordre : modules standard de Python, puis modules installés avec Pip, puis modules de ton projet) et de les trier par ordre alphabétique. Ça peut être fait automatiquement avec
isort
et je viens de créer une PR pour l'inclure dans le projet (#6721). - Tu es parti du code existant et c'est très bien, mais n'hésites pas à restructurer complètement le code (passer d'une classe à une fonction, renommer les variables, etc.) car d'une part le cas d'usage est très différent (on n'est plus du tout sur un script qui tourne tous les X minutes et fait des appels API) et d'autre part le code est vieux !
zds/utils/spam_training.py
Outdated
current_dir = os.path.dirname(os.path.abspath(__file__)) | ||
json_path = os.path.join(current_dir, "spamdata.json") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dans les projets Django, on préfère en règle générale créer les chemins à partir de BASE_DIR
pour partir de la même base partout. De plus, on préfère travailler avec des objets Path
que le module os.path
quand c'est possible. La documentation donne les équivalences entre les deux modules : https://docs.python.org/fr/3.13/library/pathlib.html#corresponding-tools
from django.conf import settings
chemin = settings.BASE_DIR / "sous-dossier" / "fichier.txt"
zds/utils/signals.py
Outdated
# Logger Setup | ||
logger = logging.getLogger(__name__) | ||
logger.setLevel(logging.ERROR) | ||
current_dir = os.path.dirname(os.path.abspath(__file__)) | ||
log_file = os.path.join(current_dir, "spam_signals.log") | ||
|
||
# File Handler | ||
handler = logging.FileHandler(log_file) | ||
handler.setLevel(logging.ERROR) | ||
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") | ||
handler.setFormatter(formatter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dans les fichiers de code du projet, on définit le logger avec seulement cette simple ligne :
# Logger Setup | |
logger = logging.getLogger(__name__) | |
logger.setLevel(logging.ERROR) | |
current_dir = os.path.dirname(os.path.abspath(__file__)) | |
log_file = os.path.join(current_dir, "spam_signals.log") | |
# File Handler | |
handler = logging.FileHandler(log_file) | |
handler.setLevel(logging.ERROR) | |
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") | |
handler.setFormatter(formatter) | |
logger = logging.getLogger(__name__) |
Le reste est géré dans la configuration :
zds-site/zds/settings/abstract_base/django.py
Lines 253 to 287 in 5b98a51
LOGGING = { | |
"version": 1, | |
"disable_existing_loggers": False, | |
"formatters": { | |
"verbose": { | |
"format": "%(levelname)s %(name)s %(message)s", | |
}, | |
}, | |
"handlers": { | |
"console": { | |
"level": "DEBUG", | |
"class": "logging.StreamHandler", | |
"formatter": "verbose", | |
}, | |
}, | |
"loggers": { | |
"django": { | |
"handlers": ["console"], | |
"level": "WARNING", | |
}, | |
"django.request": { | |
"level": "ERROR", | |
"handlers": [], | |
"propagate": False, | |
}, | |
"zds": { | |
"handlers": ["console"], | |
"level": "WARNING", | |
}, | |
"root": { | |
"handlers": ["console"], | |
"level": "WARNING", | |
}, | |
}, | |
} |
zds/utils/spam_detector.py
Outdated
current_dir = os.path.dirname(os.path.abspath(__file__)) | ||
log_file = os.path.join(current_dir, "spam_detector.log") | ||
|
||
handler = logging.FileHandler(log_file, mode="a") | ||
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") | ||
handler.setFormatter(formatter) | ||
self.logger.addHandler(handler) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idem, géré dans la configuration du projet
current_dir = os.path.dirname(os.path.abspath(__file__)) | |
log_file = os.path.join(current_dir, "spam_detector.log") | |
handler = logging.FileHandler(log_file, mode="a") | |
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") | |
handler.setFormatter(formatter) | |
self.logger.addHandler(handler) |
zds/utils/spam_detector.py
Outdated
self.logger = logging.getLogger("zds.spam") | ||
self.logger.setLevel(logging.ERROR) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
À remplacer par logger = logging.getLogger(__name__)
juste après les lignes d'importation
self.logger = logging.getLogger("zds.spam") | |
self.logger.setLevel(logging.ERROR) |
zds/utils/spam_detector.py
Outdated
from django.contrib.auth.models import User | ||
|
||
|
||
class SpamDetector: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Je pense que le code gagnerait en lisibilité en remplaçant cette classe par une fonction, car le code est assez linéaire.
zds/utils/spam_detector.py
Outdated
reported_users_file = "reported_users.txt" | ||
reported_users = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dans le fonctionnement actuel, le code est exécuté toutes les 5 minutes donc ce fichier permet de ne pas vérifier tout le temps la même biographie. Étant donné que dans le nouveau fonctionnement ce code est exécuté pour une biographie seulement à sa création ou à sa modification, ce fichier n'a plus d'utilité ! Tu peux donc retirer tout ce qui est lié à reported_users
.
Mise a jour du fork
Merge tests pour zds-antispam integration
Modification spam_training.py et spam_detector.py : 1-Remplacement des données dynamiques par un jeu de données statique 2-Utilisation de données factices pour l'entraînement du modèle Ajout des tests unitaires qui couvrent : 1-Profils sans biographie 2-Contenu spam 3-Contenu valide Lancer python3 manage.py test zds.utils.tests.tests_antispam
Modification spam_training.py et spam_detector.py : 1-Remplacement des données dynamiques par un jeu de données statique 2-Utilisation de données factices pour l'entraînement du modèle Ajout des tests unitaires qui couvrent : 1-Profils sans biographie 2-Contenu spam 3-Contenu valide Lancer python3 manage.py test zds.utils.tests.tests_antispam
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…persistance et de retrain_spam_filter.py pour les tâches planifiées.
…persistance et de retrain_spam_filter.py pour les tâches planifiées.
Restructuring
Ajout de reentraîner le modèle que nécessaire
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pas mal de petits points à revoir.
Ce serait bien d'ajouter quelques commentaires dans le code pour expliquer le fonctionnement général de l'antispam. Et également rédiger la documentation correspondante.
Par contre, la façon dont vous avez conçu le code ne prend pas du tout en compte l'évolution qui permettrait de chercher du spam dans d'autres champs. Mais il ne vous reste sans doute pas assez de temps pour mettre ça en place...
Pour réaliser la tache pour l'évolution, nous pensons qu’il suffit d’entraîner le modèle séparément sur chacun des champs (commentaire, biographie et contenu), puis de le lancer pour l’analyse de spam. Ensuite, on pourrait simplement adapter le message d’alerte en fonction du champ analysé. Est-ce bien la démarche à suivre, ou y a-t-il une autre approche recommandée ? |
C'est pas tant d'appliquer le même processus qui est difficile, mais de coder la chose de façon à ce qu'on puisse très facilement ajouter les champs dans lesquels on souhaite chercher du spam. Dans l'idéal, pour dire au système qu'on souhaite chercher du spam dans un champ supplémentaire, ce serait juste une ligne de code ajouter. On en avait discuté lors d'une réunion de dev's. |
…ec le même modèle pour tous les attributs.
zds/antispam/spam_detector.py
Outdated
alert_kwargs = { | ||
"author": User.objects.get(username="antispam"), | ||
"scope": scope, | ||
"text": _(f"Potential spam detected in {instance_info}, field '{field_name}'."), | ||
"pubdate": datetime.now(), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alert_kwargs = { | |
"author": User.objects.get(username="antispam"), | |
"scope": scope, | |
"text": _(f"Potential spam detected in {instance_info}, field '{field_name}'."), | |
"pubdate": datetime.now(), | |
} | |
scope_to_alert_kwargs = { | |
"PROFILE": "profile", | |
"FORUM": "comment", | |
"CONTENT": "content", | |
} | |
alert_kwargs = { | |
"author": User.objects.get(username="antispam"), | |
"scope": scope, | |
"text": _(f"Potential spam detected in {instance_info}, field '{field_name}'."), | |
"pubdate": datetime.now(), | |
scope_to_alert_kwargs[scope]: instance, | |
} |
Mais peut-être qu'on peut définir le scope directement à la valeur attendue par Alert
? La question c'est : est-ce qu' on a envie/besoin de distinguer les messages sur le forum et les commentaires des contenus pour détecter du spam ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
J'ai fait des changements comme j'ai compris le commentaire, je ne suis pas 100% sûre que c'était ce que était signifié par le commentaire
zds/antispam/spam_fields.py
Outdated
"model": Profile, | ||
"field": "biography", | ||
"scope": "PROFILE", | ||
"get_instance_info": lambda instance: f"Profile of user '{instance.user.username}'", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
J'ai l'impression que cet attribut correspond aux méthodes __str__()
des des objets ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C’est changé pour le profil, mais pas pour les commentaires, car cela entraînerait du HTML en clair dans les alertes.
zds/antispam/spam_model_manager.py
Outdated
def prepare_training_data(self, content_type): | ||
""" | ||
Prepare training data for the given content type. | ||
""" | ||
# Implement logic to fetch or generate training data based on content_type | ||
bios = ["example spam text", "example non-spam text"] | ||
labels = [0, 1] # 0 for spam, 1 for non-spam | ||
return bios, labels | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cette méthode n'a pas sa place dans cette classe, elle doit être directement dans les tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
En général, cette fonction doit aussi collecter des données pour l’entraînement de la base de données, mais il y avait une petite erreur dans le commit.
Pour la création des données de test, j’ai également ajouté un autre commentaire.
…gnes inutiles dans apps.py.
…model_manager.py.
…ec le même modèle pour tous les attributs.
…gnes inutiles dans apps.py.
Vérification des tests : -Dans le répertoire racine, lance : python3 manage.py test zds.utils.tests.tests_antispam
Vérification des tests : -Dans le répertoire racine, lance : python3 manage.py test zds.utils.tests.tests_spam_manager
Fichiers modifiés/ajoutés : 1-Modifié arborescence-back.rst : ajout le modèle antispam/ à l’arborescence de zds/ 2-antispam.rst : rédaction de la documentation pour le modèle antispam
…s données d’entraînement
Description
Dans cette pull request, nous fusionnons le module zds-antispam dans zds-site afin de supprimer la dépendance aux requêtes HTTP et permettre un accès direct aux données via l’ORM de Django. Concrètement, cela permettra :
Fichiers modifiés
signals.py
Exécute le Spam Detector à chaque mise à jour d'une biographie.
spam_detector.py
Contient la logique principale du module antispam : analyse des contenus et règles permettant de marquer un texte comme suspect.
spam_training.py
Regroupe les fonctions nécessaires à l’entraînement ou à la mise à jour des règles de détection de spam, en utilisant les données recueillies sur Zeste de Savoir.
Contrôle Qualité
Pour vérifier la bonne intégration de zds-antispam :
python manage.py migrate
puisyarn test
pour vous assurer du bon fonctionnement global.En cas de problème ou de question, n’hésitez pas à le mentionner dans cette discussion.
TODO: