added timing for tags
This commit is contained in:
parent
5096c3c2a9
commit
d5f9681fd8
@ -38,7 +38,7 @@ def tag_route(tag_id, user=None):
|
||||
|
||||
|
||||
def get_tag(tag_id, user):
|
||||
logger.info(f'retriving {tag_id} for {user.username}')
|
||||
logger.info(f'retrieving {tag_id} for {user.username}')
|
||||
|
||||
db_tag = Tag.collection.parent(user.key).filter('tag_id', '==', tag_id).get()
|
||||
if db_tag is not None:
|
||||
@ -62,6 +62,9 @@ def put_tag(tag_id, user):
|
||||
if request_json.get('name'):
|
||||
db_tag.name = request_json['name'].strip()
|
||||
|
||||
if request_json.get('time_objects') is not None:
|
||||
db_tag.time_objects = request_json['time_objects']
|
||||
|
||||
if request_json.get('tracks') is not None:
|
||||
db_tag.tracks = [
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
from fireo.models import Model
|
||||
from fireo.fields import TextField, DateTime, NumberField, ListField
|
||||
from fireo.fields import TextField, DateTime, NumberField, ListField, BooleanField
|
||||
|
||||
|
||||
class Tag(Model):
|
||||
@ -20,6 +20,10 @@ class Tag(Model):
|
||||
|
||||
last_updated = DateTime()
|
||||
|
||||
time_objects = BooleanField(default=False)
|
||||
total_time = TextField(default='00:00:00')
|
||||
total_time_ms = NumberField(default=0)
|
||||
|
||||
def to_dict(self):
|
||||
to_return = super().to_dict()
|
||||
|
||||
|
@ -7,6 +7,8 @@ from music.model.tag import Tag
|
||||
|
||||
from fmframework.net.network import LastFMNetworkException
|
||||
|
||||
from spotfm.timer import time, seconds_to_time_str
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -29,26 +31,48 @@ def update_tag(username, tag_id):
|
||||
return
|
||||
|
||||
net = database.get_authed_lastfm_network(user)
|
||||
|
||||
if net is None:
|
||||
logger.error(f'no last.fm network returned for {username}')
|
||||
logger.error(f'no last.fm network returned for {username} / {tag_id}')
|
||||
return
|
||||
|
||||
tag_count = 0
|
||||
if tag.time_objects:
|
||||
if user.spotify_linked:
|
||||
spotnet = database.get_authed_spotify_network(user)
|
||||
else:
|
||||
logger.warning(f'timing objects requested but no spotify linked {username} / {tag_id}')
|
||||
|
||||
tag.count = 0
|
||||
tag.total_time_ms = 0
|
||||
|
||||
try:
|
||||
user_scrobbles = net.user_scrobble_count()
|
||||
except LastFMNetworkException:
|
||||
logger.exception(f'error retrieving scrobble count {username} / {tag_id}')
|
||||
user_scrobbles = 0
|
||||
user_scrobbles = 1
|
||||
|
||||
artists = []
|
||||
for artist in tag.artists:
|
||||
try:
|
||||
if tag.time_objects and user.spotify_linked:
|
||||
total_ms, timed_tracks = time(spotnet=spotnet, fmnet=net,
|
||||
artist=artist['name'], username=user.lastfm_username,
|
||||
return_tracks=True)
|
||||
scrobbles = sum(i[0].user_scrobbles for i in timed_tracks)
|
||||
|
||||
artist['time_ms'] = total_ms
|
||||
artist['time'] = seconds_to_time_str(milliseconds=total_ms)
|
||||
tag.total_time_ms += total_ms
|
||||
|
||||
else:
|
||||
net_artist = net.artist(name=artist['name'])
|
||||
|
||||
if net_artist is not None:
|
||||
artist['count'] = net_artist.user_scrobbles
|
||||
tag_count += net_artist.user_scrobbles
|
||||
scrobbles = net_artist.user_scrobbles
|
||||
else:
|
||||
scrobbles = 0
|
||||
|
||||
artist['count'] = scrobbles
|
||||
tag.count += scrobbles
|
||||
except LastFMNetworkException:
|
||||
logger.exception(f'error during artist retrieval {username} / {tag_id}')
|
||||
|
||||
@ -57,13 +81,28 @@ def update_tag(username, tag_id):
|
||||
albums = []
|
||||
for album in tag.albums:
|
||||
try:
|
||||
if tag.time_objects and user.spotify_linked:
|
||||
total_ms, timed_tracks = time(spotnet=spotnet, fmnet=net,
|
||||
album=album['name'], artist=album['artist'],
|
||||
username=user.lastfm_username, return_tracks=True)
|
||||
scrobbles = sum(i[0].user_scrobbles for i in timed_tracks)
|
||||
|
||||
album['time_ms'] = total_ms
|
||||
album['time'] = seconds_to_time_str(milliseconds=total_ms)
|
||||
tag.total_time_ms += total_ms
|
||||
|
||||
else:
|
||||
net_album = net.album(name=album['name'], artist=album['artist'])
|
||||
|
||||
if net_album is not None:
|
||||
album['count'] = net_album.user_scrobbles
|
||||
scrobbles = net_album.user_scrobbles
|
||||
else:
|
||||
scrobbles = 0
|
||||
|
||||
album['count'] = scrobbles
|
||||
|
||||
if album['artist'].lower() not in [i.lower() for i in [j['name'] for j in artists]]:
|
||||
tag_count += net_album.user_scrobbles
|
||||
tag.count += scrobbles
|
||||
except LastFMNetworkException:
|
||||
logger.exception(f'error during album retrieval {username} / {tag_id}')
|
||||
|
||||
@ -72,13 +111,28 @@ def update_tag(username, tag_id):
|
||||
tracks = []
|
||||
for track in tag.tracks:
|
||||
try:
|
||||
if tag.time_objects and user.spotify_linked:
|
||||
total_ms, timed_tracks = time(spotnet=spotnet, fmnet=net,
|
||||
track=track['name'], artist=track['artist'],
|
||||
username=user.lastfm_username, return_tracks=True)
|
||||
scrobbles = sum(i[0].user_scrobbles for i in timed_tracks)
|
||||
|
||||
track['time_ms'] = total_ms
|
||||
track['time'] = seconds_to_time_str(milliseconds=total_ms)
|
||||
tag.total_time_ms += total_ms
|
||||
|
||||
else:
|
||||
net_track = net.track(name=track['name'], artist=track['artist'])
|
||||
|
||||
if net_track is not None:
|
||||
track['count'] = net_track.user_scrobbles
|
||||
scrobbles = net_track.user_scrobbles
|
||||
else:
|
||||
scrobbles = 0
|
||||
|
||||
track['count'] = scrobbles
|
||||
|
||||
if track['artist'].lower() not in [i.lower() for i in [j['name'] for j in artists]]:
|
||||
tag_count += net_track.user_scrobbles
|
||||
tag.count += scrobbles
|
||||
except LastFMNetworkException:
|
||||
logger.exception(f'error during track retrieval {username} / {tag_id}')
|
||||
|
||||
@ -88,9 +142,9 @@ def update_tag(username, tag_id):
|
||||
tag.albums = albums
|
||||
tag.artists = artists
|
||||
|
||||
tag.total_time = seconds_to_time_str(milliseconds=tag.total_time_ms)
|
||||
tag.total_user_scrobbles = user_scrobbles
|
||||
tag.count = tag_count
|
||||
tag.proportion = (tag_count / user_scrobbles) * 100
|
||||
tag.proportion = (tag.count / user_scrobbles) * 100
|
||||
tag.last_updated = datetime.utcnow()
|
||||
|
||||
tag.update()
|
||||
|
@ -1,8 +1,10 @@
|
||||
astroid==2.4.2
|
||||
beautifulsoup4==4.9.1
|
||||
cachetools==4.1.0
|
||||
certifi==2020.6.20
|
||||
chardet==3.0.4
|
||||
click==7.1.2
|
||||
colorama==0.4.3
|
||||
fireo==1.3.3
|
||||
Flask==1.1.2
|
||||
google-api-core==1.21.0
|
||||
@ -32,6 +34,7 @@ pytz==2020.1
|
||||
requests==2.24.0
|
||||
rsa==4.6
|
||||
six==1.15.0
|
||||
soupsieve==2.0.1
|
||||
tabulate==0.8.7
|
||||
toml==0.10.1
|
||||
urllib3==1.25.9
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
const axios = require('axios');
|
||||
|
||||
import { Card, Button, CircularProgress, CardActions, CardContent, FormControl, InputLabel, Select, Typography, Grid, TextField, MenuItem } from '@material-ui/core';
|
||||
import { Card, Button, CircularProgress, CardActions, CardContent, FormControl, InputLabel, Select, Typography, Grid, TextField, MenuItem, FormControlLabel, Checkbox } from '@material-ui/core';
|
||||
import { Delete } from '@material-ui/icons';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
@ -41,25 +41,28 @@ class View extends Component{
|
||||
this.handleRun = this.handleRun.bind(this);
|
||||
this.handleRemoveObj = this.handleRemoveObj.bind(this);
|
||||
|
||||
this.handleCheckChange = this.handleCheckChange.bind(this);
|
||||
this.makeNetworkUpdate = this.makeNetworkUpdate.bind(this);
|
||||
|
||||
this.handleAdd = this.handleAdd.bind(this);
|
||||
this.handleChangeAddType = this.handleChangeAddType.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
this.getTag();
|
||||
var intervalId = setInterval(() => {this.getTag(false)}, 5000);
|
||||
var timeoutId = setTimeout(() => {clearInterval(this.state.intervalId)}, 300000);
|
||||
// var intervalId = setInterval(() => {this.getTag(false)}, 5000);
|
||||
// var timeoutId = setTimeout(() => {clearInterval(this.state.intervalId)}, 300000);
|
||||
|
||||
this.setState({
|
||||
intervalId: intervalId,
|
||||
timeoutId: timeoutId
|
||||
});
|
||||
// this.setState({
|
||||
// intervalId: intervalId,
|
||||
// timeoutId: timeoutId
|
||||
// });
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
clearInterval(this.state.intervalId);
|
||||
clearTimeout(this.state.timeoutId);
|
||||
}
|
||||
// componentWillUnmount(){
|
||||
// clearInterval(this.state.intervalId);
|
||||
// clearTimeout(this.state.timeoutId);
|
||||
// }
|
||||
|
||||
getTag(error_toast = true){
|
||||
axios.get(`/api/tag/${ this.state.tag_id }`)
|
||||
@ -105,6 +108,24 @@ class View extends Component{
|
||||
|
||||
}
|
||||
|
||||
handleCheckChange(event){
|
||||
let payload = {...this.state.tag};
|
||||
payload[event.target.name] = event.target.checked;
|
||||
|
||||
this.setState({tag: payload});
|
||||
|
||||
switch(event.target.name){
|
||||
default:
|
||||
this.makeNetworkUpdate({[event.target.name]: event.target.checked});
|
||||
}
|
||||
}
|
||||
|
||||
makeNetworkUpdate(changes){
|
||||
axios.put(`/api/tag/${this.state.tag_id}`, changes).catch((error) => {
|
||||
showMessage(`Error updating ${Object.keys(changes).join(", ")} (${error.response.status})`);
|
||||
});
|
||||
}
|
||||
|
||||
handleRun(event){
|
||||
axios.get('/api/user')
|
||||
.then((response) => {
|
||||
@ -256,13 +277,13 @@ class View extends Component{
|
||||
<Grid container spacing={5}>
|
||||
|
||||
{ this.state.tag.artists.length > 0 && <Grid item xs={12} ><Typography color="textSecondary" variant="h4">Artists</Typography></Grid> }
|
||||
{ this.state.tag.artists.length > 0 && <ListBlock handler={this.handleRemoveObj} list={this.state.tag.artists} addType="artists"/> }
|
||||
{ this.state.tag.artists.length > 0 && <ListBlock handler={this.handleRemoveObj} list={this.state.tag.artists} addType="artists" showTime={this.state.tag.time_objects}/> }
|
||||
|
||||
{ this.state.tag.albums.length > 0 && <Grid item xs={12} ><Typography color="textSecondary" variant="h4">Albums</Typography></Grid> }
|
||||
{ this.state.tag.albums.length > 0 && <ListBlock handler={this.handleRemoveObj} list={this.state.tag.albums} addType="albums"/> }
|
||||
{ this.state.tag.albums.length > 0 && <ListBlock handler={this.handleRemoveObj} list={this.state.tag.albums} addType="albums" showTime={this.state.tag.time_objects}/> }
|
||||
|
||||
{ this.state.tag.tracks.length > 0 && <Grid item xs={12} ><Typography color="textSecondary" variant="h4">Tracks</Typography></Grid> }
|
||||
{ this.state.tag.tracks.length > 0 && <ListBlock handler={this.handleRemoveObj} list={this.state.tag.tracks} addType="tracks"/> }
|
||||
{ this.state.tag.tracks.length > 0 && <ListBlock handler={this.handleRemoveObj} list={this.state.tag.tracks} addType="tracks" showTime={this.state.tag.time_objects}/> }
|
||||
<Grid item xs={12} sm={this.state.addType != 'artists' ? 3 : 4} md={this.state.addType != 'artists' ? 3 : 4}>
|
||||
<TextField
|
||||
name="name"
|
||||
@ -301,7 +322,16 @@ class View extends Component{
|
||||
<Grid item xs={12} sm={this.state.addType != 'artists' ? 3 : 4} md={this.state.addType != 'artists' ? 3 : 4}>
|
||||
<Button variant="contained" onClick={this.handleAdd} className="full-width">Add</Button>
|
||||
</Grid>
|
||||
<StatsCard count={this.state.tag.count} proportion={this.state.tag.proportion}></StatsCard>
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox color="primary" checked={this.state.tag.time_objects} name="time_objects" onChange={this.handleCheckChange} />
|
||||
}
|
||||
label="Time Tag"
|
||||
labelPlacement="bottom"
|
||||
/>
|
||||
</Grid>
|
||||
<StatsCard count={this.state.tag.count} proportion={this.state.tag.proportion} showTime={this.state.tag.time_objects} time={this.state.tag.total_time}></StatsCard>
|
||||
<Grid item xs={12}>
|
||||
<PieChart data={data}/>
|
||||
</Grid>
|
||||
@ -331,7 +361,7 @@ function ListBlock(props) {
|
||||
alignItems="flex-start"
|
||||
style={{padding: '24px'}}>
|
||||
{props.list.map((music_obj) => <BlockGridItem music_obj={ music_obj } key={ music_obj.name }
|
||||
handler={ props.handler } addType={ props.addType }/>)}
|
||||
handler={ props.handler } addType={ props.addType } showTime={ props.showTime }/>)}
|
||||
</Grid>
|
||||
}
|
||||
|
||||
@ -352,7 +382,12 @@ function BlockGridItem (props) {
|
||||
}
|
||||
{ 'count' in props.music_obj &&
|
||||
<Grid item xs={8}>
|
||||
<Typography variant="h4" color="textPrimary" className={classes.root}>{ props.music_obj.count }</Typography>
|
||||
<Typography variant="h4" color="textPrimary" className={classes.root}>📈 { props.music_obj.count }</Typography>
|
||||
</Grid>
|
||||
}
|
||||
{ 'time' in props.music_obj && props.showTime &&
|
||||
<Grid item xs={8}>
|
||||
<Typography variant="body1" color="textSecondary" className={classes.root}>🕒 { props.music_obj.time }</Typography>
|
||||
</Grid>
|
||||
}
|
||||
</Grid>
|
||||
@ -375,11 +410,16 @@ function StatsCard (props) {
|
||||
<CardContent>
|
||||
<Grid container spacing={10}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h1" color="textPrimary" className={classes.root}>= { props.count }</Typography>
|
||||
<Typography variant="h1" color="textPrimary" className={classes.root}>📈 { props.count }</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h4" color="textSecondary" className={classes.root}>{ props.proportion.toFixed(2) }%</Typography>
|
||||
</Grid>
|
||||
{props.showTime &&
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h4" color="textSecondary" className={classes.root}>🕒 { props.time }</Typography>
|
||||
</Grid>
|
||||
}
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
Loading…
Reference in New Issue
Block a user