fix: show installed python packages on appinfo page
This commit is contained in:
parent
3665d69e0c
commit
d15ac46184
|
@ -46,12 +46,53 @@
|
|||
</div>
|
||||
</nav>
|
||||
|
||||
<${b}-collapse class="panel"
|
||||
:open="false"
|
||||
@open="openInstalledPackages">
|
||||
|
||||
<template #trigger="props">
|
||||
<div class="panel-heading"
|
||||
style="cursor: pointer;"
|
||||
role="button">
|
||||
|
||||
## TODO: for some reason buefy will "reuse" the icon
|
||||
## element in such a way that its display does not
|
||||
## refresh. so to work around that, we use different
|
||||
## structure for the two icons, so buefy is forced to
|
||||
## re-draw
|
||||
|
||||
<b-icon v-if="props.open"
|
||||
pack="fas"
|
||||
icon="angle-down" />
|
||||
|
||||
<span v-if="!props.open">
|
||||
<b-icon pack="fas"
|
||||
icon="angle-right" />
|
||||
</span>
|
||||
|
||||
<strong>Installed Packages</strong>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="panel-block">
|
||||
<div style="width: 100%;">
|
||||
${grid.render_vue_tag(ref='packagesGrid')}
|
||||
</div>
|
||||
</div>
|
||||
</${b}-collapse>
|
||||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
ThisPageData.configFiles = ${json.dumps([dict(path=p, priority=i) for i, p in enumerate(config.get_prioritized_files(), 1)])|n}
|
||||
|
||||
ThisPage.methods.openInstalledPackages = function() {
|
||||
this.$refs.packagesGrid.fetchFirstData()
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
|
|
@ -134,6 +134,9 @@
|
|||
data: ${grid.vue_component}CurrentData,
|
||||
loading: false,
|
||||
|
||||
## nb. this tracks whether grid.fetchFirstData() happened
|
||||
fetchedFirstData: false,
|
||||
|
||||
## sorting
|
||||
% if grid.sortable:
|
||||
sorters: ${json.dumps(grid.get_vue_active_sorters())|n},
|
||||
|
@ -230,6 +233,17 @@
|
|||
return params
|
||||
},
|
||||
|
||||
## nb. this is meant to call for a grid which is hidden at
|
||||
## first, when it is first being shown to the user. and if
|
||||
## it was initialized with empty data set.
|
||||
async fetchFirstData() {
|
||||
if (this.fetchedFirstData) {
|
||||
return
|
||||
}
|
||||
await this.fetchData()
|
||||
this.fetchedFirstData = true
|
||||
},
|
||||
|
||||
async fetchData() {
|
||||
|
||||
let params = new URLSearchParams(this.getBasicParams())
|
||||
|
|
|
@ -1208,8 +1208,10 @@ class MasterView(View):
|
|||
|
||||
self.set_labels(grid)
|
||||
|
||||
for key in self.get_model_key():
|
||||
grid.set_link(key)
|
||||
# TODO: i thought this was a good idea but if so it
|
||||
# needs a try/catch in case of no model class
|
||||
# for key in self.get_model_key():
|
||||
# grid.set_link(key)
|
||||
|
||||
def grid_render_notes(self, record, key, value, maxlen=100):
|
||||
"""
|
||||
|
|
|
@ -24,6 +24,11 @@
|
|||
Views for app settings
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from wuttjamaican.db.model import Setting
|
||||
|
@ -47,13 +52,47 @@ class AppInfoView(MasterView):
|
|||
model_name = 'AppInfo'
|
||||
model_title_plural = "App Info"
|
||||
route_prefix = 'appinfo'
|
||||
has_grid = False
|
||||
sort_on_backend = False
|
||||
sort_defaults = 'name'
|
||||
paginated = False
|
||||
creatable = False
|
||||
viewable = False
|
||||
editable = False
|
||||
deletable = False
|
||||
configurable = True
|
||||
|
||||
grid_columns = [
|
||||
'name',
|
||||
'version',
|
||||
'editable_project_location',
|
||||
]
|
||||
|
||||
def get_grid_data(self, columns=None, session=None):
|
||||
""" """
|
||||
|
||||
# nb. init with empty data, only load it upon user request
|
||||
if not self.request.GET.get('partial'):
|
||||
return []
|
||||
|
||||
# TODO: pretty sure this is not cross-platform. probably some
|
||||
# sort of pip methods belong on the app handler? or it should
|
||||
# have a pip handler for all that?
|
||||
pip = os.path.join(sys.prefix, 'bin', 'pip')
|
||||
output = subprocess.check_output([pip, 'list', '--format=json'], text=True)
|
||||
data = json.loads(output.strip())
|
||||
|
||||
# must avoid null values for sort to work right
|
||||
for pkg in data:
|
||||
pkg.setdefault('editable_project_location', '')
|
||||
|
||||
return data
|
||||
|
||||
def configure_grid(self, g):
|
||||
""" """
|
||||
super().configure_grid(g)
|
||||
|
||||
g.sort_multiple = False
|
||||
|
||||
def configure_get_simple_settings(self):
|
||||
""" """
|
||||
return [
|
||||
|
|
|
@ -424,7 +424,10 @@ class TestMasterView(WebTestCase):
|
|||
url_prefix='/appinfo',
|
||||
creatable=False):
|
||||
view = master.MasterView(self.request)
|
||||
response = view.render_to_response('index', {})
|
||||
response = view.render_to_response('index', {
|
||||
# nb. grid is required for this template
|
||||
'grid': MagicMock(),
|
||||
})
|
||||
self.assertIsInstance(response, Response)
|
||||
|
||||
# bad template name causes error
|
||||
|
|
|
@ -18,6 +18,19 @@ class TestAppInfoView(WebTestCase):
|
|||
def make_view(self):
|
||||
return mod.AppInfoView(self.request)
|
||||
|
||||
def test_get_grid_data(self):
|
||||
view = self.make_view()
|
||||
|
||||
# empty data by default
|
||||
data = view.get_grid_data()
|
||||
self.assertEqual(data, [])
|
||||
|
||||
# 'partial' request returns data
|
||||
self.request.GET = {'partial': '1'}
|
||||
data = view.get_grid_data()
|
||||
self.assertIsInstance(data, list)
|
||||
self.assertTrue(data)
|
||||
|
||||
def test_index(self):
|
||||
# sanity/coverage check
|
||||
view = self.make_view()
|
||||
|
|
Loading…
Reference in a new issue