Example #1
0
class CredentialsDataView(ModelView):
    column_list = ['cert', 'key']
    list_template = 'admin/credentials_list.html'
    details_template = 'admin/credentials_details.html'
    column_formatters = {
        'cert': macro('render_cert'),
        'key': macro('render_key'),
    }

    @expose('/cert.pem')
    def cert_view(self):
        creds = self.get_one(request.args.get('id'))
        return Response(creds.cert, mimetype='text/plain')

    @expose('/key.pem')
    def key_view(self):
        creds = self.get_one(request.args.get('id'))
        return Response(creds.key, mimetype='text/plain')

    @expose('/cert.txt')
    def cert_text_view(self):
        creds = self.get_one(request.args.get('id'))
        info = pki.get_certificate_text(creds.cert)
        return Response(info, mimetype='text/plain')

    def is_visible(self):
        return False
Example #2
0
class VideoModelView(BaseModelView):
    form = VideoForm
    edit_template = 'admin/edit/_video_edit.html'

    can_create = False

    list_template = 'admin/list/_video_list.html'

    def _list_thumbnail_cover(view, context, model, name):
        if not os.path.exists(current_app.config['IMG_THUMB_DEST']):
            os.makedirs(current_app.config['IMG_THUMB_DEST'])
            os.chmod(current_app.config['IMG_THUMB_DEST'],
                     stat.S_IRWXU|stat.S_IRGRP|stat.S_IWGRP|stat.S_IROTH)
        # 设置缩略图
        size = 200, 200
        im = Image.open(os.path.join(current_app.config['UPLOADS_DEFAULT_DEST'], 'images/', model.cover))
        im.thumbnail(size)
        extension = os.path.splitext(model.cover)
        thumbnail_name = extension[0] + '_thumbnail' + extension[-1]
        im.save(os.path.join(current_app.config['IMG_THUMB_DEST'], thumbnail_name))
        model.thumbnail_cover = url_for('static', filename='uploads/thumbnails/' + thumbnail_name)
        return Markup('<img src="%s" alt="视频封面"' % model.thumbnail_cover)

    def _list_url(view, context, model, name):
        return Markup('<a href="%s" target="_blank">视频链接</a>' % url_for('home.video', id=model.id))

    def _list_uploader(view, context, model, name):
        return Markup('<a href="%s" target="_blank">%s</a>' % (url_for('home.user', username=model.uploader.username),
                                                               model.uploader.username))

    column_formatters = dict(intro=macro('render_intro'),
                             thumbnail_cover=_list_thumbnail_cover,
                             url=_list_url, video_tag=macro('render_video_tag'),
                             uploader=_list_uploader)

    column_exclude_list = ['cover',]

    column_labels = {
        'title': '视频标题',
        'intro': '视频简介',
        'thumbnail_cover': '封面缩略图',
        'playnum': '播放数',
        'add_time': '上传时间',
        'video_tag': '视频分类',
        'uploader': '上传者'
    }
    column_sortable_list = ('title', 'intro', 'thumbnail_cover',
                            'url', 'playnum', 'add_time',
                            ('video_tag', 'video_tag.name'), ('uploader', 'uploader.username'))
    column_searchable_list = ('title', 'intro', 'video_tag.name', 'uploader.username')
    column_default_sort = ('add_time', True)

    def on_model_change(self, form, model, is_created):
        if not is_created:
            if Video.query.filter_by(title=form.title.data).first() != model:
                raise ValidationError('视频标题已经被使用')
            if form.tag.data != model.video_tag.name:
                tag = VideoTag.query.filter_by(name=form.tag.data).first()
                model.video_tag = tag
Example #3
0
class ImageView(BasicAuthModelView):
    list_template = 'admin/model/object_list.html'
    form_excluded_columns = ['created_at', 'verification_url', 'user']
    page_size = 50
    column_exclude_list = ('user')
    column_filters = ('gender', 'status')
    column_formatters = dict(url=macro('render_url'),
                             verification_url=macro('render_verification_url'))
Example #4
0
class FilesView(MyBaseView):
    column_list = [
        'id', 'name', 'sha1', 'date_b', 'status_f', 'score', 'message',
        'evals_count'
    ]
    column_sortable_list = [
        'id', 'name', 'sha1', 'date_b', 'status_f', 'score', 'message',
        'evals_count'
    ]
    column_exclude_list = [
        'uuid_f', 'evals_len', 'results', 'md5', 'mtype', 'exec_time',
        'expect_sandbox', 'hash'
    ]
    column_details_list = [
        'id', 'name', 'mtype', 'md5', 'sha1', 'hash', 'date_b', 'exec_time',
        'status_f', 'score', 'message', 'results', 'evals_count', 'evals'
    ]
    column_export_list = [
        'id', 'name', 'mtype', 'md5', 'sha1', 'hash', 'date_b', 'exec_time',
        'status_f', 'score', 'message', 'evals_count'
    ]
    column_export_exclude_list = ['evals', 'results']
    column_formatters_export = {}
    column_searchable_list = []
    column_default_sort = ('date_b', True)
    column_filters = ['name', 'mtype', 'sha1', 'score', 'evals_count']
    column_formatters_export = dict(
        score=fmt_render_score,
        date_b=lambda v, c, m, p: str(getattr(m, p)),
        exec_time=lambda v, c, m, p: str(tdelta(seconds=getattr(m, p))))
    column_formatters = dict(evals_count=fmt_counts,
                             evals=macro('render_evals_cname'),
                             exec_time=fmt_elapsed_time_secs,
                             name=fmt_file_details,
                             score=macro('render_score'),
                             status_f=fmt_text_bold,
                             message=macro('render_message'),
                             results=fmt_file_results)
    column_labels = dict(id='Id',
                         name='Filename',
                         mtype='MIME Type',
                         md5='MD5 Hash',
                         sha1='SHA1 Hash',
                         hash='SHA256 Hash',
                         uuid_f='UUID',
                         status_f='Status',
                         date_b='Status Date',
                         exec_time='Elapsed Time',
                         score='Is Malicious?',
                         expect_sandbox='Expect Sandbox?',
                         message='Message',
                         evals_count='# of Evaluations',
                         evals='List of Evaluations',
                         results='Results')
Example #5
0
class EvalsView(MyBaseView):
    column_list = [
        'id', 'uuid_f', 'client', 'corrid', 'date_f', 'date_b', 'status_f',
        'score', 'files_count'
    ]
    column_sortable_list = [
        'id', 'uuid_f', 'client', 'corrid', 'date_f', 'date_b', 'status_f',
        'score', 'files_count'
    ]
    column_details_list = [
        'id', 'client', 'uuid_f', 'corrid', 'date_f', 'date_b', 'score',
        'status_f', 'files_count', 'files'
    ]
    column_exclude_list = [
        '',
    ]
    column_export_list = [
        'id', 'client', 'uuid_f', 'corrid', 'date_f', 'date_b', 'score',
        'status_f', 'files_count'
    ]
    column_export_exclude_list = ['files']
    column_formatters_export = dict(
        score=fmt_render_score,
        client=lambda v, c, m, p: str(getattr(m, p)),
        date_f=lambda v, c, m, p: str(getattr(m, p)),
        date_b=lambda v, c, m, p: str(
            dtparser(str(m.date_b)) - dtparser(str(m.date_f))))
    column_searchable_list = []
    column_default_sort = ('date_f', True)
    column_filters = [
        'uuid_f', 'corrid', 'status_f', 'score', 'client', 'files_count'
    ]
    column_formatters = dict(score=macro('render_score'),
                             client=fmt_eclient_details,
                             uuid_f=fmt_eval_details,
                             corrid=fmt_text_boldital,
                             date_b=fmt_elapsed_time,
                             status_f=fmt_text_bold,
                             files_count=fmt_counts,
                             files=macro('render_files_name'))

    column_labels = dict(id='Id',
                         client_id='Client Id',
                         client='[X.509] Common Name',
                         uuid_f='Evaluation UUID',
                         corrid='Correlation ID',
                         status_f='Status',
                         date_f='Submit Date',
                         date_b='Elapsed Time',
                         score='Is Malicious?',
                         files_count='# of Files',
                         files='List of Files')
Example #6
0
class LocationSetAdminView(SetViewMixin, BaseAdminView):
    column_list = ('name', 'administrative_divisions', 'locations')
    column_labels = {
        'name': _('Name'),
        'administrative_divisions': _('Administrative Divisions'),
        'locations': _('Location Data')
    }
    column_formatters = {
        'administrative_divisions': macro('locations_builder'),
        'locations': macro('locations_list'),
    }
    form_columns = ('name', )
    inline_models = (ExtraDataInlineFormAdmin(models.LocationDataField), )
    inline_model_form_converter = LocationExtraDataModelConverter

    def on_model_delete(self, model):
        # delete dependent objects first
        models.LocationPath.query.filter_by(location_set=model).delete()
        models.Location.query.filter_by(location_set=model).delete()
        models.LocationTypePath.query.filter_by(location_set=model).delete()
        models.LocationType.query.filter_by(location_set=model).delete()
        return super().on_model_delete(model)

    @expose('/builder/<int:location_set_id>', methods=['GET', 'POST'])
    def builder(self, location_set_id):
        return locations_builder(self, location_set_id)

    @expose('/builder/<int:location_set_id>/import', methods=['POST'])
    def import_divisions(self, location_set_id):
        return import_divisions(location_set_id)

    @expose('/builder/<int:location_set_id>/export')
    def export_divisions(self, location_set_id):
        return export_divisions(location_set_id)

    @expose('/locations/<int:location_set_id>')
    def locations_list(self, location_set_id):
        return locations_list(self, location_set_id)

    @expose('/locations/<int:location_set_id>/import', methods=['POST'])
    def locations_import(self, location_set_id):
        return locations_import(location_set_id)

    @expose('/locations/<int:location_set_id>/headers/<int:upload_id>',
            methods=['GET', 'POST'])
    def locations_headers(self, location_set_id, upload_id):
        return locations_headers(self, location_set_id, upload_id)

    @expose('/location/<int:id>', methods=['GET', 'POST'])
    def location_edit(self, id):
        return location_edit(self, id)
Example #7
0
class IssueView(DeveloperModelView):
    can_create = False
    can_delete = False
    can_edit = False
    can_view_details = True
    column_default_sort = ('total', True)
    column_display_actions = False
    column_filters = ('product', 'platform', 'reason')
    column_formatters = dict(
        avg_uptime=lambda v, c, m, n: '{} s'.format(m.avg_uptime),
        actions=macro('render_actions'),
    )
    column_list = [
        'platform', 'version', 'reason', 'location', 'avg_uptime', 'last_seen',
        'total', 'actions'
    ]
    form_args = dict(total={'label': 'Total Crash Reports'})
    list_template = 'admin/issue_list.html'
    named_filter_urls = True
    page_size = 10

    @expose('/details/')
    def details_view(self):
        issue = models.Issue.objects.get(id=request.args.get('id'))
        minidumps = models.Minidump.objects(product=issue.product,
                                            version=issue.version,
                                            platform=issue.platform,
                                            crash_reason=issue.reason)
        page_num = int(request.args.get('page') or 1)
        per_page = 10
        return self.render('admin/issue_details.html',
                           issue=issue,
                           column_details_list=self.column_details_list,
                           minidumps=minidumps.paginate(page=page_num,
                                                        per_page=per_page),
                           per_page=per_page)

    @expose('/resolve', methods=['POST'])
    def resolve(self):
        issue_id = request.args.get('id')
        if not issue_id and not ObjectId.is_valid(issue_id):
            flash('Invalid request.')
            return redirect(self.get_url('.index_view'))

        issue = models.Issue.objects(id=issue_id).first()
        if not issue:
            flash('Issue not found.')
            return redirect(self.get_url('.index_view'))

        issue.resolve_issue()
        flash('Issue has been resolved.')
        return redirect(self.get_url('.index_view'))

    @action('resolve_issues', 'Resolve selected issues',
            'Are you sure you want to resolve selected issues?')
    def action_resolve_issues(self, ids):
        issues = models.Issue.objects(id__in=ids)
        for issue in issues:
            issue.resolve_issue()
        flash('Selected issues have been resolved.')
Example #8
0
class CommentModelView(BaseModelView):
    can_create = False
    can_edit = False

    list_template = 'admin/list/_comment_list.html'

    def _list_video(view, context, model, name):
        return Markup('<a href="%s" target="_blank">%s</a>' % (url_for('home.video', id=model.video.id),
                                                               model.video.title))

    def _list_author(view, context, model, name):
        return Markup('<a href="%s" target="_blank">%s</a>' % (url_for('home.user', username=model.author.username),
                                                               model.author.username))

    column_formatters = dict(content=macro('render_content'), video=_list_video, author=_list_author)

    column_labels = {
        'content': '评论内容',
        'video': '评论于',
        'author': '评论者',
        'add_time': '评论时间',
        'disabled': '屏蔽评论'
    }

    column_list = ('content', 'author', 'video', 'add_time', 'disabled')
    column_sortable_list = ('content', ('author', 'author.username'),
                            ('video', 'video.title'), 'add_time', 'disabled')
    column_searchable_list = ('content', 'author.username', 'video.title')
    column_default_sort = ('add_time', True)
Example #9
0
class ReportView(CustomView):
    """
    Report view.
    """
    can_create = False
    can_edit = False
    column_searchable_list = ('version', 'filename')
    column_filters = ('version', 'filename', 'latest')
    column_formatters = dict(changes=macro('render_changes'))

    @action('rollback', 'Rollback',
            'Are you sure you want to rollback the configuration ?')
    def rollback(self):
        """
        This is not used.
        :return:
        """
        if session.get('ipaddr') is None:
            flash('APIC Credentials have not been entered', 'error')
            return redirect(url_for('snapshotsadmin.index_view'))
        return redirect(url_for('snapshotsadmin.index_view'))

    @action('view', 'View')
    def view(*args, **kwargs):
        """

        :param args:
        :param kwargs:
        :return:
        """
        return redirect(url_for('fileview.index'))
Example #10
0
class BankAccountModelView(TimeTrackedModelView, AuthorizationRequiredView):

    list_template = 'bank_account/list.html'

    column_default_sort = 'name'

    page_size = 100

    can_delete = True

    can_view_details = True

    def get_accessible_roles(self):
        return [RolesEnum.ADMIN.value]

    form_args = _form_args

    column_descriptions = CMC.get_column_descriptions()

    column_list = _column_list

    column_labels = CMC.get_column_labels()

    # NOTE: Don't touch encode at all and delegate to Markup

    column_formatters = {
        'vendors': macro('render_vendors'),
    }
Example #11
0
class CategoryView(BaseBlogView):
    def _list_thumbnail(view, context, model, name):
        if not model.picture or not model.picture.path:
            return ''
        return Markup(
            '<img src="%s">' %
            url_for('static',
                    filename='blog/picture/' + 'thumb-' + model.picture.path))

    column_formatters = dict(abstract=macro('render_abstract'),
                             picture=_list_thumbnail)
    column_list = [
        'id', 'name', 'abstract', 'hidden', 'create_time', 'update_time',
        'articles', 'user', 'picture'
    ]

    column_editable_list = ['hidden']
    column_searchable_list = ['name', 'abstract']
    column_filters = ['name', 'create_time', 'hidden']
    column_labels = {
        'id': u'序号',
        'name': u'类别',
        'abstract': u'介绍',
        'hidden': u'状态',
        'create_time': u'创建时间',
        'update_time': u'更新时间',
        'articles': u'文章',
        'user': u'作者',
        'picture': u'配图缩略图'
    }

    def __init__(self, session, **kwargs):
        super(CategoryView, self).__init__(Category, session, **kwargs)
Example #12
0
class WorkflowView(ModelView):
    """View for managing Compliance results."""

    can_edit = False
    can_delete = False
    can_create = False
    can_view_details = True
    column_default_sort = ('created', True)

    column_list = ('created', 'modified', 'status', 'name', 'info')
    column_labels = {'workflow.name': 'Workflow name'}
    column_sortable_list = ()
    column_filters = (
        'created',
        'modified',
        'name',
        FilterStatus(column=Workflow.status, name='Status')
    )
    column_formatters = {
        'info': macro('render_info'),
        'data': data_formatter,
        'error_msg': error_msg_formatter,
        'message': msg_formatter,
    }
    column_auto_select_related = True
    column_details_list = column_list + ('message', 'error_msg', 'data', )
    column_details_exclude_list = ('info', )

    @action('resume', 'Resume', 'Are you sure?')
    def action_resume(self, ids):
        objects = Workflow.query.filter(Workflow.uuid.in_(ids)).all()

        if len(objects) != len(ids):
            raise ValueError("Invalid id for workflow(s).")

        try:
            for workflow in objects:
                for workflow_object in workflow.objects:
                    resume.apply_async((workflow_object.id,))

            flash("Selected workflow(s) resumed.", "success")
        except Exception as e:
            flash("Failed to resume all selected workflows. Reason: %s" % e.message, "error")

    @action('restart', 'Restart', 'Are you sure?')
    def action_restart(self, ids):
        try:
            for id in ids:
                restart.apply_async((id,))

            flash("Selected workflow(s) restarted.", "success")
        except Exception as e:
            flash("Failed to restart all selected workflows. Reason: %s" % e.message, "error")

    list_template = 'scoap3_workflows/admin/list.html'
Example #13
0
class ProjectView(AdminModelView):
    can_delete = False
    can_view_details = True
    column_display_actions = False
    column_editable_list = ['name']
    column_formatters = dict(actions=macro('render_actions'))
    column_labels = dict(min_version='Minimum Version')
    column_list = ['name', 'min_version', 'allowed_platforms', 'actions']
    create_modal = True
    create_modal_template = 'admin/add_project_modal.html'
    create_template = 'admin/add_project.html'
    edit_template = 'admin/edit_project.html'
    form_args = dict(
        min_version={'label': 'Minimum required version of crashed app'},
        allowed_platforms={'label': 'Allowed platforms'})
    form_create_rules = ('name', )
    form_edit_rules = ('min_version', 'allowed_platforms')
    form_overrides = dict(min_version=StringField)
    list_template = 'admin/project_list.html'

    @expose('/details/')
    def details_view(self):
        project = models.Project.objects.get(id=request.args.get('id'))
        minidump_versions = models.Minidump.get_versions_per_product(
            product=project.name)
        last_10_minidumps = models.Minidump.get_last_n_project_minidumps(
            n=10, project_name=project.name)
        issues = models.Issue.get_top_n_project_issues(
            n=10, project_name=project.name)
        return self.render('admin/project_overview.html',
                           project=project,
                           versions=minidump_versions,
                           latest_crash_reports=last_10_minidumps,
                           top_issues=issues)

    @expose('/_crash_reports')
    def crash_reports_chart(self):
        version = request.args.get('version')
        project = models.Project.objects.get(id=request.args.get('id'))
        platforms = project.get_allowed_platforms()
        if version and 'All' not in version:
            project_minidumps = models.Minidump.objects(product=project.name,
                                                        version=version)
        else:
            project_minidumps = models.Minidump.objects(product=project.name)
        data = {}
        for platform in platforms:
            platform_minidumps = project_minidumps(platform=platform)
            data[platform] = \
                models.Minidump.get_last_12_months_minidumps_counts(
                    platform_minidumps)

        labels = get_last_12_months_labels()
        return jsonify(result=get_decorated_data(
            labels=labels, data=data.values(), data_labels=list(data.keys())))
Example #14
0
class AdminAudioModelView(ModelView):
    edit_template = "test.html"
    can_export = True
    can_view_details = True
    export_types = ['xls']
    column_display_pk = True
    # column_display_all_relations               = True
    column_formatters = dict(url=macro('render_audio'))

    def is_accessible(self):
        return True
        return current_user.is_authenticated and (
            current_user.superuser or current_user.email in U.superuser_set)
Example #15
0
class QAListView(sqla.ModelView):
    column_searchable_list = ('name', )

    # columns list Data source link to admin page, has data, source_url, run log with cleaned and source, date injested
    def is_accessible(self):
        return require.perms.is_admin()

    can_delete = False
    can_create = False
    can_edit = False

    column_formatters = dict(name=macro('render_qalist'),
                             source_url=macro('render_sourceurl'),
                             report_url=macro('render_report'),
                             number_errors=macro('num_log_records'))
    #column_formatters = dict(dataset_admin_url=macro('render_price'))
    column_list = (
        'name',
        'source_url',
        'report_url',
        'number_errors',
    )
    list_template = 'adminsection/qalist.html'
Example #16
0
class ClientsView(MyBaseView):
    column_list = [
        'id', 'x509_cname', 'x509_orgname', 'x509_orgstate', 'date_fseen',
        'date_lseen', 'last_ip', 'evals_count'
    ]
    column_sortable_list = [
        'id', 'x509_serial', 'x509_cname', 'x509_orgdept', 'x509_orgname',
        'x509_orgstate', 'evals_count'
    ]
    column_details_list = [
        'id', 'fingerprint', 'x509_serial', 'x509_cname', 'x509_email',
        'x509_orgdept', 'x509_orgname', 'x509_orgstate', 'date_fseen',
        'date_lseen', 'last_ip', 'evals_count', 'evals'
    ]
    column_exclude_list = ['x509_data']
    column_export_list = [
        'id', 'fingerprint', 'x509_serial', 'x509_cname', 'x509_email',
        'x509_orgdept', 'x509_orgname', 'x509_orgstate', 'date_fseen',
        'date_lseen', 'last_ip', 'evals_count'
    ]
    column_export_exclude_list = ['x509_data', 'evals']
    column_formatters_export = {}
    column_searchable_list = []
    column_default_sort = ('id', True)
    column_filters = [
        'fingerprint', 'x509_serial', 'x509_cname', 'x509_orgname',
        'x509_email', 'x509_orgstate', 'x509_orgdept', 'evals_count'
    ]
    column_formatters = dict(x509_cname=fmt_cclient_details,
                             x509_orgname=fmt_text_bold,
                             evals_count=fmt_counts,
                             evals=macro('render_evals'))
    column_formatters_export = dict(
        x509_serial=lambda v, c, m, p: str(getattr(m, p)),
        date_fseen=lambda v, c, m, p: str(getattr(m, p)),
        date_lseen=lambda v, c, m, p: str(getattr(m, p)))
    column_labels = dict(id='Id',
                         fingerprint='[X.509] Thumbprint',
                         last_ip='Last seen IP',
                         date_fseen='First seen Date',
                         date_lseen='Last seen Date',
                         evals_count='# of Evaluations',
                         evals='List of Evaluations',
                         x509_serial='[X.509] Serial',
                         x509_cname='[X.509] Common Name',
                         x509_email='[X.509] Email Address',
                         x509_orgdept='[X.509] Department',
                         x509_orgname='[X.509] Organization',
                         x509_orgstate='[X.509] BULSTAT')
Example #17
0
class AdminModelView(BaseModelView):
    can_edit = False

    form = AdminForm

    def on_model_change(self, form, model, is_created):
        if is_created:
            model.password = form.password.data

    column_exclude_list = ['password_hash']
    column_searchable_list = ('name', 'email')

    column_labels = {
        'name': '账户名',
        'email': '账户邮箱',
        'confirmed': '确认邮箱'
    }

    list_template = 'admin/list/_admin_list.html'

    column_formatters = dict(confirmed=macro('render_confirmed'))
Example #18
0
class HomeSlideShowImagesView(ModelView):
    list_template = "admin/home_slide_show_settings.html"
    form_overrides = {
        "order": fields.IntegerField,
        "image_name": fields.FileField
    }
    column_formatters = {
        "image_name":macro("render_image")
    }
    
    def create_model(self, form):
        picture = save_picture(
            form.image_name.data,
            "static/vendor_product_pictures",
            800,800
            )
        db.HomeSlideShowImages(form.order.data,picture,form.caption.data)
        flash("Record was successfully created", "success")
    def update_model(self, form, model):
        picture = save_picture(
            form.image_name.data,
            "static/vendor_product_pictures",
            800,800
            )
        update = Session.query(
            db.HomeSlideShowImages
        ).filter(
            db.HomeSlideShowImages.image_id == model.id
        ).update(
            {
                "order":int(form.order.data),
                "image_name":picture,
                "caption": form.caption.data
            })
        if update:
            Session.commit()
            return True
        else:
            return False
Example #19
0
class UserModelView(BaseModelView):
    can_create = False

    form = UserForm

    list_template = 'admin/list/_user_list.html'
    edit_template = 'admin/edit/_user_edit.html'

    def _list_thumb_head_img(view, context, model, name):
        if not model.thumb_head_img:
            return Markup('<img src="%s" alt="头像缩略图">' % model.gravatar(size=50))
        return Markup('<img src="%s" alt="头像缩略图">' % model.thumb_head_img)

    def _list_username(view, context, model, name):
        return Markup('<a href="%s" target="_blank">%s</a>' %
                      (url_for('home.user', username=model.username),
                       model.username))

    column_formatters = dict(info=macro('render_info'), head_img=macro('render_head_img'),
                             thumb_head_img=_list_thumb_head_img, phone=macro('render_phone'),
                             location=macro('render_location'),
                             get_like_num=macro('render_get_like_num'),
                             username=_list_username, confirmed=macro('render_confirmed'))

    column_labels = {
        'username': '******',
        'email': '邮箱',
        'phone': '手机号码',
        'location': '所在地',
        'info': '简介',
        'head_img': '头像',
        'thumb_head_img': '头像缩略图',
        'confirmed': '是否确认',
        'member_since': '注册时间',
        'last_visit': '最后访问',
        'get_like_num': '获得赞数'
    }

    column_exclude_list = ['password_hash', 'avatar_hash']
    column_searchable_list = ('email', 'username', 'info', 'location', 'phone')
    column_default_sort = ('member_since', True)

    def on_model_change(self, form, model, is_created):
        if not is_created and form.dis_haed_img.data == 'True':
            model.head_img = None
            model.thumb_head_img = None
Example #20
0
class ProvisionView(ModelView):
    column_list = ['applied_at', 'config_version', 'data']
    column_details_list = ['applied_at', 'config_version', 'data']
    column_default_sort = ('applied_at', True)
    list_template = 'admin/provision_list.html'
    details_template = 'admin/provision_details.html'
    column_formatters = {
        'data': macro('render_data'),
    }

    def get_query(self):
        query = super().get_query()
        node_id = request.args.get('node_id')
        if node_id:
            query = query.filter_by(node_id=node_id)
        return query

    @expose('/ipxe')
    def raw_ipxe_view(self):
        provision = self.get_one(request.args.get('id'))
        return Response(provision.ipxe_config, mimetype='text/plain')

    @expose('/ignition.json')
    def raw_ignition_view(self):
        provision = self.get_one(request.args.get('id'))
        data = json.loads(provision.ignition_config)
        data = json.dumps(data, indent=2)
        return Response(data, mimetype='application/json')

    @expose('/ignition.tar.gz')
    def ignition_filesystem_view(self):
        provision = self.get_one(request.args.get('id'))
        tgz_data = ignition_parser.render_ignition_tgz(
            json.loads(provision.ignition_config))
        return Response(tgz_data, mimetype='application/tar+gzip')

    def is_visible(self):
        return False
Example #21
0
class ExternalObjectView(DefaultView):
    def __init__(self, *args, **kwargs):
        kwargs["category"] = "External Objects"
        kwargs["endpoint"] = kwargs["name"].lower().replace(" ", "")
        super(ExternalObjectView, self).__init__(ExternalObject, *args,
                                                 **kwargs)

    can_view_details = True
    can_export = True
    # TODO: Export formatters
    export_types = ["csv", "xls"]

    column_details_list = ("id", "type", "values_list", "links_list")
    column_formatters = {
        "name":
        attribute_formatter(partial(eq, ValueType.NAME)),
        "title":
        attribute_formatter(partial(eq, ValueType.TITLE)),
        "date":
        attribute_formatter(partial(eq, ValueType.DATE),
                            filter=lambda t: len(t) == 4),
        "genres":
        attribute_formatter(partial(eq, ValueType.GENRES), limit=3),
        "country":
        attribute_formatter(partial(eq, ValueType.COUNTRY),
                            filter=lambda t: len(t) == 2),
        "duration":
        attribute_formatter(
            partial(eq, ValueType.DURATION),
            filter=lambda t: t.replace(".", "").isdigit(),
            limit=1,
        ),
        "series":
        series_formatter,
        "season":
        meta_formatter("season"),
        "episode":
        meta_formatter("episode"),
        "episode_details":
        macro("episode_details"),
        "episodes":
        episodes_formatter,
        "episodes_list":
        macro("episodes_list"),
        "values":
        attribute_formatter(show_score=True),
        "values_list":
        macro("values_list"),
        "links_list":
        macro("links_list"),
        "links":
        count_formatter,
    }

    column_extra_row_actions = [
        EndpointLinkRowAction("glyphicon icon-search",
                              "allobjects.index_view",
                              id_arg="flt0_0")
    ]

    inline_models = (
        (Value, dict(form_columns=("id", "type", "text"))),
        (ObjectLink, dict(form_columns=("id", "platform", "external_id"))),
    )

    column_filters = [
        ExternalObjectSimilarFilter(name="Similar"),
        ExternalObjectPlatformFilter(
            column=Platform.country,
            name="Platform",
            options=[(c[0], str(c[0]).upper())
                     for c in db.session.query(Platform.country).distinct()],
        ),
        ExternalObjectPlatformFilter(
            column=Platform.type,
            name="Platform",
            options=[(t.name, t.name) for t in PlatformType],
        ),
        ExternalObjectPlatformFilter(
            column=Platform.slug,
            name="Platform",
            options=[(p.slug, p.name)
                     for p in db.session.query(Platform.slug, Platform.name)],
        ),
        ExternalObjectPlatformFilter(
            column=Platform.slug,
            invert=True,
            name="Platform",
            options=[(p.slug, p.name)
                     for p in db.session.query(Platform.slug, Platform.name)],
        ),
    ]

    def get_query(self):
        q = (super(ExternalObjectView, self).get_query().options(
            joinedload(ExternalObject.values).joinedload(Value.sources)))
        if hasattr(self, "external_object_type"):
            q = q.filter(ExternalObject.type == self.external_object_type)
        return q

    def get_count_query(self):
        q = super(ExternalObjectView, self).get_count_query()
        if hasattr(self, "external_object_type"):
            q = q.filter(ExternalObject.type == self.external_object_type)
        return q
Example #22
0
class DataModelView(ModelView, ViewAuthMixin):
    column_hide_backrefs = False
    column_exclude_list = ('cover_art', 'notes', 'lyrics', 'info')
    column_formatters = {
        'catalog': macro('format_filters'),
        'composer': macro('format_filters'),
        'disc': macro('format_filters'),
        'length': format_length
    }
    column_labels = {
        'catalog': 'Catalog number',
        'vgmdb_id': 'VGMdb id',
        'id': 'Unique ID'
    }
    form_overrides = {'lyrics': TallTextAreaField, 'notes': TallTextAreaField}
    list_template = 'admin/list_filtered.html'

    def __init__(self, model, session):
        table = model.metadata.tables[model.__tablename__]
        self.column_list = []
        self.form_columns = []

        for column in table.c:
            if model is Track and column.name == 'catalog':
                self.column_list.append(column.name)
                self.column_list.append('album')
                self.form_columns.append('album')
            elif model is Track and column.name == 'composer_name':
                self.column_list.append('composer_name')
                self.form_columns.append('composer')
            elif column.name != 'id':
                self.column_list.append(column.name)
                self.form_columns.append(column.name)

        if model is Track:
            self.form_columns.append('vocalists')
            self.form_columns.append('lyricists')

        super(DataModelView, self).__init__(model, session)
        self.superuser = False

    def get_request_filters(self):
        return {
            key: value
            for key, value in request.args.items() if key in self.column_list
        }

    def get_filtered_query(self, query):
        filters = self.get_request_filters()
        if filters:
            return query.filter_by(**filters)
        else:
            return query

    def get_query(self):
        return self.get_filtered_query(super(DataModelView, self).get_query())

    def get_count_query(self):
        return self.get_filtered_query(
            super(DataModelView, self).get_count_query())

    def render(self, template, **kw):
        filters = self.get_request_filters()
        return super(DataModelView,
                     self).render(template,
                                  column_labels=self.column_labels,
                                  request_filters=filters,
                                  **kw)
Example #23
0
class ExternalObjectView(DefaultView):
    def __init__(self, *args, **kwargs):
        kwargs['category'] = 'External Objects'
        kwargs['endpoint'] = kwargs['name'].lower() + 'object'
        super(ExternalObjectView, self).__init__(ExternalObject, *args,
                                                 **kwargs)

    can_view_details = True
    can_export = True
    # TODO: Export formatters
    export_types = ['csv', 'xls']

    column_details_list = ('id', 'type', 'attributes_list', 'links_list')
    column_formatters = {
        'name':
        attribute_formatter(partial(eq, ValueType.NAME)),
        'title':
        attribute_formatter(partial(eq, ValueType.TITLE)),
        'date':
        attribute_formatter(partial(eq, ValueType.DATE),
                            filter=lambda t: len(t) == 4),
        'genres':
        attribute_formatter(partial(eq, ValueType.GENRES), limit=3),
        'country':
        attribute_formatter(partial(eq, ValueType.COUNTRY),
                            filter=lambda t: len(t) == 2),
        'duration':
        attribute_formatter(partial(eq, ValueType.DURATION),
                            filter=lambda t: t.replace('.', '').isdigit(),
                            limit=1),
        'attributes':
        attribute_formatter(show_score=True),
        'attributes_list':
        macro('attributes_list'),
        'links_list':
        macro('links_list'),
        'links':
        count_formatter
    }

    column_extra_row_actions = [
        EndpointLinkRowAction('glyphicon icon-search',
                              'allobject.index_view',
                              id_arg='flt0_0')
    ]

    inline_models = ((Value, dict(form_columns=('id', 'type', 'text'))),
                     (ObjectLink,
                      dict(form_columns=('id', 'platform', 'external_id'))))

    column_filters = [
        ExternalObjectSimilarFilter(name='Similar'),
        ExternalObjectPlatformFilter(
            column=Platform.country,
            name='Platform',
            options=[(c[0], str(c[0]).upper())
                     for c in db.session.query(Platform.country).distinct()]),
        ExternalObjectPlatformFilter(column=Platform.type,
                                     name='Platform',
                                     options=[(t.name, t.name)
                                              for t in PlatformType]),
        ExternalObjectPlatformFilter(
            column=Platform.slug,
            name='Platform',
            options=[(p.slug, p.name)
                     for p in db.session.query(Platform.slug, Platform.name)]),
        ExternalObjectPlatformFilter(
            column=Platform.slug,
            invert=True,
            name='Platform',
            options=[(p.slug, p.name)
                     for p in db.session.query(Platform.slug, Platform.name)]),
    ]

    def get_query(self):
        q = super(ExternalObjectView, self).get_query()
        if hasattr(self, 'external_object_type'):
            q = q.filter(ExternalObject.type == self.external_object_type)
        return q

    def get_count_query(self):
        q = super(ExternalObjectView, self).get_count_query()
        if hasattr(self, 'external_object_type'):
            q = q.filter(ExternalObject.type == self.external_object_type)
        return q
Example #24
0
def test_export_csv():
    app, admin = setup()
    client = app.test_client()

    # test redirect when csv export is disabled
    view = MockModelView(Model, column_list=['col1', 'col2'], endpoint="test")
    admin.add_view(view)

    rv = client.get('/admin/test/export/csv/')
    eq_(rv.status_code, 302)

    # basic test of csv export with a few records
    view_data = {
        1: Model(1, "col1_1", "col2_1"),
        2: Model(2, "col1_2", "col2_2"),
        3: Model(3, "col1_3", "col2_3"),
    }

    view = MockModelView(Model, view_data, can_export=True,
                         column_list=['col1', 'col2'])
    admin.add_view(view)

    rv = client.get('/admin/model/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.mimetype, 'text/csv')
    eq_(rv.status_code, 200)
    ok_("Col1,Col2\r\n"
        "col1_1,col2_1\r\n"
        "col1_2,col2_2\r\n"
        "col1_3,col2_3\r\n" == data)

    # test utf8 characters in csv export
    view_data[4] = Model(1, u'\u2013ut8_1\u2013', u'\u2013utf8_2\u2013')
    view = MockModelView(Model, view_data, can_export=True,
                         column_list=['col1', 'col2'], endpoint="utf8")
    admin.add_view(view)

    rv = client.get('/admin/utf8/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 200)
    ok_(u'\u2013ut8_1\u2013,\u2013utf8_2\u2013\r\n' in data)

    # test row limit
    view_data = {
        1: Model(1, "col1_1", "col2_1"),
        2: Model(2, "col1_2", "col2_2"),
        3: Model(3, "col1_3", "col2_3"),
    }

    view = MockModelView(Model, view_data, can_export=True,
                         column_list=['col1', 'col2'], export_max_rows=2,
                         endpoint='row_limit_2')
    admin.add_view(view)

    rv = client.get('/admin/row_limit_2/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 200)
    ok_("Col1,Col2\r\n"
        "col1_1,col2_1\r\n"
        "col1_2,col2_2\r\n" == data)

    # test None type, integer type, column_labels, and column_formatters
    view_data = {
        1: Model(1, "col1_1", 1),
        2: Model(2, "col1_2", 2),
        3: Model(3, None, 3),
    }

    view = MockModelView(
        Model, view_data, can_export=True, column_list=['col1', 'col2'],
        column_labels={'col1': 'Str Field', 'col2': 'Int Field'},
        column_formatters=dict(col2=lambda v, c, m, p: m.col2*2),
        endpoint="types_and_formatters"
    )
    admin.add_view(view)

    rv = client.get('/admin/types_and_formatters/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 200)
    ok_("Str Field,Int Field\r\n"
        "col1_1,2\r\n"
        "col1_2,4\r\n"
        ",6\r\n" == data)

    # test column_formatters_export and column_formatters_export
    type_formatters = {type(None): lambda view, value: "null"}

    view = MockModelView(
        Model, view_data, can_export=True, column_list=['col1', 'col2'],
        column_formatters_export=dict(col2=lambda v, c, m, p: m.col2*3),
        column_formatters=dict(col2=lambda v, c, m, p: m.col2*2),  # overridden
        column_type_formatters_export=type_formatters,
        endpoint="export_types_and_formatters"
    )
    admin.add_view(view)

    rv = client.get('/admin/export_types_and_formatters/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 200)
    ok_("Col1,Col2\r\n"
        "col1_1,3\r\n"
        "col1_2,6\r\n"
        "null,9\r\n" == data)

    # Macros are not implemented for csv export yet and will throw an error
    view = MockModelView(
        Model, can_export=True, column_list=['col1', 'col2'],
        column_formatters=dict(col1=macro('render_macro')),
        endpoint="macro_exception"
    )
    admin.add_view(view)

    rv = client.get('/admin/macro_exception/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 500)
Example #25
0
def test_export_csv():
    app, admin = setup()
    client = app.test_client()

    # test redirect when csv export is disabled
    view = MockModelView(Model, column_list=['col1', 'col2'], endpoint="test")
    admin.add_view(view)

    rv = client.get('/admin/test/export/csv/')
    eq_(rv.status_code, 302)

    # basic test of csv export with a few records
    view_data = {
        1: Model(1, "col1_1", "col2_1"),
        2: Model(2, "col1_2", "col2_2"),
        3: Model(3, "col1_3", "col2_3"),
    }

    view = MockModelView(Model, view_data, can_export=True,
                         column_list=['col1', 'col2'])
    admin.add_view(view)

    rv = client.get('/admin/model/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.mimetype, 'text/csv')
    eq_(rv.status_code, 200)
    ok_("Col1,Col2\r\n"
        "col1_1,col2_1\r\n"
        "col1_2,col2_2\r\n"
        "col1_3,col2_3\r\n" == data)

    # test explicit use of column_export_list
    view = MockModelView(Model, view_data, can_export=True,
                         column_list=['col1', 'col2'],
                         column_export_list=['id', 'col1', 'col2'],
                         endpoint='exportinclusion')
    admin.add_view(view)

    rv = client.get('/admin/exportinclusion/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.mimetype, 'text/csv')
    eq_(rv.status_code, 200)
    ok_("Id,Col1,Col2\r\n"
        "1,col1_1,col2_1\r\n"
        "2,col1_2,col2_2\r\n"
        "3,col1_3,col2_3\r\n" == data)

    # test explicit use of column_export_exclude_list
    view = MockModelView(Model, view_data, can_export=True,
                         column_list=['col1', 'col2'],
                         column_export_exclude_list=['col2'],
                         endpoint='exportexclusion')
    admin.add_view(view)

    rv = client.get('/admin/exportexclusion/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.mimetype, 'text/csv')
    eq_(rv.status_code, 200)
    ok_("Col1\r\n"
        "col1_1\r\n"
        "col1_2\r\n"
        "col1_3\r\n" == data)

    # test utf8 characters in csv export
    view_data[4] = Model(1, u'\u2013ut8_1\u2013', u'\u2013utf8_2\u2013')
    view = MockModelView(Model, view_data, can_export=True,
                         column_list=['col1', 'col2'], endpoint="utf8")
    admin.add_view(view)

    rv = client.get('/admin/utf8/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 200)
    ok_(u'\u2013ut8_1\u2013,\u2013utf8_2\u2013\r\n' in data)

    # test None type, integer type, column_labels, and column_formatters
    view_data = {
        1: Model(1, "col1_1", 1),
        2: Model(2, "col1_2", 2),
        3: Model(3, None, 3),
    }

    view = MockModelView(
        Model, view_data, can_export=True, column_list=['col1', 'col2'],
        column_labels={'col1': 'Str Field', 'col2': 'Int Field'},
        column_formatters=dict(col2=lambda v, c, m, p: m.col2 * 2),
        endpoint="types_and_formatters"
    )
    admin.add_view(view)

    rv = client.get('/admin/types_and_formatters/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 200)
    ok_("Str Field,Int Field\r\n"
        "col1_1,2\r\n"
        "col1_2,4\r\n"
        ",6\r\n" == data)

    # test column_formatters_export and column_formatters_export
    type_formatters = {type(None): lambda view, value: "null"}

    view = MockModelView(
        Model, view_data, can_export=True, column_list=['col1', 'col2'],
        column_formatters_export=dict(col2=lambda v, c, m, p: m.col2 * 3),
        column_formatters=dict(col2=lambda v, c, m, p: m.col2 * 2),  # overridden
        column_type_formatters_export=type_formatters,
        endpoint="export_types_and_formatters"
    )
    admin.add_view(view)

    rv = client.get('/admin/export_types_and_formatters/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 200)
    ok_("Col1,Col2\r\n"
        "col1_1,3\r\n"
        "col1_2,6\r\n"
        "null,9\r\n" == data)

    # Macros are not implemented for csv export yet and will throw an error
    view = MockModelView(
        Model, can_export=True, column_list=['col1', 'col2'],
        column_formatters=dict(col1=macro('render_macro')),
        endpoint="macro_exception"
    )
    admin.add_view(view)

    rv = client.get('/admin/macro_exception/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 500)

    # We should be able to specify column_formatters_export
    # and not get an exception if a column_formatter is using a macro
    def export_formatter(v, c, m, p):
        return m.col1 if m else ''

    view = MockModelView(
        Model, view_data, can_export=True, column_list=['col1', 'col2'],
        column_formatters=dict(col1=macro('render_macro')),
        column_formatters_export=dict(col1=export_formatter),
        endpoint="macro_exception_formatter_override"
    )
    admin.add_view(view)

    rv = client.get('/admin/macro_exception_formatter_override/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 200)
    ok_("Col1,Col2\r\n"
        "col1_1,1\r\n"
        "col1_2,2\r\n"
        ",3\r\n" == data)

    # We should not get an exception if a column_formatter is
    # using a macro but it is on the column_export_exclude_list
    view = MockModelView(
        Model, view_data, can_export=True, column_list=['col1', 'col2'],
        column_formatters=dict(col1=macro('render_macro')),
        column_export_exclude_list=['col1'],
        endpoint="macro_exception_exclude_override"
    )
    admin.add_view(view)

    rv = client.get('/admin/macro_exception_exclude_override/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 200)
    ok_("Col2\r\n"
        "1\r\n"
        "2\r\n"
        "3\r\n" == data)

    # When we use column_export_list to hide the macro field
    # we should not get an exception
    view = MockModelView(
        Model, view_data, can_export=True, column_list=['col1', 'col2'],
        column_formatters=dict(col1=macro('render_macro')),
        column_export_list=['col2'],
        endpoint="macro_exception_list_override"
    )
    admin.add_view(view)

    rv = client.get('/admin/macro_exception_list_override/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 200)
    ok_("Col2\r\n"
        "1\r\n"
        "2\r\n"
        "3\r\n" == data)

    # If they define a macro on the column_formatters_export list
    # then raise an exception
    view = MockModelView(
        Model, view_data, can_export=True, column_list=['col1', 'col2'],
        column_formatters=dict(col1=macro('render_macro')),
        endpoint="macro_exception_macro_override"
    )
    admin.add_view(view)

    rv = client.get('/admin/macro_exception_macro_override/export/csv/')
    data = rv.data.decode('utf-8')
    eq_(rv.status_code, 500)
Example #26
0
class ApiRegistrationsView(ModelView):
    """View for managing access to actions by users."""

    can_view_details = True
    can_edit = False

    column_list = ('id', 'creation_date', 'name', 'partner', 'organization',
                   'email', 'role', 'country', 'description', 'accepted')

    column_default_sort = ('id', True)

    column_labels = {
        'creation_date': "Registration date",
        'name': "Name",
        'email': "E-mail",
    }

    list_template = 'scoap3_api/custom_list.html'
    details_template = 'scoap3_api/details.html'
    column_filters = ('id', 'creation_date', 'name', 'partner', 'organization',
                      'country', 'accepted')
    column_formatters = {'accepted': macro('render_tri_state_boolean_to_icon')}

    def _create_user_for_api_registration(self, api_user_id):
        api_registration = ApiRegistrations.query.filter_by(
            id=api_user_id).one()
        password = os.urandom(5).encode('hex')

        kwargs = dict(email=api_registration.email,
                      password=password,
                      active='y')
        form = ConfirmRegisterForm(MultiDict(kwargs), csrf_enabled=False)

        u = None
        # Role with id 4 is an API user
        r = Role.query.filter_by(id=4).one()

        if form.validate():
            kwargs['password'] = hash_password(kwargs['password'])
            kwargs['active'] = True
            u = _datastore.create_user(**kwargs)

            if _datastore.add_role_to_user(u, r):
                msg = TemplatedMessage(
                    template_html='scoap3_api/email/confirmed.html',
                    subject='SCOAP3 - Partner registration confirmation',
                    sender=current_app.config.get('MAIL_DEFAULT_SENDER'),
                    recipients=[api_registration.email],
                    ctx={
                        'email': api_registration.email,
                        'password': password,
                        'recipient': api_registration.name
                    })
                current_app.extensions['mail'].send(msg)
        else:
            flash('Error creating user. %s' % form.errors, 'error')

    @action('accept', 'Accept',
            'Are you sure you want to accept selected Partner registrations?')
    def action_accept(self, ids):
        """Accept users."""
        try:
            count = 0
            for api_user_id in ids:
                user = ApiRegistrations.query.filter_by(id=api_user_id).one()
                if user.accepted == 1:
                    flash('API user %s was already accepted.' % (user.email, ),
                          'warning')
                    continue
                if not ApiRegistrations.accept(api_user_id):
                    raise ValueError("Cannot find API user registration.")
                else:
                    count += 1
                self._create_user_for_api_registration(api_user_id)
            db.session.commit()
            if count > 0:
                flash('API user(s) were successfully accepted.', 'success')
        except Exception as exc:
            if not self.handle_view_exception(exc):
                raise

            current_app.logger.exception(str(exc))  # pragma: no cover
            flash('Failed to accept API users.', 'error')  # pragma: no cover

    @action('reject', 'Reject',
            'Are you sure you want to reject selected Partner registrations?')
    def action_reject(self, ids):
        """Accept users."""
        try:
            count = 0
            for api_user_id in ids:
                if not ApiRegistrations.reject(api_user_id):
                    raise ValueError("Cannot find API user registration.")
                else:
                    count += 1
            db.session.commit()
            if count > 0:
                flash('API user(s) were successfully rejected.', 'success')
        except Exception as exc:
            if not self.handle_view_exception(exc):
                raise

            current_app.logger.exception(str(exc))  # pragma: no cover
            flash('Failed to reject API users.', 'error')  # pragma: no cover
Example #27
0
class IdentityView(ModelView):
    column_list = [
        'issuer',
        'name',
        'status',
    ]
    list_template = 'admin/identity_list.html'
    column_formatters = {
        'status': macro('render_status'),
    }

    form_excluded_columns = [
        'name',
        'issues',
        'pairs',
    ]

    form_extra_fields = {
        'subj_cn':
        fields.StringField('CN',
                           description='Common Name',
                           validators=required),
        'subj_c':
        fields.StringField('C', description='Country'),
        'subj_o':
        fields.StringField('O', description='Organization'),
        'subj_ou':
        fields.StringField('OU', description='Organizational Unit'),
        'subj_dnq':
        fields.StringField('', description='Distinguished name qualifier'),
        'subj_st':
        fields.StringField('ST', description='State or province name'),
        'subj_sn':
        fields.StringField('', description='Serial number'),
        'cert_validate_since':
        fields.DateTimeField('Valid since',
                             description='Not valid before',
                             validators=required,
                             default=default_since),
        'cert_validate_till':
        fields.DateTimeField('Valid till',
                             description='Not valid after',
                             validators=required,
                             default=default_till),
        'cert_ca_path_length':
        fields.IntegerField('CA path length', default=0),
        'san_ips':
        InlineFieldList(fields.StringField('IP', [validators.IPAddress()]),
                        'IP',
                        description='IP address'),
        'san_dns_names':
        InlineFieldList(fields.StringField('DNS'),
                        'DNS',
                        description='DNS names'),
        'ku_web_server_auth':
        fields.BooleanField('Web server auth',
                            description='TLS Web Server Authentication'),
        'ku_web_client_auth':
        fields.BooleanField('Web client auth',
                            description='TLS Web Client Authentication'),
        'key_size':
        fields.IntegerField('Size', default=2048),
        'key_public_exponent':
        fields.IntegerField('Public exponent', default=65537),
    }

    form_rules = [
        rules.Field('issuer'),
        rules.FieldSet([
            'cert_validate_since',
            'cert_validate_till',
            'cert_ca_path_length',
        ], 'Certificate settings'),
        rules.FieldSet([
            'subj_cn',
            'subj_c',
            'subj_o',
            'subj_ou',
            'subj_dnq',
            'subj_st',
            'subj_sn',
        ], 'Subject'),
        rules.FieldSet([
            'san_ips',
            'san_dns_names',
        ], 'Subject Alternative Names'),
        rules.FieldSet([
            'ku_web_server_auth',
            'ku_web_client_auth',
        ], 'Key Usage'),
        rules.FieldSet([
            'key_size',
            'key_public_exponent',
        ], 'Key Settings'),
    ]

    def create_model(self, *args, **kwargs):
        with self.session.no_autoflush:
            return super().create_model(*args, **kwargs)

    def on_model_change(self, form, model, is_created):
        data = x509.CertInfo(form.data)

        if data.issuer:
            pair = models.Pair(
                *x509.issue_certificate(data, data.issuer.pair.as_tuple))
        else:
            pair = models.Pair(*x509.issue_certificate(data))

        model.pair = pair
        model.name = data.subj_cn

    @expose('/reissue/', methods=['POST'])
    def reissue_view(self):
        model = self.get_one(request.values.get('id'))
        info = x509.load_certificate_info(model.pair.as_tuple, reissue=True)

        if model.issuer:
            pair = models.Pair(
                *x509.issue_certificate(info, model.issuer.pair.as_tuple))
        else:
            pair = models.Pair(*x509.issue_certificate(info))

        model.pair = pair

        self.session.commit()
        return_url = get_redirect_target() or self.get_url('.index_view')
        flash('The identity certificate was successfully reissued', 'success')
        return redirect(return_url)

    def edit_form(self, obj=None):
        if obj:
            info = x509.load_certificate_info(obj.pair.as_tuple, reissue=True)
            for k, v in info.as_dict().items():
                if not hasattr(obj, k):
                    setattr(obj, k, v)
        return super().edit_form(obj)

    @expose('/details/')
    def details_view(self):
        model = self.get_one(request.values.get('id'))
        return_url = get_redirect_target() or self.get_url('.index_view')
        return redirect(
            self.get_url('pair.details_view', id=model.pair.id,
                         url=return_url))

    @expose('/import/', methods=['GET', 'POST'])
    def import_view(self):
        return_url = get_redirect_target() or self.get_url('.index_view')
        form = ImportForm(get_form_data())

        if self.validate_form(form):
            pair_tuple = form.data['cert'].encode(
                'ascii'), form.data['key'].encode('ascii')
            info = x509.load_certificate_info(pair_tuple)

            if not x509.does_keys_match(pair_tuple):
                flash('Failed to import identity: keys does not match.',
                      'error')
                return redirect(return_url)

            identity = models.Identity()
            identity.name = info.subj_cn

            if not info.self_signed:

                def find_issuer():
                    for issuer in models.Identity.query.filter_by(
                            name=info.issuer_cn):
                        cert_chain = issuer.get_cert_chain()
                        try:
                            x509.verify_certificate_chain(
                                pair_tuple[0], cert_chain)
                        except x509.InvalidCertificate:
                            pass
                        else:
                            return issuer

                identity.issuer = find_issuer()
                if not identity.issuer:
                    flash(
                        'Failed to import identity: issuer identity not found.',
                        'error')
                    return redirect(return_url)

            self.session.add(identity)
            pair = models.Pair(*pair_tuple)
            pair.identity = identity
            self.session.add(pair)

            try:
                self.session.commit()
            except IntegrityError:
                flash(
                    'Failed to import identity: identity with same name already exists.',
                    'error')
                return redirect(return_url)

            flash('Identity was successfully imported.', 'success')
            return redirect(self.get_save_return_url(identity,
                                                     is_created=True))

        return self.render('admin/identity_import.html',
                           form=form,
                           return_url=return_url)
Example #28
0
class ArticleView(BaseBlogView):

    list_template = '_article_list.html'
    edit_template = '_edit_get_form.html'
    # column_exclude_list = ['abstract', 'text', 'comments']

    column_list = [
        'id', 'title', 'abstract', 'text', 'create_time', 'update_time',
        'state', 'visit_num', 'category', 'tags', 'comments', 'user', 'picture'
    ]

    column_searchable_list = ['title']
    column_filters = ['title', 'create_time', 'state']

    column_editable_list = ['state', 'visit_num', 'tags', 'category']

    form_excluded_columns = ['title', 'text']

    column_default_sort = ('id', True)

    # 覆盖path默认显示
    def _list_thumbnail(view, context, model, name):
        try:
            if not model.picture or not model.picture.path:
                return ''
            return Markup('<img src="%s">' % url_for(
                'static',
                filename='blog/picture/' + 'thumb-' + model.picture.path))
        except Exception as e:
            current_app.logger.error(e)

    column_formatters = dict(text=macro('render_text'),
                             abstract=macro('render_abstract'),
                             picture=_list_thumbnail)

    column_labels = {
        'id': u'序号',
        'title': u'题目',
        'abstract': u'摘要',
        'text': u'正文',
        'create_time': u'创建时间',
        'update_time': u'更新时间',
        'state': u'状态',
        'category': u'类型',
        'tags': u'标签',
        'comments': u'评论',
        'user': u'作者',
        'visit_num': u'浏览次数',
        'picture': u'配图'
    }

    @expose('/editor_pic', methods=["POST"])
    def editor_pic(self):
        image_file = request.files['editormd-image-file']
        if image_file and allowed_photo(image_file.filename):
            try:
                filename = secure_filename(image_file.filename)
                filename = str(
                    date.today()) + '-' + random_str() + '-' + filename
                file_path = os.path.join(bpdir,
                                         'static/editor.md/photoupdate/',
                                         filename)
                qiniu_path = os.path.join(bpdir, 'static/blog/qiniu_pic/',
                                          filename)
                image_file.save(file_path)
                ting_pic(file_path, qiniu_path)
                qiniu_link = get_link(qiniu_path, filename)
                data = {
                    'success': 1,
                    'message': 'image of editor.md',
                    'url': qiniu_link
                }
                return json.dumps(data)
            except Exception as e:
                current_app.logger.error(e)
        else:
            return u"没有获得图片或图片类型不支持"

    @expose('/change_do_article', methods=["GET", "POST"])
    def change_do_article(self):
        return self.render('_change_doarticle.html')

    @expose('/create_article', methods=["GET", "POST"])
    def create_article(self):
        article_form = ArticleForm()
        article_form.picture.choices = [
            (picture, picture.name)
            for picture in Picture.query.order_by('name').filter_by(state=True)
        ]
        article_form.tags.choices = [(tag, tag.name)
                                     for tag in Tag.query.order_by('name')]
        article_form.category.choices = [
            (category, category.name)
            for category in Category.query.order_by('name')
        ]
        article_form.create_time.default = datetime.utcnow()
        article_form.update_time.default = datetime.utcnow()
        return self.render('_create_article.html', article_form=article_form)

    @expose('/save_article', methods=["GET", "POST"])
    def save_article(self):
        article_form = ArticleForm()
        article_form.tags.choices = [(tag, tag.name)
                                     for tag in Tag.query.order_by('name')]
        article_form.category.choices = [
            (category, category.name)
            for category in Category.query.order_by('name')
        ]
        article_form.picture.choices = [
            (picture, picture.name)
            for picture in Picture.query.order_by('name').filter_by(state=True)
        ]

        new_article = Article(title=article_form.title.data,
                              text=article_form.text.data,
                              html_text=article_form.html.data)
        new_article.category = article_form.category.data
        new_article.abstract = article_form.abstract.data
        new_article.tags = article_form.tags.data
        new_article.picture = article_form.picture.data
        new_article.create_time = article_form.create_time.data
        new_article.update_time = article_form.update_time.data

        if article_form.print_submit.data:
            new_article.state = True

        if article_form.picture.data:
            the_picture = Picture.query.filter_by(
                name=article_form.picture.data.name).first()
            if the_picture.name != u'暂不选择配图':
                the_picture.state = False
            db.session.add(the_picture)
        db.session.add(new_article)
        db.session.commit()
        try:
            filename = ' '.join(article_form.title.data.split()) + '.md'
            with codecs.open(bpdir + '/static/blog/mdfile/' + filename,
                             'w',
                             encoding='utf-8') as f:
                f.write(article_form.text.data)
        except Exception as e:
            current_app.logger.info(e)
        if not article_form.print_submit.data:
            return redirect(
                url_for('.edit_get_form', article_id=new_article.id))
        return redirect('/huangzp/article')

    @expose('/edit_get_form/<article_id>', methods=["GET", "POST"])
    def edit_get_form(self, article_id):
        article_form = ArticleForm()
        article_form.tags.choices = [(tag, tag.name)
                                     for tag in Tag.query.order_by('name')]
        article_form.category.choices = [
            (category, category.name)
            for category in Category.query.order_by('name')
        ]
        article_form.picture.choices = [
            (picture, picture.name)
            for picture in Picture.query.order_by('name').filter_by(state=True)
        ]

        the_article = Article.query.filter_by(id=article_id).first()

        # 修改文章默认配图
        if the_article.picture and the_article.picture.name != u'暂不选择配图':
            article_form.picture.choices.append(
                (the_article.picture, the_article.picture.name))

        article_form.title.default = the_article.title
        article_form.text.default = the_article.text
        article_form.abstract.default = the_article.abstract
        article_form.category.default = the_article.category
        article_form.tags.default = the_article.tags
        article_form.picture.default = the_article.picture
        article_form.create_time.default = the_article.create_time
        article_form.update_time.default = the_article.update_time
        article_form.process()

        return self.render('_edit_article.html',
                           article_form=article_form,
                           article_id=article_id)

    @expose('/edit_to_save/<article_id>', methods=["GET", "POST"])
    def edit_to_save(self, article_id):
        article_form = ArticleForm()
        article_form.tags.choices = [(tag, tag.name)
                                     for tag in Tag.query.order_by('name')]
        article_form.category.choices = [
            (category, category.name)
            for category in Category.query.order_by('name')
        ]
        article_form.picture.choices = [
            (picture, picture.name)
            for picture in Picture.query.order_by('name').filter_by(state=True)
        ]

        the_article = Article.query.filter_by(id=article_id).first()

        # 删除旧备份
        old_filename = ' '.join(the_article.title.split()) + '.md'
        try:
            os.remove(bpdir + '/static/blog/mdfile/' + old_filename)
        except:
            pass

        # 有时候直接删除已经关联文章的照片,会造成文章的照片属性为空
        if not the_article.picture:
            new_picture = Picture.query.filter_by(
                name=article_form.picture.data.name).first()
            if new_picture.name != u'暂不选择配图':
                new_picture.state = False
            db.session.add(new_picture)
        else:
            new_picture = Picture.query.filter_by(
                name=article_form.picture.data.name).first()
            old_picture = Picture.query.filter_by(
                name=the_article.picture.name).first()
            if new_picture == old_picture:
                pass
            elif new_picture.name == u'暂不选择配图':
                new_picture.state = True
                old_picture.state = True
                db.session.add(new_picture, old_picture)
            else:
                new_picture.state = False
                old_picture.state = True
                db.session.add(new_picture, old_picture)

        the_article.title = article_form.title.data
        the_article.tags = article_form.tags.data
        the_article.category = article_form.category.data
        the_article.picture = article_form.picture.data
        the_article.text = article_form.text.data
        the_article.html_text = article_form.html.data
        the_article.abstract = article_form.abstract.data
        the_article.create_time = article_form.create_time.data
        the_article.update_time = article_form.update_time.data
        # else确保修改的文章状态是True然后点的保存
        if article_form.print_submit.data:
            the_article.state = True
        else:
            the_article.state = False

        db.session.add(the_article)
        db.session.commit()
        try:
            new_filename = ' '.join(article_form.title.data.split()) + '.md'
            with codecs.open(bpdir + '/static/blog/mdfile/' + new_filename,
                             'w',
                             encoding='utf-8') as f:
                f.write(article_form.text.data)
        except Exception as e:
            current_app.logger.info(e)
        if not article_form.print_submit.data:
            return redirect(
                url_for('.edit_get_form', article_id=the_article.id))
        return redirect('/huangzp/article')

    @expose('/do_file', methods=["POST"])
    def do_file(self):
        md_file = request.files['file']
        if md_file and allowed_file(md_file.filename):
            try:
                fname = secure_filename(md_file.filename)
                ext = fname.rsplit('.', 1)[1]
                unix_time = int(time.time())
                new_filename = str(unix_time) + '.' + ext
                filepath = os.path.join(bpdir, 'static/blog/read_mdfile/',
                                        new_filename)
                md_file.save(filepath)
            except Exception as e:
                current_app.logger.info(e)
            with open(filepath, 'r') as f:
                file_context = f.read()

                # if file_context[:3] == codecs.BOM_UTF8:
                #     data = file_context[3:]
                #     print data.decode("utf-8")
                article_form = ArticleForm()

                article_form.tags.choices = [
                    (tag, tag.name) for tag in Tag.query.order_by('name')
                ]
                article_form.category.choices = [
                    (category, category.name)
                    for category in Category.query.order_by('name')
                ]
                article_form.picture.choices = [
                    (picture, picture.name)
                    for picture in Picture.query.order_by('name').filter_by(
                        state=True)
                ]
                article_form.text.default = file_context.decode('utf-8')
                article_form.process()
                return self.render('_create_article.html',
                                   article_form=article_form)
        else:
            return u"没有获得文件或文件类型错误"

    def __init__(self, session, **kwargs):
        super(ArticleView, self).__init__(Article, session, **kwargs)
Example #29
0
class ParticipantSetAdminView(SetViewMixin, BaseAdminView):
    column_list = ('name', 'location_set', 'participants')
    column_labels = {
        'name': _('Name'),
        'location_set': _('Location Set'),
        'participants': _('Participants'),
        'gender_hidden': _('Hide Gender'),
        'role_hidden': _('Hide Role'),
        'partner_hidden': _('Hide Organization'),
    }
    column_descriptions = {
        'gender_hidden':
        _('If enabled, will hide the gender in the participant list.'),  # noqa
        'role_hidden':
        _('If enabled, will hide the role in the participant list.'),  # noqa
        'partner_hidden':
        _('If enabled, will hide the organization in the participant list.'
          ),  # noqa
    }
    form_columns = ('name', 'location_set', 'gender_hidden', 'role_hidden',
                    'partner_hidden')
    column_formatters = {'participants': macro('participants_list')}
    inline_models = (ExtraDataInlineFormAdmin(models.ParticipantDataField), )
    inline_model_form_converter = ParticipantExtraDataModelConverter

    def create_form(self, obj=None):
        deployment = g.deployment
        form = super().create_form(obj)
        form.location_set.choices = models.LocationSet.query.filter_by(
            deployment=deployment).with_entities(
                models.LocationSet.id, models.LocationSet.name).all()

        return form

    def edit_form(self, obj=None):
        deployment = g.deployment
        form = super().edit_form(obj)
        form.location_set.choices = models.LocationSet.query.filter_by(
            deployment=deployment).with_entities(
                models.LocationSet.id, models.LocationSet.name).all()

        return form

    def after_model_change(self, form, model, is_created):
        _role = models.ParticipantRole.query.filter(
            models.ParticipantRole.name == '$FC',
            models.ParticipantRole.participant_set == model).first()
        if not _role:
            _role = models.ParticipantRole.create(name='$FC',
                                                  participant_set=model)

    @expose('/participants/<int:participant_set_id>', methods=['GET', 'POST'])
    def participants_list(self, participant_set_id):
        return participant_list(participant_set_id, self)

    @expose('/participants/<int:participant_set_id>/import', methods=['POST'])
    def participants_import(self, participant_set_id):
        return participant_list_import(participant_set_id)

    @expose('/participants/<int:participant_set_id>/headers/<int:upload_id>',
            methods=['GET', 'POST'])
    def participants_headers(self, participant_set_id, upload_id):
        return participant_headers(upload_id, participant_set_id, self)

    @expose('/participant/<int:id>', methods=['GET', 'POST'])
    def participant_edit(self, id):
        return participant_edit(id, self)
Example #30
0
class NodeView(ModelView):
    column_list = [
        'cluster',
        'fqdn',
        'ip',
        'maintenance_mode',
        'credentials',
        'config',
    ]
    list_template = 'admin/node_list.html'
    details_template = 'admin/node_details.html'
    form_excluded_columns = [
        'credentials',
        'target_config_version',
        'active_config_version',
        'provisions',
        'disks',
    ]
    column_formatters = {
        'credentials': macro('render_credentials'),
        'config': macro('render_config'),
    }
    column_labels = {
        'ip': "Public IP",
        'fqdn': "Fully Qualified Domain Name",
        'maintenance_mode': "Maintenance mode",
        'debug_boot': "Debug boot",
        'coreos_autologin': "******",
        'linux_consoles': "Linux console devices",
        'disable_ipv6': "Disable IPv6 in Linux kernel",
        'is_etcd_server': "etcd server",
        'is_k8s_schedulable': "Kubernetes schedulable",
        'is_k8s_master': "Kubernetes master",
        'mountpoints': 'Additional mountpoints',
        'addresses': 'Additional IP addresses',
    }
    column_descriptions = {
        'maintenance_mode': "If this is enabled, node will be booted in minimal CoreOS environment without "
                            "touching root partition.",
        'debug_boot': "Forward all system journal messages to kmsg for troubleshooting.",
        'coreos_autologin': "******"
                            "for debugging. Don't enable in production.",
        'linux_consoles': "Passed to kernel as `console` arguments. (Separate by comma.)",
        'disable_ipv6': "Passed to kernel as `ipv6.disable=1` argument.",
        'is_etcd_server': "Run etcd server on this node and connect other nodes to it.",
        'is_k8s_schedulable': "Run kubelet on this node and register it as schedulable.",
        'is_k8s_master': "Run kubelet on this node and add persistent kube-apiserver, kube-controller-manager, "
                         "kube-scheduler pods to it.",
    }
    inline_models = [
        (models.Mountpoint, {
            'column_descriptions': {
                'what': 'Device to mount.',
                'where': 'Mount path.',
                'wanted_by': 'WantedBy systemd unit.',
                'is_persistent': 'Use this partition to store critical data that should survive reboots.',
            }
        }),
        (models.Address, {
            'column_descriptions': {
                'interface': 'Network interface.',
                'ip': 'IP address.',
            }
        }),
    ]
    form_rules = [
        rules.Field('cluster'),
        rules.Field('ip'),
        rules.Field('fqdn'),
        rules.FieldSet([
            'maintenance_mode',
            'debug_boot',
            'coreos_autologin',
            'linux_consoles',
            'disable_ipv6',
            'mountpoints',
            'addresses',
            'additional_kernel_cmdline',
        ], 'Boot'),
        rules.FieldSet([
            'is_etcd_server',
            'is_k8s_schedulable',
            'is_k8s_master',
        ], 'Components'),
    ]

    # without this, Node is saved to database before on_model_change() gets called
    # and this happens only when there is inline_models
    def create_model(self, *args, **kwargs):
        with self.session.no_autoflush:
            return super().create_model(*args, **kwargs)

    def _issue_creds(self, model):
        with self.session.no_autoflush:
            ca_creds = model.cluster.ca_credentials
        creds = models.CredentialsData()

        creds.cert, creds.key = pki.issue_certificate('system:node:' + model.fqdn,
                                                      ca_cert=ca_creds.cert,
                                                      ca_key=ca_creds.key,
                                                      organizations=['system:nodes'],
                                                      san_dns=model.certificate_alternative_dns_names,
                                                      san_ips=model.certificate_alternative_ips,
                                                      certify_days=10000,
                                                      is_web_server=True,
                                                      is_web_client=True)
        self.session.add(creds)
        model.credentials = creds

    def on_model_change(self, form, model, is_created):
        if is_created:
            self._issue_creds(model)
        else:
            model.target_config_version += 1

    def on_model_delete(self, model):
        model.mountpoints.delete()
        model.addresses.delete()

    def after_model_delete(self, model):
        models.CredentialsData.query.filter_by(id=model.credentials_id).delete()

    @expose('/reissue-credentials', methods=['POST'])
    def reissue_creds_view(self):
        model = self.get_one(request.args.get('id'))
        model.target_config_version += 1
        self._issue_creds(model)
        self.session.add(model)
        self.session.commit()
        return_url = get_redirect_target() or self.get_url('.index_view')
        flash('The credentials successfully reissued', 'success')
        return redirect(return_url)

    @expose('/target-ignition.json')
    def target_ignition_config_view(self):
        node = self.get_one(request.args.get('id'))
        response = config_renderer.ignition.render(node, indent=True)
        return Response(response, mimetype='application/json')

    @expose('/target.ipxe')
    def target_ipxe_config_view(self):
        node = self.get_one(request.args.get('id'))
        response = config_renderer.ipxe.render(node, request.url_root)
        return Response(response, mimetype='text/plain')
Example #31
0
class AircraftInformationView(MongoCustomView):
    "飞行器的通用视图"

    create_template = 'aircraft/create.html'
    details_modal_template = 'modal/details.html'
    edit_modal_template = 'modal/edit.html'
    create_modal_template = 'modal/create.html'

    # 为了使用datepicker相关插件
    extra_js = [
        '/static/js/list_light.js',
        '/static/js/bootstrap-datetimepicker.min.js',
        '/static/js/datetimepicker.zh-cn.js',
        '/static/js/jquery.magnific-popup.min.js',
        '/static/js/custom_action.js',
        '/static/js/jquery.json-2.2.js',
        '/static/js/datetostr.js',
        # 利用率的弹出框
        '/static/js/jquery.uniform.min.js',
        '/static/js/jquery.slimscroll.min.js',
        '/static/js/jquery.mockjax.js',
        '/static/js/bootstrap-editable.js',
        # select插件
        '/static/js/jquery-migrate.min.js',
        '/static/js/jquery.blockUI.min.js',
        '/static/js/bootstrap-selectsplitter.min.js',
        '/static/js/components-form-tools2.js',
        '/static/js/bluebird.js',
    ]

    extra_css = [
        '/static/css/aircraft.css',
        # '/static/css/bootstrap-switch.min.css',
        '/static/css/bootstrap-editable.css',
        # '/static/css/fonts.css',
        '/static/css/datepicker.css',
        '/static/css/bootstrap-datetimepicker.min.css',
    ]

    column_labels = column_labels

    support_popup = True

    column_list = (
        'id',
        'planeType',
        'totalHours',
        'totalTimes',
        'boundedMxp',
    )

    details_modal = True
    create_modal = True
    edit_modal = True

    form = AircraftInformationForm

    one_line_columns = ['imageUrl', 'etag']

    column_filters = [
        FilterEqual(column='aircraftId', name='aircraftId'),
        FilterEqual(column='aircraftType', name='aircraftType'),
    ]

    def basic_operation(view, ctx, model, name):
        # 操作按钮
        html = [
            '<div class="clearfix">'
            '<div class="btn-group btn-group-xs btn-group-solid">'
        ]
        for op in _buttons_map:
            html.append(_buttons_map[op].render_ctx(ctx,
                                                    view.get_pk_value(model),
                                                    model))

        html.append('</div></div>')
        return Markup(''.join(html))

    def render_aircraft_id(view, ctx, model, name):
        # 由于分为多个页面,如果存在sub指示为非机队列表首页
        sub = request.args.get('sub', None)

        # 如果用户无法查看飞机详情,只显示对应的名称即可
        if view.can_view_details and sub is None:
            return Markup(
                '<a class="btn grey-steel btn-xs green-stripe" href="%s"><i class="fa fa-plane"></i>%s</a>'
                % (view.get_url('.aircraft_details_view',
                                id=str(model['_id']),
                                sub='basic'), model['id']))

        return Markup('<span class="label label-info">%s</span>' %
                      (model['id'], ))

    _column_formatters = {
        'id': render_aircraft_id,
        'planeType': aircrafttype_formatter,
        'operation': basic_operation,
        'departureTime': macro('timetostr'),
        'landingTime': macro('timetostr'),
        'importedDate': macro('timetostr'),
        'manufactureDate': macro('timetodate'),
        'acnDeadline': macro('timetodate'),
        'slnDeadline': macro('timetodate'),
        'nrnDeadline': macro('timetodate'),
        'totalHours': hour_formater,
    }

    # 绑定状态缓存,无需频繁查询
    # WUJG: 这里缓存还是去掉吧,正常情况下,原来担心的API访问速度慢的问题一定是
    # 部署导致的
    # @cache.memoize(timeout=3600*24)
    def get_bindable_status(self, model):
        resp = self._api_proxy.get('/v1/mxp-binding/status?id=%s' %
                                   (model['id'], ))
        bounded_status = []
        if resp.status_code == 200:
            bounded_status = resp.json()
            for item in bounded_status:
                if 'status' in item and item['status']:
                    return bounded_status, True
        return bounded_status, False

    @expose('/bind-mxp', methods=['POST'])
    def bind_mxp(self):
        mxp_id = request.args.get('id', '')
        plane_id = request.args.get('plane', '')
        return_url = request.args.get('return_url') or get_redirect_target(
        ) or self.get_url('.index_view')
        # 默认为解除绑定
        is_bind = request.args.get('bind', '')

        dest_url = '/v1/mxp-binding/unbind'
        if is_bind:
            dest_url = '/v1/mxp-binding/bind'

        if not mxp_id or not plane_id:
            return redirect(return_url)

        resp = self._api_proxy.create(
            {
                'force': True,
                'mxpId': mxp_id,
                'planeId': plane_id,
            }, dest_url)

        if resp.status_code != 200:
            return abort(503)

        cache.delete_memoized(self.get_bindable_status)
        return redirect(return_url)

    @expose('/update-util/', methods=['POST'])
    def update_util(self):

        return_url = get_redirect_target() or self.get_url('.index_view')
        # 正常应该跳转到对应飞机的详情页
        id = request.args.get('id', '')
        if request.form.get('name') == 'hours':
            times = None
            hours = request.form.get('value', None)
            if hours is not None:
                hours = float(hours)
        elif request.form.get('name') == 'times':
            hours = None
            times = request.form.get('value', None)
            if times is not None:
                times = int(float(times))

        if id:
            return_url = self.get_url('.aircraft_details_view',
                                      id=id,
                                      sub='basic')
            try:
                self._api_proxy.create(
                    {
                        'id': id,
                        'hours': hours,
                        'times': times,
                    }, '/v1/utilization/')
            except Exception as ex:
                # TODO: 客户端应该根据返回的JSON数据确定是否保存成功
                return jsonify(status='400', message=unicode(ex))
        # TODO: 根据前端所需要的JSON内容进行返回
        return jsonify(status='200', msg='ok', times=times, hours=hours)

    @expose('/aircraft-details/')
    def aircraft_details_view(self):
        sub = request.args.get('sub')
        return_url = get_redirect_target() or self.get_url('.index_view')

        if not self.can_view_details:
            return redirect(return_url)

        id = get_mdict_item_or_list(request.args, 'id')
        if id is None:
            return redirect(return_url)
        model = self.get_aircraft(id)

        if model is None:
            flash(gettext('Record does not exist.'), 'error')
            return redirect(return_url)

        # 获得绑定状态
        bind_status, bounded = self.get_bindable_status(model)

        # 获得当前飞机实例支持的绑定状态
        # 无需显示的绑定状态会在构建列表时剔除
        boundable_categories = [{
            'id': item[0],
            'name': item[1]
        } for item in support_due_list[model['planeType']] if item[2]]

        self._template_args.update({
            'sub':
            sub,
            'bind_status':
            bind_status,
            'bounded':
            bounded,
            'support_bounded_categories':
            boundable_categories,
        })

        find_method = self._delegate_to_sub('find_method') or self.get_list

        extra_args = self._delegate_to_sub('extra') or {}

        if callable(extra_args):
            extra_args = extra_args(model=model)

        self._template_args.update(**extra_args)
        self._template_args.update({
            'sub': sub,
            'time_formatter': datetime.time,
        })
        return self.render_aircraft_or_list(id, model, sub, return_url,
                                            find_method, **extra_args)

    def get_aircraft(self, id):
        return self._mongo[self.view_list['basic']['coll_name']].find_one(
            {'_id': self._get_valid_id(id)}, {
                'predictTime': False,
                'boundedItems': False
            })

    @property
    def view_list(self):
        # 每一个具体的子方案在界面视图处理时应该提供下面的信息
        # 1. 子方案的名称
        # 2. 子方案对应的主键
        # 3. 子方案的集合名称(mongo)
        # 4. 一些与flask-admin相关的视图配置信息
        return {
            'basic': dict(**Basic()),
            'flightlog': dict(**FlightLog()),
            'due_list': dict(**DuelistLogic(self)()),
            'maintenancelog': dict(**MaintenanceLog()),
        }

    def render_aircraft_or_list(self, id, model, sub, return_url, find_method,
                                **kwargs):
        """
        :param model: 通常就是飞机实例
        :param sub: 当前的子视图
        :param find_method: 根据需要可能替换为子文档的查询
        """
        template = self._delegate_to_sub('template')

        for x in model:
            if x != 'imageUrl':
                if model[x] is None:
                    model[x] = '--'
        ret = get_aircraft_afterrepaired_flytime_enginetime(model['id'])
        total_ellapse_hour = '00:00'
        total_engine_time = '00:00'
        total_propeller_time = '00:00'
        if ret:
            total_ellapse_hour = ret.flyTime
            total_engine_time = ret.engineTime
            total_propeller_time = ret.propellerTime
        if not sub or sub == 'basic':
            return self.render(template,
                               model=model,
                               total_ellapse_hour=total_ellapse_hour,
                               total_engine_time=total_engine_time,
                               total_propeller_time=total_propeller_time,
                               id=id,
                               return_url=return_url)

        # WUJG: 下面的实现,类似于显示列表页,绝大多数重复使用了原Index view的实现

        if self.can_delete:
            delete_form = self.delete_form()
        else:
            delete_form = None

        # Grab parameters from URL
        view_args = self._get_list_extra_args()
        # Map column index to column name
        sort_column = self._get_column_by_idx(view_args.sort)
        if sort_column is not None:
            sort_column = sort_column[0]

        # Get page size
        page_size = view_args.page_size or self.page_size

        # Get count and data
        count, data = find_method(view_args.page,
                                  sort_column,
                                  view_args.sort_desc,
                                  view_args.search,
                                  view_args.filters,
                                  page_size=page_size)

        list_forms = {}
        if self.column_editable_list:
            for row in data:
                list_forms[self.get_pk_value(row)] = self.list_form(obj=row)

        # Calculate number of pages
        if count is not None and page_size:
            num_pages = int(ceil(count / float(page_size)))
        elif not page_size:
            num_pages = 0  # hide pager for unlimited page_size
        else:
            num_pages = None  # use simple pager

        # Various URL generation helpers
        def pager_url(p):
            # Do not add page number if it is first page
            if p == 0:
                p = None

            return self._get_list_url(view_args.clone(page=p))

        def sort_url(column, invert=False, desc=None):
            if not desc and invert and not view_args.sort_desc:
                desc = 1

            return self._get_list_url(
                view_args.clone(sort=column, sort_desc=desc))

        def page_size_url(s):
            if not s:
                s = self.page_size

            return self._get_list_url(view_args.clone(page_size=s))

        # Actions
        actions, actions_confirmation = self.get_actions_list()
        if actions:
            action_form = self.action_form()
        else:
            action_form = None

        clear_search_url = self._get_list_url(
            view_args.clone(page=0,
                            sort=view_args.sort,
                            sort_desc=view_args.sort_desc,
                            search=None,
                            filters=None))

        return self.render(
            template,
            model=model,
            data=data,
            list_forms=list_forms,
            delete_form=delete_form,
            action_form=action_form,

            # List
            list_columns=self.get_column_names(
                only_columns=self.scaffold_list_columns(),
                excluded_columns=self.column_exclude_list,
            ),
            sortable_columns=self._sortable_columns,
            editable_columns=self.column_editable_list,
            list_row_actions=self.get_list_row_actions(),

            # Pagination
            count=count,
            pager_url=pager_url,
            num_pages=num_pages,
            can_set_page_size=self.can_set_page_size,
            page_size_url=page_size_url,
            page=view_args.page,
            page_size=page_size,
            default_page_size=self.page_size,

            # Sorting
            sort_column=view_args.sort,
            sort_desc=view_args.sort_desc,
            sort_url=sort_url,

            # Search
            search_supported=self._search_supported,
            clear_search_url=clear_search_url,
            search=view_args.search,

            # Filters
            filters=self._filters,
            filter_groups=self._get_filter_groups(),
            active_filters=view_args.filters,
            filter_args=self._get_filters(view_args.filters),

            # Actions
            actions=actions,
            actions_confirmation=actions_confirmation,

            # Misc
            enumerate=enumerate,
            get_pk_value=self.get_pk_value,
            get_value=self.get_list_value,
            return_url=self._get_list_url(view_args),
            **kwargs)

    @property
    def default_subordinate_view(self):
        return 'basic'

    @property
    def _details_columns(self):
        return self.get_column_names(
            only_columns=self._delegate_to_sub('details_columns'),
            excluded_columns=self.column_details_exclude_list)

    def get_real_url(self, url, model):
        requestPath = request.path.split('/')
        if 'delete' in requestPath:
            url = url % {'plane_type': ''}
        else:
            sub = request.args.get('sub', self.default_subordinate_view)

            plane_type_dict = dict(basic='planeType', flightlog='aircraftType')

            if sub not in plane_type_dict:
                raise ValueError('Not correct subordinate view type.')
            url = url % {'plane_type': model[plane_type_dict[sub]] + '/'}
        return url

    @expose('/new/', methods=('GET', 'POST'))
    def create_view(self):
        # if request.method == 'POST':
        #     engineTime = request.form['engineTime']
        #     if engineTime is not None:

        # 在该视图下,当前支持飞机实例和飞行日志实例的创建
        # 这里的主要逻辑是判断使用哪个窗体来创建对应的实例

        # 注意, super使用的就是MongoCustomView而非AircraftInformationView
        sub = request.args.get('sub', self.default_subordinate_view)
        aircraft_type = request.args.get('type', '').lower()
        if not aircraft_type:
            self._create_form_class = self._delegate_to_sub('form')
            return super(MongoCustomView, self).create_view()

        allowed_forms = self._delegate_to_sub('form')
        if aircraft_type not in allowed_forms:
            return abort(400)

        self._create_form_class = allowed_forms.get(aircraft_type)
        self._template_args.update({
            'sub': sub,
            'type': aircraft_type,
        })

        return super(MongoCustomView, self).create_view()

    @expose('/edit/', methods=('GET', 'POST'))
    def edit_view(self):
        # 类似于create_view的逻辑,但要复杂一些,因为编辑时,即使飞机实例本身
        # 也会存在机型信息
        return_url = request.url or self.get_url('.index_view')
        aircraft_type = request.args.get('type', '').lower()

        if not aircraft_type:
            return redirect(return_url)

        sub = request.args.get('sub', self.default_subordinate_view)
        self._template_args.update({
            'sub': sub,
            'type': aircraft_type,
        })
        if sub == self.default_subordinate_view:
            self._edit_form_class = self._delegate_to_sub('form')
            return super(MongoCustomView, self).edit_view()

        if sub == 'flightlog':
            allowed_forms = self._delegate_to_sub('form')
            if aircraft_type not in allowed_forms:
                return abort(400)

            self._edit_form_class = allowed_forms.get(aircraft_type)

        return super(MongoCustomView, self).edit_view()

    def get_save_return_url(self, model, is_created):
        sub = request.args.get('sub')
        type = request.args.get('type')

        if sub == 'basic':
            id = request.args.get('id')
            if 'edit' in request.url:
                return self.get_url('.aircraft_details_view',
                                    id=id,
                                    type=type,
                                    sub=sub)
            elif 'delete' in request.url:
                return self.get_url('.aircraft_details_view')
        elif sub == 'flightlog':
            id = request.args.get('basicId')
            return self.get_url('.aircraft_details_view',
                                flt_0=model['aircraftId'],
                                id=id,
                                flt_1=type,
                                sub=sub)
        elif sub is None:
            return self.get_url('.aircraft_details_view')

    def get_one(self, id):
        # WUJG: 重写该方法实现,避免包含不必要的数据
        return self.coll.find_one({'_id': self._get_valid_id(id)},
                                  projection={
                                      'predictTime': False,
                                      'boundedItems': False
                                  })

    def get_list(self,
                 page,
                 sort_column,
                 sort_desc,
                 search,
                 filters,
                 execute=True,
                 page_size=None):
        # WUJG: 这里的实现,几乎与flask-admin的框架实现一样,唯一不同的就是查询时做了
        # 投影的设置,避免数据过大,导致加载很慢的问题
        query = {}

        # Filters
        if self._filters:
            data = []

            for flt, flt_name, value in filters:
                f = self._filters[flt]
                data = f.apply(data, value)

            if data:
                if len(data) == 1:
                    query = data[0]
                else:
                    query['$and'] = data

        # Search
        if self._search_supported and search:
            query = self._search(query, search)

        # Get count
        count = self.coll.find(query, projection={
            'id': True,
        }).count() if not self.simple_list_pager else None

        # Sorting
        sort_by = None

        if sort_column:
            sort_by = [(sort_column,
                        pymongo.DESCENDING if sort_desc else pymongo.ASCENDING)
                       ]
        else:
            order = self._get_default_order()

            if order:
                sort_by = [
                    (order[0],
                     pymongo.DESCENDING if order[1] else pymongo.ASCENDING)
                ]

        # Pagination
        if page_size is None:
            page_size = self.page_size

        skip = 0

        if page and page_size:
            skip = page * page_size

        results = self.coll.find(query,
                                 sort=sort_by,
                                 skip=skip,
                                 limit=page_size,
                                 projection={
                                     'boundedItems': False,
                                     'predictTime': False,
                                 })

        if execute:
            results = list(results)

        return count, results

    @property
    def column_formatters(self):
        formatters = self._delegate_to_sub('column_formatters')
        if formatters is not None:
            return formatters

        return self._column_formatters

    @column_formatters.setter
    def column_formatters(self, val):
        pass

    @property
    def can_edit_status(self):
        perm = ActionNeedPermission(self._action_name, EditBoundStatus)
        return perm.can()

    @property
    def can_remove_status(self):
        perm = ActionNeedPermission(self._action_name, RemoveBoundStatus)
        return perm.can()

    @expose('/mx-bounded-status/', methods=['POST', 'GET'])
    def get_bounded_status(self):
        # TODO: 如果是POST则需要执行更新操作
        if request.method == 'GET':
            resp = self._api_proxy.get(
                '/v1/mxp-binding/details?id=%s&mxtype=%s' %
                (request.args.get('id'), request.args.get('mxtype')))
        else:
            model = request.get_json(force=True)
            resp = self._api_proxy.update(model, None,
                                          '/v1/mxp-binding/update?batch=1')

        if resp.status_code == 200:
            return jsonify(code=200, data=resp.json())

        return jsonify(code=resp.status_code, message=resp.json()['message'])

    @expose('/mx-duplicate/', methods=['GET'])
    def get_duplicate(self):
        resp = self._api_proxy.create(
            {
                'mxId': request.args.get('mxId'),
                'mxType': request.args.get('mxType'),
                'planeId': request.args.get('planeId'),
            }, '/v1/mxp-binding/duplicate')

        if resp.status_code == 200:
            return jsonify(code=200, data=resp.json())

        return jsonify(code=resp.status_code, message=resp.json()['message'])

    @expose('/get-subsidiary-work/')
    def get_subsidiary_work(self):

        # if not self.can_routine_work:
        #     return jsonify(code=403, message='You are not allowed to do so.')

        plane_id = request.args.get('plane', '')
        mx_id = request.args.get('mxid', '')

        if not plane_id or not mx_id:
            return jsonify(code=400,
                           message='You should provide plane and mx id.')

        ret = get_subsidiary_materials_related_available_work(plane_id, mx_id)
        if ret is None:
            return jsonify(code=404, message='Nothing of the related work.')

        return jsonify(code=200, **ret)

    # 下面内容为打印相关实现
    @expose('/export/<export_type>/')
    def export(self, export_type):
        mx_type = request.args.get('mxtype', 'scheduled')
        self._export_columns = self.get_export_columns(mx_type)
        return_url = get_redirect_target() or self.get_url('.index_view')

        if export_type == 'csv':
            return self._export_csv(return_url)
        else:
            return self._export_tablib(export_type, return_url)

    def get_export_columns(self, mx_type=None):
        # 定检打印字段

        col = [
            ('mxId', '维修方案编号'),
            ('description', '描述信息'),
            ('leftHour', '剩余小时'),
            ('leftTimes', '剩余次数'),
            ('leftDay', '剩余天'),
            ('leftEngineTime', '剩余发动机时间'),
            # ('best', '预计检查日期'),
            # ('warningLevel', '预警等级'),
            # ('error', '错误提示'),
            # ('completeDate', '上次完成时间')
        ]
        if mx_type != 'scheduled':
            # 时空/时寿
            col = [
                # ('mxId', '维修方案编号'),
                # ('description', '描述信息'),
                ('pn', '件号'),
                ('serialNumber', '序号'),
                ('name', '名称'),
                ('completeDate', '装机日期'),
                ('leftHour', '剩余小时'),
                ('leftTimes', '剩余次数'),
                ('leftDay', '剩余天'),
                ('leftEngineTime', '剩余发动机时间'),
                ('best', '预计检查日期'),
                # ('warningLevel', '预警等级'),
                # ('error', '错误提示'),
            ]

        return col

    def get_export_value(self, model, name):

        if name in model:
            return model[name]
        return ''

    def _export_data(self):
        # 导出数据

        export_page = request.args.get('export_page', 0, type=int)
        export_size = request.args.get('export_size', 0, type=int)
        search = request.args.get('search', '')
        air_id = request.args.get('id', '')
        mx_type = request.args.get('mxtype', 'scheduled')
        view_args = self._get_list_extra_args()
        due_list = DuelistLogic(self)

        count, export_data = due_list.get_list(export_page,
                                               None,
                                               view_args.sort_desc,
                                               search,
                                               view_args.filters,
                                               page_size=export_size)

        export_data = self.get_export_data(export_data, air_id, mx_type)

        return count, export_data

    def get_bangding(self):
        # 绑定相关信息
        com = {}
        air = self.coll.find({"_id": bson.ObjectId(request.args.get('id'))})
        if not air.count():
            return com
        bounds = air[0]['boundedItems']
        for item in bounds:
            if item['refId'].collection not in \
                    ['time_control_unit_y5b', 'life_control_unit_y5b']:
                continue
            com[item['boundedId']] = [
                self.unix_to_string(item['completeDate']), item['serialNumber']
            ]
        return com

    def get_export_data(self, data, id, mx_type):
        mxp = self.get_mx_name(mx_type)
        export_datas = []

        for item in data:

            export_data = {
                'mxId':
                mxp[item['predictTime']['mxRefId'].id][0],
                'description':
                mxp[item['predictTime']['mxRefId'].id][1],
                'warningLevel':
                item['level'],
                'error':
                item['predictTime']['err'],
                'best':
                self.unix_to_string(item['predictTime']['earliest']),
                'leftHour':
                item['intervaltype'][0]
                if 0 in item['intervaltype'].keys() else '',
                'leftTimes':
                item['intervaltype'][1]
                if 1 in item['intervaltype'].keys() else '',
                'leftDay':
                item['intervaltype'][2]
                if 2 in item['intervaltype'].keys() else '',
                'leftEngineTime':
                item['intervaltype'][9]
                if 9 in item['intervaltype'].keys() else '',
            }
            if export_data['leftHour']:
                export_data['leftHour'] = convert_float_to_hh_mm(
                    export_data['leftHour'])
            if export_data['leftEngineTime']:
                export_data['leftEngineTime'] = convert_float_to_hh_mm(
                    export_data['leftEngineTime'])
            if mx_type != 'scheduled':
                com = self.get_bangding()
                export_data["completeDate"] = com[item['predictTime']
                                                  ['itemId']][0]
                export_data["serialNumber"] = com[item['predictTime']
                                                  ['itemId']][1]
                export_data['pn'] = mxp[item['predictTime']['mxRefId'].id][2]
                export_data['name'] = mxp[item['predictTime']['mxRefId'].id][3]

            export_datas.append(export_data)
        return export_datas

    def unix_to_string(self, data):
        # unix 时间戳格式化
        if not data:
            return ''
        data = datetime.datetime.fromtimestamp(data)
        return data.strftime("%y-%m-%d")

    def get_mx_name(self, mx_type):
        # 获取维修方案相关信息
        if mx_type != 'scheduled':
            coll = 'time_control_unit_y5b' if mx_type == 'timecontrol' else 'life_control_unit_y5b'
            datas = list(self._mongo[coll].find({}, {
                "id": 1,
                "description": 1,
                "pn": 1,
                "name": 1
            }))
            data = {}
            for item in datas:
                data[item['_id']] = [
                    item['id'], item['description'], item['pn'], item['name']
                ]
            return data
        datas = list(self._mongo['scheduled_mx_check_y5b'].find(
            {}, {
                "id": 1,
                "description": 1
            }))
        data = {}
        for item in datas:
            data[item['_id']] = [item['id'], item['description']]
        return data

    # 下面的内容为通用的飞机实例与飞行日志实例同处于相同页面的实现
    # 如果日志需要分开实现,无需提供下面的操作
    @property
    def can_create_flightlog(self):
        return ActionNeedPermission('flightlog', Create).can()

    @property
    def can_edit_flightlog(self):
        return ActionNeedPermission('flightlog', Edit).can()

    @property
    def can_delete_flightlog(self):
        return ActionNeedPermission('flightlog', Delete).can()

    @property
    def can_view_details_flightlog(self):
        return ActionNeedPermission('flightlog', View).can()

    @property
    def can_routine_work(self):
        perm = ActionNeedPermission('routinework', 'create')
        return perm.can()
Example #32
0
class EventAdminView(BaseAdminView):
    column_filters = ('name', 'start', 'end')
    column_list = ('name', 'start', 'end', 'location_set', 'participant_set',
                   'archive')
    column_labels = {
        'name': _('Name'),
        'start': _('Start'),
        'end': _('End'),
        'location_set': _('Location Set'),
        'participant_set': _('Participant Set'),
        'archive': _('Archive')
    }
    column_descriptions = {
        'start': _('What time the event is to start in the local time.'),
        'end': _('What time the event is to end in the local time.'),
        'forms': _('What forms should be enabled for this event.')
    }
    form_columns = ('name', 'start', 'end', 'forms', 'participant_set')
    form_rules = [
        rules.FieldSet(('name', 'start', 'end', 'forms', 'participant_set'),
                       _('Event'))
    ]
    column_formatters = {
        'archive': macro('event_archive'),
    }

    @expose('/download/<int:event_id>')
    def download(self, event_id):
        event = services.events.find(id=event_id).first_or_404()
        eas = EventArchiveSerializer()

        fp = BytesIO()
        with ZipFile(fp, 'w', ZIP_DEFLATED) as zf:
            eas.serialize(event, zf)

        fp.seek(0)
        fname = slugify(
            f'event archive {event.name.lower()} {datetime.utcnow().strftime("%Y %m %d %H%M%S")}'
        )  # noqa

        return send_file(fp,
                         attachment_filename=f'{fname}.zip',
                         as_attachment=True)

    def get_one(self, pk):
        event = super(EventAdminView, self).get_one(pk)

        # convert start and end dates to app time zone
        event.start = event.start.astimezone(app_time_zone)
        event.end = event.end.astimezone(app_time_zone)
        return event

    @contextfunction
    def get_list_value(self, context, model, name):
        if name in ['start', 'end']:
            attribute = getattr(model, name, None)
            if attribute:
                return attribute.astimezone(app_time_zone).strftime(
                    DATETIME_FORMAT_SPEC)

            return attribute

        return super(EventAdminView, self).get_list_value(context, model, name)

    def get_query(self):
        '''Returns the queryset of the objects to list.'''
        user = current_user._get_current_object()
        return models.Event.query.filter_by(deployment_id=user.deployment.id)

    def on_model_change(self, form, model, is_created):
        # if we're creating a new event, make sure to set the
        # deployment, since it won't appear in the form
        if is_created:
            model.deployment = current_user.deployment

            # add role permissions for this event
            roles = models.Role.query.filter_by(
                deployment=model.deployment).all()
            model.roles = roles

        if form.participant_set.data:
            model.location_set = form.participant_set.data.location_set
        else:
            model.location_set = None

        # convert to the app time zone
        model.start = app_time_zone.localize(
            model.start).astimezone(utc_time_zone)
        model.end = app_time_zone.localize(model.end).astimezone(utc_time_zone)
Example #33
0
def test_export_csv():
    app, admin = setup()
    client = app.test_client()

    # test redirect when csv export is disabled
    view = MockModelView(Model, column_list=["col1", "col2"], endpoint="test")
    admin.add_view(view)

    rv = client.get("/admin/test/export/csv/")
    eq_(rv.status_code, 302)

    # basic test of csv export with a few records
    view_data = {1: Model(1, "col1_1", "col2_1"), 2: Model(2, "col1_2", "col2_2"), 3: Model(3, "col1_3", "col2_3")}

    view = MockModelView(Model, view_data, can_export=True, column_list=["col1", "col2"])
    admin.add_view(view)

    rv = client.get("/admin/model/export/csv/")
    data = rv.data.decode("utf-8")
    eq_(rv.mimetype, "text/csv")
    eq_(rv.status_code, 200)
    ok_("Col1,Col2\r\n" "col1_1,col2_1\r\n" "col1_2,col2_2\r\n" "col1_3,col2_3\r\n" == data)

    # test explicit use of column_export_list
    view = MockModelView(
        Model,
        view_data,
        can_export=True,
        column_list=["col1", "col2"],
        column_export_list=["id", "col1", "col2"],
        endpoint="exportinclusion",
    )
    admin.add_view(view)

    rv = client.get("/admin/exportinclusion/export/csv/")
    data = rv.data.decode("utf-8")
    eq_(rv.mimetype, "text/csv")
    eq_(rv.status_code, 200)
    ok_("Id,Col1,Col2\r\n" "1,col1_1,col2_1\r\n" "2,col1_2,col2_2\r\n" "3,col1_3,col2_3\r\n" == data)

    # test explicit use of column_export_exclude_list
    view = MockModelView(
        Model,
        view_data,
        can_export=True,
        column_list=["col1", "col2"],
        column_export_exclude_list=["col2"],
        endpoint="exportexclusion",
    )
    admin.add_view(view)

    rv = client.get("/admin/exportexclusion/export/csv/")
    data = rv.data.decode("utf-8")
    eq_(rv.mimetype, "text/csv")
    eq_(rv.status_code, 200)
    ok_("Col1\r\n" "col1_1\r\n" "col1_2\r\n" "col1_3\r\n" == data)

    # test utf8 characters in csv export
    view_data[4] = Model(1, u"\u2013ut8_1\u2013", u"\u2013utf8_2\u2013")
    view = MockModelView(Model, view_data, can_export=True, column_list=["col1", "col2"], endpoint="utf8")
    admin.add_view(view)

    rv = client.get("/admin/utf8/export/csv/")
    data = rv.data.decode("utf-8")
    eq_(rv.status_code, 200)
    ok_(u"\u2013ut8_1\u2013,\u2013utf8_2\u2013\r\n" in data)

    # test None type, integer type, column_labels, and column_formatters
    view_data = {1: Model(1, "col1_1", 1), 2: Model(2, "col1_2", 2), 3: Model(3, None, 3)}

    view = MockModelView(
        Model,
        view_data,
        can_export=True,
        column_list=["col1", "col2"],
        column_labels={"col1": "Str Field", "col2": "Int Field"},
        column_formatters=dict(col2=lambda v, c, m, p: m.col2 * 2),
        endpoint="types_and_formatters",
    )
    admin.add_view(view)

    rv = client.get("/admin/types_and_formatters/export/csv/")
    data = rv.data.decode("utf-8")
    eq_(rv.status_code, 200)
    ok_("Str Field,Int Field\r\n" "col1_1,2\r\n" "col1_2,4\r\n" ",6\r\n" == data)

    # test column_formatters_export and column_formatters_export
    type_formatters = {type(None): lambda view, value: "null"}

    view = MockModelView(
        Model,
        view_data,
        can_export=True,
        column_list=["col1", "col2"],
        column_formatters_export=dict(col2=lambda v, c, m, p: m.col2 * 3),
        column_formatters=dict(col2=lambda v, c, m, p: m.col2 * 2),  # overridden
        column_type_formatters_export=type_formatters,
        endpoint="export_types_and_formatters",
    )
    admin.add_view(view)

    rv = client.get("/admin/export_types_and_formatters/export/csv/")
    data = rv.data.decode("utf-8")
    eq_(rv.status_code, 200)
    ok_("Col1,Col2\r\n" "col1_1,3\r\n" "col1_2,6\r\n" "null,9\r\n" == data)

    # Macros are not implemented for csv export yet and will throw an error
    view = MockModelView(
        Model,
        can_export=True,
        column_list=["col1", "col2"],
        column_formatters=dict(col1=macro("render_macro")),
        endpoint="macro_exception",
    )
    admin.add_view(view)

    rv = client.get("/admin/macro_exception/export/csv/")
    data = rv.data.decode("utf-8")
    eq_(rv.status_code, 500)