diff --git a/members/locale/de/LC_MESSAGES/django.mo b/members/locale/de/LC_MESSAGES/django.mo index a8536595..688a4e57 100644 Binary files a/members/locale/de/LC_MESSAGES/django.mo and b/members/locale/de/LC_MESSAGES/django.mo differ diff --git a/members/locale/de/LC_MESSAGES/django.po b/members/locale/de/LC_MESSAGES/django.po index d078bd05..f5d9d092 100644 --- a/members/locale/de/LC_MESSAGES/django.po +++ b/members/locale/de/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-11-11 22:23+0100\n" +"POT-Creation-Date: 2024-11-17 11:13+0100\n" "PO-Revision-Date: 2024-10-21 09:04+00:00\n" "Last-Translator: Auto-po-lyglot using qwen2.5:3b (https://github.com/" "leolivier/auto-po-lyglot)\n" @@ -34,9 +34,8 @@ msgid "" "By checking this box, you agree to this site's privacy policy" msgstr "" -"Durch Anklicken dieses Kästchens erklärst du dich mit den Datenschutzrichtlinien " -"dieser Website einverstanden." +"Durch Anklicken dieses Kästchens erklärst du dich mit den Datenschutzrichtlinien dieser Website einverstanden." #: members/forms.py:125 msgid "Name of the invited person (will appear in the received email)" @@ -163,135 +162,140 @@ msgctxt "CSV Field" msgid "deathdate" msgstr "Todesdatum" -#: members/models.py:45 +#: members/models.py:38 +msgctxt "CSV Field" +msgid "managed_by" +msgstr "verwaltet_von" + +#: members/models.py:46 #: members/templates/members/members/members_directory.html:29 #: members/views/views_directory.py:59 msgid "Name" msgstr "Name" -#: members/models.py:47 members/templates/members/family/family_detail.html:14 +#: members/models.py:48 members/templates/members/family/family_detail.html:14 msgid "Parent family" msgstr "Elternfamilie" -#: members/models.py:50 +#: members/models.py:51 msgid "family" msgstr "Familie" -#: members/models.py:51 +#: members/models.py:52 msgid "families" msgstr "Familien" -#: members/models.py:66 members/templates/members/address/address_detail.html:8 +#: members/models.py:67 members/templates/members/address/address_detail.html:8 msgid "Number & Street name" msgstr "Nummer & Straße" -#: members/models.py:68 +#: members/models.py:69 msgid "Complementary info" msgstr "Komplizierte Informationen" -#: members/models.py:70 +#: members/models.py:71 #: members/templates/members/address/address_detail.html:16 msgid "Zip code" msgstr "Gebietscode" -#: members/models.py:72 +#: members/models.py:73 #: members/templates/members/address/address_detail.html:20 msgid "City" msgstr "Stadt" -#: members/models.py:74 +#: members/models.py:75 #: members/templates/members/address/address_detail.html:24 msgid "Country" msgstr "Land" -#: members/models.py:88 +#: members/models.py:89 msgid "address" msgstr "Adresse" -#: members/models.py:89 +#: members/models.py:90 msgid "addresses" msgstr "Adressen" -#: members/models.py:97 -msgid "Managing member" -msgstr "Geschäftsführer" +#: members/models.py:98 +msgid "Member manager" +msgstr "Mitglied Manager" -#: members/models.py:102 +#: members/models.py:103 #: members/templates/members/members/member_detail.html:37 #: members/templates/members/members/members_directory.html:33 #: members/views/views_directory.py:59 msgid "Address" msgstr "Adresse" -#: members/models.py:104 +#: members/models.py:105 #: members/templates/members/members/member_detail.html:45 #: members/templates/members/members/members_directory.html:31 #: members/views/views_directory.py:59 msgid "Phone" msgstr "Telefon" -#: members/models.py:106 +#: members/models.py:107 #: members/templates/members/members/member_detail.html:49 #: members/templates/members/members/members_directory.html:30 msgid "Birthdate" msgstr "Geburtsdatum" -#: members/models.py:107 +#: members/models.py:108 msgid "Click on the month name or the year to change them quickly" msgstr "Klicke auf den Monat oder das Jahr, um sie schnell zu ändern" -#: members/models.py:110 +#: members/models.py:111 msgid "Is dead" msgstr "Ist tot" -#: members/models.py:111 +#: members/models.py:112 msgid "Death date" msgstr "Todesdatum" -#: members/models.py:113 +#: members/models.py:114 #: members/templates/members/members/member_detail.html:53 msgid "Website" msgstr "Website" -#: members/models.py:115 +#: members/models.py:116 #: members/templates/members/members/member_detail.html:57 msgid "Family" msgstr "Familie" -#: members/models.py:117 +#: members/models.py:118 #: members/templates/members/members/member_detail.html:61 msgid "Who I am" msgstr "Wer ich bin" -#: members/models.py:118 +#: members/models.py:119 msgid "Describe yourself, your likes and dislikes..." msgstr "Beschreibe dich selbst, deine Vorlieben und Unvorlieben..." -#: members/models.py:119 +#: members/models.py:120 msgid "My hobbies" msgstr "Meine Hobbys" -#: members/models.py:120 +#: members/models.py:121 msgid "Provide a list of hobbies separated by commas" msgstr "Bitte gib eine Liste von Hobbys, getrennt durch Kommas, an." -#: members/models.py:122 +#: members/models.py:123 msgid "Privacy consent" msgstr "Datenschutz-Einwilligung" -#: members/models.py:124 +#: members/models.py:125 msgid "Followers" msgstr "Follower" -#: members/models.py:133 +#: members/models.py:134 msgid "member" msgstr "Mitglied" -#: members/models.py:134 +#: members/models.py:135 msgid "members" msgstr "Mitglieder" -#: members/models.py:192 +#: members/models.py:195 #, python-brace-format msgid "Death date {dd} is before birthdate {bd}" msgstr "Todesdatum {dd} ist vor Geburtsdatum {bd}" @@ -351,7 +355,7 @@ msgid "Greetings!" msgstr "Hallo!" #: members/templates/members/email/email_verification_msg.html:27 -#: members/tests/tests_member.py:330 +#: members/tests/tests_member.py:347 msgid "" "You received this mail because you attempted to create an account on our " "website or because a member created and activated your account" @@ -360,7 +364,7 @@ msgstr "" "Website zu erstellen oder weil ein Mitglied dein Konto aktiviert hat." #: members/templates/members/email/email_verification_msg.html:28 -#: members/tests/tests_member.py:332 +#: members/tests/tests_member.py:349 msgid "" "Please click on the link below to confirm the email and activate your " "account." @@ -850,15 +854,15 @@ msgstr "Mitglied Details" #: members/templates/members/members/member_detail.html:26 #: members/templates/members/members/member_upsert.html:26 -#: members/tests/tests_member.py:238 +#: members/tests/tests_member.py:236 msgid "Active member" msgstr "Aktive Mitglied" #: members/templates/members/members/member_detail.html:28 #: members/templates/members/members/member_upsert.html:28 -#: members/tests/tests_member.py:238 +#: members/tests/tests_member.py:236 msgid "Managed member" -msgstr "Ge manage Member" +msgstr "Verwaltetes Mitglied" #: members/templates/members/members/member_detail.html:41 #: members/templates/members/members/members_directory.html:32 @@ -996,7 +1000,7 @@ msgid "Show directory" msgstr "Zeige Verzeichnis" #: members/templates/members/members/members.html:19 -#: members/views/views_member.py:106 +#: members/views/views_member.py:122 msgid "Create Member" msgstr "Erstellen Sie Mitglied" @@ -1152,16 +1156,16 @@ msgstr "Ändern Sie die ausgewählten %(name)s" msgid "Add another %(translated_name)s" msgstr "Fügt eine weitere %(translated_name)s hinzu" -#: members/tests/tests_member.py:100 members/views/views_member.py:194 +#: members/tests/tests_member.py:100 members/views/views_member.py:210 msgid "Member deleted" msgstr "Mitglied gelöscht" -#: members/tests/tests_member.py:213 members/tests/tests_member.py:218 -#: members/views/views_member.py:143 members/views/views_member.py:157 +#: members/tests/tests_member.py:211 members/tests/tests_member.py:216 +#: members/views/views_member.py:159 members/views/views_member.py:173 msgid "You do not have permission to edit this member." msgstr "Du hast keine Berechtigung, diese Mitgliedschaft zu bearbeiten." -#: members/tests/tests_member.py:392 +#: members/tests/tests_member.py:409 msgid "Error: Cannot activate a dead member " msgstr "Fehler: Ein totes Mitglied kann nicht aktiviert werden " @@ -1287,13 +1291,14 @@ msgstr "" "Unbekannte Spalte in der CSV-Datei: \"%(fieldname)s\". Gültige Felder sind " "%(all_names)s" -#: members/views/views_import_export.py:98 +#: members/views/views_import_export.py:104 #, python-format msgid "Avatar not found: %(avatar)s for username %(username)s. Ignored..." msgstr "" "Avatar nicht gefunden: %(avatar)s für Benutzername %(username)s. Ignoriert..." -#: members/views/views_import_export.py:107 +#: members/views/views_import_export.py:113 +#, python-format msgid "" "Error saving avatar (%(warning)s): %(avatar)s for username %(username)s. " "Ignored..." @@ -1301,7 +1306,36 @@ msgstr "" "Fehler beim Speichern des Avatar- (%(warning)s): %(avatar)s für Benutzername " "%(username)s. Ignoriert..." -#: members/views/views_import_export.py:220 +#: members/views/views_import_export.py:126 +msgid "" +"You requested to activate imported members. All managers will be ignored." +msgstr "" +"Du hast beantragt, importierte Mitglieder zu aktivieren. Alle Mitglied Managers werden ignoriert." + +#: members/views/views_import_export.py:129 +#, python-format +msgid "Manager %(manager)s not found for member %(member)s. Ignoring..." +msgstr "Manager %(manager)s nicht gefunden für Mitglied %(member)s. Ignoriert..." + +#: members/views/views_import_export.py:136 +#, python-format +msgid "Member %(member)s is active. Ignoring manager %(manager)s" +msgstr "Das Mitglied %(member)s ist aktiv. Ignoriert Mitglied Manager %(manager)s" + +#: members/views/views_import_export.py:145 +#, python-format +msgid "" +"No manager provided for member %(member)s although inactive. Keeping existing " +"one (%(manager)s)..." +msgstr "Für %(member)s ist kein Manager vorgesehen, obwohl inaktiv. " +"Bestehenden Mitglied Manager beibehalten (%(manager)s)..." + +#: members/views/views_import_export.py:148 +#, python-format +msgid "Inactive member %(member)s has no manager. Please provide one!" +msgstr "Inaktives Mitglied %(member)s hat keinen Mitglied Manager. Bitte stellen Sie einen zur Verf%C3%BCgung!" + +#: members/views/views_import_export.py:258 #, python-format msgid "" "CSV file uploaded: %(nbLines)i lines read, %(nbMembers)i members created or " @@ -1310,45 +1344,46 @@ msgstr "" "CSV-Datei hochgeladen: %(nbLines)i Zeilen gelesen, %(nbMembers)i Mitglieder " "erstellt oder aktualisiert" -#: members/views/views_import_export.py:223 +#: members/views/views_import_export.py:261 +#, python-format msgid "Warning: %(warning)s" msgstr "Warnung: %(warning)s" -#: members/views/views_import_export.py:290 +#: members/views/views_import_export.py:322 msgid "Method not allowed" msgstr "Methode nicht erlaubt" -#: members/views/views_member.py:35 +#: members/views/views_member.py:34 msgid "You have been logged out" msgstr "Du wurdest ausgeloggt" -#: members/views/views_member.py:96 +#: members/views/views_member.py:112 msgid "Only superusers can create members" msgstr "Nur Superbenutzer können Mitglieder erstellen" -#: members/views/views_member.py:121 +#: members/views/views_member.py:137 msgid "Member successfully created" msgstr "Mitglied wurde erfolgreich erstellt" -#: members/views/views_member.py:129 +#: members/views/views_member.py:145 msgid "Update Member Details" msgstr "Aktualisiere Mitgliedsdetails" -#: members/views/views_member.py:130 +#: members/views/views_member.py:146 msgid "Member successfully updated" msgstr "Mitglied erfolgreich aktualisiert" -#: members/views/views_member.py:167 +#: members/views/views_member.py:183 msgid "A verification email has been sent to validate your new email address." msgstr "" "Eine E-Mail zur Überprüfung wurde an dich gesendet, um deine neue E-Mail-" "Adresse zu bestätigen." -#: members/views/views_member.py:181 +#: members/views/views_member.py:197 msgid "My Profile" msgstr "Mein Profil" -#: members/views/views_member.py:182 +#: members/views/views_member.py:198 msgid "Profile successfully updated" msgstr "Profil erfolgreich aktualisiert" diff --git a/members/locale/es/LC_MESSAGES/django.mo b/members/locale/es/LC_MESSAGES/django.mo index c95b4156..a9f57dc9 100644 Binary files a/members/locale/es/LC_MESSAGES/django.mo and b/members/locale/es/LC_MESSAGES/django.mo differ diff --git a/members/locale/es/LC_MESSAGES/django.po b/members/locale/es/LC_MESSAGES/django.po index b8abc018..18c0bce9 100644 --- a/members/locale/es/LC_MESSAGES/django.po +++ b/members/locale/es/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-11-11 22:23+0100\n" +"POT-Creation-Date: 2024-11-17 11:13+0100\n" "PO-Revision-Date: 2024-10-21 09:13+00:00\n" "Last-Translator: Auto-po-lyglot using qwen2.5:3b (https://github.com/" "leolivier/auto-po-lyglot)\n" @@ -29,12 +29,13 @@ msgid "Members" msgstr "Membres" #: members/forms.py:69 +#, python-brace-format msgid "" "By checking this box, you agree to this site's privacy policy" msgstr "" "Al marcar esta casilla, acepta que este sitio política de privacidad +"href='{privacy_url}'>política de privacidad" #: members/forms.py:125 msgid "Name of the invited person (will appear in the received email)" @@ -159,139 +160,143 @@ msgctxt "CSV Field" msgid "deathdate" msgstr "fecha_de_defuncion" -#: members/models.py:45 +#: members/models.py:38 +msgctxt "CSV Field" +msgid "managed_by" +msgstr "gestionado_por" + +#: members/models.py:46 #: members/templates/members/members/members_directory.html:29 #: members/views/views_directory.py:59 msgid "Name" msgstr "Nombre" -#: members/models.py:47 members/templates/members/family/family_detail.html:14 +#: members/models.py:48 members/templates/members/family/family_detail.html:14 msgid "Parent family" msgstr "Familia paterna" -#: members/models.py:50 +#: members/models.py:51 msgid "family" msgstr "familia" -#: members/models.py:51 +#: members/models.py:52 msgid "families" msgstr "familias" -#: members/models.py:66 members/templates/members/address/address_detail.html:8 +#: members/models.py:67 members/templates/members/address/address_detail.html:8 msgid "Number & Street name" msgstr "Número y nombre de la calle" -#: members/models.py:68 +#: members/models.py:69 msgid "Complementary info" msgstr "Información complementaria" -#: members/models.py:70 +#: members/models.py:71 #: members/templates/members/address/address_detail.html:16 msgid "Zip code" msgstr "Código postal" -#: members/models.py:72 +#: members/models.py:73 #: members/templates/members/address/address_detail.html:20 msgid "City" msgstr "Ville" -#: members/models.py:74 +#: members/models.py:75 #: members/templates/members/address/address_detail.html:24 msgid "Country" msgstr "Pais" -#: members/models.py:88 +#: members/models.py:89 msgid "address" msgstr "dirección" -#: members/models.py:89 +#: members/models.py:90 msgid "addresses" msgstr "adres" -#: members/models.py:97 -msgid "Managing member" -msgstr "Comprador gestor de este miembro" +#: members/models.py:98 +msgid "Member manager" +msgstr "Gestor de miembro" -#: members/models.py:102 +#: members/models.py:103 #: members/templates/members/members/member_detail.html:37 #: members/templates/members/members/members_directory.html:33 #: members/views/views_directory.py:59 msgid "Address" msgstr "Dirección" -#: members/models.py:104 +#: members/models.py:105 #: members/templates/members/members/member_detail.html:45 #: members/templates/members/members/members_directory.html:31 #: members/views/views_directory.py:59 msgid "Phone" msgstr "Télefono" -#: members/models.py:106 +#: members/models.py:107 #: members/templates/members/members/member_detail.html:49 #: members/templates/members/members/members_directory.html:30 msgid "Birthdate" msgstr "Fecha de nacimiento" -#: members/models.py:107 +#: members/models.py:108 msgid "Click on the month name or the year to change them quickly" msgstr "Clickea en el nombre del mes o del año para modificarlos rápidamente" -#: members/models.py:110 +#: members/models.py:111 msgid "Is dead" msgstr "Ha muerto" -#: members/models.py:111 +#: members/models.py:112 msgid "Death date" msgstr "Fecha de defunción" -#: members/models.py:113 +#: members/models.py:114 #: members/templates/members/members/member_detail.html:53 msgid "Website" msgstr "Sitio web" -#: members/models.py:115 +#: members/models.py:116 #: members/templates/members/members/member_detail.html:57 msgid "Family" msgstr "Familia" -#: members/models.py:117 +#: members/models.py:118 #: members/templates/members/members/member_detail.html:61 msgid "Who I am" msgstr "Qui soyé" -#: members/models.py:118 +#: members/models.py:119 msgid "Describe yourself, your likes and dislikes..." msgstr "Describe-te, di qué te amas o no amas..." -#: members/models.py:119 +#: members/models.py:120 msgid "My hobbies" msgstr "Mis pasiones" -#: members/models.py:120 +#: members/models.py:121 msgid "Provide a list of hobbies separated by commas" msgstr "Dá una lista de hobbies separados por comas" -#: members/models.py:122 +#: members/models.py:123 msgid "Privacy consent" msgstr "Consentimiento a la protección de la privacidad" -#: members/models.py:124 +#: members/models.py:125 msgid "Followers" msgstr "Followers" -#: members/models.py:133 +#: members/models.py:134 msgid "member" msgstr "miembro" -#: members/models.py:134 +#: members/models.py:135 msgid "members" msgstr "miembros" -#: members/models.py:192 +#: members/models.py:195 #, python-brace-format msgid "Death date {dd} is before birthdate {bd}" -msgstr "La fecha de defunción {dd} es anterior a " -"la fecha de nacimiento {bd}." +msgstr "La fecha de defunción {dd} es anterior a la fecha de nacimiento {bd}." #: members/templates/members/address/address_detail.html:3 msgid "Address Detail" @@ -348,7 +353,7 @@ msgid "Greetings!" msgstr "Salutaciones!" #: members/templates/members/email/email_verification_msg.html:27 -#: members/tests/tests_member.py:330 +#: members/tests/tests_member.py:347 msgid "" "You received this mail because you attempted to create an account on our " "website or because a member created and activated your account" @@ -357,7 +362,7 @@ msgstr "" "web o porque un miembro ha creado y activado tu cuenta" #: members/templates/members/email/email_verification_msg.html:28 -#: members/tests/tests_member.py:332 +#: members/tests/tests_member.py:349 msgid "" "Please click on the link below to confirm the email and activate your " "account." @@ -845,15 +850,15 @@ msgstr "Detalles de un Miembro" #: members/templates/members/members/member_detail.html:26 #: members/templates/members/members/member_upsert.html:26 -#: members/tests/tests_member.py:238 +#: members/tests/tests_member.py:236 msgid "Active member" msgstr "Membro activo" #: members/templates/members/members/member_detail.html:28 #: members/templates/members/members/member_upsert.html:28 -#: members/tests/tests_member.py:238 +#: members/tests/tests_member.py:236 msgid "Managed member" -msgstr "Membrecito" +msgstr "Miembro gestionado" #: members/templates/members/members/member_detail.html:41 #: members/templates/members/members/members_directory.html:32 @@ -991,7 +996,7 @@ msgid "Show directory" msgstr "Mostrar el directorio" #: members/templates/members/members/members.html:19 -#: members/views/views_member.py:106 +#: members/views/views_member.py:122 msgid "Create Member" msgstr "Criar un Miembro" @@ -1146,16 +1151,16 @@ msgstr "Modificar los %(name)s seleccionados" msgid "Add another %(translated_name)s" msgstr "Añadir otra %(translated_name)s" -#: members/tests/tests_member.py:100 members/views/views_member.py:194 +#: members/tests/tests_member.py:100 members/views/views_member.py:210 msgid "Member deleted" msgstr "Membres eliminados" -#: members/tests/tests_member.py:213 members/tests/tests_member.py:218 -#: members/views/views_member.py:143 members/views/views_member.py:157 +#: members/tests/tests_member.py:211 members/tests/tests_member.py:216 +#: members/views/views_member.py:159 members/views/views_member.py:173 msgid "You do not have permission to edit this member." msgstr "No tienes permiso para editar este miembro." -#: members/tests/tests_member.py:392 +#: members/tests/tests_member.py:409 msgid "Error: Cannot activate a dead member " msgstr "Error: No se puede activar un miembro muerto " @@ -1281,14 +1286,15 @@ msgstr "" "Fehlende Spalte in der CSV-Datei: \"%(fieldname)s\". Obligatorische Felder " "sind %(all_names)s" -#: members/views/views_import_export.py:98 +#: members/views/views_import_export.py:104 #, python-format msgid "Avatar not found: %(avatar)s for username %(username)s. Ignored..." msgstr "" "Avatar no encontrado : %(avatar)s para el nombre de usuario %(username)s. " "Ignorado..." -#: members/views/views_import_export.py:107 +#: members/views/views_import_export.py:113 +#, python-format msgid "" "Error saving avatar (%(warning)s): %(avatar)s for username %(username)s. " "Ignored..." @@ -1296,7 +1302,39 @@ msgstr "" "Error enregistrando el avatar (%(warning)s) : %(avatar)s para el nombre de " "usuario %(username)s. Ignorado..." -#: members/views/views_import_export.py:220 +#: members/views/views_import_export.py:126 +msgid "" +"You requested to activate imported members. All managers will be ignored." +msgstr "" +"Ha solicitado la activación de miembros importados. Se ignorarán todos " +"los gestores." + +#: members/views/views_import_export.py:129 +#, python-format +msgid "Manager %(manager)s not found for member %(member)s. Ignoring..." +msgstr "" +"No se ha encontrado el gestor %(manager)s para el miembro %(member)s. Ignorado..." + +#: members/views/views_import_export.py:136 +#, python-format +msgid "Member %(member)s is active. Ignoring manager %(manager)s" +msgstr "El miembro %(member)s está activo. Ignorar gestor %(manager)s " + +#: members/views/views_import_export.py:145 +#, python-format +msgid "" +"No manager provided for member %(member)s although inactive. Keeping existing " +"one (%(manager)s)..." +msgstr "" +"No se ha proporcionado ningún gestor para los %(member)s aunque estén inactivos. Mantener uno " +"existente (%(manager)s)..." + +#: members/views/views_import_export.py:148 +#, python-format +msgid "Inactive member %(member)s has no manager. Please provide one!" +msgstr "El miembro inactivo %(member)s no tiene administrador. Por favor, ¡proporcione uno!" + +#: members/views/views_import_export.py:258 #, python-format msgid "" "CSV file uploaded: %(nbLines)i lines read, %(nbMembers)i members created or " @@ -1305,45 +1343,46 @@ msgstr "" "CSV-Datei hochgeladen: %(nbLines)i Zeilen gelesen, %(nbMembers)i Mitglieder " "erstellt oder aktualisiert" -#: members/views/views_import_export.py:223 +#: members/views/views_import_export.py:261 +#, python-format msgid "Warning: %(warning)s" msgstr "Alerta: %(warning)s" -#: members/views/views_import_export.py:290 +#: members/views/views_import_export.py:322 msgid "Method not allowed" msgstr "Método no permitido" -#: members/views/views_member.py:35 +#: members/views/views_member.py:34 msgid "You have been logged out" msgstr "Has sido deslogerado(a)" -#: members/views/views_member.py:96 +#: members/views/views_member.py:112 msgid "Only superusers can create members" msgstr "Solo los superusuarios pueden crear miembros" -#: members/views/views_member.py:121 +#: members/views/views_member.py:137 msgid "Member successfully created" msgstr "El miembro fue creado con éxito" -#: members/views/views_member.py:129 +#: members/views/views_member.py:145 msgid "Update Member Details" msgstr "Modificar los detalles de un Miembro" -#: members/views/views_member.py:130 +#: members/views/views_member.py:146 msgid "Member successfully updated" msgstr "El miembro fue actualizado con éxito" -#: members/views/views_member.py:167 +#: members/views/views_member.py:183 msgid "A verification email has been sent to validate your new email address." msgstr "" "Se ha enviado un correo electrónico de verificación para validar tu nueva " "dirección de correo electrónico." -#: members/views/views_member.py:181 +#: members/views/views_member.py:197 msgid "My Profile" msgstr "Mi Perfil" -#: members/views/views_member.py:182 +#: members/views/views_member.py:198 msgid "Profile successfully updated" msgstr "Perfil actualizado con éxito" diff --git a/members/locale/fr/LC_MESSAGES/django.mo b/members/locale/fr/LC_MESSAGES/django.mo index 89a5f39e..e79ef15f 100644 Binary files a/members/locale/fr/LC_MESSAGES/django.mo and b/members/locale/fr/LC_MESSAGES/django.mo differ diff --git a/members/locale/fr/LC_MESSAGES/django.po b/members/locale/fr/LC_MESSAGES/django.po index ff526ab0..eccbec5d 100644 --- a/members/locale/fr/LC_MESSAGES/django.po +++ b/members/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-11-11 22:23+0100\n" +"POT-Creation-Date: 2024-11-17 11:12+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Olivier LEVILLAIN \n" "Language-Team: French \n" @@ -23,6 +23,7 @@ msgid "Members" msgstr "Membres" #: members/forms.py:69 +#, python-brace-format msgid "" "By checking this box, you agree to this site's privacy policy" @@ -153,135 +154,140 @@ msgctxt "CSV Field" msgid "deathdate" msgstr "date_de_deces" -#: members/models.py:45 +#: members/models.py:38 +msgctxt "CSV Field" +msgid "managed_by" +msgstr "gere_par" + +#: members/models.py:46 #: members/templates/members/members/members_directory.html:29 #: members/views/views_directory.py:59 msgid "Name" msgstr "Nom" -#: members/models.py:47 members/templates/members/family/family_detail.html:14 +#: members/models.py:48 members/templates/members/family/family_detail.html:14 msgid "Parent family" msgstr "Famille parente" -#: members/models.py:50 +#: members/models.py:51 msgid "family" msgstr "famille" -#: members/models.py:51 +#: members/models.py:52 msgid "families" msgstr "familles" -#: members/models.py:66 members/templates/members/address/address_detail.html:8 +#: members/models.py:67 members/templates/members/address/address_detail.html:8 msgid "Number & Street name" msgstr "N° et nom de la voie" -#: members/models.py:68 +#: members/models.py:69 msgid "Complementary info" msgstr "Infos complémentaires" -#: members/models.py:70 +#: members/models.py:71 #: members/templates/members/address/address_detail.html:16 msgid "Zip code" msgstr "Code postal" -#: members/models.py:72 +#: members/models.py:73 #: members/templates/members/address/address_detail.html:20 msgid "City" msgstr "Ville" -#: members/models.py:74 +#: members/models.py:75 #: members/templates/members/address/address_detail.html:24 msgid "Country" msgstr "Pays" -#: members/models.py:88 +#: members/models.py:89 msgid "address" msgstr "adresse" -#: members/models.py:89 +#: members/models.py:90 msgid "addresses" msgstr "adresses" -#: members/models.py:97 -msgid "Managing member" -msgstr "Compte gérant ce membre" +#: members/models.py:98 +msgid "Member manager" +msgstr "Gestionnaire de ce membre" -#: members/models.py:102 +#: members/models.py:103 #: members/templates/members/members/member_detail.html:37 #: members/templates/members/members/members_directory.html:33 #: members/views/views_directory.py:59 msgid "Address" msgstr "Adresse" -#: members/models.py:104 +#: members/models.py:105 #: members/templates/members/members/member_detail.html:45 #: members/templates/members/members/members_directory.html:31 #: members/views/views_directory.py:59 msgid "Phone" msgstr "Téléphone" -#: members/models.py:106 +#: members/models.py:107 #: members/templates/members/members/member_detail.html:49 #: members/templates/members/members/members_directory.html:30 msgid "Birthdate" msgstr "Date de naissance" -#: members/models.py:107 +#: members/models.py:108 msgid "Click on the month name or the year to change them quickly" msgstr "Cliquez sur le nom du mois ou de l'année pour les modifier rapidement" -#: members/models.py:110 +#: members/models.py:111 msgid "Is dead" msgstr "Est décédé(e)" -#: members/models.py:111 +#: members/models.py:112 msgid "Death date" msgstr "Date de décès" -#: members/models.py:113 +#: members/models.py:114 #: members/templates/members/members/member_detail.html:53 msgid "Website" msgstr "Site web" -#: members/models.py:115 +#: members/models.py:116 #: members/templates/members/members/member_detail.html:57 msgid "Family" msgstr "Famille" -#: members/models.py:117 +#: members/models.py:118 #: members/templates/members/members/member_detail.html:61 msgid "Who I am" msgstr "Qui je suis" -#: members/models.py:118 +#: members/models.py:119 msgid "Describe yourself, your likes and dislikes..." msgstr "Décris-toi, dis ce que tu aimes ou n'aimes pas..." -#: members/models.py:119 +#: members/models.py:120 msgid "My hobbies" msgstr "Mes loisirs" -#: members/models.py:120 +#: members/models.py:121 msgid "Provide a list of hobbies separated by commas" msgstr "Donne un liste de loisirs séparés par des virgules" -#: members/models.py:122 +#: members/models.py:123 msgid "Privacy consent" msgstr "Consentement à la protection de la vie privée" -#: members/models.py:124 +#: members/models.py:125 msgid "Followers" msgstr "Followers" -#: members/models.py:133 +#: members/models.py:134 msgid "member" msgstr "membre" -#: members/models.py:134 +#: members/models.py:135 msgid "members" msgstr "membres" -#: members/models.py:192 +#: members/models.py:195 #, python-brace-format msgid "Death date {dd} is before birthdate {bd}" msgstr "La date de décès {dd} est avant la date de naissance {bd}" @@ -341,7 +347,7 @@ msgid "Greetings!" msgstr "Bonjour!" #: members/templates/members/email/email_verification_msg.html:27 -#: members/tests/tests_member.py:330 +#: members/tests/tests_member.py:347 msgid "" "You received this mail because you attempted to create an account on our " "website or because a member created and activated your account" @@ -350,7 +356,7 @@ msgstr "" "bien parce qu'un membre a créé et activé ton compte" #: members/templates/members/email/email_verification_msg.html:28 -#: members/tests/tests_member.py:332 +#: members/tests/tests_member.py:349 msgid "" "Please click on the link below to confirm the email and activate your " "account." @@ -840,13 +846,13 @@ msgstr "Détail d'un Membre" #: members/templates/members/members/member_detail.html:26 #: members/templates/members/members/member_upsert.html:26 -#: members/tests/tests_member.py:238 +#: members/tests/tests_member.py:236 msgid "Active member" msgstr "Membre actif" #: members/templates/members/members/member_detail.html:28 #: members/templates/members/members/member_upsert.html:28 -#: members/tests/tests_member.py:238 +#: members/tests/tests_member.py:236 msgid "Managed member" msgstr "Membre géré" @@ -986,7 +992,7 @@ msgid "Show directory" msgstr "Afficher l'annuaire" #: members/templates/members/members/members.html:19 -#: members/views/views_member.py:106 +#: members/views/views_member.py:122 msgid "Create Member" msgstr "Créer un Membre" @@ -1141,16 +1147,16 @@ msgstr "Modifier les %(name)s sélectionnées" msgid "Add another %(translated_name)s" msgstr "Ajouter une autre %(translated_name)s" -#: members/tests/tests_member.py:100 members/views/views_member.py:194 +#: members/tests/tests_member.py:100 members/views/views_member.py:210 msgid "Member deleted" msgstr "Membre supprimé" -#: members/tests/tests_member.py:213 members/tests/tests_member.py:218 -#: members/views/views_member.py:143 members/views/views_member.py:157 +#: members/tests/tests_member.py:211 members/tests/tests_member.py:216 +#: members/views/views_member.py:159 members/views/views_member.py:173 msgid "You do not have permission to edit this member." msgstr "Tu n'as pas la permission de modifier ce membre." -#: members/tests/tests_member.py:392 +#: members/tests/tests_member.py:409 msgid "Error: Cannot activate a dead member " msgstr "Erreur: Impossible d'activer un membre mort " @@ -1276,22 +1282,53 @@ msgstr "" "Colonne manquante dans le fichier CSV: \"%(fieldname)s\". Les champs " "obligatoires sont %(all_names)s" -#: members/views/views_import_export.py:98 +#: members/views/views_import_export.py:104 #, python-format msgid "Avatar not found: %(avatar)s for username %(username)s. Ignored..." msgstr "" "Avatar introuvable : %(avatar)s pour le nom d'utilisateur %(username)s. " "Ignoré..." -#: members/views/views_import_export.py:107 +#: members/views/views_import_export.py:113 +#, python-format msgid "" "Error saving avatar (%(warning)s): %(avatar)s for username %(username)s. " "Ignored..." msgstr "" -"Erreur lors de l'enregistrement de l'avatar (%(warning)s) : %(avatar)s pour le " -"nom d'utilisateur %(username)s. Ignoré..." +"Erreur lors de l'enregistrement de l'avatar (%(warning)s) : %(avatar)s pour " +"le nom d'utilisateur %(username)s. Ignoré..." -#: members/views/views_import_export.py:220 +#: members/views/views_import_export.py:126 +msgid "" +"You requested to activate imported members. All managers will be ignored." +msgstr "" +"Tu as demandé à activer les membres importés. Tous les gestionnaires de membres seront ignorés..." + +#: members/views/views_import_export.py:129 +#, python-format +msgid "Manager %(manager)s not found for member %(member)s. Ignoring..." +msgstr "Gestionnaire %(manager)s non trouvé pour le membre %(member)s. Ignoré..." + +#: members/views/views_import_export.py:136 +#, python-format +msgid "Member %(member)s is active. Ignoring manager %(manager)s" +msgstr "Le membre %(member)s est actif. Gestionnaire %(manager)s ignoré." + +#: members/views/views_import_export.py:145 +#, python-format +msgid "" +"No manager provided for member %(member)s although inactive. Keeping existing " +"one (%(manager)s)..." +msgstr "" +"Pas de gestionnaire fourni pour le membre %(member)s bien qu'il soit inactif. " +"Le gestionnaire existant (%(manager)s) est conservé." + +#: members/views/views_import_export.py:148 +#, python-format +msgid "Inactive member %(member)s has no manager. Please provide one!" +msgstr "Le membre inactif %(member)s n'a pas de gestionnaire. Merci d'en fournir un!" + +#: members/views/views_import_export.py:258 #, python-format msgid "" "CSV file uploaded: %(nbLines)i lines read, %(nbMembers)i members created or " @@ -1300,45 +1337,46 @@ msgstr "" "Fichier CSV téléchargé : %(nbLines)i lignes lues, %(nbMembers)i membres " "créés ou mis à jour" -#: members/views/views_import_export.py:223 +#: members/views/views_import_export.py:261 +#, python-format msgid "Warning: %(warning)s" msgstr "Attention: %(warning)s" -#: members/views/views_import_export.py:290 +#: members/views/views_import_export.py:322 msgid "Method not allowed" msgstr "Méthode non autorisée" -#: members/views/views_member.py:35 +#: members/views/views_member.py:34 msgid "You have been logged out" msgstr "Tu as été déconnecté(e)" -#: members/views/views_member.py:96 +#: members/views/views_member.py:112 msgid "Only superusers can create members" msgstr "Seuls les superutilisateurs peuvent créer des membres" -#: members/views/views_member.py:121 +#: members/views/views_member.py:137 msgid "Member successfully created" msgstr "Le membre a été créé avec succès" -#: members/views/views_member.py:129 +#: members/views/views_member.py:145 msgid "Update Member Details" msgstr "Modifier les détails d'un Membre" -#: members/views/views_member.py:130 +#: members/views/views_member.py:146 msgid "Member successfully updated" msgstr "Le membre a été mis à jour avec succès" -#: members/views/views_member.py:167 +#: members/views/views_member.py:183 msgid "A verification email has been sent to validate your new email address." msgstr "" "Un courriel de vérification a été envoyé pour valider ta nouvelle adresse " "électronique." -#: members/views/views_member.py:181 +#: members/views/views_member.py:197 msgid "My Profile" msgstr "Mon Profil" -#: members/views/views_member.py:182 +#: members/views/views_member.py:198 msgid "Profile successfully updated" msgstr "Profil mis à jour avec succès" diff --git a/members/locale/it/LC_MESSAGES/django.mo b/members/locale/it/LC_MESSAGES/django.mo index cdaf78bb..977c6865 100644 Binary files a/members/locale/it/LC_MESSAGES/django.mo and b/members/locale/it/LC_MESSAGES/django.mo differ diff --git a/members/locale/it/LC_MESSAGES/django.po b/members/locale/it/LC_MESSAGES/django.po index 0dca3c69..e27fe87e 100644 --- a/members/locale/it/LC_MESSAGES/django.po +++ b/members/locale/it/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-11-11 22:23+0100\n" +"POT-Creation-Date: 2024-11-17 12:15+0100\n" "PO-Revision-Date: 2024-10-21 10:17+00:00\n" "Last-Translator: Auto-po-lyglot using qwen2.5:3b (https://github.com/" "leolivier/auto-po-lyglot)\n" @@ -29,6 +29,7 @@ msgid "Members" msgstr "Membri" #: members/forms.py:69 +#, python-brace-format msgid "" "By checking this box, you agree to this site's privacy policy" @@ -160,137 +161,142 @@ msgctxt "CSV Field" msgid "deathdate" msgstr "data_di_morte" -#: members/models.py:45 +#: members/models.py:38 +msgctxt "CSV Field" +msgid "managed_by" +msgstr "gestito_da" + +#: members/models.py:46 #: members/templates/members/members/members_directory.html:29 #: members/views/views_directory.py:59 msgid "Name" msgstr "Nome" -#: members/models.py:47 members/templates/members/family/family_detail.html:14 +#: members/models.py:48 members/templates/members/family/family_detail.html:14 msgid "Parent family" msgstr "Famiglia d'origine" -#: members/models.py:50 +#: members/models.py:51 msgid "family" msgstr "famiglia" -#: members/models.py:51 +#: members/models.py:52 msgid "families" msgstr "famiglie" -#: members/models.py:66 members/templates/members/address/address_detail.html:8 +#: members/models.py:67 members/templates/members/address/address_detail.html:8 msgid "Number & Street name" msgstr "Numero civico e nome della via" -#: members/models.py:68 +#: members/models.py:69 msgid "Complementary info" msgstr "Informazioni complementari" -#: members/models.py:70 +#: members/models.py:71 #: members/templates/members/address/address_detail.html:16 msgid "Zip code" msgstr "Codice postale" -#: members/models.py:72 +#: members/models.py:73 #: members/templates/members/address/address_detail.html:20 msgid "City" msgstr "Città" -#: members/models.py:74 +#: members/models.py:75 #: members/templates/members/address/address_detail.html:24 msgid "Country" msgstr "Paese" -#: members/models.py:88 +#: members/models.py:89 msgid "address" msgstr "indirizzo" -#: members/models.py:89 +#: members/models.py:90 msgid "addresses" msgstr "indirizzi" -#: members/models.py:97 -msgid "Managing member" -msgstr "Membro gestore" +#: members/models.py:98 +msgid "Member manager" +msgstr "Membro manager" -#: members/models.py:102 +#: members/models.py:103 #: members/templates/members/members/member_detail.html:37 #: members/templates/members/members/members_directory.html:33 #: members/views/views_directory.py:59 msgid "Address" msgstr "Indirizzo" -#: members/models.py:104 +#: members/models.py:105 #: members/templates/members/members/member_detail.html:45 #: members/templates/members/members/members_directory.html:31 #: members/views/views_directory.py:59 msgid "Phone" msgstr "Telefono" -#: members/models.py:106 +#: members/models.py:107 #: members/templates/members/members/member_detail.html:49 #: members/templates/members/members/members_directory.html:30 msgid "Birthdate" msgstr "Data di nascita" -#: members/models.py:107 +#: members/models.py:108 msgid "Click on the month name or the year to change them quickly" msgstr "Clicca sul nome del mese o dell'anno per modificarli rapidamente" -#: members/models.py:110 +#: members/models.py:111 msgid "Is dead" msgstr "E' morto" -#: members/models.py:111 +#: members/models.py:112 #, fuzzy #| msgid "Birthdate" msgid "Death date" msgstr "Data di morte" -#: members/models.py:113 +#: members/models.py:114 #: members/templates/members/members/member_detail.html:53 msgid "Website" msgstr "Sito web" -#: members/models.py:115 +#: members/models.py:116 #: members/templates/members/members/member_detail.html:57 msgid "Family" msgstr "Famiglia" -#: members/models.py:117 +#: members/models.py:118 #: members/templates/members/members/member_detail.html:61 msgid "Who I am" msgstr "Chi sono" -#: members/models.py:118 +#: members/models.py:119 msgid "Describe yourself, your likes and dislikes..." msgstr "Descriviti, parla di ciò che ti piace e non ti piace..." -#: members/models.py:119 +#: members/models.py:120 msgid "My hobbies" msgstr "I miei passatempi" -#: members/models.py:120 +#: members/models.py:121 msgid "Provide a list of hobbies separated by commas" msgstr "Fornisci un elenco di hobby separati da virgole" -#: members/models.py:122 +#: members/models.py:123 msgid "Privacy consent" msgstr "Consenso sulla privacy" -#: members/models.py:124 +#: members/models.py:125 msgid "Followers" msgstr "Followers" -#: members/models.py:133 +#: members/models.py:134 msgid "member" msgstr "membro" -#: members/models.py:134 +#: members/models.py:135 msgid "members" msgstr "membri" -#: members/models.py:192 +#: members/models.py:195 #, python-brace-format msgid "Death date {dd} is before birthdate {bd}" msgstr "Data di morte {dd} precede la data di nascita {bd}" @@ -350,7 +356,7 @@ msgid "Greetings!" msgstr "Saluti!" #: members/templates/members/email/email_verification_msg.html:27 -#: members/tests/tests_member.py:330 +#: members/tests/tests_member.py:347 msgid "" "You received this mail because you attempted to create an account on our " "website or because a member created and activated your account" @@ -359,7 +365,7 @@ msgstr "" "sito web o perche' un membro ha creato e attivato il tuo account" #: members/templates/members/email/email_verification_msg.html:28 -#: members/tests/tests_member.py:332 +#: members/tests/tests_member.py:349 msgid "" "Please click on the link below to confirm the email and activate your " "account." @@ -844,13 +850,13 @@ msgstr "Dettaglio del Membro" #: members/templates/members/members/member_detail.html:26 #: members/templates/members/members/member_upsert.html:26 -#: members/tests/tests_member.py:238 +#: members/tests/tests_member.py:236 msgid "Active member" msgstr "Membro attivo" #: members/templates/members/members/member_detail.html:28 #: members/templates/members/members/member_upsert.html:28 -#: members/tests/tests_member.py:238 +#: members/tests/tests_member.py:236 msgid "Managed member" msgstr "Membro gestito" @@ -989,7 +995,7 @@ msgid "Show directory" msgstr "Mostra l'elenco" #: members/templates/members/members/members.html:19 -#: members/views/views_member.py:106 +#: members/views/views_member.py:122 msgid "Create Member" msgstr "Crea Membro" @@ -1146,16 +1152,16 @@ msgstr "Modifica %(name)s selezionati" msgid "Add another %(translated_name)s" msgstr "Aggiungi un'altra %(translated_name)s" -#: members/tests/tests_member.py:100 members/views/views_member.py:194 +#: members/tests/tests_member.py:100 members/views/views_member.py:210 msgid "Member deleted" msgstr "Membro eliminato" -#: members/tests/tests_member.py:213 members/tests/tests_member.py:218 -#: members/views/views_member.py:143 members/views/views_member.py:157 +#: members/tests/tests_member.py:211 members/tests/tests_member.py:216 +#: members/views/views_member.py:159 members/views/views_member.py:173 msgid "You do not have permission to edit this member." msgstr "Non hai il permesso di modificare questo membro." -#: members/tests/tests_member.py:392 +#: members/tests/tests_member.py:409 msgid "Error: Cannot activate a dead member " msgstr "Errore: Impossibile attivare un membro morto " @@ -1281,13 +1287,13 @@ msgstr "" "Colonna mancante nel file CSV: \"%(fieldname)s\". I campi obbligatori sono " "%(all_names)s" -#: members/views/views_import_export.py:98 +#: members/views/views_import_export.py:104 #, python-format msgid "Avatar not found: %(avatar)s for username %(username)s. Ignored..." msgstr "" "Avatar non trovato: %(avatar)s per il nome utente %(username)s. Ignorato..." -#: members/views/views_import_export.py:107 +#: members/views/views_import_export.py:113 #, python-format msgid "" "Error saving avatar (%(warning)s): %(avatar)s for username %(username)s. " @@ -1296,7 +1302,37 @@ msgstr "" "Errore nel salvare l'avatar (%(warning)s): %(avatar)s per il nome utente " "%(username)s. Ignorato..." -#: members/views/views_import_export.py:220 +#: members/views/views_import_export.py:126 +msgid "" +"You requested to activate imported members. All managers will be ignored." +msgstr "" +"È stato richiesto di attivare i membri importati. Tutti i gestori verranno ignorati." + +#: members/views/views_import_export.py:129 +#, python-format +msgid "Manager %(manager)s not found for member %(member)s. Ignoring..." +msgstr "Il manager %(manager)s non è stato trovato per il membro %(member)s. Ignorare..." + +#: members/views/views_import_export.py:136 +#, python-format +msgid "Member %(member)s is active. Ignoring manager %(manager)s" +msgstr "Il membro %(membro)s è attivo. Ignorare il manager %(manager)s." + +#: members/views/views_import_export.py:145 +#, python-format +msgid "" +"No manager provided for member %(member)s although inactive. Keeping " +"existing one (%(manager)s)..." +msgstr "" +"Non è previsto nessun gestore per i membri %(member)s, anche se inattivi. " +"Mantenere quello esistente (%(manager)s)..." + +#: members/views/views_import_export.py:148 +#, python-format +msgid "Inactive member %(member)s has no manager. Please provide one!" +msgstr "Il membro inattivo %(member)s non ha un manager. Si prega di fornirne uno!" + +#: members/views/views_import_export.py:258 #, python-format msgid "" "CSV file uploaded: %(nbLines)i lines read, %(nbMembers)i members created or " @@ -1305,46 +1341,46 @@ msgstr "" "File CSV caricato: %(nbLines)i righe lette, %(nbMembers)i membri creati o " "aggiornati" -#: members/views/views_import_export.py:223 +#: members/views/views_import_export.py:261 #, python-format msgid "Warning: %(warning)s" msgstr "Attenzione: %(warning)s" -#: members/views/views_import_export.py:290 +#: members/views/views_import_export.py:322 msgid "Method not allowed" msgstr "Metodo non consentito" -#: members/views/views_member.py:35 +#: members/views/views_member.py:34 msgid "You have been logged out" msgstr "Sei stato disconnesso" -#: members/views/views_member.py:96 +#: members/views/views_member.py:112 msgid "Only superusers can create members" msgstr "Solo i superutenti possono creare membri" -#: members/views/views_member.py:121 +#: members/views/views_member.py:137 msgid "Member successfully created" msgstr "Il membro è stato creato con successo" -#: members/views/views_member.py:129 +#: members/views/views_member.py:145 msgid "Update Member Details" msgstr "Aggiorna i dettagli del membro" -#: members/views/views_member.py:130 +#: members/views/views_member.py:146 msgid "Member successfully updated" msgstr "Il membro è stato aggiornato con successo" -#: members/views/views_member.py:167 +#: members/views/views_member.py:183 msgid "A verification email has been sent to validate your new email address." msgstr "" "Un'email di verifica è stata inviata per convalidare il tuo nuovo indirizzo " "email." -#: members/views/views_member.py:181 +#: members/views/views_member.py:197 msgid "My Profile" msgstr "Il mio profilo" -#: members/views/views_member.py:182 +#: members/views/views_member.py:198 msgid "Profile successfully updated" msgstr "Profilo aggiornato con successo" diff --git a/members/locale/pt/LC_MESSAGES/django.mo b/members/locale/pt/LC_MESSAGES/django.mo index a629f7c5..ce98ec21 100644 Binary files a/members/locale/pt/LC_MESSAGES/django.mo and b/members/locale/pt/LC_MESSAGES/django.mo differ diff --git a/members/locale/pt/LC_MESSAGES/django.po b/members/locale/pt/LC_MESSAGES/django.po index a511f9ea..cc5a7770 100644 --- a/members/locale/pt/LC_MESSAGES/django.po +++ b/members/locale/pt/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-11-11 22:23+0100\n" +"POT-Creation-Date: 2024-11-17 12:15+0100\n" "PO-Revision-Date: 2024-10-21 10:37+00:00\n" "Last-Translator: Auto-po-lyglot using qwen2.5:3b (https://github.com/" "leolivier/auto-po-lyglot)\n" @@ -160,137 +160,142 @@ msgctxt "CSV Field" msgid "deathdate" msgstr "data_de_morte" -#: members/models.py:45 +#: members/models.py:38 +msgctxt "CSV Field" +msgid "managed_by" +msgstr "gerido_por" + +#: members/models.py:46 #: members/templates/members/members/members_directory.html:29 #: members/views/views_directory.py:59 msgid "Name" msgstr "Nome" -#: members/models.py:47 members/templates/members/family/family_detail.html:14 +#: members/models.py:48 members/templates/members/family/family_detail.html:14 msgid "Parent family" msgstr "Familia parental" -#: members/models.py:50 +#: members/models.py:51 msgid "family" msgstr "Família" -#: members/models.py:51 +#: members/models.py:52 msgid "families" msgstr "Famílias" -#: members/models.py:66 members/templates/members/address/address_detail.html:8 +#: members/models.py:67 members/templates/members/address/address_detail.html:8 msgid "Number & Street name" msgstr "Número e nome da rua" -#: members/models.py:68 +#: members/models.py:69 msgid "Complementary info" msgstr "Informações complementares" -#: members/models.py:70 +#: members/models.py:71 #: members/templates/members/address/address_detail.html:16 msgid "Zip code" msgstr "Numéro de código postal" -#: members/models.py:72 +#: members/models.py:73 #: members/templates/members/address/address_detail.html:20 msgid "City" msgstr "Cidade" -#: members/models.py:74 +#: members/models.py:75 #: members/templates/members/address/address_detail.html:24 msgid "Country" msgstr "País" -#: members/models.py:88 +#: members/models.py:89 msgid "address" msgstr "Endereço" -#: members/models.py:89 +#: members/models.py:90 msgid "addresses" msgstr "endereços" -#: members/models.py:97 -msgid "Managing member" -msgstr "Membro gerente" +#: members/models.py:98 +msgid "Member manager" +msgstr "Gestor de membros" -#: members/models.py:102 +#: members/models.py:103 #: members/templates/members/members/member_detail.html:37 #: members/templates/members/members/members_directory.html:33 #: members/views/views_directory.py:59 msgid "Address" msgstr "Endereço" -#: members/models.py:104 +#: members/models.py:105 #: members/templates/members/members/member_detail.html:45 #: members/templates/members/members/members_directory.html:31 #: members/views/views_directory.py:59 msgid "Phone" msgstr "Telefone" -#: members/models.py:106 +#: members/models.py:107 #: members/templates/members/members/member_detail.html:49 #: members/templates/members/members/members_directory.html:30 msgid "Birthdate" msgstr "Data do nascimento" -#: members/models.py:107 +#: members/models.py:108 msgid "Click on the month name or the year to change them quickly" msgstr "Clique em nome do mês ou do ano para mudá-los rapidamente" -#: members/models.py:110 +#: members/models.py:111 msgid "Is dead" msgstr "Está morto" -#: members/models.py:111 +#: members/models.py:112 #, fuzzy #| msgid "Birthdate" msgid "Death date" msgstr "Data de morte" -#: members/models.py:113 +#: members/models.py:114 #: members/templates/members/members/member_detail.html:53 msgid "Website" msgstr "Site" -#: members/models.py:115 +#: members/models.py:116 #: members/templates/members/members/member_detail.html:57 msgid "Family" msgstr "Família" -#: members/models.py:117 +#: members/models.py:118 #: members/templates/members/members/member_detail.html:61 msgid "Who I am" msgstr "Quem sou" -#: members/models.py:118 +#: members/models.py:119 msgid "Describe yourself, your likes and dislikes..." msgstr "Descreva-se, seus gostos e desgostos..." -#: members/models.py:119 +#: members/models.py:120 msgid "My hobbies" msgstr "Meus hobbies" -#: members/models.py:120 +#: members/models.py:121 msgid "Provide a list of hobbies separated by commas" msgstr "Liste os hobbies separados por vírgulas." -#: members/models.py:122 +#: members/models.py:123 msgid "Privacy consent" msgstr "Consentimento à proteção da vida privada" -#: members/models.py:124 +#: members/models.py:125 msgid "Followers" msgstr "Seguidores" -#: members/models.py:133 +#: members/models.py:134 msgid "member" msgstr "membro" -#: members/models.py:134 +#: members/models.py:135 msgid "members" msgstr "membros" -#: members/models.py:192 +#: members/models.py:195 #, python-brace-format msgid "Death date {dd} is before birthdate {bd}" msgstr "Data de morte {dd} é antes da data de nascimento {bd}" @@ -350,7 +355,7 @@ msgid "Greetings!" msgstr "Ossé!" #: members/templates/members/email/email_verification_msg.html:27 -#: members/tests/tests_member.py:330 +#: members/tests/tests_member.py:347 msgid "" "You received this mail because you attempted to create an account on our " "website or because a member created and activated your account" @@ -359,7 +364,7 @@ msgstr "" "porque um membro criou e ativou sua conta." #: members/templates/members/email/email_verification_msg.html:28 -#: members/tests/tests_member.py:332 +#: members/tests/tests_member.py:349 msgid "" "Please click on the link below to confirm the email and activate your " "account." @@ -845,13 +850,13 @@ msgstr "Detalhe do Membro" #: members/templates/members/members/member_detail.html:26 #: members/templates/members/members/member_upsert.html:26 -#: members/tests/tests_member.py:238 +#: members/tests/tests_member.py:236 msgid "Active member" msgstr "Membro ativo" #: members/templates/members/members/member_detail.html:28 #: members/templates/members/members/member_upsert.html:28 -#: members/tests/tests_member.py:238 +#: members/tests/tests_member.py:236 msgid "Managed member" msgstr "Membro gerido" @@ -991,7 +996,7 @@ msgid "Show directory" msgstr "Mostrar diretório" #: members/templates/members/members/members.html:19 -#: members/views/views_member.py:106 +#: members/views/views_member.py:122 msgid "Create Member" msgstr "Criar Membro" @@ -1143,16 +1148,16 @@ msgstr "Alterar os %(name)s selecionados" msgid "Add another %(translated_name)s" msgstr "Adicionar mais %(translated_name)s" -#: members/tests/tests_member.py:100 members/views/views_member.py:194 +#: members/tests/tests_member.py:100 members/views/views_member.py:210 msgid "Member deleted" msgstr "Membro excluído" -#: members/tests/tests_member.py:213 members/tests/tests_member.py:218 -#: members/views/views_member.py:143 members/views/views_member.py:157 +#: members/tests/tests_member.py:211 members/tests/tests_member.py:216 +#: members/views/views_member.py:159 members/views/views_member.py:173 msgid "You do not have permission to edit this member." msgstr "Você não tem permissão para editar esse membro." -#: members/tests/tests_member.py:392 +#: members/tests/tests_member.py:409 msgid "Error: Cannot activate a dead member " msgstr "Erro: não pode ativar um membro morto " @@ -1277,14 +1282,14 @@ msgstr "" "Coluna faltante no arquivo CSV: '%(fieldname)s'. Campos obrigatórios são " "%(all_names)s" -#: members/views/views_import_export.py:98 +#: members/views/views_import_export.py:104 #, python-format msgid "Avatar not found: %(avatar)s for username %(username)s. Ignored..." msgstr "" "Avatar não encontrado: %(avatar)s para o nome de usuário %(username)s. " "Ignorado..." -#: members/views/views_import_export.py:107 +#: members/views/views_import_export.py:113 #, python-format msgid "" "Error saving avatar (%(warning)s): %(avatar)s for username %(username)s. " @@ -1293,7 +1298,37 @@ msgstr "" "Erro ao salvar avatar (%(warning)s): %(avatar)s para o usuário %(username)s. " "Ignorado..." -#: members/views/views_import_export.py:220 +#: members/views/views_import_export.py:126 +msgid "" +"You requested to activate imported members. All managers will be ignored." +msgstr "" +"Pediu para ativar membros importados. Todos os gestores serão ignorados." + +#: members/views/views_import_export.py:129 +#, python-format +msgid "Manager %(manager)s not found for member %(member)s. Ignoring..." +msgstr "Gestor %(manager)s não encontrado para membro %(member)s. Ignorando..." + +#: members/views/views_import_export.py:136 +#, python-format +msgid "Member %(member)s is active. Ignoring manager %(manager)s" +msgstr "O membro %(member)s está ativo. Ignorar o gestor %(manager)s." + +#: members/views/views_import_export.py:145 +#, python-format +msgid "" +"No manager provided for member %(member)s although inactive. Keeping " +"existing one (%(manager)s)..." +msgstr "" +"Não é atribuído nenhum gestor aos %(member)s membros, embora estejam inativos. " +"Manter o atual (%(manager)s)." + +#: members/views/views_import_export.py:148 +#, python-format +msgid "Inactive member %(member)s has no manager. Please provide one!" +msgstr "O membro inativo %(member)s não tem gestor. Por favor, indique um!" + +#: members/views/views_import_export.py:258 #, python-format msgid "" "CSV file uploaded: %(nbLines)i lines read, %(nbMembers)i members created or " @@ -1302,46 +1337,46 @@ msgstr "" "Arquivo CSV carregado: %(nbLines)i linhas lidas, %(nbMembers)i membros " "criados ou atualizados" -#: members/views/views_import_export.py:223 +#: members/views/views_import_export.py:261 #, python-format msgid "Warning: %(warning)s" msgstr "Alerta: %(warning)s" -#: members/views/views_import_export.py:290 +#: members/views/views_import_export.py:322 msgid "Method not allowed" msgstr "Método não permitido" -#: members/views/views_member.py:35 +#: members/views/views_member.py:34 msgid "You have been logged out" msgstr "Você foi desconectado(a)." -#: members/views/views_member.py:96 +#: members/views/views_member.py:112 msgid "Only superusers can create members" msgstr "Somente os superusuários podem criar membros." -#: members/views/views_member.py:121 +#: members/views/views_member.py:137 msgid "Member successfully created" msgstr "O membro foi criado com sucesso." -#: members/views/views_member.py:129 +#: members/views/views_member.py:145 msgid "Update Member Details" msgstr "Atualizar Detalhes de Membro" -#: members/views/views_member.py:130 +#: members/views/views_member.py:146 msgid "Member successfully updated" msgstr "O membro foi atualizado com sucesso." -#: members/views/views_member.py:167 +#: members/views/views_member.py:183 msgid "A verification email has been sent to validate your new email address." msgstr "" "Um e-mail de verificação foi enviado para validar sua nova endereço " "eletrônico." -#: members/views/views_member.py:181 +#: members/views/views_member.py:197 msgid "My Profile" msgstr "Minha Conta de Usuário" -#: members/views/views_member.py:182 +#: members/views/views_member.py:198 msgid "Profile successfully updated" msgstr "Perfil atualizado com sucesso" diff --git a/members/migrations/0008_alter_member_managing_member.py b/members/migrations/0008_alter_member_managing_member.py new file mode 100644 index 00000000..9e80c50d --- /dev/null +++ b/members/migrations/0008_alter_member_managing_member.py @@ -0,0 +1,25 @@ +# Generated by Django 5.1.1 on 2024-11-17 11:45 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0007_member_deathdate_member_is_dead'), + ] + + operations = [ + migrations.AlterField( + model_name='member', + name='managing_member', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='managed_members', to=settings.AUTH_USER_MODEL, verbose_name='Member manager'), + ), + migrations.RenameField( + model_name='member', + old_name='managing_member', + new_name='member_manager', + ), + ] diff --git a/members/models.py b/members/models.py index 77ab66a0..b32fdd7d 100644 --- a/members/models.py +++ b/members/models.py @@ -35,6 +35,7 @@ 'family': pgettext_lazy('CSV Field', 'family'), 'avatar': pgettext_lazy('CSV Field', 'avatar'), 'deathdate': pgettext_lazy('CSV Field', 'deathdate'), + 'managed_by': pgettext_lazy('CSV Field', 'managed_by') } @@ -94,8 +95,8 @@ class Meta: class Member(AbstractUser): - managing_member = models.ForeignKey('self', verbose_name=_('Managing member'), on_delete=models.CASCADE, - related_name='managed_members', null=True, blank=True, default=None) + member_manager = models.ForeignKey('self', verbose_name=_('Member manager'), on_delete=models.CASCADE, + related_name='managed_members', null=True, blank=True, default=None) avatar = models.ImageField(upload_to=settings.AVATARS_DIR, blank=True, null=True) @@ -184,7 +185,7 @@ def age(self) -> int: return years if (today >= self.next_birthday) else years-1 def get_manager(self): - return self.managing_member if self.managing_member else self + return self.member_manager if self.member_manager else self def clean(self): if self.deathdate: @@ -197,14 +198,14 @@ def clean(self): self.is_active = False else: self.is_dead = False - # If member is active, set managing member to None - if self.is_active and self.managing_member is not None: - logger.debug(f"Cleaning member {self.full_name}: removing managing member") - self.managing_member = None - elif not self.is_active and self.managing_member is None: - # If no managing member and member is inactive, use admin member - logger.debug(f"Cleaning member {self.full_name}: changing managing member to admin") - self.managing_member = Member.objects.filter(is_superuser=True).first() + # If member is active, set member manager to None + if self.is_active and self.member_manager is not None: + logger.debug(f"Cleaning member {self.full_name}: removing member manager") + self.member_manager = None + elif not self.is_active and self.member_manager is None: + # If no member manager and member is inactive, use admin member + logger.debug(f"Cleaning member {self.full_name}: changing member manager to admin") + self.member_manager = Member.objects.filter(is_superuser=True).first() def _resize_avatar(self, max_size, save_path): img = Image.open(self.avatar.path) diff --git a/members/tests/resources/import_members-fr.csv b/members/tests/resources/import_members-fr.csv index a0e81c02..6ab6b101 100644 --- a/members/tests/resources/import_members-fr.csv +++ b/members/tests/resources/import_members-fr.csv @@ -1,5 +1,5 @@ -"nom_de_compte","email","prenom","nom","date_de_naissance","telephone","ville","famille" -"member-fr1","member1@test.com","John","Doe",2000-01-01,"+45 01 02 03","Manchester","Les Does" -"member-fr2","member2@test.com","Jane","Doe",2000-01-02,"+45 01 02 04","Liverpool","Les Does" -"member-fr3","member3@test.com","John","Smith",2000-01-03,"+45 01 02 05","London","Les Smiths" -"member-fr4","member4@test.com","Jane","Smith",2000-01-04,"+45 01 02 06","Blackpool","Les Smiths" +"nom_de_compte","email","prenom","nom","date_de_naissance","telephone","ville","famille","gere_par" +"member-fr1","member1@test.com","John","Doe",2000-01-01,"+45 01 02 03","Manchester","Les Does","" +"member-fr2","member2@test.com","Jane","Doe",2000-01-02,"+45 01 02 04","Liverpool","Les Does","" +"member-fr3","member3@test.com","John","Smith",2000-01-03,"+45 01 02 05","London","Les Smiths","member-fr1" +"member-fr4","member4@test.com","Jane","Smith",2000-01-04,"+45 01 02 06","Blackpool","Les Smiths","" diff --git a/members/tests/tests_import_export.py b/members/tests/tests_import_export.py index f20daf73..d13237ed 100644 --- a/members/tests/tests_import_export.py +++ b/members/tests/tests_import_export.py @@ -36,7 +36,7 @@ def do_upload_file(self, file, lang, activate_users=True): def do_test_import(self, file, lang, expected_num, activate_users=True, member_prefix='member'): prev_num = Member.objects.count() - response = self.do_upload_file(file, lang) + response = self.do_upload_file(file, lang, activate_users) self.assertEqual(response.status_code, 200) # Check that expected_num members were imported self.assertEqual(Member.objects.count(), prev_num+expected_num) @@ -52,9 +52,9 @@ def do_test_import(self, file, lang, expected_num, activate_users=True, member_p m = Member.objects.get(username=name) if activate_users: self.assertEqual(m.is_active, activate_users) - self.assertIsNone(m.managing_member) - else: # managing_member is None if and only if active - self.assertEqual(m.managing_member is None, m.is_active) + self.assertIsNone(m.member_manager) + else: # member_manager is None if and only if active + self.assertEqual(m.member_manager is None, m.is_active) class TestMemberImport(TestImportMixin, MemberTestCase): @@ -92,6 +92,23 @@ def test_import_en(self): def test_import_fr(self): self.do_test_import('import_members-fr.csv', 'fr', 4, member_prefix='member-fr') + def test_import_manager(self): + # first make sure the member for which we change the managing member either does not exist or is not active + m3 = Member.objects.filter(username="member-fr3").first() + if m3 is not None: + # print("setting {m3.full_name} to inactive") + m3.is_active = False + m3.member_manager = None + m3.save() + # else: + # print("Member {m3.full_name} does not exist") + + self.do_test_import('import_members-fr.csv', 'fr', 4, member_prefix='member-fr', activate_users=False) + # check member manager import + m1 = Member.objects.get(username="member-fr1") + m3 = Member.objects.get(username="member-fr3") + self.assertEqual(m3.member_manager, m1) + def test_wrong_field(self): response = self.do_upload_file('import_members-wrong-field.csv', 'en-us') self.assertEqual(response.status_code, 200) diff --git a/members/tests/tests_member.py b/members/tests/tests_member.py index 36956079..32709961 100644 --- a/members/tests/tests_member.py +++ b/members/tests/tests_member.py @@ -65,7 +65,7 @@ def create_member_by_view(self, member_data=None): new_member = Member.objects.filter(username=member_data['username']).first() self.assertIsNotNone(new_member) self.assertFalse(new_member.is_active) - self.assertEqual(new_member.managing_member, self.member) + self.assertEqual(new_member.member_manager, self.member) self.created_members.append(new_member) return new_member @@ -83,7 +83,7 @@ def test_create_managed_member_in_view(self): # a new member has been created self.assertEqual(new_number, prev_number+1) # managed is managed by the creating member - self.assertEqual(managed.managing_member, self.member) + self.assertEqual(managed.member_manager, self.member) class MemberDeleteTest(MemberViewTestMixin, MemberTestCase): @@ -130,7 +130,7 @@ def test_member_profile_view(self): maxlength="150" class="input" id="id_last_name" required>''', html=True) self.assertTrue(self.member.is_active) - self.assertIsNone(self.member.managing_member) + self.assertIsNone(self.member.member_manager) new_data = self.get_changed_member_data(self.member) response = self.client.post(profile_url, new_data, follow=True) # print(vars(response)) @@ -139,7 +139,7 @@ def test_member_profile_view(self): self.member.refresh_from_db() # self.is_active becomes false because of the sending of the verification email (which changed) # self.assertTrue(self.member.is_active) - # self.assertIsNone(self.member.managing_member) + # self.assertIsNone(self.member.member_manager) self.assertEqual(self.member.first_name, new_data['first_name']) self.assertEqual(self.member.phone, new_data['phone']) @@ -332,7 +332,7 @@ def test_activate(self): managed = self.create_member() self.assertIsNotNone(managed) self.assertFalse(managed.is_active) - self.assertEqual(managed.managing_member, self.member) + self.assertEqual(managed.member_manager, self.member) mail.outbox = [] response = self.client.post(reverse("members:activate", args=[managed.id]), follow=True) self.assertEqual(response.status_code, 200) @@ -373,8 +373,8 @@ def get_dead_member_data(self): def check_dead_member(self, member): self.assertFalse(member.is_active) self.assertTrue(member.is_dead) - # managing member can be self.member (ie member creator) or superuser - self.assertIn(member.managing_member.id, [self.member.id, self.superuser.id]) + # member manager can be self.member (ie member creator) or superuser + self.assertIn(member.member_manager.id, [self.member.id, self.superuser.id]) self.assertIsNotNone(member.deathdate) self.assertGreater(member.deathdate, member.birthdate) diff --git a/members/tests/tests_member_base.py b/members/tests/tests_member_base.py index d69f73f0..226b8b5b 100644 --- a/members/tests/tests_member_base.py +++ b/members/tests/tests_member_base.py @@ -150,7 +150,7 @@ def create_member(self, member_data=None, is_active=False): else: passwd = member_data['password'] new_member = Member.objects.create_member(**member_data, is_active=is_active, - managing_member=None if is_active else self.member) + member_manager=None if is_active else self.member) # store real password instead of hashed one so that we can login with it afterward new_member.password = passwd self.created_members.append(new_member) diff --git a/members/views/views_activate.py b/members/views/views_activate.py index 078c122f..7c79fce7 100644 --- a/members/views/views_activate.py +++ b/members/views/views_activate.py @@ -20,8 +20,8 @@ def activate_member(request, pk): if member.is_dead: messages.error(request, _("Error: Cannot activate a dead member")) elif member.is_active: - if member.managing_member is not None: - member.managing_member = None + if member.member_manager is not None: + member.member_manager = None member.save() messages.error(request, _("Error: Member already active")) elif not member.email: diff --git a/members/views/views_import_export.py b/members/views/views_import_export.py index a44e012c..3f524116 100644 --- a/members/views/views_import_export.py +++ b/members/views/views_import_export.py @@ -53,6 +53,15 @@ class CSVImportView(LoginRequiredMixin, generic.FormView): template_name = "members/members/import_members.html" form_class = CSVImportMembersForm success_url = reverse_lazy("members:members") + warned_on_activate_users = False + warnings = [] + errors = [] + row = None + created_num = 0 + updated_num = 0 + rows_num = 0 + changed = False + current_member = None def get_context_data(self, *args, **kwargs): optional_fields = {str(s) for s in ALL_FIELD_NAMES.values()} - {str(s) for s in MANDATORY_MEMBER_FIELD_NAMES.values()} @@ -62,164 +71,190 @@ def get_context_data(self, *args, **kwargs): 'media_root': settings.MEDIA_ROOT, } - def _create_member(self, row, activate_users): - """create new member based on row content. + def _create_member(self): + """create new member based on current row content. returns created member and warnings if any """ - member = Member() - warnings = [] + self.current_member = Member(is_active=False) + self.changed = True + # print(f"newly created member is active:{member.is_active}") for field in MEMBER_FIELD_NAMES: trfield = t(field) - if trfield in row and row[trfield]: - if field == 'family': - self._manage_family(member, row[trfield]) - elif field == 'avatar': - warning = self._manage_avatar(member, row[trfield], row[t('username')]) - if warning: - warnings.append(warning) - elif member.__dict__[field] != row[trfield]: - setattr(member, field, row[trfield]) - - member.is_active = activate_users and not member.is_dead - # set manager to people who imported the file if imported users are not activated - member.managing_member = None if member.is_active else Member.objects.get(id=self.request.user.id) - member.password = generate_random_string(16) - - return member, warnings - - def _manage_avatar(self, member, avatar_file, username): + if trfield in self.row and self.row[trfield]: + match field: + case 'family': + self._manage_family(self.row[trfield]) + case 'managed_by': + self._manage_managed_by(self.row[trfield]) + case 'avatar': + self._manage_avatar(self.row[trfield], self.row[t('username')]) + case _: + if self.current_member.__dict__[field] != self.row[trfield]: + setattr(self.current_member, field, self.row[trfield]) + + self.current_member.is_active = self.activate_users and not self.current_member.is_dead + # set manager to people who imported the file if imported users are not activated and not yet mmanaged + if self.current_member.is_active: + self.current_member.member_manager = None + else: + self.current_member.member_manager = self.current_member.member_manager or Member.objects.get(id=self.request.user.id) + self.current_member.password = generate_random_string(16) + if self.changed: + self.created_num += 1 + + def _manage_avatar(self, avatar_file, username): avatar = os.path.join(settings.MEDIA_REL, settings.AVATARS_DIR, avatar_file) # avatar not changed - if member.avatar and member.avatar.path == avatar: - return (None, False) + if self.current_member.avatar and self.current_member.avatar.path == avatar: + return # avatar image must already exist if not os.path.exists(avatar): - return (_("Avatar not found: %(avatar)s for username %(username)s. Ignored...") % - {'avatar': avatar, 'username': username}, False) + self.warnings.append(_("Avatar not found: %(avatar)s for username %(username)s. Ignored...") % + {'avatar': avatar, 'username': username}) else: try: with open(avatar, 'rb') as image_file: image = File(image_file) - member.avatar.save(avatar_file, image) - return (None, False) + self.current_member.avatar.save(avatar_file, image) + self.changed = True except Exception as e: - return (_("Error saving avatar (%(warning)s): %(avatar)s for username %(username)s. Ignored...") % - {'warning': e, 'avatar': avatar, 'username': username}, False) - - def _manage_family(self, member, family_name): - if member.family and member.family.name == family_name: - return False - member.family, created = Family.objects.get_or_create(name=family_name) - return True - - def _update_member_field(self, member, field, value, username): - changed = False - warnings = [] - - match field: - case 'username': - pass - case 'family': - changed = self._manage_family(member, value) - case 'avatar': - warning, modified = self._manage_avatar(member, value, username) - changed = changed or modified - if warning: - warnings.append(warning) - case _: - if member.__dict__[field] != value: - setattr(member, field, value) - changed = True - - return (changed, warnings) - - def _update_member(self, member, row, activate_users): - "update an existing member based on row content" - changed = False + self.warnings.append(_("Error saving avatar (%(warning)s): %(avatar)s for username %(username)s. Ignored...") % + {'warning': e, 'avatar': avatar, 'username': username}) + + def _manage_family(self, family_name): + if not self.current_member.family or self.current_member.family.name != family_name: + self.current_member.family, _ = Family.objects.get_or_create(name=family_name) + self.changed = True + + def _manage_managed_by(self, manager_username): + # print(f"in manage_manage_by, member {self.current_member.username}, manager {manager_username}. " + # f"Member is {'active' if self.current_member.is_active else 'inactive'}. " + # f"Activate during Import is {self.activate_users}") + if manager_username: + new_member_manager = Member.objects.filter(username=manager_username).first() + if self.activate_users and not self.warned_on_activate_users: + self.warned_on_activate_users = True + self.warnings.append(_("You requested to activate imported members. All managers will be ignored.")) + elif not new_member_manager: + self.warnings.append(_("Manager %(manager)s not found for member %(member)s. Ignoring...") % { + 'manager': manager_username, + 'member': self.current_member.full_name}) + elif self.current_member.member_manager == new_member_manager: + # no change + pass + elif self.current_member.is_active: + self.warnings.append(_("Member %(member)s is active. Ignoring manager %(manager)s") % + {'member': self.current_member.full_name, 'manager': manager_username}) + else: + self.current_member.member_manager = new_member_manager + self.changed = True + else: # no manager provided + if not self.current_member.is_active: # we need one + if self.current_member.member_manager: + # just ignore with a warning + self.warnings.append(_("No manager provided for member %(member)s although inactive. " + "Keeping existing one (%(manager)s)...") % { + 'member': self.current_member.full_name, 'manager': self.current_member.member_manager.full_name}) + else: # member is not active and has no manager, and none provided ==> error but continue + self.errors.append(_("Inactive member %(member)s has no manager. Please provide one!") % { + 'member': self.current_member.full_name + }) + # print(f"member {self.current_member.username} manager is " + # f"{self.current_member.member_manager.username if self.current_member.member_manager else 'None'}") + + def _update_member_field(self, field, value, username): + match field: + case 'username': + pass + case 'family': + self._manage_family(value) + case 'avatar': + self._manage_avatar(value, username) + case 'managed_by': + self._manage_managed_by(value) + case _: + if self.current_member.__dict__[field] != value: + setattr(self.current_member, field, value) + self.changed = True + + def _update_member(self): + "update an existing member based on current row content" # for all member fields but username # if new value for this field, then override existing one - warnings = [] - username = row[t('username')] + username = self.row[t('username')] for field in MEMBER_FIELD_NAMES: trfield = t(field) - if trfield in row and row[trfield]: - modified, new_warnings = self._update_member_field(member, field, row[trfield], username) - changed = changed or modified - warnings.extend(new_warnings) - if activate_users and not member.is_dead: - if member.managing_member is not None: - member.managing_member = None - changed = True - if not member.is_active: - member.is_active = changed = True - - return changed, warnings - - def _update_address(self, member, row): - changed = False + if trfield in self.row and self.row[trfield]: + self._update_member_field(field, self.row[trfield], username) + if self.activate_users and not self.current_member.is_active: + self.current_member.member_manager = None + self.current_member.is_active = True + self.changed = True + if self.changed: + self.updated_num += 1 + + def _update_address(self): address = {} for field in ADDRESS_FIELD_NAMES: trfield = t(field) - if trfield in row: - address[field] = row[trfield] + if trfield in self.row: + address[field] = self.row[trfield] # if len(address) == 5: #we don't care if the address is incomplete if len(address) > 0: found = Address.objects.filter(**address).first() if found: - if member.address != found: - member.address = found - changed = True + if self.current_member.address != found: + self.current_member.address = found + self.changed = True else: address = Address.objects.create(**address) address.save() - member.address = address - changed = True - return changed - - def _import_csv(self, csv_file, activate_users): - nbMembers = 0 - nbLines = 0 - all_warnings = [] + self.current_member.address = address + self.changed = True + + def _import_csv(self, csv_file): csvf = io.TextIOWrapper(csv_file, encoding="utf-8", newline="") reader = csv.DictReader(csvf) check_fields(reader.fieldnames) random.seed() - for row in reader: + for self.row in reader: + # reset all row variables + self.warnings = [] + self.errors = [] + self.current_member = None + self.changed = False # normalize username using slugify - username = slugify(row[t('username')]) - row[t('username')] = username + self.row[t('username')] = slugify(self.row[t('username')]) # search for an existing member with this username - member = Member.objects.filter(username=username).first() - if member: # found, use it - changed_member, new_warnings = self._update_member(member, row, activate_users) + self.current_member = Member.objects.filter(username=self.row[t('username')]).first() + if self.current_member: # found, update it + self._update_member() else: # not found, create it - member, new_warnings = self._create_member(row, activate_users) - changed_member = True - - changed_address = self._update_address(member, row) - all_warnings.extend(new_warnings) - - nbLines += 1 + self._create_member() - if changed_address or changed_member: - member.save() - nbMembers += 1 + self._update_address() + self.rows_num += 1 - return (nbLines, nbMembers, all_warnings) + if self.changed: + self.current_member.save() def post(self, request, *args, **kwargs): self.request = request form = CSVImportMembersForm(request.POST, request.FILES) if form.is_valid(): csv_file = request.FILES["csv_file"] - activate_users = form.cleaned_data["activate_users"] + self.activate_users = form.cleaned_data["activate_users"] try: - nbLines, nbMembers, warnings = self._import_csv(csv_file, activate_users) - messages.success(request, _("CSV file uploaded: %(nbLines)i lines read, %(nbMembers)i members created or updated") % - {'nbLines': nbLines, 'nbMembers': nbMembers}) - for warning in warnings: + self._import_csv(csv_file) + messages.success(request, + _("CSV file uploaded: %(rows_num)i lines read, %(created_num)i members created " + "and %(updated_num)i updated.") % + {'rows_num': self.rows_num, 'created_num': self.created_num, 'updated_num': self.updated_num}) + for error in self.errors: + messages.error(request, _("Error: %(error)s") % {'warning': error}) + for warning in self.warnings: messages.warning(request, _("Warning: %(warning)s") % {'warning': warning}) except ValidationError as ve: messages.error(request, ve.message) diff --git a/members/views/views_member.py b/members/views/views_member.py index f7e0d706..8fdbc800 100644 --- a/members/views/views_member.py +++ b/members/views/views_member.py @@ -38,12 +38,12 @@ def logout_member(request): def editable(request, member): if request.user.is_superuser: return True - manager = member.managing_member or member + manager = member.member_manager or member return manager.id == request.user.id -def managing_member_name(member): - return Member.objects.get(id=member.managing_member.id).full_name if member and member.managing_member else None +def member_manager_name(member): + return Member.objects.get(id=member.member_manager.id).full_name if member and member.member_manager else None def register_remove_accents(): @@ -93,7 +93,7 @@ def get_context_data(self, **kwargs): return super().get_context_data(**kwargs) | \ { "can_edit": editable(self.request, member), - "managing_member_name": member.managing_member.username if member.managing_member else None, + "member_manager_name": member.member_manager.username if member.member_manager else None, "hobbies_list": [s.strip() for s in member.hobbies.split(',')] if member.hobbies else [], } @@ -131,9 +131,9 @@ def post(self, request, *args, **kwargs): member = form.save() # if new managed member is created, it must be inactivated member.is_active = False - # force managing_member to the logged in user - member.managing_member = Member.objects.get(id=request.user.id) - member.save(update_fields=['is_active', 'managing_member']) + # force member_manager to the logged in user + member.member_manager = Member.objects.get(id=request.user.id) + member.save(update_fields=['is_active', 'member_manager']) messages.success(request, _('Member successfully created')) return redirect("members:detail", member.id) @@ -149,10 +149,10 @@ class EditMemberView(LoginRequiredMixin, generic.UpdateView): def _can_edit(self, request, member): if request.user.is_superuser: return True - if member.managing_member is None: + if member.member_manager is None: return (member.id == request.user.id) else: - return (member.managing_member.id == request.user.id) + return (member.member_manager.id == request.user.id) def get(self, request, pk): member = get_object_or_404(Member, pk=pk) @@ -166,7 +166,7 @@ def get(self, request, pk): "family_form": FamilyUpdateForm(instance=member.family), "pk": pk, "title": self.title, - "managing_member_name": managing_member_name(member)}) + "member_manager_name": member_manager_name(member)}) def post(self, request, pk): member = get_object_or_404(Member, pk=pk) diff --git a/members/views/views_registration.py b/members/views/views_registration.py index f7b8df0b..90ce4e8d 100644 --- a/members/views/views_registration.py +++ b/members/views/views_registration.py @@ -43,8 +43,8 @@ def check_before_register(self, request, encoded_email, token): ) return redirect(reverse("members:login")) else: - # if member is already registered but is not active, ask him to contact his/her managing member - manager = Member.objects.get(id=member.id).managing_member + # if member is already registered but is not active, ask him to contact his/her member manager + manager = Member.objects.get(id=member.id).member_manager messages.error(request, _("You are already registered but not active. Please contact %(admin)s to activate your account") % {'admin': manager.full_name})