Skip to content

Commit

Permalink
fix/improve parsing of last active dates
Browse files Browse the repository at this point in the history
parseDT only uses the magic json.Unmarshal method if theres an error
with the better version. Error came from some times being sent without a
"Z" at the end denoting UTC.
  • Loading branch information
hrfee committed Dec 3, 2020
1 parent 3c952d2 commit a1e30ff
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 79 deletions.
Empty file added %1
Empty file.
17 changes: 14 additions & 3 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -839,10 +839,20 @@ type dateToParse struct {
Parsed time.Time `json:"parseme"`
}

// json magically parses datetimes so why not
func parseDt(date string) time.Time {
func parseDT(date string) time.Time {
// decent method
dt, err := time.Parse("2006-01-02T15:04:05.000000", date)
if err == nil {
return dt
}
// magic method
// some stored dates from jellyfin have no timezone at the end, if not we assume UTC
if date[len(date)-1] != 'Z' {
date += "Z"
}
timeJSON := []byte("{ \"parseme\": \"" + date + "\" }")
var parsed dateToParse
// Magically turn it into a time.Time
json.Unmarshal(timeJSON, &parsed)
return parsed.Parsed
}
Expand All @@ -869,8 +879,9 @@ func (app *appContext) GetUsers(gc *gin.Context) {
var user respUser
user.LastActive = "n/a"
if jfUser["LastActivityDate"] != nil {
date := parseDt(jfUser["LastActivityDate"].(string))
date := parseDT(jfUser["LastActivityDate"].(string))
user.LastActive = app.formatDatetime(date)
// fmt.Printf("%s: %s, %s, %+v\n", jfUser["Name"].(string), jfUser["LastActivityDate"].(string), user.LastActive, date)
}
user.ID = jfUser["Id"].(string)
user.Name = jfUser["Name"].(string)
Expand Down
8 changes: 8 additions & 0 deletions nohup.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Nov 29 23:37 : exception: Failed to bind to '[::]:6600'; Failed to bind socket: Address already in use
/usr/bin/mpDris2:1367: DeprecationWarning: The SafeConfigParser class has been renamed to ConfigParser in Python 3.2. This alias will be removed in future versions. Use ConfigParser directly instead.
config = configparser.SafeConfigParser()
2020-11-29 23:37:53,454 mpDris2 INFO: Using file:///home/hrfee/Music as music library path.
2020-11-29 23:37:53,454 mpDris2 INFO: Using Mutagen to read covers from music files.
2020-11-29 23:37:53,456 base INFO: Calling MPD connect('localhost', '6600', timeout=None)
2020-11-29 23:37:57,776 mpDris2 INFO: Replaced by :1.24 (PID 1128)
2020-11-29 23:37:57,777 base INFO: Calling MPD disconnect()
68 changes: 4 additions & 64 deletions ts/accounts.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,15 @@
import { checkCheckboxes, populateUsers, populateRadios } from "./modules/accounts.js";
import { _post, _get, _delete, rmAttr, addAttr } from "./modules/common.js";
import { checkCheckboxes, populateUsers, populateRadios, changeEmail, validateEmail } from "./modules/accounts.js";
import { _post, _get, _delete, rmAttr, addAttr, createEl } from "./modules/common.js";
import { populateProfiles } from "./modules/settings.js";
import { Focus, Unfocus, createEl, storeDefaults } from "./modules/admin.js";
import { Focus, Unfocus, storeDefaults } from "./modules/admin.js";

interface aWindow extends Window {
changeEmail(icon: HTMLElement, id: string): void;
}

declare var window: aWindow;

const validateEmail = (email: string): boolean => /\S+@\S+\.\S+/.test(email);

window.changeEmail = (icon: HTMLElement, id: string): void => {
const iconContent = icon.outerHTML;
icon.setAttribute('class', '');
const entry = icon.nextElementSibling as HTMLInputElement;
const ogEmail = entry.value;
entry.readOnly = false;
entry.classList.remove('form-control-plaintext');
entry.classList.add('form-control');
if (ogEmail == "") {
entry.placeholder = 'Address';
}
const tick = createEl(`
<i class="fa fa-check d-inline-block icon-button text-success" style="margin-left: 0.5rem; margin-right: 0.5rem;"></i>
`);
tick.onclick = (): void => {
const newEmail = entry.value;
if (!validateEmail(newEmail) || newEmail == ogEmail) {
return;
}
cross.remove();
const spinner = createEl(`
<div class="spinner-border spinner-border-sm" role="status" style="width: 1rem; height: 1rem; margin-left: 0.5rem;">
<span class="sr-only">Saving...</span>
</div>
`);
tick.replaceWith(spinner);
let send = {};
send[id] = newEmail;
_post("/users/emails", send, function (): void {
if (this.readyState == 4) {
if (this.status == 200 || this.status == 204) {
entry.nextElementSibling.remove();
} else {
entry.value = ogEmail;
}
}
});
icon.outerHTML = iconContent;
entry.readOnly = true;
entry.classList.remove('form-control');
entry.classList.add('form-control-plaintext');
entry.placeholder = '';
};
const cross = createEl(`
<i class="fa fa-close d-inline-block icon-button text-danger"></i>
`);
cross.onclick = (): void => {
tick.remove();
cross.remove();
icon.outerHTML = iconContent;
entry.readOnly = true;
entry.classList.remove('form-control');
entry.classList.add('form-control-plaintext');
entry.placeholder = '';
entry.value = ogEmail;
};
icon.parentNode.appendChild(tick);
icon.parentNode.appendChild(cross);
};
window.changeEmail = changeEmail;

(<HTMLInputElement>document.getElementById('selectAll')).onclick = function (): void {
const checkboxes: NodeListOf<HTMLInputElement> = document.getElementById('accountsList').querySelectorAll('input[type=checkbox]');
Expand Down
68 changes: 63 additions & 5 deletions ts/modules/accounts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { _get, _post, _delete } from "../modules/common.js";
import { _get, _post, _delete, createEl } from "../modules/common.js";
import { Focus, Unfocus } from "../modules/admin.js";

interface aWindow extends Window {
Expand All @@ -7,6 +7,8 @@ interface aWindow extends Window {

declare var window: aWindow;

export const validateEmail = (email: string): boolean => /\S+@\S+\.\S+/.test(email);

export const checkCheckboxes = (): void => {
const defaultsButton = document.getElementById('accountsTabSetDefaults');
const deleteButton = document.getElementById('accountsTabDelete');
Expand All @@ -28,6 +30,66 @@ export const checkCheckboxes = (): void => {

window.checkCheckboxes = checkCheckboxes;

export function changeEmail(icon: HTMLElement, id: string): void {
const iconContent = icon.outerHTML;
icon.setAttribute('class', '');
const entry = icon.nextElementSibling as HTMLInputElement;
const ogEmail = entry.value;
entry.readOnly = false;
entry.classList.remove('form-control-plaintext');
entry.classList.add('form-control');
if (ogEmail == "") {
entry.placeholder = 'Address';
}
const tick = createEl(`
<i class="fa fa-check d-inline-block icon-button text-success" style="margin-left: 0.5rem; margin-right: 0.5rem;"></i>
`);
tick.onclick = (): void => {
const newEmail = entry.value;
if (!validateEmail(newEmail) || newEmail == ogEmail) {
return;
}
cross.remove();
const spinner = createEl(`
<div class="spinner-border spinner-border-sm" role="status" style="width: 1rem; height: 1rem; margin-left: 0.5rem;">
<span class="sr-only">Saving...</span>
</div>
`);
tick.replaceWith(spinner);
let send = {};
send[id] = newEmail;
_post("/users/emails", send, function (): void {
if (this.readyState == 4) {
if (this.status == 200 || this.status == 204) {
entry.nextElementSibling.remove();
} else {
entry.value = ogEmail;
}
}
});
icon.outerHTML = iconContent;
entry.readOnly = true;
entry.classList.remove('form-control');
entry.classList.add('form-control-plaintext');
entry.placeholder = '';
};
const cross = createEl(`
<i class="fa fa-close d-inline-block icon-button text-danger"></i>
`);
cross.onclick = (): void => {
tick.remove();
cross.remove();
icon.outerHTML = iconContent;
entry.readOnly = true;
entry.classList.remove('form-control');
entry.classList.add('form-control-plaintext');
entry.placeholder = '';
entry.value = ogEmail;
};
icon.parentNode.appendChild(tick);
icon.parentNode.appendChild(cross);
};

export function populateUsers(): void {
const acList = document.getElementById('accountsList');
acList.innerHTML = `
Expand All @@ -53,10 +115,6 @@ export function populateUsers(): void {
return entry.outerHTML;
};
const template = (id: string, username: string, email: string, lastActive: string, admin: boolean): string => {
let isAdmin = "No";
if (admin) {
isAdmin = "Yes";
}
let fci = "form-check-input";
if (window.bsVersion != 5) {
fci = "";
Expand Down
8 changes: 1 addition & 7 deletions ts/modules/admin.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { rmAttr, addAttr, _post, _get, _delete } from "../modules/common.js";
import { rmAttr, addAttr, _post, _get, _delete, createEl } from "../modules/common.js";

export const Focus = (el: HTMLElement): void => rmAttr(el, 'unfocused');
export const Unfocus = (el: HTMLElement): void => addAttr(el, 'unfocused');

export function createEl(html: string): HTMLElement {
let div = document.createElement('div') as HTMLDivElement;
div.innerHTML = html;
return div.firstElementChild as HTMLElement;
}

export function storeDefaults(users: string | Array<string>): void {
const button = document.getElementById('storeDefaults') as HTMLButtonElement;
button.disabled = true;
Expand Down
6 changes: 6 additions & 0 deletions ts/modules/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
declare var window: Window;

export function createEl(html: string): HTMLElement {
let div = document.createElement('div') as HTMLDivElement;
div.innerHTML = html;
return div.firstElementChild as HTMLElement;
}

export function serializeForm(id: string): Object {
const form = document.getElementById(id) as HTMLFormElement;
let formData = {};
Expand Down

0 comments on commit a1e30ff

Please sign in to comment.