question as csv

This commit is contained in:
Holger Sielaff
2024-07-13 14:38:42 +02:00
parent e7b2dff233
commit 1531b0f6d3
6 changed files with 99 additions and 44 deletions

View File

@@ -1,5 +1,5 @@
import csv
import json import json
import logging
import os import os
from colorfield.fields import ColorField from colorfield.fields import ColorField
@@ -12,6 +12,7 @@ from filer.fields.file import FilerFileField
from lib.core.db.models.base import SharedPermissionBase from lib.core.db.models.base import SharedPermissionBase
from lib.core.db.models.mixins import DateAware, AuthorAware, DescriptionAware, NameAware, PublishedAware from lib.core.db.models.mixins import DateAware, AuthorAware, DescriptionAware, NameAware, PublishedAware
from tablequizwiki.settings import BASE_DIR
class MediaFile(NameAware, DateAware, AuthorAware, DescriptionAware): class MediaFile(NameAware, DateAware, AuthorAware, DescriptionAware):
@@ -88,37 +89,59 @@ class Question(DateAware, AuthorAware, PublishedAware, DescriptionAware):
| models.Q(buzzword__icontains=term) \ | models.Q(buzzword__icontains=term) \
| models.Q(labels__name__icontains=term) | models.Q(labels__name__icontains=term)
@staticmethod @staticmethod
def get_by_tearchterm(term, queryset=None): def get_by_tearchterm(term, queryset=None):
if not queryset: if not queryset:
queryset = Question.objects queryset = Question.objects
return queryset.filter(Question.searchdomain(term)).annotate(cnt=models.Count('id')) return queryset.filter(Question.searchdomain(term)).annotate(cnt=models.Count('id'))
def to_view(self, for_players: bool = False):
ret = {}
ret['name'] = '{}{}'.format(self.name, f'({self.buzzword})' if self.buzzword else '')
ret['question'] = self.question
if self.description:
ret['question'] += ' \n\n------------------------------\n\n' + self.description
ret['awnser'] = self.awnser
ret['level'] = self.level.value
ret['medias'] = []
ret['links'] = []
for media in self.medias.all():
ret.setdefault('medias', []).append(os.path.basename(media.file.path))
for link in self.links.all():
ret.setdefault('links', []).append(link.url)
ret['question'] = mark_safe(ret['question'].replace('\n', '\n<br />'))
return ret
def to_view(self, for_players: bool = False): @property
ret = {} def zippath(self):
ret['name'] = '{}{}'.format(self.name, f'({self.buzzword})' if self.buzzword else '') return str(BASE_DIR) + f'/filestore/zip/question.{self.id}.zip'
ret['question'] = self.question
if self.description:
ret['question'] += ' \n\n------------------------------\n\n' + self.description
ret['awnser'] = self.awnser
ret['level'] = self.level.value
ret['medias'] = []
ret['links'] = []
for media in self.medias.all():
ret.setdefault('medias', []).append(os.path.basename(media.file.path))
for link in self.links.all():
ret.setdefault('links', []).append(link.url)
ret['question'] = mark_safe(ret['question'].replace('\n', '\n<br />'))
logging.error(ret)
return ret
@property
def pdfpath(self):
return str(BASE_DIR) + f'/filestore/pdf/question.{self.id}.pdf'
def default_json(o): @property
if isinstance(o, (MediaFile,)): def csvpath(self):
return str(o) return str(BASE_DIR) + f'/filestore/csv/question.{self.id}.csv'
return json.dumps(model_to_dict(o))
def default_json(o):
if isinstance(o, (MediaFile,)):
return str(o)
return json.dumps(model_to_dict(o))
def to_csv(self, fh=None):
if fh:
cf = fh
else:
cf = open(self.csvpath, 'w')
items = self.to_view()
keys = items.keys()
dw = csv.DictWriter(cf, fieldnames=list(keys))
dw.writeheader()
dw.writerow(items)
if not fh:
cf.close()
return cf if fh else self.csvpath
class QuestionVersion(DateAware, AuthorAware): class QuestionVersion(DateAware, AuthorAware):

View File

@@ -1,5 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<h3>Questions</h3>
<form action="/questions" method="get" id="search_q_form"> <form action="/questions" method="get" id="search_q_form">
<div id="question-search-field" class="row mb-5 mt-5"> <div id="question-search-field" class="row mb-5 mt-5">
<div class="col-12 p-0"> <div class="col-12 p-0">
@@ -23,6 +24,7 @@
<div class="col-3">Question</div> <div class="col-3">Question</div>
<div class="col-1">Files</div> <div class="col-1">Files</div>
<div class="col-1">Labels</div> <div class="col-1">Labels</div>
<div class="col-1"><i class="fa fa-download"></i></div>
</div> </div>
{% for q in items %} {% for q in items %}
<div class="row p-2"> <div class="row p-2">
@@ -36,6 +38,9 @@
{% if not forloop.last %},{% endif %} {% if not forloop.last %},{% endif %}
{% endfor %} {% endfor %}
</div> </div>
<div class="col-1">
<a href="/questions/{{ q.id }}/csv" target="_blank">CSV</a>
</div>
</div> </div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}

View File

@@ -3,5 +3,6 @@ from content.views import public
urlpatterns = [ urlpatterns = [
path('questions/', public.search_question, name='search-questions'), path('questions/', public.search_question, name='search-questions'),
path('questions/<int:id>/csv/', public.as_csv, name='csv-question'),
path('', public.search_question, name='search-questions'), path('', public.search_question, name='search-questions'),
] ]

View File

@@ -1,3 +1,4 @@
from django.http import HttpResponseNotFound, HttpResponse, HttpResponseBadRequest
from django.shortcuts import render from django.shortcuts import render
from content.models import Question from content.models import Question
@@ -12,3 +13,14 @@ def search_question(request):
else: else:
items = published.all()[:10] items = published.all()[:10]
return render(request, 'questions.html', {'items': items}) return render(request, 'questions.html', {'items': items})
def as_csv(request, **kwargs):
try:
q = Question.objects.get(id=kwargs['id'])
response = HttpResponse(content_type='text/csv')
return q.to_csv(fh=response)
except KeyError:
return HttpResponseBadRequest('No id given')
except Question.DoesNotExist:
return HttpResponseNotFound('Question not found')

View File

@@ -62,13 +62,18 @@ class Quiz(AuthorAware, DateAware, PublishedAware):
return self.pdfpath return self.pdfpath
def to_csv(self, fh=None): def to_csv(self, fh=None):
with open(self.csvpath, 'w') as cf: if fh:
items = self.to_view()['items'] cf = fh
keys = items[0].keys() else:
dw = csv.DictWriter(cf, fieldnames=list(keys)) cf = open(self.csvpath, 'w')
dw.writeheader() items = self.to_view()['items']
dw.writerows(items) keys = items[0].keys()
return self.csvpath dw = csv.DictWriter(cf, fieldnames=list(keys))
dw.writeheader()
dw.writerows(items)
if not fh:
cf.close()
return cf if fh else self.csvpath
def to_zip(self, fh=None): def to_zip(self, fh=None):
with zipfile.ZipFile(self.zippath, 'w') as zf: with zipfile.ZipFile(self.zippath, 'w') as zf:

View File

@@ -11,31 +11,40 @@
-moz-border-radius: 4px; -moz-border-radius: 4px;
border-radius: 4px; border-radius: 4px;
} }
.input-group-unstyled .input-group-addon { .input-group-unstyled .input-group-addon {
border-radius: 4px; border-radius: 4px;
border: 0px; border: 0px;
background-color: transparent; background-color: transparent;
} }
</style> </style>
<link href="https://ka-f.fontawesome.com/releases/v6.5.2/css/free.min.css?token=5f65fb5684" rel="stylesheet" /> <link href="https://ka-f.fontawesome.com/releases/v6.5.2/css/free.min.css?token=5f65fb5684" rel="stylesheet"/>
<link href="https://ka-f.fontawesome.com/releases/v6.5.2/css/free-v4-shims.min.css?token=5f65fb5684" rel="stylesheet" /> <link href="https://ka-f.fontawesome.com/releases/v6.5.2/css/free-v4-shims.min.css?token=5f65fb5684" rel="stylesheet"/>
<link href="https://ka-f.fontawesome.com/releases/v6.5.2/css/free-v5-font-face.min.css?token=5f65fb5684" rel="stylesheet" /> <link href="https://ka-f.fontawesome.com/releases/v6.5.2/css/free-v5-font-face.min.css?token=5f65fb5684" rel="stylesheet"/>
<link href="https://ka-f.fontawesome.com/releases/v6.5.2/css/free-v4-font-face.min.css?token=5f65fb5684" rel="stylesheet" /> <link href="https://ka-f.fontawesome.com/releases/v6.5.2/css/free-v4-font-face.min.css?token=5f65fb5684" rel="stylesheet"/>
{# <script src="https://kit.fontawesome.com/5f65fb5684.js" crossorigin="anonymous"></script> #} {# <script src="https://kit.fontawesome.com/5f65fb5684.js" crossorigin="anonymous"></script> #}
</head> </head>
<body> <body>
<div class="container mt-2"> <div class="container mt-2">
<header> <header>
<h1>Tablequiz DB</h1> <div class="row">
<nav> <div class="col-5">
<a class="nav-item" href="/questions/">Questions</a> <h1>Tablequiz DB</h1>
<a class="nav-item" href="/quizes/">Quizes</a> </div>
</nav> <div class="col-7" style="vertical-align: bottom">
</header> <nav style="vertical-align: bottom; width: auto; justify-content: flex-end" class="navbar right">
{% block content %} <a class="nav-item p-3 btn btn-outline-dark mx-2" href="/questions/">Questions</a>
{% endblock %} <a class="nav-item p-3 btn btn-outline-dark mx-2" href="/quizes/">Quizes</a>
</nav>
</div> </div>
</div>
</header>
<hr class="my-3 mx-0 p-0" />
{% block content %}
{% endblock %}
</div>
</body> </body>
</html> </html>