Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vueify footer #4520

Merged
merged 6 commits into from
Jun 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#### New Features

#### Improvements
- Converted the footer to a Vue component ([#4520](https://github.com/pymedusa/Medusa/pull/4520))

#### Fixes

Expand Down
20 changes: 5 additions & 15 deletions medusa/server/web/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
import os
import re
import sys
import time
import traceback
from builtins import str
from concurrent.futures import ThreadPoolExecutor

from mako.exceptions import RichTraceback
Expand All @@ -28,6 +26,7 @@

from six import (
iteritems,
text_type,
viewitems,
)

Expand Down Expand Up @@ -75,8 +74,8 @@ def __init__(self, rh, filename):
lookup = get_lookup()
self.template = lookup.get_template(filename)

base_url = (rh.request.headers.get('X-Forwarded-Proto', rh.request.protocol) + '://' +
rh.request.headers.get('X-Forwarded-Host', rh.request.host))
base_url = (rh.request.headers.get('X-Forwarded-Proto', rh.request.protocol) + '://'
+ rh.request.headers.get('X-Forwarded-Host', rh.request.host))

self.arguments = {
'sbHttpPort': app.WEB_PORT,
Expand All @@ -85,8 +84,7 @@ def __init__(self, rh, filename):
'sbHandleReverseProxy': app.HANDLE_REVERSE_PROXY,
'sbDefaultPage': app.DEFAULT_PAGE,
'loggedIn': rh.get_current_user(),
'sbStartTime': rh.startTime,
'sbPID': str(app.PID),
'sbPID': text_type(app.PID),
'title': 'FixME',
'header': 'FixME',
'controller': 'FixME',
Expand Down Expand Up @@ -114,7 +112,6 @@ def render(self, *args, **kwargs):
if key not in kwargs:
kwargs[key] = self.arguments[key]

kwargs['makoStartTime'] = time.time()
try:
return self.template.render_unicode(*args, **kwargs)
except Exception:
Expand All @@ -130,15 +127,8 @@ def render(self, *args, **kwargs):
class BaseHandler(RequestHandler):
"""Base Handler for the server."""

startTime = 0.

def __init__(self, *args, **kwargs):
self.startTime = time.time()

super(BaseHandler, self).__init__(*args, **kwargs)

def write_error(self, status_code, **kwargs):
"""Base error Handler for 404's."""
"""Error handler for 404's."""
# handle 404 http errors
if status_code == 404:
url = self.request.uri
Expand Down
7 changes: 5 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ multiline-quotes = double
docstring-quotes = double
; flake8
max-line-length = 160
; If per-file ignores are needed, use: https://github.com/snoack/flake8-per-file-ignores
per-file-ignores =
; http://flake8.pycqa.org/en/latest/user/options.html#cmdoption-flake8-per-file-ignores
; [Example]
; medusa/server/web/core/base.py:D100,D101,D102,D103,D107
ignore =
; Error codes reference: https://git.io/fNlTP
; Q002: Handled by flake8-docstrings
Expand Down Expand Up @@ -131,7 +134,7 @@ flake8-ignore =
medusa/server/web/config/subtitles.py D102 D200 D204 D400 E501 N802
medusa/server/web/core/__init__.py D104 F401
medusa/server/web/core/authentication.py D102 D200 D202 D204 D205 D400
medusa/server/web/core/base.py D100 D101 D102 D103 D200 D204 D210 D400 D401 N802 N815
medusa/server/web/core/base.py D100 D101 D102 D103 N802
medusa/server/web/core/calendar.py D200 D204 D205 D400 D401
medusa/server/web/core/file_browser.py D100 D101 D102 N803
medusa/server/web/core/history.py D100 D101 D102 N802
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def packages():
},
cmdclass={'test': PyTest},
tests_require=tests_runtime_require + [
'flake8>=3.5.0',
'flake8>=3.7.7',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version 3.7.0 added native support for per-file-ignores.
http://flake8.pycqa.org/en/latest/user/options.html#cmdoption-flake8-per-file-ignores

'flake8-docstrings>=1.3.0',
'flake8-import-order>=0.18',
'flake8-quotes>=1.0.0',
Expand Down
1 change: 1 addition & 0 deletions themes-default/slim/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"optimize-css-assets-webpack-plugin": "5.0.1",
"stylelint": "10.1.0",
"stylelint-config-standard": "18.3.0",
"timekeeper": "2.2.0",
"vue-jest": "3.0.4",
"vue-loader": "15.7.0",
"webpack": "4.34.0",
Expand Down
3 changes: 2 additions & 1 deletion themes-default/slim/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const app = new Vue({
const { $store } = this;
Promise.all([
$store.dispatch('login', { username: window.username }),
$store.dispatch('getConfig')
$store.dispatch('getConfig'),
$store.dispatch('getStats')
]).then(([_, config]) => {
if (isDevelopment) {
console.log('App Loaded!');
Expand Down
113 changes: 113 additions & 0 deletions themes-default/slim/src/components/app-footer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<template>
<footer>
<div class="footer clearfix">
<span class="footerhighlight">{{ stats.overall.shows.total }}</span> Shows (<span class="footerhighlight">{{ stats.overall.shows.active }}</span> Active)
| <span class="footerhighlight">{{ stats.overall.episodes.downloaded }}</span>
<template v-if="stats.overall.episodes.snatched">
<span class="footerhighlight"><app-link :href="`manage/episodeStatuses?whichStatus=${snatchedStatus}`" title="View overview of snatched episodes">+{{ stats.overall.episodes.snatched }}</app-link></span>
Snatched
</template>
/ <span class="footerhighlight">{{ stats.overall.episodes.total }}</span> Episodes Downloaded <span v-if="episodePercentage" class="footerhighlight">({{ episodePercentage }})</span>
| Daily Search: <span class="footerhighlight">{{ schedulerNextRun('dailySearch') }}</span>
| Backlog Search: <span class="footerhighlight">{{ schedulerNextRun('backlog') }}</span>
<div>
<template v-if="system.memoryUsage">
Memory used: <span class="footerhighlight">{{ system.memoryUsage }}</span> |
</template>
<!-- Load time: <span class="footerhighlight">{{ loadTime }}s</span> / Mako: <span class="footerhighlight">{{ makoTime }}s</span> | -->
Branch: <span class="footerhighlight">{{ config.branch || 'Unknown' }}</span> |
Now: <span class="footerhighlight">{{ nowInUserPreset }}</span>
</div>
</div>
</footer>
</template>

<script>
import { mapGetters, mapState } from 'vuex';
import formatDate from 'date-fns/format';

import { convertDateFormat } from '../utils/core';
import { AppLink } from './helpers';

export default {
name: 'app-footer',
components: {
AppLink
},
computed: {
...mapState([
'config',
'stats',
'system'
]),
...mapGetters([
'getStatus',
'getScheduler'
]),
snatchedStatus() {
const status = this.getStatus({ key: 'snatched' });
return status ? status.value : '';
},
episodePercentage() {
const { downloaded, total } = this.stats.overall.episodes;
if (!total) {
return '';
}
const raw = (downloaded / total) * 100;
return raw.toFixed(1) + '%';
},
nowInUserPreset() {
const { datePreset, timePreset } = this.config;
const preset = convertDateFormat(`${datePreset} ${timePreset}`);
return formatDate(new Date(), preset);
}
},
methods: {
/**
* Return a formatted next run time of the scheduler matching the provided key.
*
* @param {string} scheduler A scheduler key.
* @returns {string} The formatted next run time.
*/
schedulerNextRun(scheduler) {
/** @type {import('../store/modules/system').Scheduler} */
const { nextRun } = this.getScheduler(scheduler);
// The next run can be `undefined` when the scheduler was not initialized
// on the backend, and `null` when the scheduler is not enabled.
if (nextRun === undefined) {
return '??:??:??';
}
if (nextRun === null) {
return 'Disabled';
}
return this.formatTimeDuration(nextRun);
},
/**
* Return a formatted string representing the provided duration.
*
* This function will not use any units greater than a day.
* @param {number} durationInMs Duration of time in milliseconds.
* @returns {string} The formatted duration.
*
* @example
*/
formatTimeDuration(durationInMs) {
const days = parseInt(durationInMs / 86400000, 10);
let daysText = '';
if (days > 0) {
daysText = String(days) + (days > 1 ? ' days, ' : ' day, ');
}

const date = new Date(durationInMs % 86400000);
const zeroPad = (num, len = 2) => String(num).padStart(len, '0');
const hours = String(date.getUTCHours());
const minutes = zeroPad(date.getUTCMinutes());
const seconds = zeroPad(date.getUTCSeconds() + Math.round(date.getUTCMilliseconds() / 1000));
return daysText + [hours, minutes, seconds].join(':');
}
}
};
</script>

<style scoped>
</style>
1 change: 1 addition & 0 deletions themes-default/slim/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export { default as AddRecommended } from './add-recommended.vue';
export { default as AddShowOptions } from './add-show-options.vue';
export { default as AddShows } from './add-shows.vue';
export { default as AnidbReleaseGroupUi } from './anidb-release-group-ui.vue';
export { default as AppFooter } from './app-footer.vue';
export { default as AppHeader } from './app-header.vue';
export { default as Backstretch } from './backstretch.vue';
export { default as Config } from './config.vue';
Expand Down
5 changes: 4 additions & 1 deletion themes-default/slim/src/global-vue-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Snotify from 'vue-snotify';
import {
AddShowOptions,
AnidbReleaseGroupUi,
AppFooter,
AppHeader,
AppLink,
Asset,
Expand Down Expand Up @@ -46,6 +47,7 @@ export const registerGlobalComponents = () => {
// @TODO: These should be registered in an `App.vue` component when possible,
// along with some of the `main.mako` template
components = components.concat([
AppFooter,
AppHeader,
ScrollButtons,
SubMenu
Expand Down Expand Up @@ -119,7 +121,8 @@ export default () => {
/* This is used by the `app-header` component
to only show the logout button if a username is set */
store.dispatch('login', { username }),
store.dispatch('getConfig')
store.dispatch('getConfig'),
store.dispatch('getStats')
]).then(([_, config]) => {
this.$emit('loaded');
// Legacy - send config.main to jQuery (received by index.js)
Expand Down
40 changes: 37 additions & 3 deletions themes-default/slim/src/store/modules/system.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
import { ADD_CONFIG } from '../mutation-types';

/**
* An object representing a scheduler.
*
* If a scheduler isn't initialized on the backend,
* this object will only have the `key` and `name` properties.
* @typedef {object} Scheduler
* @property {string} key
* A camelCase key representing this scheduler.
* @property {string} name
* The scheduler's name.
* @property {boolean} [isAlive]
* Is the scheduler alive?
* @property {boolean|string} [isEnabled]
* Is the scheduler enabled? For the `backlog` scheduler, the value might be `Paused`.
* @property {boolean} [isActive]
* Is the scheduler's action currently running?
* @property {string|null} [startTime]
* The time of day in which this scheduler runs (format: ISO-8601 time), or `null` if not applicable.
* @property {number} [cycleTime]
* The duration in milliseconds between each run, or `null` if not applicable.
* @property {number} [nextRun]
* The duration in milliseconds until the next run.
* @property {string} [lastRun]
* The date and time of the previous run (format: ISO-8601 date-time).
* @property {boolean} [isSilent]
* Is the scheduler silent?
*/

const state = {
memoryUsage: null,
schedulers: [],
Expand All @@ -15,9 +43,15 @@ const mutations = {
};

const getters = {
// Get a scheduler object using a key
getScheduler: state => key => {
return state.schedulers.find(scheduler => key === scheduler.key);
getScheduler: state => {
/**
* Get a scheduler object using a key.
*
* @param {string} key The combined quality to split.
* @returns {Scheduler|object} The scheduler object or an empty object if not found.
*/
const _getScheduler = key => state.schedulers.find(scheduler => key === scheduler.key) || {};
return _getScheduler;
}
};

Expand Down