added tag routes and js template, fixed faas trigger
This commit is contained in:
parent
1aee2feb16
commit
97ffc1f141
17
main.py
17
main.py
@ -5,22 +5,17 @@ app = app
|
||||
|
||||
|
||||
def update_tag(event, context):
|
||||
import base64
|
||||
import logging
|
||||
import json
|
||||
|
||||
logger = logging.getLogger('music')
|
||||
|
||||
if 'data' in event:
|
||||
body = json.loads(base64.b64decode(event['data']).decode('utf-8'))
|
||||
|
||||
if 'username' not in body or 'tag_id' not in body:
|
||||
logger.error('malformed body')
|
||||
return
|
||||
|
||||
do_update_tag(username=body["username"], tag_id=body["tag_id"])
|
||||
if event.get('attributes'):
|
||||
if 'username' in event['attributes'] and 'tag_id' in event['attributes']:
|
||||
do_update_tag(username=event['attributes']['username'], tag_id=event['attributes']["tag_id"])
|
||||
else:
|
||||
logger.error('no parameters in event attributes')
|
||||
else:
|
||||
logger.error('no data in event')
|
||||
logger.error('no attributes in event')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -4,3 +4,4 @@ from .fm import blueprint as fm_blueprint
|
||||
from .spotfm import blueprint as spotfm_blueprint
|
||||
from .spotify import blueprint as spotify_blueprint
|
||||
from .admin import blueprint as admin_blueprint
|
||||
from .tag import blueprint as tag_blueprint
|
||||
|
127
music/api/tag.py
Normal file
127
music/api/tag.py
Normal 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)
|
@ -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}')
|
||||
|
||||
|
||||
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}')
|
||||
|
||||
tag = get_tag(username=username, tag_id=tag_id)
|
||||
|
||||
if tag:
|
||||
tag.db_ref.delete()
|
||||
return True
|
||||
else:
|
||||
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])
|
||||
|
@ -5,7 +5,7 @@ import os
|
||||
|
||||
from music.auth import auth_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()
|
||||
|
||||
@ -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(spotify_blueprint, url_prefix='/api/spotify')
|
||||
app.register_blueprint(admin_blueprint, url_prefix='/api/admin')
|
||||
app.register_blueprint(tag_blueprint, url_prefix='/api')
|
||||
|
||||
|
||||
@app.route('/')
|
||||
|
@ -5,16 +5,17 @@ Click==7.0
|
||||
Flask==1.1.1
|
||||
google-api-core==1.16.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-logging==1.14.0
|
||||
google-cloud-pubsub==1.1.0
|
||||
google-cloud-tasks==1.3.0
|
||||
googleapis-common-protos==1.51.0
|
||||
grpc-google-iam-v1==0.12.3
|
||||
grpcio==1.26.0
|
||||
idna==2.8
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.10.3
|
||||
Jinja2==2.11.1
|
||||
MarkupSafe==1.1.1
|
||||
numpy==1.18.1
|
||||
opencv-python==4.1.2.30
|
||||
@ -27,4 +28,4 @@ rsa==4.0
|
||||
six==1.14.0
|
||||
tabulate==0.8.6
|
||||
urllib3==1.25.8
|
||||
Werkzeug==0.16.0
|
||||
Werkzeug==0.16.1
|
||||
|
163
src/js/Tag/TagList.js
Normal file
163
src/js/Tag/TagList.js
Normal 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;
|
Loading…
Reference in New Issue
Block a user