Commit e28f7a3b authored by ibuler's avatar ibuler

Add export and import

parent be99eb82
...@@ -141,4 +141,4 @@ class UserGroupPrivateAssetPermissionForm(forms.ModelForm): ...@@ -141,4 +141,4 @@ class UserGroupPrivateAssetPermissionForm(forms.ModelForm):
class FileForm(forms.Form): class FileForm(forms.Form):
excel = forms.FileField() users = forms.FileField()
{% extends '_modal.html' %} {% extends '_modal.html' %}
{% load i18n %} {% load i18n %}
{% block modal_id %}user_import_modal{% endblock %} {% block modal_id %}user_import_modal{% endblock %}
{% block modal_title%}{% trans "Import User" %}{% endblock %} {% block modal_title%}{% trans "Import user" %}{% endblock %}
{% block modal_body %} {% block modal_body %}
<p class="text-success">{% trans " * CSV format should be same as export" %}</p> <p class="text-success">{% trans " * CSV format should be same as export" %}</p>
<form method="post" class="form-horizontal" action="{% url 'users:user-import' %}" id="fm_user_import" enctype="multipart/form-data"> <form method="post" action="{% url 'users:user-import' %}" id="fm_user_import" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2 col-lg-2 " for="id_excel">{% trans "CSV" %}</label> <label class="control-label" for="id_users">{% trans "Users csv file" %}</label>
<div class=" col-sm-9 col-lg-9 "> <input id="id_users" type="file" name="users" />
<input id="id_excel" type="file" name="excel" />
</div>
</div> </div>
</form> </form>
{% endblock %} {% endblock %}
......
...@@ -3,17 +3,18 @@ ...@@ -3,17 +3,18 @@
{% block table_search %} {% block table_search %}
<div class="html5buttons"> <div class="html5buttons">
<div class="dt-buttons btn-group"> <div class="dt-buttons btn-group">
{# <a class="btn btn-default buttons-pdf" tabindex="0" href="#">#} <a class="btn btn-default btn_import" data-toggle="modal" data-target="#user_import_modal" tabindex="0">
{# <span>PDF</span></a>#} <span>{% trans "Import" %}</span>
<a class="btn btn-default buttons-csv" tabindex="0" href="#"> </a>
<span>CSV</span> <a class="btn btn-default btn_export" tabindex="0">
<span>{% trans "Export" %}</span>
</a> </a>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block table_container %} {% block table_container %}
<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create user" %} </a></div> <div class="uc pull-left m-l-5 m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create user" %} </a></div>
<div class="uc pull-left"><a href="javascript:void(0);" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div> {#<div class="uc pull-left"><a href="javascript:void(0);" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div>#}
<table class="table table-striped table-bordered table-hover " id="user_list_table" > <table class="table table-striped table-bordered table-hover " id="user_list_table" >
<thead> <thead>
<tr> <tr>
...@@ -88,7 +89,7 @@ $(document).ready(function(){ ...@@ -88,7 +89,7 @@ $(document).ready(function(){
}; };
var table = jumpserver.initDataTable(options); var table = jumpserver.initDataTable(options);
$('.buttons-csv').click(function () { $('.btn_export').click(function () {
var users = []; var users = [];
var rows = table.rows('.selected').data(); var rows = table.rows('.selected').data();
$.each(rows, function (index, obj) { $.each(rows, function (index, obj) {
...@@ -108,6 +109,26 @@ $(document).ready(function(){ ...@@ -108,6 +109,26 @@ $(document).ready(function(){
}) })
}); });
$('#btn_user_import').click(function() {
var $form = $('#fm_user_import');
$form.find('.help-block').remove();
function success (data) {
if (data.valid === false) {
var $help = $form.find('.help-block');
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_users'));
} else {
{# $('#user_import_modal').modal('hide');#}
{# var $data_table = $('#user_list_table').DataTable();#}
{# toastr.success("{% trans 'Import User Success.' %}");#}
$('<span />', {class: 'help-block text-danger'}).html(data.errors.join(',')).insertAfter($('#id_users'));
$('<span />', {class: 'help-block text-warning'}).html(data.updated.join(',')).insertAfter($('#id_users'));
$('<span />', {class: 'help-block text-primary'}).html(data.created.join(',')).insertAfter($('#id_users'));
{# $data_table.ajax.reload();#}
}
}
$form.ajaxSubmit({success: success});
})
}).on('click', '#btn_bulk_update', function(){ }).on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val(); var action = $('#slct_bulk_update').val();
var $data_table = $('#user_list_table').DataTable(); var $data_table = $('#user_list_table').DataTable();
...@@ -219,7 +240,7 @@ $(document).ready(function(){ ...@@ -219,7 +240,7 @@ $(document).ready(function(){
new_groups = body.groups.map(Number); new_groups = body.groups.map(Number);
body.groups = new_groups; body.groups = new_groups;
} }
var $data_table = $('#user_list_table').DataTable() var $data_table = $('#user_list_table').DataTable();
var post_list = []; var post_list = [];
$data_table.rows({selected: true}).every(function(){ $data_table.rows({selected: true}).every(function(){
var content = Object.assign({id: this.data().id}, body); var content = Object.assign({id: this.data().id}, body);
...@@ -237,22 +258,7 @@ $(document).ready(function(){ ...@@ -237,22 +258,7 @@ $(document).ready(function(){
}; };
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success}); APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});
$('#user_bulk_update_modal').modal('hide'); $('#user_bulk_update_modal').modal('hide');
}).on('click', '#btn_user_import', function() { });
var $form = $('#fm_user_import');
$form.find('.help-block').remove();
function success (data) {
if (data.success === false) {
var $help = $form.find('.help-block');
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_excel'));
} else {
$('#user_import_modal').modal('hide');
var $data_table = $('#user_list_table').DataTable();
toastr.success("{% trans 'Import User Success.' %}");
$data_table.ajax.reload();
}
}
$form.ajaxSubmit({success: success});
})
</script> </script>
{% endblock %} {% endblock %}
...@@ -24,7 +24,7 @@ urlpatterns = [ ...@@ -24,7 +24,7 @@ urlpatterns = [
url(r'^user/(?P<pk>[0-9]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'), url(r'^user/(?P<pk>[0-9]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
url(r'^user/(?P<pk>[0-9]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'), url(r'^user/(?P<pk>[0-9]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'),
url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'), url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'),
url(r'^import/$', views.BulkImportUserView.as_view(), name='user-import'), url(r'^user/import/$', views.BulkImportUserView.as_view(), name='user-import'),
# url(r'^user/(?P<pk>[0-9]+)/assets-perm$', views.UserDetailView.as_view(), name='user-detail'), # url(r'^user/(?P<pk>[0-9]+)/assets-perm$', views.UserDetailView.as_view(), name='user-detail'),
url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'), url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'),
url(r'^user/(?P<pk>[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'), url(r'^user/(?P<pk>[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'),
......
...@@ -5,11 +5,11 @@ import json ...@@ -5,11 +5,11 @@ import json
import uuid import uuid
from io import BytesIO from io import BytesIO
from reportlab.pdfgen import canvas
import unicodecsv as csv import unicodecsv as csv
from django import forms from django import forms
from django.utils import timezone from django.utils import timezone
from django.core.cache import cache from django.core.cache import cache
from django.db import IntegrityError
from django.contrib.auth import login as auth_login, logout as auth_logout from django.contrib.auth import login as auth_login, logout as auth_logout
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
...@@ -496,45 +496,41 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView): ...@@ -496,45 +496,41 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
return self.render_json_response(data) return self.render_json_response(data)
def form_valid(self, form): def form_valid(self, form):
from openpyxl import load_workbook users_csv = form.cleaned_data['users']
try: users_csv_f = csv.reader(users_csv, encoding='utf-8')
wb = load_workbook(form.cleaned_data['excel']) header_need = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
ws = wb['users'] header = next(users_csv_f)
except Exception as e: print(header)
print e if header != header_need:
error = _('Not a valid Excel file.') data = {'valid': False, 'msg': 'Must be same format as export csv: name, ...'}
data = {
'success': False,
'msg': error
}
return self.render_json_response(data) return self.render_json_response(data)
created = []
updated = []
errors = [] errors = []
for index, row in enumerate(ws.rows): for row in users_csv_f:
user_data = [cell.value for cell in row] user_dict = dict(zip(header, row))
if len(user_data) != 4: groups_name = user_dict.pop('groups').split(',')
errors.append("Row {}: invalid user data format.".format(index)) groups = UserGroup.objects.filter(name__in=groups_name)
continue try:
username, email, enable_otp, role = user_data user = User.objects.create(**user_dict)
data = { user.groups.add(*tuple(groups))
'username': username, user.save()
'email': email, created.append(user_dict['username'])
'enable_otp': True if enable_otp in ['T', '1', 1, True] else False, except IntegrityError:
'role': role user = User.objects.filter(username=user_dict['username'])
} user.update(**user_dict)
form = forms.UserBulkImportForm(data, auto_id=False) user[0].groups.add(*tuple(groups))
if form.is_valid(): updated.append(user_dict['username'])
form.save() except TypeError:
else: errors.append(user_dict['username'])
form_errors = form.errors.as_data()
for key, err_list in form_errors.iteritems():
error_line = "{} :".format(key)
for errs in err_list:
error_line = "{}{}".format(error_line, ";".join([err for err in errs.messages]))
errors.append("Row {}: {}".format(index, error_line))
data = { data = {
'success': True if not errors else False, 'created': created,
'msg': 'ok' if not errors else '<br />'.join(errors) 'updated': updated,
'errors': errors,
'valid': True,
'msg': 'Created: {}. Updated: {}, Error: {}'.format(len(created), len(updated), len(errors))
} }
return self.render_json_response(data) return self.render_json_response(data)
...@@ -543,22 +539,21 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView): ...@@ -543,22 +539,21 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
class ExportUserCsvView(View): class ExportUserCsvView(View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
spm = request.GET.get('spm', '') spm = request.GET.get('spm', '')
print(spm)
users_id = cache.get(spm) users_id = cache.get(spm)
if not users_id and not isinstance(users_id, list): if not users_id and not isinstance(users_id, list):
return HttpResponse('May be expired', status=404) return HttpResponse('May be expired', status=404)
users = User.objects.filter(id__in=users_id) users = User.objects.filter(id__in=users_id)
filename = 'users-%s.csv' % timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H:%M:%D') filename = 'users-%s.csv' % timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S')
response = HttpResponse(content_type='application/csv') response = HttpResponse(content_type='application/csv')
response['Content-Disposition'] = 'attachment; filename="%s"' % filename response['Content-Disposition'] = 'attachment; filename="%s"' % filename
writer = csv.writer(response, delimiter=str(","), lineterminator='\n', writer = csv.writer(response, delimiter=str(","), lineterminator='\n',
quoting=csv.QUOTE_ALL, dialect='excel') quoting=csv.QUOTE_ALL, dialect='excel')
header = [_("name"), _('username'), _('email'), _('user group'), header = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
_('role'), _('phone'), _('wechat'), _('comment')]
writer.writerow(header) writer.writerow(header)
for user in users: for user in users:
writer.writerow([user.name, user.username, user.email, ','.join([group.name for group in user.groups.all()]), writer.writerow([user.name, user.username, user.email,
','.join([group.name for group in user.groups.all()]),
user.role, user.phone, user.wechat, user.comment]) user.role, user.phone, user.wechat, user.comment])
return response return response
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment