Table working

This commit is contained in:
Ben Arc 2021-04-29 22:38:57 +01:00
parent 38c2270abf
commit 245a819f19
6 changed files with 178 additions and 93 deletions

View File

@ -6,6 +6,7 @@ from lnbits.helpers import urlsafe_short_hash
async def create_jukebox(
user: str,
wallet: str,
title: str,
price: int,
@ -19,11 +20,12 @@ async def create_jukebox(
juke_id = urlsafe_short_hash()
result = await db.execute(
"""
INSERT INTO jukebox (id, title, wallet, sp_user, sp_secret, sp_access_token, sp_refresh_token, sp_device, sp_playlists, price)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
INSERT INTO jukebox (id, user, title, wallet, sp_user, sp_secret, sp_access_token, sp_refresh_token, sp_device, sp_playlists, price)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
juke_id,
user,
title,
wallet,
sp_user,
@ -40,12 +42,12 @@ async def create_jukebox(
return jukebox
async def update_jukebox(sp_user: str, **kwargs) -> Optional[Jukebox]:
async def update_jukebox(id: str, **kwargs) -> Optional[Jukebox]:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
await db.execute(
f"UPDATE jukebox SET {q} WHERE sp_user = ?", (*kwargs.values(), sp_user)
f"UPDATE jukebox SET {q} WHERE id = ?", (*kwargs.values(), id)
)
row = await db.fetchone("SELECT * FROM jukebox WHERE sp_user = ?", (sp_user,))
row = await db.fetchone("SELECT * FROM jukebox WHERE id = ?", (id,))
return Jukebox(**row) if row else None
@ -58,16 +60,18 @@ async def get_jukebox_by_user(user: str) -> Optional[Jukebox]:
row = await db.fetchone("SELECT * FROM jukebox WHERE sp_user = ?", (user,))
return Jukebox(**row) if row else None
async def get_jukeboxs(user: str) -> List[Jukebox]:
rows = await db.fetchall("SELECT * FROM jukebox WHERE user = ?", (user,))
for row in rows:
if not row.sp_playlists:
await delete_jukebox(row.id)
rows.remove(row)
return [Jukebox.from_row(row) for row in rows]
async def get_jukeboxs(id: str) -> Optional[Jukebox]:
rows = await db.fetchone("SELECT * FROM jukebox WHERE id = ?", (id,))
return [Jukebox(**row) for row in rows]
async def delete_jukebox(shop: int, item_id: int):
async def delete_jukebox(id: str):
await db.execute(
"""
DELETE FROM jukebox WHERE id = ?
""",
(Jukebox, item_id),
(id),
)

View File

@ -6,6 +6,7 @@ async def m001_initial(db):
"""
CREATE TABLE jukebox (
id TEXT PRIMARY KEY,
user TEXT,
title TEXT,
wallet TEXT,
sp_user TEXT NOT NULL,

View File

@ -9,6 +9,7 @@ from sqlite3 import Row
class Jukebox(NamedTuple):
id: str
user: str
title: str
wallet: str
sp_user: str

View File

@ -2,20 +2,63 @@
Vue.component(VueQrcode.name, VueQrcode)
const pica = window.pica()
var mapJukebox = obj => {
obj._data = _.clone(obj)
obj.device = obj.sp_device.split("-")[0]
playlists = obj.sp_playlists.split(",")
var i;
playlistsar = []
for (i = 0; i < playlists.length; i++) {
playlistsar.push(playlists[i].split("-")[0])
}
obj.playlist = playlistsar.join()
return obj
}
new Vue({
el: '#vue',
mixins: [windowMixin],
data() {
return {
JukeboxTable: {
columns: [
{
name: 'title',
align: 'left',
label: 'Title',
field: 'title'
},
{
name: 'device',
align: 'left',
label: 'Device',
field: 'device'
},
{
name: 'playlist',
align: 'left',
label: 'Playlist',
field: 'playlist'
},
{
name: 'price',
align: 'left',
label: 'Price',
field: 'price'
},
],
pagination: {
rowsPerPage: 10
}
},
isPwd: true,
tokenFetched: true,
devices: [],
filter: '',
jukebox: {},
playlists: [],
JukeboxLinks: [],
step: 1,
locationcbPath: "",
locationcb: "",
@ -27,20 +70,42 @@ new Vue({
}
},
computed: {
printItems() {
return this.jukebox.items.filter(({enabled}) => enabled)
}
},
methods: {
getJukeboxes(){
self = this
LNbits.api
.request('GET', '/jukebox/api/v1/jukebox', self.g.user.wallets[0].inkey)
.then(function (response) {
self.JukeboxLinks = response.data.map(mapJukebox)
})
.catch(err => {
LNbits.utils.notifyApiError(err)
})
},
deleteJukebox(juke_id){
self = this
LNbits.api
.request('DELETE', '/jukebox/api/v1/jukebox/' + juke_id, self.g.user.wallets[0].adminkey)
.then(function (response) {
self.JukeboxLinks = _.reject(self.JukeboxLinks, function (obj) {
return obj.id === juke_id
})
})
.catch(err => {
LNbits.utils.notifyApiError(err)
})
},
closeFormDialog() {
this.jukeboxDialog.data = {}
this.jukeboxDialog.show = false
this.step = 1
},
submitSpotify() {
self = this
console.log(self.jukeboxDialog.data)
self.jukeboxDialog.data.user = self.g.user.id
self.requestAuthorization()
this.$q.notify({
spinner: true,
@ -56,7 +121,6 @@ new Vue({
.then(response => {
if(response.data){
var timerId = setInterval(function(){
console.log(response.data)
if(!self.jukeboxDialog.data.sp_user){
clearInterval(timerId);
}
@ -68,15 +132,7 @@ new Vue({
self.jukeboxDialog.data.sp_access_token = response.data.sp_access_token
self.step = 3
self.fetchAccessToken()
clearInterval(timerId)
// self.refreshPlaylists(response.data.sp_token)
// self.$q.notify({
// message: 'Success! App is now linked!',
// timeout: 3000
// })
//set devices, playlists
}
})
.catch(err => {
@ -97,7 +153,7 @@ new Vue({
url += '&redirect_uri=' + encodeURI(self.locationcbPath) + self.jukeboxDialog.data.sp_user
url += "&show_dialog=true"
url += '&scope=user-read-private user-read-email user-modify-playback-state user-read-playback-position user-library-read streaming user-read-playback-state user-read-recently-played playlist-read-private'
console.log(url)
window.open(url)
},
openNewDialog() {
@ -111,19 +167,16 @@ new Vue({
},
createJukebox(){
self = this
this.jukeboxDialog.data.sp_playlists = this.jukeboxDialog.data.sp_playlists.join()
LNbits.api.request(
'PUT',
'/jukebox/api/v1/jukebox/' + this.jukeboxDialog.data.sp_id,
self.g.user.wallets[0].adminkey,
self.jukeboxDialog.data
)
.then(response => {
console.log(response.data)
})
.catch(err => {
LNbits.utils.notifyApiError(err)
.then(function (response) {
self.JukeboxLinks.push(mapJukebox(response.data))
self.jukeboxDialog.show = false
})
},
@ -156,12 +209,11 @@ new Vue({
xhr.send(body)
xhr.onload = function() {
let responseObj = JSON.parse(xhr.response)
console.log(responseObj.devices[0])
self.devices = []
var i;
for (i = 0; i < responseObj.devices.length; i++) {
self.devices.push(responseObj.devices[i].name + "-" + responseObj.devices[i].id)
console.log(responseObj.devices[i].name)
}
}
@ -191,20 +243,19 @@ new Vue({
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.setRequestHeader('Authorization', 'Basic ' + btoa(this.jukeboxDialog.data.sp_user + ":" + this.jukeboxDialog.data.sp_secret))
xhr.send(body)
console.log(('Authorization', 'Basic ' + btoa(this.jukeboxDialog.data.sp_user + ":" + this.jukeboxDialog.data.sp_secret)))
xhr.onload = function() {
let responseObj = JSON.parse(xhr.response)
alert(responseObj.access_token)
alert(responseObj.refresh_token)
self.jukeboxDialog.data.sp_access_token = responseObj.access_token
self.jukeboxDialog.data.sp_refresh_token = responseObj.refresh_token
console.log(self.jukeboxDialog.data)
self.refreshPlaylists()
self.refreshDevices()
}
},
},
created() {
var getJukeboxes = this.getJukeboxes
getJukeboxes()
this.selectedWallet = this.g.user.wallets[0]
this.locationcbPath = String([
window.location.protocol,
@ -212,7 +263,6 @@ new Vue({
window.location.host,
'/jukebox/api/v1/jukebox/spotify/cb/'
].join(''))
console.log(this.locationcbPath)
this.locationcb = this.locationcbPath
}
})

View File

@ -11,30 +11,36 @@
@click="openNewDialog()"
>Add Spotify Jukebox</q-btn
>
<div class="row items-center no-wrap q-mb-md">
<h5 class="text-subtitle1 q-my-none">Items</h5>
</div>
{% raw %}
<q-table
dense
flat
selection="multiple"
:data="jukebox.items"
dense
:data="JukeboxLinks"
row-key="id"
no-data-label="No items for sale yet"
:pagination="{rowsPerPage: 0}"
:binary-state-sort="true"
:columns="JukeboxTable.columns"
:pagination.sync="JukeboxTable.pagination"
:filter="filter"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th auto-width>Name</q-th>
<q-th auto-width>Description</q-th>
<q-th auto-width>Image</q-th>
<q-th auto-width>Price</q-th>
<q-th auto-width></q-th>
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
auto-width
>
<div v-if="col.name == 'id'"></div>
<div v-else>{{ col.label }}</div>
</q-th>
<q-th auto-width></q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td auto-width>
@ -42,44 +48,35 @@
unelevated
dense
size="xs"
:icon="props.row.enabled ? 'done' : 'block'"
:color="props.row.enabled ? 'green' : ($q.dark.isActive ? 'grey-7' : 'grey-5')"
icon="link"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
type="a"
@click="toggleItem(props.row.id)"
:href="props.row.displayUrl"
target="_blank"
></q-btn>
</q-td>
<q-td auto-width class="text-center">{{ props.row.name }}</q-td>
<q-td auto-width> {{ props.row.description }} </q-td>
<q-td class="text-center" auto-width>
<img
v-if="props.row.image"
:src="props.row.image"
style="height: 1.5em"
/>
</q-td>
<q-td class="text-center" auto-width>
{{ props.row.price }} {{ props.row.unit }}
>
<q-tooltip> Jukebox link </q-tooltip>
</q-btn>
</q-td>
<q-td auto-width>
<q-btn
flat
dense
size="xs"
@click="openUpdateDialog(props.row.id)"
icon="edit"
color="light-blue"
></q-btn>
<q-btn
unelevated
dense
size="xs"
icon="delete"
color="negative"
type="a"
@click="deleteItem(props.row.id)"
target="_blank"
></q-btn>
@click="deleteJukebox(props.row.id)"
icon="cancel"
color="pink"
>
<q-tooltip> Delete Jukebox </q-tooltip>
</q-btn>
</q-td>
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
auto-width
>
<div v-if="col.name == 'id'"></div>
<div v-else>{{ col.value }}</div>
</q-td>
</q-tr>
</template>
@ -253,9 +250,13 @@
></q-select>
<div class="row q-mt-md">
<div class="col-5">
<q-btn color="green-7" @click="createJukebox"
<q-btn
v-if="jukeboxDialog.data.sp_device != null && jukeboxDialog.data.sp_playlists != null"
color="green-7"
@click="createJukebox"
>Create Jukebox</q-btn
>
<q-btn v-else color="green-7" disable>Create Jukebox</q-btn>
</div>
<div class="col-7">
<q-btn

View File

@ -19,7 +19,20 @@ from .models import Jukebox
@jukebox_ext.route("/api/v1/jukebox", methods=["GET"])
@api_check_wallet_key("invoice")
async def api_get_jukeboxs():
jsonify([jukebox._asdict() for jukebox in await get_jukeboxs(g.wallet.id)]),
try:
return (
jsonify(
[
{
**jukebox._asdict()
}
for jukebox in await get_jukeboxs(g.wallet.user)
]
),
HTTPStatus.OK,
)
except:
return "", HTTPStatus.NO_CONTENT
##################SPOTIFY AUTH#####################
@ -61,6 +74,7 @@ async def api_check_credentials_check(sp_id):
@api_check_wallet_key("admin")
@api_validate_post_request(
schema={
"user": {"type": "string", "empty": False, "required": True},
"title": {"type": "string", "empty": False, "required": True},
"wallet": {"type": "string", "empty": False, "required": True},
"sp_user": {"type": "string", "empty": False, "required": True},
@ -74,13 +88,27 @@ async def api_check_credentials_check(sp_id):
)
async def api_create_update_jukebox(item_id=None):
if item_id:
jukebox = await update_jukebox(**g.data)
jukebox = await create_jukebox(**g.data)
jukebox = await update_jukebox(item_id, **g.data)
else:
jukebox = await create_jukebox(**g.data)
return jsonify(jukebox._asdict()), HTTPStatus.CREATED
@jukebox_ext.route("/api/v1/jukebox/<juke_id>", methods=["DELETE"])
@api_check_wallet_key("admin")
async def api_delete_item(juke_id):
shop = await delete_jukebox(juke_id)
return "", HTTPStatus.NO_CONTENT
await delete_jukebox(juke_id)
try:
return (
jsonify(
[
{
**jukebox._asdict()
}
for jukebox in await get_jukeboxs(g.wallet.user)
]
),
HTTPStatus.OK,
)
except:
return "", HTTPStatus.NO_CONTENT