Commit 5418d0b4 authored by ibuler's avatar ibuler

[Change] Using csv replace xlsx

parent bcc065eb
......@@ -86,7 +86,6 @@ class Asset(models.Model):
def __unicode__(self):
return '%s <%s: %s>' % (self.hostname, self.ip, self.port)
__str__ = __unicode__
@property
......
......@@ -23,6 +23,7 @@ class AssetGroup(models.Model):
def __unicode__(self):
return self.name
__str__ = __unicode__
class Meta:
ordering = ['name']
......
......@@ -36,6 +36,7 @@ class IDC(models.Model):
def __unicode__(self):
return self.name
__str__ = __unicode__
@classmethod
def initial(cls):
......
......@@ -50,7 +50,6 @@ class AdminUser(models.Model):
def __unicode__(self):
return self.name
__str__ = __unicode__
@property
......
......@@ -7,11 +7,14 @@
{% csrf_token %}
<div class="form-group">
<label class="control-label" for="id_assets">{% trans "Template" %}</label>
<a href="{{ MEDIA_URL }}files/asset_import_template.xlsx" style="display: block">{% trans 'Download' %}</a>
<a href="{% url 'assets:asset-export' %}" style="display: block">{% trans 'Download' %}</a>
</div>
<div class="form-group">
<label class="control-label" for="id_users">{% trans "Asset excel file" %}</label>
<input id="id_assets" type="file" name="file" />
<span class="help-block">
{% trans 'If set id, will use this id update asset existed' %}
</span>
</div>
</form>
<p>
......
# coding:utf-8
from __future__ import absolute_import, unicode_literals
import csv
import json
import uuid
import codecs
from io import StringIO
from openpyxl import Workbook
from openpyxl.writer.excel import save_virtual_workbook
from openpyxl import load_workbook
from django.conf import settings
from django.db import IntegrityError
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, ListView, View
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from django.urls import reverse_lazy
......@@ -206,45 +206,42 @@ class AssetModalListView(AdminUserRequiredMixin, ListView):
@method_decorator(csrf_exempt, name='dispatch')
class AssetExportView(View):
@staticmethod
def get_asset_attr(asset, attr):
if attr in ['admin_user', 'idc']:
return getattr(asset, attr)
elif attr in ['status', 'type', 'env']:
return getattr(asset, 'get_{}_display'.format(attr))()
else:
return getattr(asset, attr)
def get(self, request, *args, **kwargs):
spm = request.GET.get('spm', '')
assets_id = cache.get(spm)
if not assets_id and not isinstance(assets_id, list):
return HttpResponse('May be expired', status=404)
assets_id = cache.get(spm, [Asset.objects.first().id])
print(assets_id)
fields = [
field for field in Asset._meta.fields
if field.name not in [
'date_created'
]
]
filename = 'assets-{}.csv'.format(
timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
response.write(codecs.BOM_UTF8)
assets = Asset.objects.filter(id__in=assets_id)
wb = Workbook()
ws = wb.active
ws.title = 'Asset'
header = ['hostname', 'ip', 'port', 'admin_user', 'idc', 'memory', 'disk',
'mac_address', 'other_ip', 'remote_card_ip', 'os', 'cabinet_no',
'cabinet_pos', 'number', 'status', 'type', 'env', 'sn', 'comment']
ws.append(header)
writer = csv.writer(response, dialect='excel',
quoting=csv.QUOTE_MINIMAL)
for asset in assets:
ws.append([self.get_asset_attr(asset, attr) for attr in header])
header = [field.verbose_name for field in fields]
header.append(_('Asset groups'))
writer.writerow(header)
filename = 'assets-{}.xlsx'.format(timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
response = HttpResponse(save_virtual_workbook(wb), content_type='applications/vnd.ms-excel')
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
for asset in assets:
groups = ','.join([group.name for group in asset.groups.all()])
data = [getattr(asset, field.name) for field in fields]
data.append(groups)
writer.writerow(data)
return response
def post(self, request, *args, **kwargs):
try:
assets_id = json.loads(request.body).get('assets_id', [])
print(assets_id)
except ValueError:
return HttpResponse('Json object not valid', status=400)
spm = uuid.uuid4().get_hex()
spm = uuid.uuid4().hex
cache.set(spm, assets_id, 300)
url = reverse_lazy('assets:asset-export') + '?spm=%s' % spm
return JsonResponse({'redirect': url})
......@@ -254,67 +251,74 @@ class BulkImportAssetView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
form_class = forms.FileForm
def form_valid(self, form):
try:
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_all = ['hostname', 'ip', 'port', 'admin_user', 'idc', 'cpu', 'memory', 'disk',
'mac_address', 'other_ip', 'remote_card_ip', 'os', 'cabinet_no',
'cabinet_pos', 'number', 'status', 'type', 'env', 'sn', 'comment']
header_min = ['hostname', 'ip', 'port', 'admin_user', 'comment']
header = [col.value for col in next(rows)]
if not set(header).issubset(set(header_all)) and not set(header).issuperset(set(header_min)):
data = {'valid': False, 'msg': 'Must be same format as template or export file'}
file = form.cleaned_data['file']
data = file.read().decode('utf-8').strip(
codecs.BOM_UTF8.decode('utf-8'))
csv_file = StringIO(data)
reader = csv.reader(csv_file)
csv_data = [row for row in reader]
fields = [
field for field in Asset._meta.fields
if field.name not in [
'date_created'
]
]
header_ = csv_data[0]
mapping_reverse = {field.verbose_name: field.name for field in fields}
mapping_reverse[_('Asset groups')] = 'groups'
attr = [mapping_reverse.get(n, None) for n in header_]
if None in attr:
data = {'valid': False,
'msg': 'Must be same format as '
'template or export file'}
return self.render_json_response(data)
created = []
updated = []
failed = []
for row in rows:
asset_dict = dict(zip(header, [col.value for col in row]))
if asset_dict.get('admin_user', None):
admin_user = get_object_or_none(AdminUser, name=asset_dict['admin_user'])
asset_dict['admin_user'] = admin_user
if asset_dict.get('idc'):
idc = get_object_or_none(IDC, name=asset_dict['idc'])
asset_dict['idc'] = idc
if asset_dict.get('type'):
asset_display_type_map = dict(zip(dict(Asset.TYPE_CHOICES).values(), dict(Asset.TYPE_CHOICES).keys()))
asset_type = asset_display_type_map.get(asset_dict['type'], 'Server')
asset_dict['type'] = asset_type
if asset_dict.get('status'):
asset_display_status_map = dict(zip(dict(Asset.STATUS_CHOICES).values(),
dict(Asset.STATUS_CHOICES).keys()))
asset_status = asset_display_status_map.get(asset_dict['status'], 'In use')
asset_dict['status'] = asset_status
if asset_dict.get('env'):
asset_display_env_map = dict(zip(dict(Asset.ENV_CHOICES).values(),
dict(Asset.ENV_CHOICES).keys()))
asset_env = asset_display_env_map.get(asset_dict['env'], 'Prod')
asset_dict['env'] = asset_env
created, updated, failed = [], [], []
for row in csv_data[1:]:
if set(row) == {''}:
continue
asset_dict = dict(zip(attr, row))
id_ = asset_dict.pop('id', 0)
asset = get_object_or_none(Asset, id=id_)
for k, v in asset_dict.items():
if k == 'idc':
v = get_object_or_none(IDC, name=v)
elif k == 'is_active':
v = bool(v)
elif k == 'admin_user':
v = get_object_or_none(AdminUser, name=v)
elif k in ['port', 'cabinet_pos', 'cpu_count', 'cpu_cores']:
try:
Asset.objects.create(**asset_dict)
created.append(asset_dict['ip'])
except IntegrityError as e:
asset = Asset.objects.filter(ip=asset_dict['ip'], port=asset_dict['port'])
v = int(v)
except ValueError:
v = 0
elif k == 'groups':
groups_name = v.split(',')
v = AssetGroup.objects.filter(name__in=groups_name)
else:
continue
asset_dict[k] = v
if not asset:
failed.append(asset_dict['ip'])
try:
groups = asset_dict.pop('groups')
asset = Asset.objects.create(**asset_dict)
asset.groups.set(groups)
created.append(asset_dict['hostname'])
except IndexError as e:
failed.append('%s: %s' % (asset_dict['hostname'], str(e)))
else:
for k, v in asset_dict.items():
if k == 'groups':
asset.groups.set(v)
continue
asset.update(**asset_dict)
updated.append(asset_dict['ip'])
except TypeError as e:
print(e)
failed.append(asset_dict['ip'])
if v:
setattr(asset, k, v)
try:
asset.save()
updated.append(asset_dict['hostname'])
except Exception as e:
failed.append('%s: %s' % (asset_dict['hostname'], str(e)))
data = {
'created': created,
......@@ -324,7 +328,8 @@ class BulkImportAssetView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
'failed': failed,
'failed_info': 'Failed {}'.format(len(failed)),
'valid': True,
'msg': 'Created: {}. Updated: {}, Error: {}'.format(len(created), len(updated), len(failed))
'msg': 'Created: {}. Updated: {}, Error: {}'.format(
len(created), len(updated), len(failed))
}
return self.render_json_response(data)
......
......@@ -260,7 +260,7 @@ MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace('\\', '/') + '/'
# Use django-bootstrap-form to format template, input max width arg
BOOTSTRAP_COLUMN_COUNT = 11
# BOOTSTRAP_COLUMN_COUNT = 11
# Init data or generate fake data source for development
FIXTURE_DIRS = [os.path.join(BASE_DIR, 'fixtures'), ]
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-04-07 17:03+0800\n"
"POT-Creation-Date: 2017-04-08 21:58+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
......@@ -78,7 +78,7 @@ msgstr "登录地址"
#: assets/templates/assets/system_user_list.html:21 perms/models.py:40
#: perms/templates/perms/asset_permission_detail.html:98
#: users/models/group.py:19 users/models/user.py:43
#: users/templates/users/user_detail.html:109
#: users/templates/users/user_detail.html:113
#: users/templates/users/user_group_detail.html:70
#: users/templates/users/user_group_list.html:14
#: users/templates/users/user_profile.html:118
......@@ -134,7 +134,7 @@ msgstr "在线session"
#: assets/templates/assets/asset_list.html:74 perms/models.py:33
#: perms/templates/perms/asset_permission_create_update.html:47
#: users/templates/users/_select_user_modal.html:18
#: users/templates/users/user_detail.html:126
#: users/templates/users/user_detail.html:130
#: users/templates/users/user_list.html:29
#: users/templates/users/user_profile.html:63
msgid "Active"
......@@ -234,8 +234,8 @@ msgstr "其它"
#: assets/templates/assets/system_user_detail.html:137
#: perms/templates/perms/asset_permission_create_update.html:67
#: users/templates/users/_user.html:49
#: users/templates/users/user_detail.html:158
#: users/templates/users/user_detail.html:166
#: users/templates/users/user_detail.html:162
#: users/templates/users/user_detail.html:170
#: users/templates/users/user_password_update.html:58
#: users/templates/users/user_profile.html:139
#: users/templates/users/user_profile.html:147
......@@ -259,7 +259,7 @@ msgstr "重置"
#: audits/templates/audits/proxy_log_online_list.html:124
#: perms/templates/perms/asset_permission_create_update.html:68
#: users/templates/users/_user.html:50
#: users/templates/users/first_login.html:60
#: users/templates/users/first_login.html:62
#: users/templates/users/forgot_password.html:44
#: users/templates/users/user_asset_permission.html:100
#: users/templates/users/user_group_asset_permission.html:100
......@@ -569,7 +569,7 @@ msgstr "主机名原始"
#: assets/templates/assets/idc_detail.html:93
#: assets/templates/assets/system_user_detail.html:94 perms/models.py:37
#: perms/templates/perms/asset_permission_detail.html:94
#: users/models/user.py:47 users/templates/users/user_detail.html:97
#: users/models/user.py:47 users/templates/users/user_detail.html:101
msgid "Created by"
msgstr "创建者"
......@@ -733,22 +733,17 @@ msgstr "选择系统用户"
msgid "Import asset"
msgstr "导入资产"
#: assets/templates/assets/_asset_import_modal.html:6
#: users/templates/users/_user_import_modal.html:6
msgid "Download template or use export excel format"
msgstr "下载模板"
#: assets/templates/assets/_asset_import_modal.html:10
#: assets/templates/assets/_asset_import_modal.html:9
#: users/templates/users/_user_import_modal.html:10
msgid "Template"
msgstr "模板"
#: assets/templates/assets/_asset_import_modal.html:11
#: assets/templates/assets/_asset_import_modal.html:10
#: users/templates/users/_user_import_modal.html:11
msgid "Download"
msgstr "下载"
#: assets/templates/assets/_asset_import_modal.html:14
#: assets/templates/assets/_asset_import_modal.html:13
msgid "Asset excel file"
msgstr "资产excel"
......@@ -888,13 +883,13 @@ msgid "Platform"
msgstr ""
#: assets/templates/assets/asset_detail.html:152
#: users/templates/users/user_detail.html:101
#: users/templates/users/user_detail.html:105
#: users/templates/users/user_profile.html:88
msgid "Date joined"
msgstr "创建日期"
#: assets/templates/assets/asset_detail.html:168
#: users/templates/users/user_detail.html:120
#: users/templates/users/user_detail.html:124
#: users/templates/users/user_profile.html:130
msgid "Quick modify"
msgstr "快速修改"
......@@ -929,9 +924,9 @@ msgstr "添加到资产组"
#: assets/templates/assets/system_user_asset.html:96
#: assets/templates/assets/system_user_list.html:102
#: assets/templates/assets/user_asset_list.html:167 templates/_modal.html:16
#: users/templates/users/user_detail.html:337
#: users/templates/users/user_detail.html:362
#: users/templates/users/user_detail.html:385
#: users/templates/users/user_detail.html:341
#: users/templates/users/user_detail.html:366
#: users/templates/users/user_detail.html:389
#: users/templates/users/user_group_create_update.html:45
#: users/templates/users/user_group_list.html:92
#: users/templates/users/user_list.html:171
......@@ -940,8 +935,8 @@ msgid "Confirm"
msgstr "确认"
#: assets/templates/assets/asset_detail.html:374
#: users/templates/users/user_detail.html:271
#: users/templates/users/user_detail.html:284
#: users/templates/users/user_detail.html:275
#: users/templates/users/user_detail.html:288
msgid "Update successfully!"
msgstr "更新成功"
......@@ -985,8 +980,8 @@ msgstr "批量更新"
#: assets/templates/assets/idc_list.html:94
#: assets/templates/assets/system_user_list.html:97
#: assets/templates/assets/user_asset_list.html:162
#: users/templates/users/user_detail.html:332
#: users/templates/users/user_detail.html:357
#: users/templates/users/user_detail.html:336
#: users/templates/users/user_detail.html:361
#: users/templates/users/user_group_list.html:87
#: users/templates/users/user_list.html:166
msgid "Are you sure?"
......@@ -1149,10 +1144,8 @@ msgstr "IDC删除失败"
#: assets/templates/assets/system_user_asset.html:20
#: assets/templates/assets/system_user_detail.html:21
#, fuzzy
#| msgid "Attach Asset"
msgid "Attached assets"
msgstr "关联资产"
msgstr "关联资产"
#: assets/templates/assets/system_user_asset.html:28
msgid "Assets of "
......@@ -1179,7 +1172,6 @@ msgid "Attach AssetGroup"
msgstr "添加到资产组"
#: assets/templates/assets/system_user_detail.html:79
#: templates/_header_bar.html:41
msgid "Home"
msgstr ""
......@@ -1494,7 +1486,7 @@ msgid "Select user groups"
msgstr "添加到用户组"
#: perms/models.py:35 perms/templates/perms/asset_permission_detail.html:86
#: users/models/user.py:46 users/templates/users/user_detail.html:93
#: users/models/user.py:46 users/templates/users/user_detail.html:97
#: users/templates/users/user_profile.html:96
msgid "Date expired"
msgstr "失效日期"
......@@ -1527,7 +1519,7 @@ msgstr "添加资产组"
#: perms/templates/perms/asset_permission_asset.html:146
#: perms/templates/perms/asset_permission_detail.html:170
#: users/templates/users/user_detail.html:194
#: users/templates/users/user_detail.html:198
msgid "Join"
msgstr "加入"
......@@ -1570,7 +1562,7 @@ msgstr "创建授权规则"
#: perms/templates/perms/asset_permission_list.html:13 templates/_nav.html:13
#: users/models/user.py:34 users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:177
#: users/templates/users/user_detail.html:181
#: users/templates/users/user_list.html:28
msgid "User group"
msgstr "用户组"
......@@ -1659,18 +1651,18 @@ msgstr "注销登录"
msgid "Login"
msgstr "登录"
#: templates/_header_bar.html:41 templates/_nav.html:4
msgid "Dashboard"
msgstr "仪表盘"
#: templates/_modal.html:15
msgid "Close"
msgstr "关闭"
#: templates/_nav.html:4
msgid "Dashboard"
msgstr "仪表盘"
#: templates/_nav.html:9 users/templates/users/user_group_create_update.html:28
#: users/views/group.py:31 users/views/group.py:45 users/views/group.py:80
#: users/views/group.py:105 users/views/login.py:185 users/views/user.py:54
#: users/views/user.py:70 users/views/user.py:106 users/views/user.py:118
#: users/views/group.py:105 users/views/login.py:185 users/views/user.py:58
#: users/views/user.py:74 users/views/user.py:110 users/views/user.py:122
msgid "Users"
msgstr "用户管理"
......@@ -1734,22 +1726,20 @@ msgid ""
" "
msgstr ""
"\n"
" 你的信息不完整,请点击 <a href="
"\"%(first_login_url)s\"> 链接 </a>to 补充完整\n"
" 你的信息不完整,请点击 <a href=\"%(first_login_url)s\"> 链接 "
"</a> 补充完整\n"
" "
#: templates/base.html:37
#, python-format
msgid ""
"\n"
" Your ssh-public-key has been expired. Please click <a href="
"\"%(profile_url)s\"> this link </a>to update your ssh-public-key.\n"
"\"%(user_pubkey_update)s\"> this link </a>to update your ssh-public-key.\n"
" "
msgstr ""
"\n"
" 你的SSH key已经过期,点击 <a href="
"\"%(profile_url)s\"> 链接 </a>更新 \n"
" 你的SSH key已经过期,点击 <a href=\"%(user_pubkey_update)s\"> 链接 "
"</a>更新 \n"
" "
#: templates/captcha/image.html:3
......@@ -1815,7 +1805,7 @@ msgstr ""
msgid "Invalid token or cache refreshed."
msgstr ""
#: users/forms.py:36 users/templates/users/user_detail.html:185
#: users/forms.py:36 users/templates/users/user_detail.html:189
msgid "Join user groups"
msgstr "添加到用户组"
......@@ -1876,7 +1866,8 @@ msgid "Wechat"
msgstr "微信"
#: users/models/user.py:39 users/templates/users/_user.html:36
#: users/templates/users/user_detail.html:140
#: users/templates/users/user_detail.html:93
#: users/templates/users/user_detail.html:144
msgid "Enable OTP"
msgstr "二次验证"
......@@ -1917,6 +1908,10 @@ msgstr "组"
msgid "Import user"
msgstr "导入"
#: users/templates/users/_user_import_modal.html:6
msgid "Download template or use export excel format"
msgstr "下载模板"
#: users/templates/users/_user_import_modal.html:14
msgid "Users excel file"
msgstr "用户excel"
......@@ -1934,11 +1929,11 @@ msgstr "首次登陆"
msgid "Step"
msgstr "Step"
#: users/templates/users/first_login.html:57
#: users/templates/users/first_login.html:59
msgid "First step"
msgstr "第一步"
#: users/templates/users/first_login.html:58
#: users/templates/users/first_login.html:60
msgid "Prev step"
msgstr "上一步"
......@@ -1968,8 +1963,8 @@ msgid "Captcha invalid"
msgstr "验证码错误"
#: users/templates/users/reset_password.html:45
#: users/templates/users/user_detail.html:155
#: users/templates/users/user_detail.html:323
#: users/templates/users/user_detail.html:159
#: users/templates/users/user_detail.html:327
#: users/templates/users/user_profile.html:136 users/utils.py:71
msgid "Reset password"
msgstr "重置密码"
......@@ -1987,7 +1982,7 @@ msgstr "设置"
#: users/templates/users/user_granted_asset.html:18
#: users/templates/users/user_group_asset_permission.html:18
#: users/templates/users/user_group_granted_asset.html:18
#: users/views/user.py:119
#: users/views/user.py:123
msgid "User detail"
msgstr "用户详情"
......@@ -2015,7 +2010,7 @@ msgid "Revoke Successfully!"
msgstr "回收成功"
#: users/templates/users/user_create.html:4
#: users/templates/users/user_list.html:16 users/views/user.py:70
#: users/templates/users/user_list.html:16 users/views/user.py:74
msgid "Create user"
msgstr "创建用户"
......@@ -2023,47 +2018,47 @@ msgstr "创建用户"
msgid "Reset link will be generated and sent to the user. "
msgstr "生成重置密码连接,通过邮件发送给用户"
#: users/templates/users/user_detail.html:105
#: users/templates/users/user_detail.html:109
#: users/templates/users/user_profile.html:92
msgid "Last login"
msgstr "最后登录"
#: users/templates/users/user_detail.html:163
#: users/templates/users/user_detail.html:167
msgid "Reset ssh key"
msgstr "重置密钥"
#: users/templates/users/user_detail.html:322
#: users/templates/users/user_detail.html:326
msgid "An e-mail has been sent to the user\\'s mailbox."
msgstr "已发送邮件到用户邮箱"
#: users/templates/users/user_detail.html:333
#: users/templates/users/user_detail.html:337
msgid ""
"This will reset the user's password. A password-reset email will be sent to "
"the user\\'s mailbox."
msgstr ""
#: users/templates/users/user_detail.html:347
#: users/templates/users/user_detail.html:351
msgid ""
"The reset-ssh-public-key E-mail has been sent successfully. Please inform "
"the user to update his new ssh public key."
msgstr ""
#: users/templates/users/user_detail.html:348
#: users/templates/users/user_detail.html:352
#: users/templates/users/user_profile.html:144
msgid "Reset SSH public key"
msgstr "重置SSH密钥"
#: users/templates/users/user_detail.html:358
#: users/templates/users/user_detail.html:362
msgid "This will reset the user\\"
msgstr "重置"
#: users/templates/users/user_detail.html:375
#: users/templates/users/user_detail.html:379
#: users/templates/users/user_profile.html:170
msgid "Successfully updated the SSH public key."
msgstr ""
#: users/templates/users/user_detail.html:376
#: users/templates/users/user_detail.html:380
#: users/templates/users/user_detail.html:384
#: users/templates/users/user_profile.html:171
#: users/templates/users/user_profile.html:176
msgid "User SSH Public Key Update"
......@@ -2071,23 +2066,17 @@ msgstr "ssh密钥"
#: users/templates/users/user_granted_asset.html:32
#: users/templates/users/user_group_granted_asset.html:32
#, fuzzy
#| msgid "Asset group list"
msgid "Assets granted of "
msgstr "资产组列表"
msgstr "授权资产"
#: users/templates/users/user_granted_asset.html:68
#: users/templates/users/user_group_granted_asset.html:68
#, fuzzy
#| msgid "Asset group list"
msgid "Asset groups granted of "
msgstr "资产组列表"
msgstr "授权资产组"
#: users/templates/users/user_group_asset_permission.html:71
#, fuzzy
#| msgid "Create perm"
msgid "Quick create permission for user group"
msgstr "创建权限"
msgstr "快速授权"
#: users/templates/users/user_group_create_update.html:30
msgid "Select User"
......@@ -2194,7 +2183,7 @@ msgstr "指纹"
msgid "Update public key"
msgstr "更新密钥"
#: users/templates/users/user_update.html:3 users/views/user.py:106
#: users/templates/users/user_update.html:3 users/views/user.py:110
msgid "Update user"
msgstr "编辑用户"
......@@ -2336,12 +2325,6 @@ msgstr "用户组列表"
msgid "Update user group"
msgstr "编辑用户组"
#: users/views/group.py:106
#, fuzzy
#| msgid "User group detail"
msgid "User Group Detail"
msgstr "资产组详情"
#: users/views/login.py:76
msgid "Logout success"
msgstr "退出登录成功"
......@@ -2383,16 +2366,15 @@ msgstr "密码不一致"
msgid "First login"
msgstr "首次登陆"
#: users/views/user.py:55
#: users/views/user.py:59
msgid "User list"
msgstr "用户列表"
#: users/views/user.py:66 users/views/user.py:334
#: users/views/user.py:70 users/views/user.py:344
#, python-brace-format
msgid "Create user <a href=\"{url}\">{name}</a> successfully."
msgstr "创建用户 <a href=\"{url}\">{name}</a> 成功"
#: users/views/user.py:175
#: users/views/user.py:188
msgid "Invalid file."
msgstr "文件错误"
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% load bootstrap3 %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
......
......@@ -33,9 +33,9 @@
{% block update_public_key_message %}
{% if user.is_authenticated and not user.is_public_key_valid %}
<div class="alert alert-danger" style="margin: 20px auto 0px">
{% url 'users:user-profile' as profile_url %}
{% url 'users:user-pubkey-update' as user_pubkey_update %}
{% blocktrans %}
Your ssh-public-key has been expired. Please click <a href="{{ profile_url }}"> this link </a>to update your ssh-public-key.
Your ssh-public-key has been expired. Please click <a href="{{ user_pubkey_update }}"> this link </a>to update your ssh-public-key.
{% endblocktrans %}
</div>
{% endif %}
......
......@@ -44,10 +44,12 @@
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form|bootstrap }}
{% bootstrap_form form %}
{# {{ form|bootstrap }}#}
{% endfor %}
{% else %}
{{ wizard.form|bootstrap }}
{# {{ wizard.form|bootstrap }}#}
{% bootstrap_form wizard.form %}
{% endif %}
</form>
</div>
......
......@@ -89,6 +89,10 @@
<td>{% trans 'Role' %}:</td>
<td><b>{{ user_object.get_role_display }}</b></td>
</tr>
<tr>
<td>{% trans 'Enable OTP' %}:</td>
<td><b>{{ user_object.enable_otp|yesno:"Yes,No,Unknown"}}</b></td>
</tr>
<tr>
<td>{% trans 'Date expired' %}:</td>
<td><b>{{ user_object.date_expired|date:"Y-m-j H:i:s" }}</b></td>
......
......@@ -61,6 +61,7 @@
<label>{{ user.public_key_obj.hash_md5 }}</label>
</div>
</div>
<div class="hr-line-dashed"></div>
<h3>{% trans 'Update public key' %}</h3>
{% bootstrap_field form.public_key layout="horizontal" %}
<div class="hr-line-dashed"></div>
......
......@@ -103,7 +103,7 @@ class UserGroupDetailView(AdminUserRequiredMixin, DetailView):
users = User.objects.exclude(id__in=self.object.users.all())
context = {
'app': _('Users'),
'action': _('User Group Detail'),
'action': _('User group detail'),
'users': users,
}
kwargs.update(context)
......
......@@ -4,10 +4,9 @@ from __future__ import unicode_literals
import json
import uuid
from openpyxl import load_workbook
from openpyxl import Workbook
from openpyxl.writer.excel import save_virtual_workbook
import csv
import codecs
from io import StringIO
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
......@@ -31,7 +30,7 @@ from .. import forms
from ..models import User, UserGroup
from ..utils import AdminUserRequiredMixin, user_add_success_next
from common.mixins import JSONResponseMixin
from common.utils import get_logger
from common.utils import get_logger, get_object_or_none
from perms.models import AssetPermission
__all__ = ['UserListView', 'UserCreateView', 'UserDetailView',
......@@ -123,34 +122,44 @@ class UserDetailView(AdminUserRequiredMixin, DetailView):
return super(UserDetailView, self).get_context_data(**kwargs)
USER_ATTR_MAPPING = (
('name', 'Name'),
('username', 'Username'),
('email', 'Email'),
('groups', 'User groups'),
('role', 'Role'),
('phone', 'Phone'),
('wechat', 'Wechat'),
('comment', 'Comment'),
)
@method_decorator(csrf_exempt, name='dispatch')
class UserExportView(View):
def get(self, request):
mapping = [
(k, _(v)) for k, v in USER_ATTR_MAPPING
]
spm = request.GET.get('spm', '')
users_id = cache.get(spm)
if not users_id and not isinstance(users_id, list):
return HttpResponse('May be expired', status=404)
users_id = cache.get(spm, ['1'])
filename = 'users-{}.csv'.format(
timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
response.write(codecs.BOM_UTF8)
users = User.objects.filter(id__in=users_id)
print(users)
wb = Workbook()
ws = wb.active
ws.title = 'User'
header = ["name", 'username', 'email', 'groups',
"role", "phone", "wechat", "comment"]
ws.append(header)
writer = csv.writer(response, dialect='excel', quoting=csv.QUOTE_MINIMAL)
header = [v for k, v in mapping]
writer.writerow(header)
for user in users:
ws.append([user.name, user.username, user.email,
','.join([group.name for group in user.groups.all()]),
user.role, user.phone, user.wechat, user.comment])
groups = ','.join([group.name for group in user.groups.all()])
writer.writerow([
user.name, user.username, user.email, groups,
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='applications/vnd.ms-excel')
response[
'Content-Disposition'] = 'attachment; filename="%s"' % filename
return response
def post(self, request):
......@@ -158,7 +167,7 @@ class UserExportView(View):
users_id = json.loads(request.body).get('users_id', [])
except ValueError:
return HttpResponse('Json object not valid', status=400)
spm = uuid.uuid4().get_hex()
spm = uuid.uuid4().hex
cache.set(spm, users_id, 300)
url = reverse('users:user-export') + '?spm=%s' % spm
return JsonResponse({'redirect': url})
......@@ -179,54 +188,51 @@ class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
return self.render_json_response(data)
def form_valid(self, form):
try:
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 = [col.value for col in next(rows)]
print(header)
if header != header_need:
data = {'valid': False, 'msg': 'Must be same format as '
file = form.cleaned_data['file']
data = file.read().decode('utf-8').strip(codecs.BOM_UTF8.decode('utf-8'))
csv_file = StringIO(data)
reader = csv.reader(csv_file)
csv_data = [row for row in reader]
header_ = csv_data[0]
mapping_reverse = {_(v): k for k, v in USER_ATTR_MAPPING}
user_attr = [mapping_reverse.get(n, None) for n in header_]
if None in user_attr:
data = {'valid': False,
'msg': 'Must be same format as '
'template or export file'}
return self.render_json_response(data)
created = []
updated = []
failed = []
for row in rows:
user_dict = dict(zip(header, [col.value for col in row]))
created, updated, failed = [], [], []
for row in csv_data[1:]:
if set(row) == {''}:
continue
user_dict = dict(zip(user_attr, row))
groups_name = user_dict.pop('groups')
if groups_name:
groups_name = groups_name.split(',')
groups = UserGroup.objects.filter(name__in=groups_name)
else:
groups = None
username = user_dict['username']
user = get_object_or_none(User, username=username)
if not user:
try:
user = User.objects.create(**user_dict)
user_add_success_next(user)
created.append(user_dict['username'])
except User.IntegrityError as e:
user = User.objects.filter(username=user_dict['username'])
if not user:
failed.append(user_dict['username'])
continue
user.update(**user_dict)
user = user[0]
user_add_success_next(user)
except Exception as e:
failed.append('%s: %s' % (user_dict['username'], str(e)))
else:
for k, v in user_dict.items():
if v:
setattr(user, k, v)
try:
user.save()
updated.append(user_dict['username'])
except TypeError as e:
print(e)
failed.append(user_dict['username'])
user = None
except Exception as e:
failed.append('%s: %s' % (user_dict['username'], str(e)))
if user and groups:
user.groups.add(*tuple(groups))
user.groups = groups
user.save()
data = {
......
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