mirror of https://github.com/jumpserver/jumpserver
Update user import and export
parent
e28f7a3bec
commit
eae580e51f
|
@ -44,6 +44,7 @@ def join_attr(seq, attr=None, sep=None):
|
||||||
print(seq)
|
print(seq)
|
||||||
return sep.join(seq)
|
return sep.join(seq)
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def IntToStr(value):
|
def int_to_str(value):
|
||||||
return str(value)
|
return str(value)
|
|
@ -98,6 +98,7 @@ TEMPLATES = [
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
'django.template.context_processors.static',
|
'django.template.context_processors.static',
|
||||||
'django.template.context_processors.request',
|
'django.template.context_processors.request',
|
||||||
|
'django.template.context_processors.media',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Binary file not shown.
|
@ -141,4 +141,4 @@ class UserGroupPrivateAssetPermissionForm(forms.ModelForm):
|
||||||
|
|
||||||
|
|
||||||
class FileForm(forms.Form):
|
class FileForm(forms.Form):
|
||||||
users = forms.FileField()
|
file = forms.FileField()
|
||||||
|
|
|
@ -3,13 +3,25 @@
|
||||||
{% 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 "Download template or use export excel format" %}</p>
|
||||||
<form method="post" 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" for="id_users">{% trans "Users csv file" %}</label>
|
<label class="control-label" for="id_users">{% trans "Template" %}</label>
|
||||||
<input id="id_users" type="file" name="users" />
|
<a href="{{ MEDIA_URL }}files/user_import_template.xlsx" style="display: block">{% trans 'Download' %}</a>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label" for="id_users">{% trans "Users excel file" %}</label>
|
||||||
|
<input id="id_users" type="file" name="file" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<p>
|
||||||
|
<p class="text-success" id="id_created"></p>
|
||||||
|
<p id="id_created_detail"></p>
|
||||||
|
<p class="text-warning" id="id_updated"></p>
|
||||||
|
<p id="id_updated_detail"></p>
|
||||||
|
<p class="text-danger" id="id_failed"></p>
|
||||||
|
<p id="id_failed_detail"></p>
|
||||||
|
</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block modal_confirm_id %}btn_user_import{% endblock %}
|
{% block modal_confirm_id %}btn_user_import{% endblock %}
|
||||||
|
|
|
@ -114,16 +114,16 @@ $(document).ready(function(){
|
||||||
$form.find('.help-block').remove();
|
$form.find('.help-block').remove();
|
||||||
function success (data) {
|
function success (data) {
|
||||||
if (data.valid === false) {
|
if (data.valid === false) {
|
||||||
var $help = $form.find('.help-block');
|
|
||||||
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_users'));
|
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_users'));
|
||||||
} else {
|
} else {
|
||||||
{# $('#user_import_modal').modal('hide');#}
|
$('#id_created').html(data.created_info);
|
||||||
{# var $data_table = $('#user_list_table').DataTable();#}
|
$('#id_created_detail').html(data.created.join(','));
|
||||||
{# toastr.success("{% trans 'Import User Success.' %}");#}
|
$('#id_updated').html(data.updated_info);
|
||||||
$('<span />', {class: 'help-block text-danger'}).html(data.errors.join(',')).insertAfter($('#id_users'));
|
$('#id_updated_detail').html(data.updated.join(','));
|
||||||
$('<span />', {class: 'help-block text-warning'}).html(data.updated.join(',')).insertAfter($('#id_users'));
|
$('#id_failed').html(data.failed_info);
|
||||||
$('<span />', {class: 'help-block text-primary'}).html(data.created.join(',')).insertAfter($('#id_users'));
|
$('#id_failed_detail').html(data.failed.join(','));
|
||||||
{# $data_table.ajax.reload();#}
|
var $data_table = $('#user_list_table').DataTable();
|
||||||
|
$data_table.ajax.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$form.ajaxSubmit({success: success});
|
$form.ajaxSubmit({success: success});
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
from io import BytesIO
|
import codecs
|
||||||
|
|
||||||
|
from openpyxl import Workbook
|
||||||
|
from openpyxl.writer.excel import save_virtual_workbook
|
||||||
|
from openpyxl import load_workbook
|
||||||
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
|
||||||
|
@ -37,8 +40,6 @@ from .utils import AdminUserRequiredMixin, user_add_success_next, send_reset_pas
|
||||||
from .hands import write_login_log_async
|
from .hands import write_login_log_async
|
||||||
from . import forms
|
from . import forms
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,7 +97,11 @@ class UserListView(AdminUserRequiredMixin, TemplateView):
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(UserListView, self).get_context_data(**kwargs)
|
context = super(UserListView, self).get_context_data(**kwargs)
|
||||||
context.update({'app': _('Users'), 'action': _('User list'), 'groups': UserGroup.objects.all()})
|
context.update({
|
||||||
|
'app': _('Users'),
|
||||||
|
'action': _('User list'),
|
||||||
|
'groups': UserGroup.objects.all()
|
||||||
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -496,41 +501,61 @@ 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):
|
||||||
users_csv = form.cleaned_data['users']
|
try:
|
||||||
users_csv_f = csv.reader(users_csv, encoding='utf-8')
|
wb = load_workbook(form.cleaned_data['file'])
|
||||||
|
ws = wb.get_active_sheet()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
data = {'valid': False, 'msg': 'Not a valid Excel file'}
|
||||||
|
return self.render_json_response(data)
|
||||||
|
|
||||||
|
rows = ws.rows
|
||||||
header_need = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
|
header_need = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
|
||||||
header = next(users_csv_f)
|
header = [col.value for col in next(rows)]
|
||||||
print(header)
|
print(header)
|
||||||
if header != header_need:
|
if header != header_need:
|
||||||
data = {'valid': False, 'msg': 'Must be same format as export csv: name, ...'}
|
data = {'valid': False, 'msg': 'Must be same format as template or export file'}
|
||||||
return self.render_json_response(data)
|
return self.render_json_response(data)
|
||||||
|
|
||||||
created = []
|
created = []
|
||||||
updated = []
|
updated = []
|
||||||
errors = []
|
failed = []
|
||||||
for row in users_csv_f:
|
for row in rows:
|
||||||
user_dict = dict(zip(header, row))
|
user_dict = dict(zip(header, [col.value for col in row]))
|
||||||
groups_name = user_dict.pop('groups').split(',')
|
groups_name = user_dict.pop('groups')
|
||||||
groups = UserGroup.objects.filter(name__in=groups_name)
|
if groups_name:
|
||||||
|
groups_name = groups_name.split(',')
|
||||||
|
groups = UserGroup.objects.filter(name__in=groups_name)
|
||||||
|
else:
|
||||||
|
groups = None
|
||||||
try:
|
try:
|
||||||
user = User.objects.create(**user_dict)
|
user = User.objects.create(**user_dict)
|
||||||
user.groups.add(*tuple(groups))
|
|
||||||
user.save()
|
|
||||||
created.append(user_dict['username'])
|
created.append(user_dict['username'])
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
user = User.objects.filter(username=user_dict['username'])
|
user = User.objects.filter(username=user_dict['username'])
|
||||||
|
if not user:
|
||||||
|
failed.append(user_dict['username'])
|
||||||
|
continue
|
||||||
user.update(**user_dict)
|
user.update(**user_dict)
|
||||||
user[0].groups.add(*tuple(groups))
|
user = user[0]
|
||||||
updated.append(user_dict['username'])
|
updated.append(user_dict['username'])
|
||||||
except TypeError:
|
except TypeError:
|
||||||
errors.append(user_dict['username'])
|
failed.append(user_dict['username'])
|
||||||
|
user = None
|
||||||
|
|
||||||
|
if user and groups:
|
||||||
|
user.groups.add(*tuple(groups))
|
||||||
|
user.save()
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'created': created,
|
'created': created,
|
||||||
|
'created_info': 'Created {}'.format(len(created)),
|
||||||
'updated': updated,
|
'updated': updated,
|
||||||
'errors': errors,
|
'updated_info': 'Updated {}'.format(len(updated)),
|
||||||
|
'failed': failed,
|
||||||
|
'failed_info': 'Failed {}'.format(len(failed)),
|
||||||
'valid': True,
|
'valid': True,
|
||||||
'msg': 'Created: {}. Updated: {}, Error: {}'.format(len(created), len(updated), len(errors))
|
'msg': 'Created: {}. Updated: {}, Error: {}'.format(len(created), len(updated), len(failed))
|
||||||
}
|
}
|
||||||
return self.render_json_response(data)
|
return self.render_json_response(data)
|
||||||
|
|
||||||
|
@ -544,22 +569,24 @@ class ExportUserCsvView(View):
|
||||||
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-%S')
|
wb = Workbook()
|
||||||
response = HttpResponse(content_type='application/csv')
|
ws = wb.active
|
||||||
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
|
ws.title = 'User'
|
||||||
writer = csv.writer(response, delimiter=str(","), lineterminator='\n',
|
|
||||||
quoting=csv.QUOTE_ALL, dialect='excel')
|
|
||||||
header = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
|
header = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
|
||||||
writer.writerow(header)
|
ws.append(header)
|
||||||
|
|
||||||
for user in users:
|
for user in users:
|
||||||
writer.writerow([user.name, user.username, user.email,
|
ws.append([user.name, user.username, user.email,
|
||||||
','.join([group.name for group in user.groups.all()]),
|
','.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])
|
||||||
|
|
||||||
|
filename = 'users-{}.xlsx'.format(timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
|
||||||
|
response = HttpResponse(save_virtual_workbook(wb), content_type='application/vnd.ms-excel')
|
||||||
|
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
print(request.body)
|
|
||||||
users_id = json.loads(request.body).get('users_id', [])
|
users_id = json.loads(request.body).get('users_id', [])
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return HttpResponse('Json object not valid', status=400)
|
return HttpResponse('Json object not valid', status=400)
|
||||||
|
|
Loading…
Reference in New Issue