Compare commits

..

5 Commits

Author SHA1 Message Date
R. Miles McCain
17bb5cda0d Improve litepicker box shadow 2021-05-14 15:32:16 +00:00
R. Miles McCain
84c647ad43 Update packages 2021-05-14 15:32:08 +00:00
Paweł Jastrzębski
0e37e7f042 Update litepicker and add ranges plugin
Fix litepicker colors

Fix litepicker event

Add custom ranges to litepicker

Fix code style

Remove some date ranges

Fix date ranges

Replace yesterday date range with last 3 days
2021-05-14 15:09:14 +00:00
CasperVerswijvelt
a76e0feaf3 Add custom location url from environment variable
Remove trailing dollar in long and lat placeholder
2021-05-14 15:07:49 +00:00
CasperVerswijvelt
109d977932 Make favicon not squish and add ellipsis overflow
General styling improvements

Many UI Improvements

- Consistent spacing between titles and content
- Removed many ugly text squishing by hiding overflowing text with ellipsis
- Fixed Service favicon being squisched by long service name
- Hide scrollbar in 'more session' screen when content isn't scrollable
- Fix apexcharts tooltips and labels being cut off by card class

Disable wrapping in table cells, prefer ellipsis

Ellipsis overflow for long url on hit page

Fix flex grow in header not working as intended

Remove forgotten truncatechars

Fix code checks, add button role and tabindex
2021-05-14 15:04:30 +00:00
11 changed files with 23 additions and 127 deletions

View File

@@ -1,46 +0,0 @@
name: Build docker images
on:
push:
tags:
- "*"
jobs:
publish_to_docker_hub:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Prepare tags
id: prep
run: |
DOCKER_IMAGE=milesmcc/shynet
VERSION=${GITHUB_REF#refs/tags/}
TAGS="${DOCKER_IMAGE}:${VERSION},${DOCKER_IMAGE}:latest"
echo ::set-output name=tags::${TAGS}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Build and push advanced image
id: docker_build
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: ${{ steps.prep.outputs.tags }}

View File

@@ -1,4 +1,4 @@
FROM python:alpine3.12 FROM python:3-alpine
# Getting things ready # Getting things ready
WORKDIR /usr/src/shynet WORKDIR /usr/src/shynet

View File

@@ -92,7 +92,3 @@ AGGRESSIVE_HASH_SALTING=True
# - https://www.google.com/maps/search/?api=1&query=$LATITUDE,$LONGITUDE # - https://www.google.com/maps/search/?api=1&query=$LATITUDE,$LONGITUDE
# - https://www.mapquest.com/near-$LATITUDE,$LONGITUDE # - https://www.mapquest.com/near-$LATITUDE,$LONGITUDE
LOCATION_URL=https://www.openstreetmap.org/?mlat=$LATITUDE&mlon=$LONGITUDE LOCATION_URL=https://www.openstreetmap.org/?mlat=$LATITUDE&mlon=$LONGITUDE
# How many services should be displayed on dashboard page?
# Set to big number if you don't want pagination at all.
DASHBOARD_PAGE_SIZE=5

View File

@@ -127,11 +127,6 @@
"description": "Custom location url to link to in frontend.", "description": "Custom location url to link to in frontend.",
"value": "https://www.openstreetmap.org/?mlat=$LATITUDE&mlon=$LONGITUDE", "value": "https://www.openstreetmap.org/?mlat=$LATITUDE&mlon=$LONGITUDE",
"required": false "required": false
},
"DASHBOARD_PAGE_SIZE": {
"description": "How many services should be displayed on dashboard page?",
"value": "5",
"required": false
} }
} }
} }

View File

@@ -8,14 +8,11 @@
var Shynet = { var Shynet = {
idempotency: null, idempotency: null,
heartbeatTaskId: null, heartbeatTaskId: null,
skipHeartbeat: false,
sendHeartbeat: function () { sendHeartbeat: function () {
try { try {
if (document.hidden || Shynet.skipHeartbeat) { if (document.hidden) {
return; return;
} }
Shynet.skipHeartbeat = true;
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open( xhr.open(
"POST", "POST",
@@ -23,12 +20,6 @@ var Shynet = {
true true
); );
xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader("Content-Type", "application/json");
xhr.onload = function () {
Shynet.skipHeartbeat = false;
};
xhr.onerror = function () {
Shynet.skipHeartbeat = false;
};
xhr.send( xhr.send(
JSON.stringify({ JSON.stringify({
idempotency: Shynet.idempotency, idempotency: Shynet.idempotency,
@@ -39,14 +30,13 @@ var Shynet = {
window.performance.timing.navigationStart, window.performance.timing.navigationStart,
}) })
); );
} catch (e) {} } catch (e) { }
}, },
newPageLoad: function () { newPageLoad: function () {
if (Shynet.heartbeatTaskId != null) { if (Shynet.heartbeatTaskId != null) {
clearInterval(Shynet.heartbeatTaskId); clearInterval(Shynet.heartbeatTaskId);
} }
Shynet.idempotency = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); Shynet.idempotency = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
Shynet.skipHeartbeat = false;
Shynet.heartbeatTaskId = setInterval(Shynet.sendHeartbeat, parseInt("{{heartbeat_frequency}}")); Shynet.heartbeatTaskId = setInterval(Shynet.sendHeartbeat, parseInt("{{heartbeat_frequency}}"));
Shynet.sendHeartbeat(); Shynet.sendHeartbeat();
} }

View File

@@ -8,12 +8,12 @@ from django.conf import settings
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.db.models.functions import TruncDate, TruncHour from django.db.models.functions import TruncDate
from django.db.utils import NotSupportedError from django.db.utils import NotSupportedError
from django.shortcuts import reverse from django.shortcuts import reverse
from django.utils import timezone from django.utils import timezone
# How long a session a needs to go without an update to no longer be considered 'active' (i.e., currently online)
ACTIVE_USER_TIMEDELTA = timezone.timedelta( ACTIVE_USER_TIMEDELTA = timezone.timedelta(
milliseconds=settings.SCRIPT_HEARTBEAT_FREQUENCY * 2 milliseconds=settings.SCRIPT_HEARTBEAT_FREQUENCY * 2
) )
@@ -125,10 +125,8 @@ class Service(models.Model):
Session = apps.get_model("analytics", "Session") Session = apps.get_model("analytics", "Session")
Hit = apps.get_model("analytics", "Hit") Hit = apps.get_model("analytics", "Hit")
tz_now = timezone.now()
currently_online = Session.objects.filter( currently_online = Session.objects.filter(
service=self, last_seen__gt=tz_now - ACTIVE_USER_TIMEDELTA service=self, last_seen__gt=timezone.now() - ACTIVE_USER_TIMEDELTA
).count() ).count()
sessions = Session.objects.filter( sessions = Session.objects.filter(
@@ -212,24 +210,6 @@ class Service(models.Model):
if session_count == 0: if session_count == 0:
avg_session_duration = None avg_session_duration = None
# Show hourly chart for date ranges of 3 days or less, otherwise daily chart
if (end_time - start_time).days < 3:
session_chart_tooltip_format = "MM/dd HH:mm"
session_chart_granularity = "hourly"
session_chart_data = {
k["hour"]: k["count"]
for k in sessions.annotate(hour=TruncHour("start_time"))
.values("hour")
.annotate(count=models.Count("uuid"))
.order_by("hour")
}
for hour_offset in range(int((end_time - start_time).total_seconds() / 3600) + 1):
hour = (start_time + timezone.timedelta(hours=hour_offset))
if hour not in session_chart_data:
session_chart_data[hour] = 0 if hour <= tz_now else None
else:
session_chart_tooltip_format = "MMM d"
session_chart_granularity = "daily"
session_chart_data = { session_chart_data = {
k["date"]: k["count"] k["date"]: k["count"]
for k in sessions.annotate(date=TruncDate("start_time")) for k in sessions.annotate(date=TruncDate("start_time"))
@@ -240,7 +220,7 @@ class Service(models.Model):
for day_offset in range((end_time - start_time).days + 1): for day_offset in range((end_time - start_time).days + 1):
day = (start_time + timezone.timedelta(days=day_offset)).date() day = (start_time + timezone.timedelta(days=day_offset)).date()
if day not in session_chart_data: if day not in session_chart_data:
session_chart_data[day] = 0 if day <= tz_now.date() else None session_chart_data[day] = 0
return { return {
"currently_online": currently_online, "currently_online": currently_online,
@@ -269,8 +249,6 @@ class Service(models.Model):
) )
] ]
), ),
"session_chart_tooltip_format": session_chart_tooltip_format,
"session_chart_granularity": session_chart_granularity,
"online": True, "online": True,
} }

View File

@@ -51,7 +51,7 @@
</div> </div>
<hr class="sep h-4"> <hr class="sep h-4">
<div style="bottom: -1px;"> <div style="bottom: -1px;">
{% include 'dashboard/includes/time_chart.html' with data=stats.session_chart_data sparkline=True height=100 name=object.uuid tooltip_format=stats.session_chart_tooltip_format granularity=stats.session_chart_granularity %} {% include 'dashboard/includes/time_chart.html' with data=stats.session_chart_data sparkline=True height=100 name=object.uuid %}
</div> </div>
{% endwith %} {% endwith %}
</a> </a>

View File

@@ -6,9 +6,6 @@
}, },
tooltip: { tooltip: {
shared: false, shared: false,
x: {
format: '{{tooltip_format|default:"MMM d"}}',
},
}, },
colors: ["#805AD5"], colors: ["#805AD5"],
chart: { chart: {
@@ -37,14 +34,6 @@
stops: [0, 75, 100] stops: [0, 75, 100]
}, },
}, },
{% if granularity == "daily" and click_zoom %}
events: {
markerClick: function(event, chartContext, { seriesIndex, dataPointIndex, w: {config}}) {
const day = config.series[seriesIndex].data[dataPointIndex].x
window.location.href = `?startDate=${day}&endDate=${day}`
},
},
{% endif %}
}, },
grid: { grid: {
padding: { padding: {
@@ -74,9 +63,6 @@
}, },
xaxis: { xaxis: {
type: "datetime", type: "datetime",
labels: {
datetimeUTC: false
},
}, },
stroke: { stroke: {
width: 1.5, width: 1.5,

View File

@@ -94,7 +94,7 @@
{% endwith %} {% endwith %}
</div> </div>
<div class="card overflow-visible ~neutral !low py-0 mb-6"> <div class="card overflow-visible ~neutral !low py-0 mb-6">
{% include 'dashboard/includes/time_chart.html' with data=stats.session_chart_data tooltip_format=stats.session_chart_tooltip_format granularity=stats.session_chart_granularity click_zoom=True %} {% include 'dashboard/includes/time_chart.html' with data=stats.session_chart_data %}
</div> </div>
{% endif %} {% endif %}
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">

View File

@@ -25,7 +25,7 @@ from .mixins import DateRangeMixin
class DashboardView(LoginRequiredMixin, DateRangeMixin, ListView): class DashboardView(LoginRequiredMixin, DateRangeMixin, ListView):
model = Service model = Service
template_name = "dashboard/pages/dashboard.html" template_name = "dashboard/pages/dashboard.html"
paginate_by = settings.DASHBOARD_PAGE_SIZE paginate_by = 5
def get_queryset(self): def get_queryset(self):
return Service.objects.filter( return Service.objects.filter(

View File

@@ -18,7 +18,7 @@ import urllib.parse as urlparse
from django.contrib.messages import constants as messages from django.contrib.messages import constants as messages
# Increment on new releases # Increment on new releases
VERSION = "v0.9.1" VERSION = "v0.8.2"
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -347,6 +347,3 @@ AGGRESSIVE_HASH_SALTING = os.getenv("AGGRESSIVE_HASH_SALTING", "False") == "True
# What location url should be linked to in the frontend? # What location url should be linked to in the frontend?
LOCATION_URL = os.getenv("LOCATION_URL", "https://www.openstreetmap.org/?mlat=$LATITUDE&mlon=$LONGITUDE") LOCATION_URL = os.getenv("LOCATION_URL", "https://www.openstreetmap.org/?mlat=$LATITUDE&mlon=$LONGITUDE")
# How many services should be displayed on dashboard page?
DASHBOARD_PAGE_SIZE = int(os.getenv("DASHBOARD_PAGE_SIZE", "5"))