From cdcf6abcb526f517846a2835cfd0712f11c9fe14 Mon Sep 17 00:00:00 2001 From: Nazar Kotjuk Date: Mon, 16 Dec 2013 09:13:35 +0200 Subject: [PATCH] Import events view --- functions/translate.py | 36 +++- import_xls/__init__.py | 1 + import_xls/custom_forms.py | 136 ++++++++++++++ import_xls/excel_settings.py | 98 ++++++++++ import_xls/views.py | 175 ++++++++++++++++++ proj/admin_urls.py | 2 + proj/settings.py | 1 + .../admin/import templates/export_event.html | 92 +++++++++ .../admin/import templates/import_event.html | 42 +++++ templates/base.html | 6 +- webinar/models.py | 2 +- 11 files changed, 588 insertions(+), 3 deletions(-) create mode 100644 import_xls/__init__.py create mode 100644 import_xls/custom_forms.py create mode 100644 import_xls/excel_settings.py create mode 100644 import_xls/views.py create mode 100644 templates/admin/import templates/export_event.html create mode 100644 templates/admin/import templates/import_event.html diff --git a/functions/translate.py b/functions/translate.py index a409192c..392cf9b4 100644 --- a/functions/translate.py +++ b/functions/translate.py @@ -88,4 +88,38 @@ def populate_all(Model, data={}, id=None, zero_fields={}): else: obj = Model._meta.translations_model.objects.get(language_code = code,master__id=id) populate(obj,data,code,zero_fields) - obj.save() \ No newline at end of file + obj.save() + + +def get_all_fields(obj, exclude_fields=[]): + """ + Return all simple fields with translated hvad fields + + can exclude some fields + """ + + # simple fields from django object + fields = [i for i in obj._meta.get_all_field_names() if i not in exclude_fields] + # get hvad translation objects + translation = obj.translations.all() + # fields which present in database but don't need + bad_fields = ['id', 'master', 'language_code'] + # get translated fields without bad fields and exclude fields + translated_fields = [i.name for i in translation[0]._meta.fields if i.name not in bad_fields and i.name not in exclude_fields] + +# fields += translated_fields +# rields2 = [fields for i in field_settings] + + return fields + +def get_all_verbose_names(obj, fields): + names = {} + for field in fields: + try: + names[field] = obj._meta.get_field_by_name(field)[0].verbose_name + except: + translations = obj.translations.all() + tr = translations[0] + names[field] = tr._meta.get_field_by_name(field)[0].verbose_name + + return names diff --git a/import_xls/__init__.py b/import_xls/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/import_xls/__init__.py @@ -0,0 +1 @@ + diff --git a/import_xls/custom_forms.py b/import_xls/custom_forms.py new file mode 100644 index 00000000..93276370 --- /dev/null +++ b/import_xls/custom_forms.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +from django import forms +from django.conf import settings + +languages = [code for code in settings.LANGUAGES] + + + +class ImportEventForm(forms.Form): + excel_file = forms.FileField(label='Выберите файл') + event = forms.ChoiceField(label='Выберите тип события', choices=[('exposition', 'Выставка'), + ('conference', 'Конференция'), + ('seminar', 'Семинар'), + ('webinar', 'Вебинар')]) + language = forms.ChoiceField(label='Выберите язык', choices=languages) + + +from theme.models import Theme +from country.models import Country +from django.db.models.loading import get_model +import StringIO +from functions.translate import get_all_fields, get_all_verbose_names + + +class ExportEventForm(forms.Form): + event = forms.ChoiceField(label='Выберите тип события', choices=[('exposition.Exposition', 'Выставка'), + ('conference.Conference', 'Конференция'), + ('seminar.Seminar', 'Семинар'), + ('webinar.Webinar', 'Вебинар')]) + language = forms.ChoiceField(label='Выберите язык', choices=languages) + date_from = forms.DateField(label='С') + date_to = forms.DateField(label='До') + theme = forms.ModelMultipleChoiceField(label='Направление', queryset=Theme.objects.all(), required=False) + country = forms.ModelMultipleChoiceField(label='Страны', queryset=Country.objects.all(), required=False) + + def generate_dicts(self): + data = self.cleaned_data + event = data['event'] + language = data['language'] + date_from = data['date_from'] + date_to = data['date_to'] + theme = data['theme'] + country = data['country'] + # get model of event + model = get_model(event.split('.')[0], event.split('.')[1]) + # + objects = model.objects.language(language).filter(data_begin__range=[date_from, date_to], + country__in=country, + theme__in=theme + ).distinct() + + if not objects: + return None + # get fields + # in list exclude fields + fields = get_all_fields(objects[0],['organiser', 'tag', 'descriptions', 'keywords', 'title', 'theme', + 'users', 'company', 'translations', 'business_program', 'created', + 'modified']) + # readable names + names = get_all_verbose_names(objects[0], fields) + + objects_list = [] + + for object in objects: + obj_dict = {} + # generate dict with field values + for field in fields: + obj_dict = {'name': field, 'value': getattr(object, field), + 'verbose_name': names[field]} + + #obj_dict[field] = {'value': getattr(object, field), 'verbose_name': names[field]} + + objects_list.append(obj_dict) + + return objects_list + + """ + def generate_dicts(self): + data = self.cleaned_data + event = data['event'] + language = data['language'] + date_from = data['date_from'] + date_to = data['date_to'] + theme = data['theme'] + country = data['country'] + # get model of event + model = get_model(event.split('.')[0], event.split('.')[1]) + # + objects = model.objects.language(language).filter(data_begin__range=[date_from, date_to], + country__in=country, + theme__in=theme + ).distinct() + + if not objects: + return None + # get fields + # in list exclude fields + fields = get_all_fields(objects[0],['organiser', 'tag', 'descriptions', 'keywords', 'title', 'theme', + 'users', 'company', 'translations', 'business_program', 'created', + 'modified']) + # readable names + names = get_all_verbose_names(objects[0], fields) + + objects_list = [] + + for object in objects: + obj_dict = {} + # generate dict with field values + for field in fields: + obj_dict[field] = {'value': getattr(object, field), 'verbose_name': names[field]} + + objects_list.append(obj_dict) + + return objects_list + """ + + def clean_theme(self): + """ + Set all themes if no theme selected + """ + theme = self.cleaned_data.get('theme') + if not theme: + theme = Theme.objects.all() + + return theme + + + def clean_country(self): + """ + Set all countries if no country selected + """ + country = self.cleaned_data.get('country') + if not country: + country = Country.objects.all() + + return country \ No newline at end of file diff --git a/import_xls/excel_settings.py b/import_xls/excel_settings.py new file mode 100644 index 00000000..716b711b --- /dev/null +++ b/import_xls/excel_settings.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +import xlwt +import datetime + +def get_bool(value): + if value: + return 1 + return 0 + +def get_int(value): + if not value: + return '' + return value + +def get_audience(value): + if not value: + return 'Не выбрано' + elif value == 'experts': + return 'Специалисты' + elif value == 'experts and consumers': + return 'Специалисты и потребители' + elif value == 'general public': + return 'Широкая публика' + +field_settings = [ + {'name': 'id', 'verbose_name': 'id', 'type': get_int, 'width':1500}, + {'name': 'url', 'verbose_name': 'url', 'type': str}, + {'name': 'name', 'verbose_name': 'Имя', 'type': str, 'width':8000,}, + {'name': 'data_begin', 'verbose_name': 'Дата начала', 'type': str}, + {'name': 'data_end', 'verbose_name': 'Дата окончания', 'type': str}, + {'name': 'main_title', 'verbose_name': 'Краткое описание', 'type': str}, + {'name': 'description', 'verbose_name': 'Описание', 'type': str}, + {'name': 'audience', 'verbose_name': 'Аудитория', type: get_audience}, + {'name': 'country', 'verbose_name': 'Страна', 'type': str}, + {'name': 'city', 'verbose_name': 'Город', 'type': str}, + {'name': 'periodic', 'verbose_name': 'Периодичность', 'type': str}, + {'name': 'web_page', 'verbose_name': 'Веб страница', 'type': str}, + {'name': 'time', 'verbose_name': 'Время проведения', 'type': str}, + {'name': 'products', 'verbose_name': 'Экспонируемые продукты', 'type': str}, + {'name': 'foundation_year', 'verbose_name': 'Год основания', 'type': get_int}, + {'name': 'tax', 'verbose_name': 'Налог включен', 'type': get_bool, 'width':1000}, + {'name': 'currency', 'verbose_name': 'Валюта', 'type': str}, + {'name': 'max_price', 'verbose_name': 'Максимальная цена', 'type': get_int}, + {'name': 'min_price', 'verbose_name': 'Минимальная цена', 'type': get_int}, + {'name': 'registration_payment', 'verbose_name': 'Регистрационный взнос', 'type': get_int}, + {'name': 'min_closed_area', 'verbose_name': 'Минимальная цена закрытой НЕ оборудованной площади', 'type': get_int}, + {'name': 'max_closed_area', 'verbose_name': 'Максимальная цена закрытой НЕ оборудованной площади', 'type': get_int}, + {'name': 'min_closed_equipped_area', 'verbose_name': 'Минимальная цена закрытой оборудованной площади ', 'type': get_int}, + {'name': 'max_closed_equipped_area', 'verbose_name': 'Максимальная цена закрытой оборудованной площади', 'type': get_int}, + {'name': 'min_open_area', 'verbose_name': 'Минимальная цена закрытой площади', 'type': get_int}, + {'name': 'max_open_area', 'verbose_name': 'Максимальная цена открытой площади', 'type': get_int}, + {'name': 'min_area', 'verbose_name': 'Минимальная площадь', 'type': get_int}, + {'name': 'max_area', 'verbose_name': 'Максимальная площадь', 'type': get_int}, + {'name': 'is_published', 'verbose_name': 'Опубликована', 'type': get_bool, 'width':1000}, + {'name': 'canceled_by_administrator', 'verbose_name': 'Отменена администратором', 'type': get_bool, 'width':1000} + ] + + +def get_cell(field_name, value): + return {'tax': get_bool(value), 'foundation_year': get_int(value), 'max_closed_area': get_int(value), + 'min_closed_equipped_area': get_int(value), 'registration_payment': get_int(value), + 'max_closed_equipped_area': get_int(value), 'max_open_area': get_int(value), 'min_closed_area': get_int(value), + 'min_area': get_int(value), 'canceled_by_administrator': get_bool(value), 'is_published': get_bool(value), + 'min_open_area': get_int(value), 'audience': get_audience(value) + }.get(field_name, str(value)) + + +#--------------------------------------- + +#--------------------------------------- + +def get_cell_style(field_name): + font = xlwt.Font() + # Create the Font + font.name = 'Times New Roman' + default_style = xlwt.XFStyle() + # Create the Style + default_style.font = font + return { + + }.get(field_name, default_style) + +#------------------------------------------ + +#------------------------------------------ +sorted_names = [u'id', 'name', 'data_begin', 'data_end', 'main_title', 'description', 'country', 'city'] +cols_width = {'id': 1500, 'name':8000} + + +#field_setting.get('width', 20) + +def sort_by_names(val): + try: + # put element on the index of sorted names + return sorted_names.index(val) + except ValueError: + # put element on the end + return len(sorted_names) \ No newline at end of file diff --git a/import_xls/views.py b/import_xls/views.py new file mode 100644 index 00000000..d6cedc70 --- /dev/null +++ b/import_xls/views.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- +from django.core.context_processors import csrf +from django.shortcuts import render_to_response +from django.http import HttpResponseRedirect, HttpResponse +from django.contrib.auth.decorators import login_required +# +import xlwt +import xlrd +import tempfile +# +from custom_forms import ImportEventForm, ExportEventForm + +from excel_settings import sort_by_names, get_cell_style, get_cell +from excel_settings import cols_width + + + +@login_required +def import_event(request): + if request.POST: + form = ImportEventForm(request.POST, request.FILES) + temp_file = tempfile.TemporaryFile() + import_file = request.FILES.get(request.FILES.keys()[0]) + book = xlrd.open_workbook(file_contents=import_file) + sheet = book.sheet_by_index(0) + row_list = [sheet.row_values(row_number) for row_number in range(sheet.nrows)] + + return HttpResponse(row_list[0]) + + else: + form = ImportEventForm() + + args = {} + args.update(csrf(request)) + args['form'] = form + return render_to_response('import_event.html', args) + + + +def xls_to_response(xls, fname): + response = HttpResponse(mimetype="application/ms-excel") + response['Content-Disposition'] = 'attachment; filename=%s' % fname + xls.save(response) + return response + + +import datetime + + +from excel_settings import field_settings + +from django.db.models.loading import get_model + +def export_event(request): + if request.POST: + form = ExportEventForm(request.POST) + if form.is_valid(): + + data = form.cleaned_data + # get model + model = get_model(data['event'].split('.')[0], data['event'].split('.')[1]) + # get objects + objects = model.objects.language(data['language']).filter(data_begin__range=[data['date_from'], data['date_to']], + country__in=data['country'], + theme__in=data['theme'] + ).distinct() + + if not objects: + # not found any objects + message = 'По даному запросу объектов не найдено' + args = {} + args.update(csrf(request)) + args['message'] = message + args['form'] = form + return render_to_response('export_event.html', args) + + workbook = xlwt.Workbook(encoding = 'utf8') + # new tab + worksheet = workbook.add_sheet('My Worksheet') + + # Create the Font for first rows + font = xlwt.Font() + font.name = 'Times New Roman' + font.bold = True + style = xlwt.XFStyle() + # Create the Style + style.font = font + + for row, object in enumerate(objects): + for col, field in enumerate(field_settings): + try: + value = getattr(object, field['name']) + except AttributeError: + continue + if row == 0: + # first iteration. set names. set columns width + worksheet.write(0, col, field.get('verbose_name', 'default'), style) + worksheet.write(1, col, field.get('name'), style) + worksheet.col(col).width = field.get('width', 3333) + + worksheet.write(row+2, col, field.get('type', str)(value)) + + return xls_to_response(workbook, 'My Worksheet.xls') + else: + form = ExportEventForm() + + + args = {} + args.update(csrf(request)) + + args['form'] = form + + return render_to_response('export_event.html', args) +""" +def export_event(request): + if request.POST: + form = ExportEventForm(request.POST) + + if form.is_valid(): + # dict of objects + objects = form.generate_dicts() + return HttpResponse(str(objects)) + + if not objects: + # empty dict + message = 'По даному запросу объектов не найдено' + args = {} + args.update(csrf(request)) + args['message'] = message + args['form'] = form + return render_to_response('export_event.html', args) + + workbook = xlwt.Workbook(encoding = 'utf8') + # new tab + worksheet = workbook.add_sheet('My Worksheet') + + # Create the Font + font = xlwt.Font() + font.name = 'Times New Roman' + font.bold = True + style = xlwt.XFStyle() + # Create the Style + style.font = font + + # get names of fields sorted like in sorted_names list + fields = sorted(objects[0].keys(), key=sort_by_names) + + # generate first row with verbose names + for i, field in enumerate(fields): + worksheet.write(0, i, objects[0][field]['verbose_name'], style) + # generate second row and set columns width + for i, field in enumerate(fields): + worksheet.write(1, i,field, style) + if field in cols_width: + worksheet.col(i).width = cols_width.get(field, 3333) + # fill row with objects + for row, object in enumerate(objects): + for key, value in object.iteritems(): + # current row + col = fields.index(key) + # begins from third row + # get cell formats value (True -> 1, None -> '', ...) + worksheet.write(row+2, col, get_cell(key, value['value']), get_cell_style(key)) + + return xls_to_response(workbook, 'My Worksheet.xls') + else: + form = ExportEventForm() + + args = {} + args.update(csrf(request)) + + args['form'] = form + + return render_to_response('export_event.html', args) +""" \ No newline at end of file diff --git a/proj/admin_urls.py b/proj/admin_urls.py index 1c01bd06..df841a44 100644 --- a/proj/admin_urls.py +++ b/proj/admin_urls.py @@ -4,6 +4,8 @@ from django.conf.urls import patterns, include, url urlpatterns = patterns('', url(r'^$', 'proj.views.home', name='home'), + url(r'^import-event/$', 'import_xls.views.import_event'), + url(r'^export-event/$', 'import_xls.views.export_event'), url(r'^accounts/', include('registration.backends.default.urls')), url(r'^accounts/', include('accounts.urls')), url(r'^article/', include('article.urls')), diff --git a/proj/settings.py b/proj/settings.py index b47bd667..87feb05f 100644 --- a/proj/settings.py +++ b/proj/settings.py @@ -157,6 +157,7 @@ TEMPLATE_DIRS = ( '/home/kotzilla/Documents/qwer/proj/templates/admin/conference', '/home/kotzilla/Documents/qwer/proj/templates/admin/directories', '/home/kotzilla/Documents/qwer/proj/templates/admin/exposition', + '/home/kotzilla/Documents/qwer/proj/templates/admin/import templates', '/home/kotzilla/Documents/qwer/proj/templates/admin/news', '/home/kotzilla/Documents/qwer/proj/templates/admin/organiser', '/home/kotzilla/Documents/qwer/proj/templates/admin/place_conference', diff --git a/templates/admin/import templates/export_event.html b/templates/admin/import templates/export_event.html new file mode 100644 index 00000000..0e121a10 --- /dev/null +++ b/templates/admin/import templates/export_event.html @@ -0,0 +1,92 @@ +{% extends 'base.html' %} +{% load static %} + +{% block scripts %} + {# selects #} + + + + {# datetimepicker #} + + + +{% endblock %} + +{% block body %} +
{% csrf_token %} + {# event #} +
+ +
+ {{ form.event }} + {{ form.event.errors }} +
+
+ {# language #} +
+ +
+ {{ form.language }} + {{ form.language.errors }} +
+
+ {# date_from #} +
+ +
+ {{ form.date_from }} + {{ form.date_from.errors }} +
+
+ {# date_to #} +
+ +
+ {{ form.date_to }} + {{ form.date_to.errors }} +
+
+ {# theme #} +
+ +
+ {{ form.theme }} + {{ form.theme.errors }} +
+
+ {# country #} +
+ +
+ {{ form.country }} + {{ form.country.errors }} +
+
+ +
+ + +
+ + +
+ {% if message %} +
+ {{ message }} +
+ {% endif %} + +{% endblock %} \ No newline at end of file diff --git a/templates/admin/import templates/import_event.html b/templates/admin/import templates/import_event.html new file mode 100644 index 00000000..58b6fd74 --- /dev/null +++ b/templates/admin/import templates/import_event.html @@ -0,0 +1,42 @@ +{% extends 'base.html' %} +{% load static %} + +{% block scripts %} +{% endblock %} + +{% block body %} +
{% csrf_token %} + + {# excel_file #} +
+ +
+ {{ form.excel_file }} + {{ form.excel_file.errors }} +
+
+ {# event #} +
+ +
+ {{ form.event }} + {{ form.event.errors }} +
+
+ {# language #} +
+ +
+ {{ form.language }} + {{ form.language.errors }} +
+
+ + +
+ + +
+ +
+{% endblock %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 666ee2a1..b330788d 100644 --- a/templates/base.html +++ b/templates/base.html @@ -101,8 +101,11 @@
  • Выставка
  • Конференция
  • Семинар
  • +
  • Вебинар
  • -
  • Вебинар
  • +
  • Импорт
  • +
  • Экспорт
  • + @@ -147,6 +150,7 @@
  • Валюты
  • +
  • Зарегестрировать
    пользователя
  • Выйти
  • diff --git a/webinar/models.py b/webinar/models.py index 71fe1f4b..466a3a06 100644 --- a/webinar/models.py +++ b/webinar/models.py @@ -50,7 +50,7 @@ class Webinar(TranslatableModel): tax = models.BooleanField(verbose_name='Налог', default=1) min_price = models.PositiveIntegerField(verbose_name='Минимальная цена', blank=True, null=True) max_price = models.PositiveIntegerField(verbose_name='Максимальная цена', blank=True, null=True) - ## + # #administrator can cancel webinar canceled_by_administrator = models.BooleanField(default=0) #can publish not immediately