added tag routes and js template, fixed faas trigger

This commit is contained in:
aj 2020-02-03 11:51:46 +00:00
parent 1aee2feb16
commit 97ffc1f141
7 changed files with 332 additions and 16 deletions

17
main.py
View File

@ -5,22 +5,17 @@ app = app
def update_tag(event, context): def update_tag(event, context):
import base64
import logging import logging
import json
logger = logging.getLogger('music') logger = logging.getLogger('music')
if 'data' in event: if event.get('attributes'):
body = json.loads(base64.b64decode(event['data']).decode('utf-8')) if 'username' in event['attributes'] and 'tag_id' in event['attributes']:
do_update_tag(username=event['attributes']['username'], tag_id=event['attributes']["tag_id"])
if 'username' not in body or 'tag_id' not in body: else:
logger.error('malformed body') logger.error('no parameters in event attributes')
return
do_update_tag(username=body["username"], tag_id=body["tag_id"])
else: else:
logger.error('no data in event') logger.error('no attributes in event')
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -4,3 +4,4 @@ from .fm import blueprint as fm_blueprint
from .spotfm import blueprint as spotfm_blueprint from .spotfm import blueprint as spotfm_blueprint
from .spotify import blueprint as spotify_blueprint from .spotify import blueprint as spotify_blueprint
from .admin import blueprint as admin_blueprint from .admin import blueprint as admin_blueprint
from .tag import blueprint as tag_blueprint

127
music/api/tag.py Normal file
View File

@ -0,0 +1,127 @@
from flask import Blueprint, jsonify, request
import logging
from google.cloud import pubsub_v1
import music.db.database as database
from music.api.decorators import login_or_basic_auth
blueprint = Blueprint('task', __name__)
logger = logging.getLogger(__name__)
publisher = pubsub_v1.PublisherClient()
@blueprint.route('/tag', methods=['GET'])
@login_or_basic_auth
def tags(username=None):
logger.info(f'retrieving tags for {username}')
return jsonify({
'tags': [i.to_dict() for i in database.get_user_tags(username)]
}), 200
@blueprint.route('/tag/<tag_id>', methods=['GET', 'PUT', 'POST', "DELETE"])
@login_or_basic_auth
def tag(tag_id, username=None):
if request.method == 'GET':
return put_tag(tag_id, username)
elif request.method == 'PUT':
return put_tag(tag_id, username)
elif request.method == 'POST':
return post_tag(tag_id, username)
elif request.method == 'DELETE':
return delete_tag(tag_id, username)
def get_tag(tag_id, username):
logger.info(f'retriving {tag_id} for {username}')
db_tag = database.get_tag(username=username, tag_id=tag_id)
if db_tag is not None:
return jsonify({
'tag': db_tag.to_dict()
}), 200
else:
return jsonify({"error": 'tag not found'}), 404
def put_tag(tag_id, username):
logger.info(f'updating {tag_id} for {username}')
db_tag = database.get_tag(username=username, tag_id=tag_id)
if db_tag is None:
return jsonify({"error": 'tag not found'}), 404
request_json = request.get_json()
if request_json.get('name'):
db_tag.name = request_json['name']
update_required = False
tracks = []
if request_json.get('tracks'):
update_required = True
for track in request_json['tracks']:
if track.get('name') and track.get('artist'):
tracks.append({
'name': track['name'],
'artist': track['artist']
})
db_tag.tracks = tracks
albums = []
if request_json.get('albums'):
update_required = True
for album in request_json['albums']:
if album.get('name') and album.get('artist'):
albums.append({
'name': album['name'],
'artist': album['artist']
})
db_tag.album = albums
artists = []
if request_json.get('artists'):
update_required = True
for artist in request_json['tracks']:
if artist.get('name') and artist.get('artist'):
artists.append({
'name': artist['name']
})
db_tag.artists = artists
if update_required:
update_tag(username=username, tag_id=tag_id)
return jsonify({"message": 'tag updated', "status": "success"}), 200
def post_tag(tag_id, username):
logger.info(f'creating {tag_id} for {username}')
new_tag = database.create_tag(username=username, tag_id=tag_id)
if new_tag is not None:
return jsonify({"message": 'tag added', "status": "success"}), 201
else:
return jsonify({"error": 'tag not created'}), 400
def delete_tag(tag_id, username):
logger.info(f'deleting {tag_id} for {username}')
response = database.delete_tag(username=username, tag_id=tag_id)
if response is not None:
return jsonify({"message": 'tag deleted', "status": "success"}), 201
else:
return jsonify({"error": 'tag not deleted'}), 400
def update_tag(username, tag_id):
logger.info(f'queuing {tag_id} update for {username}')
publisher.publish('projects/sarsooxyz/topics/update_tag', b'', tag_id=tag_id, username=username)

View File

@ -411,12 +411,40 @@ def update_tag(username: str, tag_id: str, updates: dict) -> None:
logger.debug(f'nothing to update for {tag_id} for {username}') logger.debug(f'nothing to update for {tag_id} for {username}')
def delete_tag(username: str, tag_id: str) -> None: def delete_tag(username: str, tag_id: str) -> bool:
logger.info(f'deleting {tag_id} for {username}') logger.info(f'deleting {tag_id} for {username}')
tag = get_tag(username=username, tag_id=tag_id) tag = get_tag(username=username, tag_id=tag_id)
if tag: if tag:
tag.db_ref.delete() tag.db_ref.delete()
return True
else: else:
logger.error(f'playlist {tag_id} not found for {username}') logger.error(f'playlist {tag_id} not found for {username}')
return False
def create_tag(username: str, tag_id: str):
user = get_user(username)
if user is None:
logger.error(f'{username} not found')
return None
if tag_id in [i.tag_id for i in get_user_tags(username)]:
logger.error(f'{tag_id} already exists for {username}')
return None
return parse_tag_reference(user.db_ref.collection(u'tags').add({
'tag_id': tag_id,
'name': tag_id,
'tracks': [],
'albums': [],
'artists': [],
'count': 0,
'proportion': 0.0,
'total_user_scrobbles': 0,
'last_updated': None
})[1])

View File

@ -5,7 +5,7 @@ import os
from music.auth import auth_blueprint from music.auth import auth_blueprint
from music.api import api_blueprint, player_blueprint, fm_blueprint, \ from music.api import api_blueprint, player_blueprint, fm_blueprint, \
spotfm_blueprint, spotify_blueprint, admin_blueprint spotfm_blueprint, spotify_blueprint, admin_blueprint, tag_blueprint
db = firestore.Client() db = firestore.Client()
@ -18,6 +18,7 @@ app.register_blueprint(fm_blueprint, url_prefix='/api/fm')
app.register_blueprint(spotfm_blueprint, url_prefix='/api/spotfm') app.register_blueprint(spotfm_blueprint, url_prefix='/api/spotfm')
app.register_blueprint(spotify_blueprint, url_prefix='/api/spotify') app.register_blueprint(spotify_blueprint, url_prefix='/api/spotify')
app.register_blueprint(admin_blueprint, url_prefix='/api/admin') app.register_blueprint(admin_blueprint, url_prefix='/api/admin')
app.register_blueprint(tag_blueprint, url_prefix='/api')
@app.route('/') @app.route('/')

View File

@ -5,16 +5,17 @@ Click==7.0
Flask==1.1.1 Flask==1.1.1
google-api-core==1.16.0 google-api-core==1.16.0
google-auth==1.11.0 google-auth==1.11.0
google-cloud-core==1.2.0 google-cloud-core==1.3.0
google-cloud-firestore==1.6.1 google-cloud-firestore==1.6.1
google-cloud-logging==1.14.0 google-cloud-logging==1.14.0
google-cloud-pubsub==1.1.0
google-cloud-tasks==1.3.0 google-cloud-tasks==1.3.0
googleapis-common-protos==1.51.0 googleapis-common-protos==1.51.0
grpc-google-iam-v1==0.12.3 grpc-google-iam-v1==0.12.3
grpcio==1.26.0 grpcio==1.26.0
idna==2.8 idna==2.8
itsdangerous==1.1.0 itsdangerous==1.1.0
Jinja2==2.10.3 Jinja2==2.11.1
MarkupSafe==1.1.1 MarkupSafe==1.1.1
numpy==1.18.1 numpy==1.18.1
opencv-python==4.1.2.30 opencv-python==4.1.2.30
@ -27,4 +28,4 @@ rsa==4.0
six==1.14.0 six==1.14.0
tabulate==0.8.6 tabulate==0.8.6
urllib3==1.25.8 urllib3==1.25.8
Werkzeug==0.16.0 Werkzeug==0.16.1

163
src/js/Tag/TagList.js Normal file
View File

@ -0,0 +1,163 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Button, ButtonGroup, Typography, Card, Grid, CircularProgress } from '@material-ui/core';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
const axios = require('axios');
import showMessage from "../Toast.js"
class TagList extends Component {
constructor(props){
super(props);
this.state = {
isLoading: true
}
this.getTags();
this.handleRunTag = this.handleRunTag.bind(this);
this.handleDeleteTag = this.handleDeleteTag.bind(this);
this.handleRunAll = this.handleRunAll.bind(this);
}
getTags(){
var self = this;
axios.get('/api/tags')
.then((response) => {
var tags = response.data.tags.slice();
tags.sort(function(a, b){
if(a.name.toLowerCase() < b.name.toLowerCase()) { return -1; }
if(a.name.toLowerCase() > b.name.toLowerCase()) { return 1; }
return 0;
});
self.setState({
playlists: tags,
isLoading: false
});
})
.catch((error) => {
showMessage(`Error Getting Playlists (${error.response.status})`);
});
}
handleRunTag(name, event){
axios.get('/api/user')
.then((response) => {
if(response.data.spotify_linked == true){
axios.get('/api/tag/run', {params: {name: name}})
.then((response) => {
showMessage(`${name} ran`);
})
.catch((error) => {
showMessage(`Error Running ${name} (${error.response.status})`);
});
}else{
showMessage(`Link Spotify Before Running`);
}
}).catch((error) => {
showMessage(`Error Running ${this.state.name} (${error.response.status})`);
});
}
handleDeleteTag(name, event){
axios.delete('/api/playlist', { params: { name: name } })
.then((response) => {
showMessage(`${name} Deleted`);
this.getTags();
}).catch((error) => {
showMessage(`Error Deleting ${name} (${error.response.status})`);
});
}
handleRunAll(event){
axios.get('/api/user')
.then((response) => {
if(response.data.spotify_linked == true){
axios.get('/api/tag/run/user')
.then((response) => {
showMessage("All Tags Ran");
})
.catch((error) => {
showMessage(`Error Running All (${error.response.status})`);
});
}else{
showMessage(`Link Spotify Before Running`);
}
}).catch((error) => {
showMessage(`Error Running ${this.state.name} (${error.response.status})`);
});
}
render() {
const grid = <TagGrid tags={this.state.tags}
handleRunTag={this.handleRunTag}
handleDeleteTag={this.handleDeleteTag}
handleRunAll={this.handleRunAll}/>;
return this.state.isLoading ? <CircularProgress /> : grid;
}
}
function TagGrid(props){
return (
<Grid container
spacing={3}
direction="row"
justify="flex-start"
alignItems="flex-start"
style={{padding: '24px'}}>
<Grid item xs>
<ButtonGroup
color="primary"
orientation="vertical"
className="full-width">
<Button component={Link} to='tags/new' >New</Button>
<Button onClick={props.handleRunAll}>Run All</Button>
</ButtonGroup>
</Grid>
{ props.tags.length == 0 ? (
<Grid item xs={12} sm={6} md={3}>
<Typography variant="h5" component="h2">No Tags</Typography>
</Grid>
) : (
props.tags.map((tag) => <TagCard tag={ tag }
handleRunTag={props.handleRunTag}
handleDeleteTag={props.handleDeleteTag}
key={ tag.name }/>)
)}
</Grid>
);
}
function TagCard(props){
return (
<Grid item xs>
<Card>
<CardContent>
<Typography variant="h5" component="h2">
{ props.tag.name }
</Typography>
</CardContent>
<CardActions>
<ButtonGroup
color="primary"
variant="contained">
<Button component={Link} to={getTagLink(props.tag.name)}>View</Button>
<Button onClick={(e) => props.handleRunTag(props.tag.name, e)}>Update</Button>
<Button onClick={(e) => props.handleDeleteTag(props.tag.name, e)}>Delete</Button>
</ButtonGroup>
</CardActions>
</Card>
</Grid>
);
}
function getTagLink(tagName){
return `/app/tag/${tagName}/edit`;
}
export default TagList;