added time bar chart, included all Js docs in sphinx

This commit is contained in:
andy 2021-06-12 13:53:57 +01:00
parent 4fc4676041
commit e64d0e2cd8
16 changed files with 171 additions and 59 deletions

View File

@ -2,18 +2,12 @@ Music Tools
======================================= =======================================
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 2
:caption: Contents: :caption: Contents:
Modules <src/modules> Py <src/music>
src/music Js <src/MusicTools>
src/music.api All Modules <src/modules>
src/music.auth
src/music.cloud
src/music.db
src/music.model
src/music.tasks
src/MusicTools
`Music Tools <https://music.sarsoo.xyz>`_ `Music Tools <https://music.sarsoo.xyz>`_
---------------------------------------------- ----------------------------------------------
@ -22,7 +16,7 @@ Music Tools
Music Tools is a web app for creating smart Spotify playlists. The app is based on `spotframework <https://github.com/Sarsoo/spotframework>`_ and `fmframework <https://github.com/Sarsoo/pyfmframework>`_ for interfacing with Spotify and Last.fm. The app is currently hosted on Google's Cloud Platform. Music Tools is a web app for creating smart Spotify playlists. The app is based on `spotframework <https://github.com/Sarsoo/spotframework>`_ and `fmframework <https://github.com/Sarsoo/pyfmframework>`_ for interfacing with Spotify and Last.fm. The app is currently hosted on Google's Cloud Platform.
The system is composed of a Flask web server with a Fireo ORM layer and longer tasks dispatched to Cloud Tasks or Functions. The backend is composed of a Flask web server with a Fireo ORM layer and longer tasks dispatched to Cloud Tasks or Functions. The frontend is a React app with material UI components and Axios for HTTP requests.
.. image:: Playlists.png .. image:: Playlists.png

View File

@ -0,0 +1,34 @@
Admin
=================
Router
--------
.. js:autoclass:: Admin
:members:
:private-members:
Lock
------------------
.. js:autoclass:: Lock
:members:
:private-members:
.. js:autofunction:: Row
Functions
--------------------
.. js:autoclass:: Functions
:members:
:private-members:
Tasks
--------------------
.. js:autoclass:: Tasks
:members:
:private-members:
.. js:autofunction:: TaskType

View File

@ -0,0 +1,17 @@
Maths
=================
Bar Chart
-----------------
.. js:autoclass:: BarChart
:members:
:private-members:
Pie Chart
------------------
.. js:autoclass:: PieChart
:members:
:private-members:

View File

@ -1,6 +0,0 @@
MusicTools
=================
.. js:autoclass:: MusicTools
:members:
:private-members:

View File

@ -8,7 +8,13 @@ Router
:members: :members:
:private-members: :private-members:
Playlists List For managing playlists list pages for diverting to new playlist page
.. js:autoclass:: PlaylistRouter.View
:members:
:private-members:
Cards List
------------------ ------------------
.. js:autoclass:: PlaylistsView .. js:autoclass:: PlaylistsView
@ -32,14 +38,7 @@ New Playlist Card
:members: :members:
:private-members: :private-members:
Playlist Router View/Edit Card
------------------
.. js:autoclass:: PlaylistRouter.View
:members:
:private-members:
Playlist View
------------------ ------------------
.. js:autoclass:: Edit .. js:autoclass:: Edit
@ -52,7 +51,7 @@ Playlist View
.. js:autofunction:: Edit.BlockGridItem .. js:autofunction:: Edit.BlockGridItem
Playlist Stats View Stats Card
----------------------- -----------------------
.. js:autoclass:: Count .. js:autoclass:: Count

View File

@ -0,0 +1,34 @@
Settings
=================
Router
--------
.. js:autoclass:: Settings
:members:
:private-members:
Change Password
------------------
.. js:autoclass:: ChangePassword
:members:
:private-members:
Spotify Link
------------------
.. js:autoclass:: SpotifyLink
:members:
:private-members:
.. js:autofunction:: AuthButton
.. js:autofunction:: DeAuthButton
Last.fm Username
------------------
.. js:autoclass:: LastFM
:members:
:private-members:

View File

@ -8,7 +8,7 @@ Router
:members: :members:
:private-members: :private-members:
Tags List Cards List
------------------ ------------------
.. js:autoclass:: TagList .. js:autoclass:: TagList
@ -28,3 +28,10 @@ New Tag Card
:members: :members:
:private-members: :private-members:
View/Edit Tag
--------------------
.. js:autoclass:: TagView
:members:
:private-members:

View File

@ -1,13 +1,15 @@
Music Tools React React Frontend
=================== ===================
Subpackages
-----------
.. toctree:: .. toctree::
:maxdepth: 4 :maxdepth: 4
MusicTools.MusicTools
MusicTools.Playlist MusicTools.Playlist
MusicTools.Tag MusicTools.Tag
MusicTools.Maths
MusicTools.Admin
MusicTools.Settings
.. js:autoclass:: MusicTools
:members:
:private-members:

View File

@ -1,5 +1,5 @@
music Music Tools Modules
===== ======================
.. toctree:: .. toctree::
:maxdepth: 4 :maxdepth: 4

View File

@ -1,8 +1,5 @@
music Flask Backend
============= ====================
Subpackages
-----------
.. toctree:: .. toctree::
:maxdepth: 4 :maxdepth: 4
@ -14,17 +11,14 @@ Subpackages
music.model music.model
music.tasks music.tasks
Module contents music Root Module
--------------- ------------------
.. automodule:: music .. automodule:: music
:members: :members:
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:
music.music module
------------------
.. automodule:: music.music .. automodule:: music.music
:members: :members:
:undoc-members: :undoc-members:

View File

@ -91,7 +91,7 @@ class Lock extends Component {
/** /**
* Grid of account cards with lock buttons * Grid of account cards with lock buttons
* @param {*} props * @param {*} props
* @returns * @returns Card component wrapped in grid cell
*/ */
function Row(props){ function Row(props){
const classes = useStyles(); const classes = useStyles();

View File

@ -53,7 +53,7 @@ class Tasks extends Component {
/** /**
* Grid of task cards * Grid of task cards
* @param {*} props * @param {*} props
* @returns * @returns Card compnent wrapped in grid cell
*/ */
function TaskType(props) { function TaskType(props) {
return ( return (

View File

@ -3,6 +3,9 @@ import { Chart, BarElement, BarController, LinearScale, CategoryScale, Legend, T
Chart.register(BarElement, BarController, LinearScale, CategoryScale, Legend, Title, Tooltip); Chart.register(BarElement, BarController, LinearScale, CategoryScale, Legend, Title, Tooltip);
/**
* Bar chart component using Chart.js
*/
class BarChart extends Component { class BarChart extends Component {
constructor(props) { constructor(props) {
@ -10,6 +13,9 @@ class BarChart extends Component {
this.chartRef = React.createRef(); this.chartRef = React.createRef();
} }
/**
* Load data from react properties
*/
componentDidMount() { componentDidMount() {
this.chart = new Chart(this.chartRef.current, { this.chart = new Chart(this.chartRef.current, {
type: 'bar', type: 'bar',
@ -43,7 +49,7 @@ class BarChart extends Component {
ticks: { ticks: {
color: "#d8d8d8", color: "#d8d8d8",
font: { font: {
size: 20 size: 16
} }
} }
}, },
@ -59,7 +65,10 @@ class BarChart extends Component {
} }
}); });
} }
/**
* Re-apply data to chart on update
*/
componentDidUpdate() { componentDidUpdate() {
this.chart.data.labels = this.props.data.map(d => d.label); this.chart.data.labels = this.props.data.map(d => d.label);
this.chart.data.datasets[0].data = this.props.data.map(d => d.value); this.chart.data.datasets[0].data = this.props.data.map(d => d.value);

View File

@ -11,6 +11,9 @@ var pieColours = ['rgb(55, 61, 255)', //blue
'rgb(242, 31, 235)', //pink 'rgb(242, 31, 235)', //pink
'rgb(242, 164, 31)']; 'rgb(242, 164, 31)'];
/**
* Pie chart component using Chart.js
*/
class PieChart extends Component { class PieChart extends Component {
constructor(props) { constructor(props) {
@ -18,6 +21,9 @@ class PieChart extends Component {
this.chartRef = React.createRef(); this.chartRef = React.createRef();
} }
/**
* Load data from react properties
*/
componentDidMount() { componentDidMount() {
this.chart = new Chart(this.chartRef.current, { this.chart = new Chart(this.chartRef.current, {
type: 'doughnut', type: 'doughnut',
@ -52,6 +58,9 @@ class PieChart extends Component {
}); });
} }
/**
* Re-apply data to chart on update
*/
componentDidUpdate() { componentDidUpdate() {
this.chart.data.labels = this.props.data.map(d => d.label); this.chart.data.labels = this.props.data.map(d => d.label);
this.chart.data.datasets[0].data = this.props.data.map(d => d.value); this.chart.data.datasets[0].data = this.props.data.map(d => d.value);

View File

@ -129,7 +129,7 @@ function TagCard(props){
{/* COUNT */} {/* COUNT */}
{'count' in props.tag && {'count' in props.tag &&
<Typography variant="h6" style={{color: "#b3b3b3"}}> <Typography variant="h6" style={{color: "#b3b3b3"}}>
{ props.tag.count } { props.tag.count.toLocaleString("en-GB") }
</Typography> </Typography>
} }
</CardContent> </CardContent>

View File

@ -20,7 +20,7 @@ const useStyles = makeStyles({
/** /**
* Tag View card * Tag View card
*/ */
class View extends Component{ class TagView extends Component{
constructor(props){ constructor(props){
super(props); super(props);
@ -221,7 +221,7 @@ class View extends Component{
/** /**
* Validate input, make tag part add request of API * Validate input, make tag part add request of API
* @returns * @returns Nothing
*/ */
handleAdd(){ handleAdd(){
@ -298,7 +298,7 @@ class View extends Component{
var all = [...this.state.tag.artists, ...this.state.tag.albums, ...this.state.tag.tracks]; var all = [...this.state.tag.artists, ...this.state.tag.albums, ...this.state.tag.tracks];
var data = all.map((entry) => { var scrobbleData = all.map((entry) => {
return { return {
"label": entry.name, "label": entry.name,
"value": entry.count "value": entry.count
@ -309,6 +309,17 @@ class View extends Component{
return 0; return 0;
}); });
var timeData = all.map((entry) => {
return {
"label": entry.name,
"value": entry.time_ms / (1000 * 60 * 60)
};
}).sort((a, b) => {
if(a.value < b.value) { return 1; }
if(a.value > b.value) { return -1; }
return 0;
});
const table = ( const table = (
<div style={{maxWidth: '1000px', margin: 'auto', marginTop: '20px'}}> <div style={{maxWidth: '1000px', margin: 'auto', marginTop: '20px'}}>
<Card align="center"> <Card align="center">
@ -394,13 +405,21 @@ class View extends Component{
{/* PIE CHART */} {/* PIE CHART */}
<Grid item xs={12}> <Grid item xs={12}>
<PieChart data={data} padding={100}/> <PieChart data={scrobbleData} padding={50}/>
</Grid> </Grid>
{/* BAR CHART */} {/* SCROBBLE BAR CHART */}
<Grid item xs={12}> <Grid item xs={12}>
<BarChart data={data} title='scrobbles' indexAxis='y'/> <BarChart data={scrobbleData} title='plays (scrobbles)' indexAxis='y'/>
</Grid> </Grid>
{/* TIME BAR CHART */}
{ this.state.tag.time_objects &&
<Grid item xs={12}>
<BarChart data={timeData} title='time listened (hours)' indexAxis='y'/>
</Grid>
}
</Grid> </Grid>
</CardContent> </CardContent>
{/* UPDATE BUTTON */} {/* UPDATE BUTTON */}
@ -415,7 +434,7 @@ class View extends Component{
} }
} }
export default View; export default TagView;
/** /**
* Grid component for holding artist/album/track cards * Grid component for holding artist/album/track cards
@ -462,7 +481,7 @@ function BlockGridItem (props) {
{/* SCROBBLE COUNT */} {/* SCROBBLE COUNT */}
{ 'count' in props.music_obj && { 'count' in props.music_obj &&
<Grid item xs={8}> <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.toLocaleString("en-GB") }</Typography>
</Grid> </Grid>
} }
{/* TIME */} {/* TIME */}
@ -500,7 +519,7 @@ function StatsCard (props) {
{/* SCROBBLE COUNT */} {/* SCROBBLE COUNT */}
<Grid item xs={12}> <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.toLocaleString("en-GB") }</Typography>
</Grid> </Grid>
{/* PERCENT */} {/* PERCENT */}