From 8e90e6452b77bebeac26e71bd422fdebfa9e9a01 Mon Sep 17 00:00:00 2001 From: Erich Hasl Date: Fri, 24 Sep 2021 12:46:48 +0200 Subject: [PATCH 1/2] introduce some new fields on members and ljp lists --- jdav_web/locale/de/LC_MESSAGES/django.po | 2 +- .../mailer/locale/de/LC_MESSAGES/django.po | 2 +- .../material/locale/de/LC_MESSAGES/django.po | 2 +- jdav_web/members/admin.py | 109 +++++++++++++++- .../members/locale/de/LC_MESSAGES/django.po | 116 ++++++++++-------- jdav_web/members/models.py | 9 ++ .../startpage/locale/de/LC_MESSAGES/django.po | 2 +- 7 files changed, 181 insertions(+), 61 deletions(-) diff --git a/jdav_web/locale/de/LC_MESSAGES/django.po b/jdav_web/locale/de/LC_MESSAGES/django.po index e895249..ecb9b0f 100644 --- a/jdav_web/locale/de/LC_MESSAGES/django.po +++ b/jdav_web/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-26 14:15+0200\n" +"POT-Creation-Date: 2021-09-24 12:24+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/jdav_web/mailer/locale/de/LC_MESSAGES/django.po b/jdav_web/mailer/locale/de/LC_MESSAGES/django.po index dfcfb75..dd2df63 100644 --- a/jdav_web/mailer/locale/de/LC_MESSAGES/django.po +++ b/jdav_web/mailer/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-26 14:15+0200\n" +"POT-Creation-Date: 2021-09-24 12:24+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/jdav_web/material/locale/de/LC_MESSAGES/django.po b/jdav_web/material/locale/de/LC_MESSAGES/django.po index cc4634a..d410db3 100644 --- a/jdav_web/material/locale/de/LC_MESSAGES/django.po +++ b/jdav_web/material/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-26 14:15+0200\n" +"POT-Creation-Date: 2021-09-24 12:24+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/jdav_web/members/admin.py b/jdav_web/members/admin.py index 89e72fa..6be4ae6 100644 --- a/jdav_web/members/admin.py +++ b/jdav_web/members/admin.py @@ -68,11 +68,13 @@ class RegistrationFilter(admin.SimpleListFilter): class MemberAdmin(admin.ModelAdmin): fields = ['prename', 'lastname', 'email', 'email_parents', 'street', 'plz', 'town', 'phone_number', 'phone_number_parents', 'birth_date', 'group', - 'gets_newsletter', 'registered', 'registration_form', 'comments'] - list_display = ('name', 'birth_date', 'get_group', 'gets_newsletter', - 'registered', 'comments', 'activity_score') + 'gets_newsletter', 'registered', 'registration_form', 'active', + 'not_waiting', 'comments'] + list_display = ('name', 'birth_date', 'age', 'get_group', 'gets_newsletter', + 'registered', 'active', 'not_waiting', 'comments', 'activity_score') search_fields = ('prename', 'lastname') - list_filter = ('group', 'gets_newsletter', RegistrationFilter) + list_filter = ('group', 'gets_newsletter', RegistrationFilter, 'active', + 'not_waiting') #formfield_overrides = { # ManyToManyField: {'widget': forms.CheckboxSelectMultiple}, # ForeignKey: {'widget': apply_select2(forms.Select)} @@ -384,7 +386,7 @@ class FreizeitAdmin(admin.ModelAdmin): list_display = ['__str__', 'date'] search_fields = ('name',) ordering = ('-date',) - actions = ['convert_to_pdf', 'generate_notes'] + actions = ['convert_to_pdf', 'generate_notes', 'convert_to_ljp'] #formfield_overrides = { # ManyToManyField: {'widget': forms.CheckboxSelectMultiple}, # ForeignKey: {'widget': apply_select2(forms.Select)} @@ -616,6 +618,103 @@ class FreizeitAdmin(admin.ModelAdmin): return response generate_notes.short_description = _('Generate overview') + def convert_to_ljp(self, request, queryset): + """Converts a member list to pdf but without email and with birth date. + Suitable for LJP lists. + """ + for memberlist in queryset: + # create a unique filename + filename = memberlist.name + "_ljp_" + datetime.today().strftime("%d_%m_%Y") + filename = filename.replace(' ', '_').replace('&', '') + # drop umlauts, accents etc. + filename = unicodedata.normalize('NFKD', filename).\ + encode('ASCII', 'ignore').decode() + filename_table = 'table_' + filename + filename_tex = filename + '.tex' + filename_pdf = filename + '.pdf' + + # open temporary file for table + with open(media_path(filename_table), 'w+', encoding='utf-8') as f: + if memberlist.membersonlist.count() == 0: + f.write('{0} & {1} & {2} & {3} \\\\ \n'.format( + 'keine Teilnehmer', '-', '-', '-' + )) + for memberonlist in memberlist.membersonlist.all(): + # write table of members in latex compatible format + member = memberonlist.member + # use parents phone number if available + phone_number = member.phone_number_parents if\ + member.phone_number_parents else member.phone_number + # use parents email address if available + email = member.email_parents if\ + member.email_parents else member.email + line = '{0} {1} & {2} & {3} & & & \\\\ \\hline \n'.format( + esc_all(memberonlist.member.prename), + esc_all(memberonlist.member.lastname), + esc_all(memberonlist.member.address), + esc_all(memberonlist.member.birth_date.strftime("%d.%m.%Y"))) + f.write(line) + + # copy and adapt latex memberlist template + shutil.copy(media_path('memberlist_ljp_template.tex'), + media_path(filename_tex)) + + # read in template + with open(media_path(filename_tex), 'r', encoding='utf-8') as f: + template_content = f.read() + + # adapt template + name = esc_all(memberlist.name) + template_content = template_content.replace('ACTIVITY', name) + groups = ', '.join(g.name for g in + memberlist.groups.all()) + template_content = template_content.replace('GROUP', + esc_all(groups)) + destination = esc_all(memberlist.destination) + template_content = template_content.replace('DESTINATION', + destination) + place = esc_all(memberlist.place) + template_content = template_content.replace('PLACE', place) + template_content = template_content.replace('MEMBERLIST-DATE', + datetime.today().strftime('%d.%m.%Y')) + time_period = memberlist.date.strftime('%d.%m.%Y') + if memberlist.end != memberlist.date: + time_period += " - " + memberlist.end.strftime('%d.%m.%Y') + template_content = template_content.replace('TIME-PERIOD', time_period) + jugendleiter = ', '.join(j.name for j in memberlist.jugendleiter.all()) + template_content = template_content.replace('JUGENDLEITER', jugendleiter) + template_content = template_content.replace('TABLE-NAME', + filename_table) + + # write adapted template to file + with open(media_path(filename_tex), 'w', encoding='utf-8') as f: + f.write(template_content) + + # compile using pdflatex + oldwd = os.getcwd() + os.chdir(media_dir()) + subprocess.call(['pdflatex', filename_tex]) + time.sleep(1) + + # do some cleanup + for f in glob.glob('*.log'): + os.remove(f) + for f in glob.glob('*.aux'): + os.remove(f) + os.remove(filename_tex) + os.remove(filename_table) + + os.chdir(oldwd) + + # provide the user with the resulting pdf file + with open(media_path(filename_pdf), 'rb') as pdf: + response = HttpResponse(FileWrapper(pdf))#, content='application/pdf') + response['Content-Type'] = 'application/pdf' + response['Content-Disposition'] = 'attachment; filename='+filename_pdf + + return response + convert_to_ljp.short_description = _('Generate list for LJP') + class KlettertreffAdminForm(forms.ModelForm): class Meta: diff --git a/jdav_web/members/locale/de/LC_MESSAGES/django.po b/jdav_web/members/locale/de/LC_MESSAGES/django.po index 71ae579..7697ebc 100644 --- a/jdav_web/members/locale/de/LC_MESSAGES/django.po +++ b/jdav_web/members/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-26 14:15+0200\n" +"POT-Creation-Date: 2021-09-24 12:24+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: members/admin.py:29 members/models.py:75 +#: members/admin.py:29 members/models.py:77 msgid "Registration complete" msgstr "Anmeldung vollständig" @@ -34,19 +34,19 @@ msgstr "Nein" msgid "All" msgstr "Alle" -#: members/admin.py:187 +#: members/admin.py:189 msgid "Compose new mail to selected members" msgstr "Neue Nachricht an ausgewählte Teilnehmer verfassen" -#: members/admin.py:204 +#: members/admin.py:206 msgid "activity" msgstr "Aktivität" -#: members/admin.py:220 +#: members/admin.py:222 msgid "Difficulty" msgstr "Schwierigkeit" -#: members/admin.py:224 +#: members/admin.py:226 msgid "Tour type" msgstr "Art der Tour" @@ -58,182 +58,194 @@ msgstr "Kriseninterventionsliste erstellen" msgid "Generate overview" msgstr "Hinweise für Jugendleiter erstellen" -#: members/apps.py:7 members/models.py:157 +#: members/admin.py:716 +msgid "Generate list for LJP" +msgstr "LJP Liste erstellen" + +#: members/apps.py:7 members/models.py:166 msgid "members" msgstr "Teilnehmer" -#: members/models.py:22 +#: members/models.py:24 msgid "Name" msgstr "Name" -#: members/models.py:23 +#: members/models.py:25 msgid "Description" msgstr "Beschreibung" -#: members/models.py:29 members/models.py:178 members/models.py:257 +#: members/models.py:31 members/models.py:187 members/models.py:266 #: members/templates/members/change_member.html:17 msgid "Activity" msgstr "Aktivität" -#: members/models.py:30 +#: members/models.py:32 msgid "Activities" msgstr "Aktivitäten" -#: members/models.py:38 +#: members/models.py:40 msgid "name" msgstr "Name" -#: members/models.py:40 +#: members/models.py:42 msgid "minimum age (years)" msgstr "Mindestalter (Jahre)" -#: members/models.py:47 members/models.py:68 +#: members/models.py:49 members/models.py:70 msgid "group" msgstr "Gruppe" -#: members/models.py:48 +#: members/models.py:50 msgid "groups" msgstr "Gruppen" -#: members/models.py:56 +#: members/models.py:58 msgid "prename" msgstr "Vorname" -#: members/models.py:57 +#: members/models.py:59 msgid "last name" msgstr "Nachname" -#: members/models.py:58 +#: members/models.py:60 msgid "street" msgstr "Straße" -#: members/models.py:59 +#: members/models.py:61 msgid "Postcode" msgstr "PLZ" -#: members/models.py:61 +#: members/models.py:63 msgid "town" msgstr "Stadt" -#: members/models.py:62 +#: members/models.py:64 msgid "phone number" msgstr "Telefonnummer" -#: members/models.py:63 +#: members/models.py:65 msgid "parents phone number" msgstr "Telefonnummer der Eltern" -#: members/models.py:66 +#: members/models.py:68 msgid "Parents' Email" msgstr "Email der Eltern" -#: members/models.py:67 +#: members/models.py:69 msgid "birth date" msgstr "Geburtsdatum" -#: members/models.py:69 +#: members/models.py:71 msgid "receives newsletter" msgstr "Erhält den Newsletter" -#: members/models.py:73 +#: members/models.py:75 msgid "comments" msgstr "Kommentare" -#: members/models.py:74 +#: members/models.py:76 msgid "created" msgstr "erstellt" -#: members/models.py:76 +#: members/models.py:78 +msgid "Active" +msgstr "Aktiv" + +#: members/models.py:79 +msgid "Not waiting" +msgstr "NICHT Warteliste" + +#: members/models.py:80 msgid "registration form" msgstr "Anmeldeformular" -#: members/models.py:153 members/models.py:326 +#: members/models.py:162 members/models.py:335 msgid "Group" msgstr "Gruppe" -#: members/models.py:156 +#: members/models.py:165 msgid "member" msgstr "Teilnehmer" -#: members/models.py:180 members/models.py:259 +#: members/models.py:189 members/models.py:268 msgid "Place" msgstr "Ort" -#: members/models.py:181 members/models.py:260 +#: members/models.py:190 members/models.py:269 msgid "Destination (optional)" msgstr "Ziel (optional)" -#: members/models.py:183 members/models.py:262 members/models.py:304 -#: members/models.py:322 +#: members/models.py:192 members/models.py:271 members/models.py:313 +#: members/models.py:331 msgid "Date" msgstr "Datum" -#: members/models.py:184 members/models.py:263 +#: members/models.py:193 members/models.py:272 msgid "End (optional)" msgstr "Ende" -#: members/models.py:186 members/models.py:265 +#: members/models.py:195 members/models.py:274 msgid "Groups" msgstr "Gruppen" -#: members/models.py:194 members/models.py:273 +#: members/models.py:203 members/models.py:282 msgid "Categories" msgstr "Kategorien" -#: members/models.py:195 members/models.py:274 +#: members/models.py:204 members/models.py:283 msgid "easy" msgstr "leicht" -#: members/models.py:195 members/models.py:274 +#: members/models.py:204 members/models.py:283 msgid "medium" msgstr "mittel" -#: members/models.py:195 members/models.py:274 +#: members/models.py:204 members/models.py:283 msgid "hard" msgstr "schwer" -#: members/models.py:204 +#: members/models.py:213 msgid "Memberlist" msgstr "Teilnehmerliste" -#: members/models.py:205 +#: members/models.py:214 msgid "Memberlists" msgstr "Teilnehmerlisten" -#: members/models.py:223 members/models.py:231 members/models.py:239 -#: members/models.py:250 members/models.py:357 members/models.py:364 +#: members/models.py:232 members/models.py:240 members/models.py:248 +#: members/models.py:259 members/models.py:366 members/models.py:373 msgid "Member" msgstr "Teilnehmer" -#: members/models.py:225 members/models.py:244 +#: members/models.py:234 members/models.py:253 msgid "Comment" msgstr "Kommentar" -#: members/models.py:232 members/models.py:251 members/models.py:365 +#: members/models.py:241 members/models.py:260 members/models.py:374 msgid "Members" msgstr "Teilnehmer" -#: members/models.py:303 +#: members/models.py:312 msgid "Title" msgstr "Titel" -#: members/models.py:323 +#: members/models.py:332 msgid "Location" msgstr "Ort" -#: members/models.py:324 +#: members/models.py:333 msgid "Topic" msgstr "Thema" -#: members/models.py:348 +#: members/models.py:357 msgid "Jugendleiter" msgstr "Jugendleiter" -#: members/models.py:351 +#: members/models.py:360 msgid "Klettertreff" msgstr "Klettertreff" -#: members/models.py:352 +#: members/models.py:361 msgid "Klettertreffs" msgstr "Klettertreffs" diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index 852a85c..59c5d2f 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -9,6 +9,8 @@ from django.contrib.contenttypes.models import ContentType from utils import RestrictedFileField import os +from dateutil.relativedelta import relativedelta + GEMEINSCHAFTS_TOUR = 0 FUEHRUNGS_TOUR = 1 AUSBILDUNGS_TOUR = 2 @@ -73,6 +75,8 @@ class Member(models.Model): comments = models.TextField(_('comments'), default='', blank=True) created = models.DateField(auto_now=True, verbose_name=_('created')) registered = models.BooleanField(default=False, verbose_name=_('Registration complete')) + active = models.BooleanField(default=True, verbose_name=_('Active')) + not_waiting = models.BooleanField(default=True, verbose_name=_('Not waiting')) registration_form = RestrictedFileField(verbose_name=_('registration form'), upload_to='registration_forms', blank=True, @@ -86,6 +90,11 @@ class Member(models.Model): """String representation""" return self.name + @property + def age(self): + """Age of member""" + return relativedelta(datetime.today(), self.birth_date).years + def generate_key(self): self.unsubscribe_key = uuid.uuid4().hex self.unsubscribe_expire = timezone.now() + timezone.timedelta(days=1) diff --git a/jdav_web/startpage/locale/de/LC_MESSAGES/django.po b/jdav_web/startpage/locale/de/LC_MESSAGES/django.po index 89eb720..9a89878 100644 --- a/jdav_web/startpage/locale/de/LC_MESSAGES/django.po +++ b/jdav_web/startpage/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-26 14:15+0200\n" +"POT-Creation-Date: 2021-09-24 12:24+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" From 2fa9374762d209afb920fa91d78e8c1d9cc654c2 Mon Sep 17 00:00:00 2001 From: Erich Hasl Date: Fri, 24 Sep 2021 20:23:51 +0200 Subject: [PATCH 2/2] tweak some software versions to fix attachments --- requirements.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7173d2d..ca00fe7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,26 +1,27 @@ -amqp==2.6.1 +amqp==2.5.2 anyjson==0.3.3 -billiard==3.6.4.0 +billiard==3.6.3.0 celery==4.4.1 certifi==2021.5.30 charset-normalizer==2.0.4 Django==1.11 django-appconf==1.0.3 django-celery==3.3.0 -django-celery-email==1.1.5 +django-celery-email==2.0.2 django-easy-select2==1.5.6 django-jet==1.0.8 django-multiselectfield==0.1.12 idna==3.2 -importlib-metadata==4.8.1 +importlib-metadata==1.5.0 jet==1.0 -kombu==4.6.11 +kombu==4.6.8 mysqlclient==2.0.3 Pillow==5.0.0 +python-dateutil==2.8.2 pytz==2021.1 redis==3.4.1 requests==2.26.0 -six==1.16.0 +six==1.14.0 sqlparse==0.3.1 typing-extensions==3.10.0.2 urllib3==1.26.6