From 1ba789411db19afd836b50444811e67c0c10e9cf Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 29 Apr 2020 06:52:39 +0200 Subject: [PATCH] Import data of old database --- .gitignore | 3 + apps/member/admin.py | 17 +- .../management/commands/import_olddb.py | 225 ++++++++++++++++++ apps/member/models.py | 111 ++++++--- apps/tournament/models.py | 9 + locale/fr/LC_MESSAGES/django.po | 151 +++++++----- tfjm/settings.py | 5 + 7 files changed, 412 insertions(+), 109 deletions(-) create mode 100644 apps/member/management/commands/import_olddb.py diff --git a/.gitignore b/.gitignore index f908240..aa0a2fc 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ db.sqlite3 # Ignore migrations during first phase dev migrations/ + +# Don't git personal data +import_olddb/ diff --git a/apps/member/admin.py b/apps/member/admin.py index 5f3e6e7..0ba73a3 100644 --- a/apps/member/admin.py +++ b/apps/member/admin.py @@ -1,6 +1,6 @@ from django.contrib.auth.admin import admin, UserAdmin from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin -from member.models import TFJMUser, AbstractDocument, Document, Solution, Synthesis +from member.models import TFJMUser, Document, Solution, Synthesis, MotivationLetter, Authorization @admin.register(TFJMUser) @@ -8,14 +8,19 @@ class TFJMUserAdmin(UserAdmin): list_display = ('email', 'first_name', 'last_name', 'role', ) -@admin.register(AbstractDocument) -class AbstractDocumentAdmin(PolymorphicParentModelAdmin): - child_models = (Document, Solution, Synthesis,) +@admin.register(Document) +class DocumentAdmin(PolymorphicParentModelAdmin): + child_models = (Authorization, MotivationLetter, Solution, Synthesis,) polymorphic_list = True -@admin.register(Document) -class DocumentAdmin(PolymorphicChildModelAdmin): +@admin.register(Authorization) +class AuthorizationAdmin(PolymorphicChildModelAdmin): + pass + + +@admin.register(MotivationLetter) +class MotivationLetterAdmin(PolymorphicChildModelAdmin): pass diff --git a/apps/member/management/commands/import_olddb.py b/apps/member/management/commands/import_olddb.py new file mode 100644 index 0000000..f007215 --- /dev/null +++ b/apps/member/management/commands/import_olddb.py @@ -0,0 +1,225 @@ +from django.core.management import BaseCommand +from django.db import transaction + +from member.models import TFJMUser, Document, Solution, Synthesis, Authorization, MotivationLetter +from tournament.models import Team, Tournament + + +class Command(BaseCommand): + def add_arguments(self, parser): + parser.add_argument('--tournaments', '-t', action="store_true", help="Import tournaments") + parser.add_argument('--teams', '-T', action="store_true", help="Import teams") + parser.add_argument('--users', '-u', action="store_true", help="Import users") + parser.add_argument('--documents', '-d', action="store_true", help="Import all documents") + + def handle(self, *args, **options): + if "tournaments" in options: + self.import_tournaments() + + if "teams" in options: + self.import_teams() + + if "users" in options: + self.import_users() + + if "documents" in options: + self.import_documents() + + @transaction.atomic + def import_tournaments(self): + print("Importing tournaments...") + with open("import_olddb/tournaments.csv") as f: + first_line = True + for line in f: + if first_line: + first_line = False + continue + + line = line[:-1].replace("\"", "") + args = line.split(";") + args = [arg if arg and arg != "NULL" else None for arg in args] + + if Tournament.objects.filter(pk=args[0]).exists(): + continue + + obj_dict = { + "id": args[0], + "name": args[1], + "size": args[2], + "place": args[3], + "price": args[4], + "description": args[5], + "date_start": args[6], + "date_end": args[7], + "date_inscription": args[8], + "date_solutions": args[9], + "date_syntheses": args[10], + "final": args[11], + "year": args[12], + } + with transaction.atomic(): + Tournament.objects.create(**obj_dict) + print(self.style.SUCCESS("Tournaments imported")) + + @transaction.atomic + def import_teams(self): + self.stdout.write("Importing teams...") + with open("import_olddb/teams.csv") as f: + first_line = True + for line in f: + if first_line: + first_line = False + continue + + line = line[:-1].replace("\"", "") + args = line.split(";") + args = [arg if arg and arg != "NULL" else None for arg in args] + + if Team.objects.filter(pk=args[0]).exists(): + continue + + obj_dict = { + "id": args[0], + "name": args[1], + "trigram": args[2], + "tournament": Tournament.objects.get(pk=args[3]), + "inscription_date": args[13], + "validation_status": args[14].lower(), + "selected_for_final": args[15], + "access_code": args[16], + "year": args[17], + } + with transaction.atomic(): + Team.objects.create(**obj_dict) + print(self.style.SUCCESS("Teams imported")) + + @transaction.atomic + def import_users(self): + self.stdout.write("Importing users...") + with open("import_olddb/users.csv") as f: + first_line = True + for line in f: + if first_line: + first_line = False + continue + + line = line[:-1].replace("\"", "") + args = line.split(";") + args = [arg if arg and arg != "NULL" else None for arg in args] + + if TFJMUser.objects.filter(pk=args[0]).exists(): + continue + + obj_dict = { + "id": args[0], + "email": args[1], + "username": args[1], + "password": "bcrypt$" + args[2], + "last_name": args[3], + "first_name": args[4], + "birth_date": args[5], + "gender": "male" if args[6] == "M" else "female", + "address": args[7], + "postal_code": args[8], + "city": args[9], + "country": args[10], + "phone_number": args[11], + "school": args[12], + "student_class": args[13], + "responsible_name": args[14], + "responsible_phone": args[15], + "responsible_email": args[16], + "description": args[17].replace("\\n", "\n") if args[17] else None, + "role": args[18].lower(), + "team": Team.objects.get(pk=args[19]) if args[19] else None, + "year": args[20], + "date_joined": args[23], + "is_active": args[18] == "ADMIN", # TODO Replace it with "True" + "is_staff": args[18] == "ADMIN", + "is_superuser": args[18] == "ADMIN", + } + with transaction.atomic(): + TFJMUser.objects.create(**obj_dict) + self.stdout.write(self.style.SUCCESS("Users imported")) + + @transaction.atomic + def import_documents(self): + self.stdout.write("Importing documents...") + with open("import_olddb/documents.csv") as f: + first_line = True + for line in f: + if first_line: + first_line = False + continue + + line = line[:-1].replace("\"", "") + args = line.split(";") + args = [arg if arg and arg != "NULL" else None for arg in args] + + if Document.objects.filter(file=args[0]).exists(): + continue + + obj_dict = { + "file": args[0], + "uploaded_at": args[5], + } + if args[4] != "MOTIVATION_LETTER": + obj_dict["user"] = TFJMUser.objects.get(args[1]), + obj_dict["type"] = args[4].lower() + else: + obj_dict["team"] = Team.objects.get(pk=args[2]) + with transaction.atomic(): + if args[4] != "MOTIVATION_LETTER": + Authorization.objects.create(**obj_dict) + else: + MotivationLetter.objects.create(**obj_dict) + self.stdout.write(self.style.SUCCESS("Authorizations imported")) + + with open("import_olddb/solutions.csv") as f: + first_line = True + for line in f: + if first_line: + first_line = False + continue + + line = line[:-1].replace("\"", "") + args = line.split(";") + args = [arg if arg and arg != "NULL" else None for arg in args] + + if Document.objects.filter(file=args[0]).exists(): + continue + + obj_dict = { + "file": args[0], + "team": Team.objects.get(pk=args[1]), + "problem": args[3], + "uploaded_at": args[4], + } + with transaction.atomic(): + Solution.objects.create(**obj_dict) + self.stdout.write(self.style.SUCCESS("Solutions imported")) + + with open("import_olddb/syntheses.csv") as f: + first_line = True + for line in f: + if first_line: + first_line = False + continue + + line = line[:-1].replace("\"", "") + args = line.split(";") + args = [arg if arg and arg != "NULL" else None for arg in args] + + if Document.objects.filter(file=args[0]).exists(): + continue + + obj_dict = { + "file": args[0], + "team": Team.objects.get(pk=args[1]), + "dest": "defender" if args[3] == "DEFENDER" else "opponent" + if args[4] == "OPPOSANT" else "rapporteur", + "uploaded_at": args[4], + } + with transaction.atomic(): + Synthesis.objects.create(**obj_dict) + self.stdout.write(self.style.SUCCESS("Syntheses imported")) diff --git a/apps/member/models.py b/apps/member/models.py index c6fa1a3..72ac9c8 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -147,58 +147,74 @@ class TFJMUser(AbstractUser): return self.first_name + " " + self.last_name -class AbstractDocument(PolymorphicModel): +class Document(PolymorphicModel): file = models.FileField( unique=True, upload_to="files/", verbose_name=_("file"), ) - team = models.ForeignKey( - Team, - on_delete=models.CASCADE, - related_name="documents", - verbose_name=_("team"), - ) - - tournament = models.ForeignKey( - Tournament, - on_delete=models.CASCADE, - related_name="documents", - verbose_name=_("tournament"), - ) - - type = models.CharField( - max_length=32, - choices=[ - ("parental_consent", _("Parental consent")), - ("photo_consent", _("Photo consent")), - ("sanitary_plug", _("Sanitary plug")), - ("motivation_letter", _("Motivation letter")), - ("scholarship", _("Scholarship")), - ("solution", _("Solution")), - ("synthesis", _("Synthesis")), - ], - verbose_name=_("type"), - ) - uploaded_at = models.DateTimeField( auto_now_add=True, verbose_name=_("uploaded at"), ) - class Meta: - verbose_name = _("abstract document") - verbose_name_plural = _("abstract documents") - - -class Document(AbstractDocument): class Meta: verbose_name = _("document") verbose_name_plural = _("documents") +class Authorization(Document): + user = models.ForeignKey( + TFJMUser, + on_delete=models.CASCADE, + related_name="authorizations", + verbose_name=_("user"), + ) + + type = models.CharField( + max_length=32, + choices=[ + ("parental_consent", _("Parental consent")), + ("photo_consent", _("Photo consent")), + ("sanitary_plug", _("Sanitary plug")), + ("scholarship", _("Scholarship")), + ], + verbose_name=_("type"), + ) + + class Meta: + verbose_name = _("authorization") + verbose_name_plural = _("authorizations") + + def __str__(self): + return _("{authorization} for user {user}").format(authorization=self.type, user=str(self.user)) + + +class MotivationLetter(Document): + team = models.ForeignKey( + Team, + on_delete=models.CASCADE, + related_name="motivation_letters", + verbose_name=_("team"), + ) + + class Meta: + verbose_name = _("motivation letter") + verbose_name_plural = _("motivation letters") + + def __str__(self): + return _("Motivation letter of team {team} ({trigram})").format(team=self.team.name, trigram=self.team.trigram) + + class Solution(Document): + team = models.ForeignKey( + Team, + on_delete=models.CASCADE, + related_name="solutions", + verbose_name=_("team"), + ) + problem = models.PositiveSmallIntegerField( verbose_name=_("problem"), ) @@ -211,10 +227,27 @@ class Solution(Document): verbose_name = _("solution") verbose_name_plural = _("solutions") + def __str__(self): + return _("Solution of team {trigram} for problem {problem}")\ + .format(trigram=self.team.trigram, problem=self.problem) + class Synthesis(Document): - problem = models.PositiveSmallIntegerField( - verbose_name=_("problem"), + team = models.ForeignKey( + Team, + on_delete=models.CASCADE, + related_name="syntheses", + verbose_name=_("team"), + ) + + dest = models.CharField( + max_length=16, + choices=[ + ("defender", _("Defender")), + ("opponent", _("Opponent")), + ("rapporteur", _("Rapporteur")), + ], + verbose_name=_("dest"), ) def save(self, **kwargs): @@ -224,3 +257,7 @@ class Synthesis(Document): class Meta: verbose_name = _("synthesis") verbose_name_plural = _("syntheses") + + def __str__(self): + return _("Synthesis of team {trigram} that is {dest} for problem {problem}")\ + .format(trigram=self.team.trigram, dest=self.dest, problem=self.problem) diff --git a/apps/tournament/models.py b/apps/tournament/models.py index f4cf6da..4e13909 100644 --- a/apps/tournament/models.py +++ b/apps/tournament/models.py @@ -69,6 +69,9 @@ class Tournament(models.Model): verbose_name = _("tournament") verbose_name_plural = _("tournaments") + def __str__(self): + return self.name + class Team(models.Model): name = models.CharField( @@ -130,6 +133,9 @@ class Team(models.Model): verbose_name_plural = _("teams") unique_together = (('name', 'year',), ('trigram', 'year',),) + def __str__(self): + return self.name + class Payment(models.Model): user = models.OneToOneField( @@ -173,3 +179,6 @@ class Payment(models.Model): class Meta: verbose_name = _("payment") verbose_name_plural = _("payments") + + def __str__(self): + return _("Payment of {user}").format(str(self.user)) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index edcc0a6..456aeff 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: TFJM2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-29 02:45+0000\n" +"POT-Creation-Date: 2020-04-29 04:43+0000\n" "PO-Revision-Date: 2020-04-29 02:30+0000\n" "Last-Translator: Yohann D'ANELLO \n" "Language-Team: fr \n" @@ -26,8 +26,9 @@ msgstr "membre" msgid "email" msgstr "Adresse électronique" -#: apps/member/models.py:23 apps/member/models.py:161 -#: apps/tournament/models.py:129 apps/tournament/models.py:146 +#: apps/member/models.py:23 apps/member/models.py:196 apps/member/models.py:209 +#: apps/member/models.py:230 apps/tournament/models.py:132 +#: apps/tournament/models.py:152 msgid "team" msgstr "équipe" @@ -116,11 +117,12 @@ msgid "participant" msgstr "participant" #: apps/member/models.py:135 apps/tournament/models.py:61 -#: apps/tournament/models.py:117 +#: apps/tournament/models.py:120 msgid "year" msgstr "année" -#: apps/member/models.py:139 apps/tournament/models.py:139 +#: apps/member/models.py:139 apps/member/models.py:172 +#: apps/tournament/models.py:145 msgid "user" msgstr "utilisateur" @@ -132,84 +134,96 @@ msgstr "utilisateurs" msgid "file" msgstr "fichier" -#: apps/member/models.py:168 apps/tournament/apps.py:7 -#: apps/tournament/models.py:69 apps/tournament/models.py:87 -msgid "tournament" -msgstr "tournoi" +#: apps/member/models.py:159 +msgid "uploaded at" +msgstr "téléversé le" -#: apps/member/models.py:174 +#: apps/member/models.py:163 +msgid "document" +msgstr "document" + +#: apps/member/models.py:164 +msgid "documents" +msgstr "documents" + +#: apps/member/models.py:178 msgid "Parental consent" msgstr "Autorisation parentale" -#: apps/member/models.py:175 +#: apps/member/models.py:179 msgid "Photo consent" msgstr "Autorisation de droit à l'image" -#: apps/member/models.py:176 +#: apps/member/models.py:180 msgid "Sanitary plug" msgstr "Fiche sanitaire" -#: apps/member/models.py:177 -msgid "Motivation letter" -msgstr "Lettre de motivation" - -#: apps/member/models.py:178 apps/tournament/models.py:157 +#: apps/member/models.py:181 apps/tournament/models.py:163 msgid "Scholarship" msgstr "Bourse" -#: apps/member/models.py:179 -msgid "Solution" -msgstr "Solution" - -#: apps/member/models.py:180 -msgid "Synthesis" -msgstr "Synthèse" - -#: apps/member/models.py:182 +#: apps/member/models.py:183 msgid "type" msgstr "type" #: apps/member/models.py:187 -msgid "uploaded at" -msgstr "téléversé le" +msgid "authorization" +msgstr "autorisation" -#: apps/member/models.py:191 -msgid "abstract document" -msgstr "document générique" +#: apps/member/models.py:188 +msgid "authorizations" +msgstr "autorisations" -#: apps/member/models.py:192 -msgid "abstract documents" -msgstr "documents génériques" +#: apps/member/models.py:200 +msgid "motivation letter" +msgstr "lettre de motivation" -#: apps/member/models.py:197 -msgid "document" -msgstr "document" +#: apps/member/models.py:201 +msgid "motivation letters" +msgstr "lettres de motivation" -#: apps/member/models.py:198 -msgid "documents" -msgstr "documents" - -#: apps/member/models.py:203 apps/member/models.py:217 +#: apps/member/models.py:213 msgid "problem" msgstr "problème" -#: apps/member/models.py:211 +#: apps/member/models.py:221 msgid "solution" msgstr "solution" -#: apps/member/models.py:212 +#: apps/member/models.py:222 msgid "solutions" msgstr "solutions" -#: apps/member/models.py:225 +#: apps/member/models.py:236 +msgid "Defender" +msgstr "Défenseur" + +#: apps/member/models.py:237 +msgid "Opponent" +msgstr "Opposant" + +#: apps/member/models.py:238 +msgid "Rapporteur" +msgstr "Rapporteur" + +#: apps/member/models.py:240 +msgid "dest" +msgstr "destinataire" + +#: apps/member/models.py:248 msgid "synthesis" msgstr "synthèse" -#: apps/member/models.py:226 +#: apps/member/models.py:249 msgid "syntheses" msgstr "synthèses" -#: apps/tournament/models.py:10 apps/tournament/models.py:76 +#: apps/tournament/apps.py:7 apps/tournament/models.py:69 +#: apps/tournament/models.py:90 +msgid "tournament" +msgstr "tournoi" + +#: apps/tournament/models.py:10 apps/tournament/models.py:79 msgid "name" msgstr "nom" @@ -253,78 +267,83 @@ msgstr "finale" msgid "tournaments" msgstr "tournois" -#: apps/tournament/models.py:81 +#: apps/tournament/models.py:84 msgid "trigram" msgstr "trigramme" -#: apps/tournament/models.py:92 +#: apps/tournament/models.py:95 msgid "inscription date" msgstr "date d'inscription" -#: apps/tournament/models.py:98 apps/tournament/models.py:166 +#: apps/tournament/models.py:101 apps/tournament/models.py:172 msgid "Registration not validated" msgstr "Inscription non validée" -#: apps/tournament/models.py:99 apps/tournament/models.py:167 +#: apps/tournament/models.py:102 apps/tournament/models.py:173 msgid "Waiting for validation" msgstr "En attente de validation" -#: apps/tournament/models.py:100 apps/tournament/models.py:168 +#: apps/tournament/models.py:103 apps/tournament/models.py:174 msgid "Registration validated" msgstr "Inscription validée" -#: apps/tournament/models.py:102 apps/tournament/models.py:170 +#: apps/tournament/models.py:105 apps/tournament/models.py:176 msgid "validation status" msgstr "statut de validation" -#: apps/tournament/models.py:107 +#: apps/tournament/models.py:110 msgid "selected for final" msgstr "sélectionnée pour la finale" -#: apps/tournament/models.py:113 +#: apps/tournament/models.py:116 msgid "access code" msgstr "code d'accès" -#: apps/tournament/models.py:130 +#: apps/tournament/models.py:133 msgid "teams" msgstr "équipes" -#: apps/tournament/models.py:152 +#: apps/tournament/models.py:158 msgid "Not paid" msgstr "Non payé" -#: apps/tournament/models.py:153 +#: apps/tournament/models.py:159 msgid "Credit card" msgstr "Carte bancaire" -#: apps/tournament/models.py:154 +#: apps/tournament/models.py:160 msgid "Bank check" msgstr "Chèque bancaire" -#: apps/tournament/models.py:155 +#: apps/tournament/models.py:161 msgid "Bank transfer" msgstr "Virement bancaire" -#: apps/tournament/models.py:156 +#: apps/tournament/models.py:162 msgid "Cash" msgstr "Espèces" -#: apps/tournament/models.py:160 +#: apps/tournament/models.py:166 msgid "payment method" msgstr "moyen de paiement" -#: apps/tournament/models.py:174 +#: apps/tournament/models.py:180 msgid "payment" msgstr "paiement" -#: apps/tournament/models.py:175 +#: apps/tournament/models.py:181 msgid "payments" msgstr "paiements" -#: tfjm/settings.py:123 +#: apps/tournament/models.py:184 +#, python-brace-format +msgid "Payment of {user}" +msgstr "Paiement de {user}" + +#: tfjm/settings.py:128 msgid "English" msgstr "Anglais" -#: tfjm/settings.py:124 +#: tfjm/settings.py:129 msgid "French" msgstr "Français" diff --git a/tfjm/settings.py b/tfjm/settings.py index a4b4e74..2f7d9a8 100644 --- a/tfjm/settings.py +++ b/tfjm/settings.py @@ -113,6 +113,11 @@ AUTH_PASSWORD_VALIDATORS = [ AUTH_USER_MODEL = 'member.TFJMUser' +PASSWORD_HASHERS = [ + 'django.contrib.auth.hashers.PBKDF2PasswordHasher', + 'django.contrib.auth.hashers.BCryptPasswordHasher', +] + # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/