verejny kontaktnicek #71
							
								
								
									
										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,16 @@ from personalni.models import Resitel, Organizator | ||||||
| 
 | 
 | ||||||
| from various.models import SeminarModelBase | from various.models import SeminarModelBase | ||||||
| from tvorba.models import Rocnik, Problem, aux_generate_filename | from tvorba.models import Rocnik, Problem, aux_generate_filename | ||||||
|  | import secrets | ||||||
|  | import string | ||||||
|  | from django.utils import timezone | ||||||
|  | 
 | ||||||
|  | def generate_filename_kontaktnicek(self, filename): | ||||||
|  | 	# generate random string | ||||||
|  | 	length = 32 | ||||||
|  | 	fname = "kontaktnicky/" + timezone.now().strftime('%Y-%m-%d-%H_%M') | ||||||
|  | 	fname += ''.join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(length)) | ||||||
|  | |||||||
|  | 	return os.path.join(settings.KOREKTURY_PDF_DIR, fname) | ||||||
| 
				
					
						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`… | |||||||
| 
 | 
 | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  | @ -66,6 +76,9 @@ class Soustredeni(SeminarModelBase): | ||||||
| 	exportovat = models.BooleanField('export do AESOPa', db_column='exportovat', default=False, | 	exportovat = models.BooleanField('export do AESOPa', db_column='exportovat', default=False, | ||||||
| 			help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti)') | 			help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti)') | ||||||
| 	 | 	 | ||||||
|  | 	kontaktnicek_vcf = models.FileField('kontaktníček vcf', upload_to=generate_filename_kontaktnicek, blank=True,) | ||||||
|  | 	kontaktnicek_pdf = models.FileField('kontaktníček pdf', upload_to=generate_filename_kontaktnicek, blank=True,) | ||||||
| 
				
					
						zelvuska
						commented  Nemá to být  Nemá to být `null=True`? (Ale já tomu nerozumím…) | |||||||
|  | 
 | ||||||
| 	def __str__(self): | 	def __str__(self): | ||||||
| 		return '{} ({})'.format(self.misto, self.datum_zacatku) | 		return '{} ({})'.format(self.misto, self.datum_zacatku) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -35,6 +35,14 @@ | ||||||
|             {% endif %} |             {% endif %} | ||||||
|           {% endfor %} |           {% endfor %} | ||||||
|         {% endif %} |         {% 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> |       </ul> | ||||||
|       {% if user.je_org %} |       {% if user.je_org %} | ||||||
|         <div class="mam-org-only"> |         <div class="mam-org-only"> | ||||||
|  |  | ||||||
|  | @ -44,6 +44,16 @@ urlpatterns = [ | ||||||
| 					org_required(views.SoustredeniAbstraktyView.as_view()), | 					org_required(views.SoustredeniAbstraktyView.as_view()), | ||||||
| 					name='soustredeni_abstrakty' | 					name='soustredeni_abstrakty' | ||||||
| 				), | 				), | ||||||
|  |                 path( | ||||||
|  |                     'kontaktnicek_pdf', | ||||||
|  |                     views.soustredeniKontaktnicekPdfView, | ||||||
|  |                     name='soustredeni_kontaktnicek' | ||||||
|  | 				), | ||||||
|  |                 path( | ||||||
|  |                     'kontaktnicek_vcf', | ||||||
|  |                     views.soustredeniKontaktnicekVcfView, | ||||||
|  |                     name='soustredeni_kontaktnicek' | ||||||
| 
				
					
						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( | 				path( | ||||||
| 					'fotogalerie/', | 					'fotogalerie/', | ||||||
| 					include('galerie.urls') | 					include('galerie.urls') | ||||||
|  |  | ||||||
|  | @ -106,3 +106,32 @@ class SoustredeniAbstraktyView(generic.DetailView): | ||||||
| 	model = Soustredeni | 	model = Soustredeni | ||||||
| 	template_name = 'soustredeni/export_do_abstraktu.html' | 	template_name = 'soustredeni/export_do_abstraktu.html' | ||||||
| 	pk_url_kwarg = 'soustredeni' # v url bude <int:soustredeni> místo defaultně požadovaného <int:pk> | 	pk_url_kwarg = 'soustredeni' # v url bude <int:soustredeni> místo defaultně požadovaného <int:pk> | ||||||
|  | 
 | ||||||
|  | # kontaktnicek | ||||||
|  | def soustredeniKontaktnicekPdfView(request, soustredeni): | ||||||
| 
				
				zelvuska marked this conversation as resolved
				
					
					
						Outdated
					
				
			 
				
					
						ledoian
						commented  Diakritika maybe? Diakritika maybe? | |||||||
|  | 	return soustredeniKontaktnicekView(request, soustredeni, "pdf") | ||||||
|  | 
 | ||||||
|  | def soustredeniKontaktnicekVcfView(request, soustredeni): | ||||||
|  | 	return soustredeniKontaktnicekView(request, soustredeni, "vcf") | ||||||
|  | 
 | ||||||
| 
				
				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 soustredeniKontaktnicekView(request, soustredeni, typ): | ||||||
|  | 	soustredeni = get_object_or_404(Soustredeni, id=soustredeni) | ||||||
|  | 
 | ||||||
|  | 	if (not request.user in [u.osoba.user for u in soustredeni.ucastnici.all()]): | ||||||
|  | 		if not request.user.je_org: | ||||||
|  | 			return HttpResponse("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 soustredeni.kontaktnicek_pdf and typ == "pdf": | ||||||
|  | 		return HttpResponse("Kontaktníček není k dispozici") | ||||||
|  | 	elif not soustredeni.kontaktnicek_vcf and typ == "vcf": | ||||||
| 
				
					
						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). | |||||||
|  | 		return HttpResponse("Kontaktníček není k dispozici") | ||||||
| 
				
					
						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. | |||||||
|  | 	 | ||||||
|  | 	if typ == "pdf": | ||||||
|  | 		with open(soustredeni.kontaktnicek_pdf.path, 'rb') as pdf: | ||||||
|  | 			response = HttpResponse(pdf.read(), content_type='application/pdf') | ||||||
|  | 		return response | ||||||
|  | 	elif typ == "vcf": | ||||||
| 
				
					
						ledoian
						commented  Možno rovnou: Možno rovnou:
```python3
with open(…) as whatever:
	return HttpResponse(…)
``` | |||||||
|  | 		with open(soustredeni.kontaktnicek_vcf.path, 'rb') as vcf: | ||||||
|  | 			response = HttpResponse(vcf.read(), content_type='text/vcard') | ||||||
|  | 		return response | ||||||
|  | 	else: | ||||||
|  | 		return HttpResponse("Neplatný typ kontaktníčku") | ||||||
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č.