verejny kontaktnicek #71
|
@ -342,6 +342,7 @@ SEMINAR_KONFERY_DIR = os.path.join('konfery')
|
|||
KOREKTURY_PDF_DIR = os.path.join('korektury', 'pdf')
|
||||
KOREKTURY_IMG_DIR = os.path.join('korektury', 'img')
|
||||
CISLO_IMG_DIR = os.path.join('cislo', 'img')
|
||||
SOUSTREDENI_KONTAKTNICKY_DIR = os.path.join('soustredeni', 'kontaktnicky')
|
||||
|
||||
|
||||
|
||||
|
|
18
soustredeni/migrations/0011_soustredeni_kontaktnicek_pdf.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2.13 on 2024-11-05 21:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('soustredeni', '0010_tvorba_post'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='soustredeni',
|
||||
name='kontaktnicek_pdf',
|
||||
field=models.FileField(blank=True, upload_to='kontaktnicky', verbose_name='kontaktníček'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 4.2.13 on 2024-11-05 21:07
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('soustredeni', '0011_soustredeni_kontaktnicek_pdf'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='soustredeni',
|
||||
name='kontaktnicek_vcf',
|
||||
field=models.FileField(blank=True, upload_to='kontaktnicky', verbose_name='kontaktníček vcf'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='soustredeni',
|
||||
name='kontaktnicek_pdf',
|
||||
field=models.FileField(blank=True, upload_to='kontaktnicky', verbose_name='kontaktníček pdf'),
|
||||
),
|
||||
]
|
|
@ -11,6 +11,17 @@ from personalni.models import Resitel, Organizator
|
|||
|
||||
from various.models import SeminarModelBase
|
||||
from tvorba.models import Rocnik, Problem, aux_generate_filename
|
||||
import secrets
|
||||
import string
|
||||
from django.utils import timezone
|
||||
|
||||
def generate_filename_kontaktnicek(self, filename, file_type):
|
||||
# generate random string
|
||||
length = 32
|
||||
fname = timezone.now().strftime('%Y-%m-%d-%H_%M') + "-"
|
||||
fname += ''.join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(length))
|
||||
|
||||
fname += '.' + file_type
|
||||
ledoian
commented
Prosím ne Prosím ne `KOREKTURY_PDF_DIR`!!
ledoian
commented
Also: možná je snazší a bezpečnější použít Also: možná je snazší a bezpečnější použít `pathlib` než `os.path.join`…
|
||||
return os.path.join(settings.SOUSTREDENI_KONTAKTNICKY_DIR, fname)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -65,7 +76,9 @@ class Soustredeni(SeminarModelBase):
|
|||
|
||||
exportovat = models.BooleanField('export do AESOPa', db_column='exportovat', default=False,
|
||||
help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti)')
|
||||
|
||||
#using lambda to avoid circular import
|
||||
kontaktnicek_vcf = models.FileField('kontaktníček vcf', upload_to=lambda instance, filename: generate_filename_kontaktnicek(instance, filename, 'vcf'), blank=True, null=True)
|
||||
zelvuska
commented
Nemá to být Nemá to být `null=True`? (Ale já tomu nerozumím…)
|
||||
kontaktnicek_pdf = models.FileField('kontaktníček pdf', upload_to=lambda instance, filename: generate_filename_kontaktnicek(instance, filename, 'pdf'), blank=True, null=True)
|
||||
def __str__(self):
|
||||
return '{} ({})'.format(self.misto, self.datum_zacatku)
|
||||
|
||||
|
|
|
@ -35,6 +35,14 @@
|
|||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% for i in soustredeni.ucastnici.all %}
|
||||
{% if i.osoba.user == user %}
|
||||
<li><a href="../{{soustredeni.pk}}/kontaktnicek_pdf">kontaktnicek pdf</a></li>
|
||||
<li><a href="../{{soustredeni.pk}}/kontaktnicek_vcf">kontaktnicek vcf</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</ul>
|
||||
{% if user.je_org %}
|
||||
<div class="mam-org-only">
|
||||
|
|
|
@ -44,11 +44,21 @@ urlpatterns = [
|
|||
org_required(views.SoustredeniAbstraktyView.as_view()),
|
||||
name='soustredeni_abstrakty'
|
||||
),
|
||||
path(
|
||||
'kontaktnicek_pdf',
|
||||
views.soustredeniKontaktnicekPdfView,
|
||||
name='soustredeni_kontaktnicek_pdf'
|
||||
),
|
||||
path(
|
||||
'kontaktnicek_vcf',
|
||||
views.soustredeniKontaktnicekVcfView,
|
||||
name='soustredeni_kontaktnicek_vcf'
|
||||
ledoian
commented
Stejný Stejný `name` pro obě URL nevypadá moc správně :-(
|
||||
),
|
||||
zelvuska marked this conversation as resolved
zelvuska
commented
Tady je nějaké divné odsazení… Tady je nějaké divné odsazení…
|
||||
path(
|
||||
'fotogalerie/',
|
||||
include('galerie.urls')
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
|
|
|
@ -2,6 +2,8 @@ from django.shortcuts import get_object_or_404
|
|||
from django.http import HttpResponse
|
||||
from django.views import generic
|
||||
from django.contrib.staticfiles.finders import find
|
||||
from django.http import Http404
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
import csv
|
||||
|
||||
|
@ -107,3 +109,35 @@ class SoustredeniAbstraktyView(generic.DetailView):
|
|||
model = Soustredeni
|
||||
template_name = 'soustredeni/export_do_abstraktu.html'
|
||||
pk_url_kwarg = 'soustredeni' # v url bude <int:soustredeni> místo defaultně požadovaného <int:pk>
|
||||
zelvuska marked this conversation as resolved
Outdated
ledoian
commented
Diakritika maybe? Diakritika maybe?
|
||||
|
||||
# Kontaktníčky
|
||||
def soustredeniKontaktnicekPdfView(request, soustredeni):
|
||||
return soustredeniKontaktnicekView(request, soustredeni, "pdf")
|
||||
|
||||
ledoian marked this conversation as resolved
ledoian
commented
Ty views jsou OK-ish. (Šlo by mít URL typu Ty views jsou OK-ish. (Šlo by mít URL typu `kontaktnicek_<typ:str>` a rovnou používat výsledný view, ale asi to nepřidává moc a spíš to zhoršuje čitelnost…)
|
||||
def soustredeniKontaktnicekVcfView(request, soustredeni):
|
||||
return soustredeniKontaktnicekView(request, soustredeni, "vcf")
|
||||
|
||||
def soustredeniKontaktnicekView(request, soustredeni, typ):
|
||||
soustredeni = get_object_or_404(Soustredeni, id=soustredeni)
|
||||
# nebyl jsi tam, nebo nejsi org
|
||||
zelvuska
commented
Asi bych napsal popisnější hlášku, vynechal bych tu část z orgem, a naopak bych tam připsal, že nejsi přihlášený (to se stane asi častěji než že bych se dostal na špatný kontaktníček). Také si nejsem jistý, jestli HttpResponse je to správné. (Jakože vrátí to něco, co moc nevypadá jako M&M stránky, ne?) Asi bych napsal popisnější hlášku, vynechal bych tu část z orgem, a naopak bych tam připsal, že nejsi přihlášený (to se stane asi častěji než že bych se dostal na špatný kontaktníček).
Také si nejsem jistý, jestli HttpResponse je to správné. (Jakože vrátí to něco, co moc nevypadá jako M&M stránky, ne?)
ledoian
commented
Uh, je to ekvivalentní s Uh, je to ekvivalentní s `if request.user not in […] and not request.user.is_org:`?
|
||||
if (not request.user in [u.osoba.user for u in soustredeni.ucastnici.all()]) and not request.user.je_org:
|
||||
raise PermissionDenied()
|
||||
|
||||
ledoian
commented
404 není správný návratový kód (kontaktníček zjevně existuje, ale nemám k němu přístup), má být 403 ( 404 není správný návratový kód (kontaktníček zjevně existuje, ale nemám k němu přístup), má být 403 (`PermissionDenied` se myslím jmenuje ta Djangová výjimka).
|
||||
kontaktnicky = {
|
||||
ledoian
commented
Poněkud se mi nelíbí místní duplikace kódu, když se tam mění dohromady tři věci (až přidáme další, tak někde něco zapomeneme přepsat a bude to bug…). Co třeba:
Poněkud se mi nelíbí místní duplikace kódu, když se tam mění dohromady tři věci (až přidáme další, tak někde něco zapomeneme přepsat a bude to bug…). Co třeba:
```python3
# typ -> (field, mime_type, otevreni)
kontaktnicky = {
| 'pdf': (soustredeni.kontaktnicek_pdf, 'applcation/pdf', 'rb'),
| 'vcf': (soustredeni.kontaktnicek_vcf, 'text/vcard', 'r'), # vcf je texťák, nevím, jestli je potřeba ho otevítat binárně.
}
try:
field, mime, otevreni = kontaktnicky[typ]
except KeyError as e:
raise ValueError("Neznámý typ kontaktníčku") from e
if not field:
| ...
with open(field.path, otevreni) as soubor:
| return HttpResponse(soubor.read(), content_type=mime)
```
ledoian
commented
Uh, ten Uh, ten `if not field: ...` byl náznak toho, že není potřeba výčet typů v `if (not soustredeni.kontaktnicek_pdf and typ == "pdf") or (not soustredeni.kontaktnicek_vcf and typ == "vcf"):`, sorry za neexplicitnost.
|
||||
'pdf': (soustredeni.kontaktnicek_pdf, 'applcation/pdf', 'rb'),
|
||||
'vcf': (soustredeni.kontaktnicek_vcf, 'text/vcard', 'rb'),
|
||||
}
|
||||
|
||||
try:
|
||||
field, mime, otevreni = kontaktnicky[typ]
|
||||
ledoian
commented
Možno rovnou:
Možno rovnou:
```python3
with open(…) as whatever:
return HttpResponse(…)
```
|
||||
except KeyError as e:
|
||||
raise ValueError("Neznámý typ kontaktníčku") from e
|
||||
|
||||
# není k dispozici
|
||||
if not field:
|
||||
raise Http404()
|
||||
|
||||
with open(field.path, otevreni) as kontaktnicek:
|
||||
response = HttpResponse(kontaktnicek.read(), content_type=mime)
|
||||
response['Content-Disposition'] = 'attachment; filename="kontaktnicek.{}"'.format(typ)
|
||||
return response
|
||||
|
|
Tohle nezachvá příponu, což je spíš bug – když se dívám na soubory na Gimlim, tak chci vědět, co jsou zač.