2
0
Fork 0

fix: show installed python packages on appinfo page

This commit is contained in:
Lance Edgar 2024-08-20 21:26:38 -05:00
parent 3665d69e0c
commit d15ac46184
6 changed files with 116 additions and 4 deletions

View file

@ -46,12 +46,53 @@
</div> </div>
</nav> </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>
<%def name="modify_vue_vars()"> <%def name="modify_vue_vars()">
${parent.modify_vue_vars()} ${parent.modify_vue_vars()}
<script> <script>
ThisPageData.configFiles = ${json.dumps([dict(path=p, priority=i) for i, p in enumerate(config.get_prioritized_files(), 1)])|n} 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> </script>
</%def> </%def>

View file

@ -134,6 +134,9 @@
data: ${grid.vue_component}CurrentData, data: ${grid.vue_component}CurrentData,
loading: false, loading: false,
## nb. this tracks whether grid.fetchFirstData() happened
fetchedFirstData: false,
## sorting ## sorting
% if grid.sortable: % if grid.sortable:
sorters: ${json.dumps(grid.get_vue_active_sorters())|n}, sorters: ${json.dumps(grid.get_vue_active_sorters())|n},
@ -230,6 +233,17 @@
return params 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() { async fetchData() {
let params = new URLSearchParams(this.getBasicParams()) let params = new URLSearchParams(this.getBasicParams())

View file

@ -1208,8 +1208,10 @@ class MasterView(View):
self.set_labels(grid) self.set_labels(grid)
for key in self.get_model_key(): # TODO: i thought this was a good idea but if so it
grid.set_link(key) # 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): def grid_render_notes(self, record, key, value, maxlen=100):
""" """

View file

@ -24,6 +24,11 @@
Views for app settings Views for app settings
""" """
import json
import os
import sys
import subprocess
from collections import OrderedDict from collections import OrderedDict
from wuttjamaican.db.model import Setting from wuttjamaican.db.model import Setting
@ -47,13 +52,47 @@ class AppInfoView(MasterView):
model_name = 'AppInfo' model_name = 'AppInfo'
model_title_plural = "App Info" model_title_plural = "App Info"
route_prefix = 'appinfo' route_prefix = 'appinfo'
has_grid = False sort_on_backend = False
sort_defaults = 'name'
paginated = False
creatable = False creatable = False
viewable = False viewable = False
editable = False editable = False
deletable = False deletable = False
configurable = True 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): def configure_get_simple_settings(self):
""" """ """ """
return [ return [

View file

@ -424,7 +424,10 @@ class TestMasterView(WebTestCase):
url_prefix='/appinfo', url_prefix='/appinfo',
creatable=False): creatable=False):
view = master.MasterView(self.request) 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) self.assertIsInstance(response, Response)
# bad template name causes error # bad template name causes error

View file

@ -18,6 +18,19 @@ class TestAppInfoView(WebTestCase):
def make_view(self): def make_view(self):
return mod.AppInfoView(self.request) 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): def test_index(self):
# sanity/coverage check # sanity/coverage check
view = self.make_view() view = self.make_view()