This commit is contained in:
Xavier Fiechter 2024-03-19 23:34:01 +01:00
parent fedbd8ab0b
commit b9ef1818ae
18 changed files with 110 additions and 113 deletions

View file

@ -7,13 +7,17 @@ from background_task.models import CompletedTask
logger = logging.getLogger('labelbase')
def _remove_completed_task():
threshold = timezone.now() - timedelta(days=1)
deleted_count, _ = CompletedTask.objects.filter(locked_at__lte=threshold).delete()
return deleted_count
class Command(BaseCommand):
help = "Remove completed tasks after 1 day."
def handle(self, *args, **options):
try:
threshold = timezone.now() - timedelta(days=1)
deleted_count, _ = CompletedTask.objects.filter(locked_at__lte=threshold).delete()
deleted_count = _remove_completed_task()
self.stdout.write(self.style.SUCCESS(f"Deleted {deleted_count} completed tasks."))
except Exception as ex:
logger.exception("Error occurred while deleting completed tasks")

View file

@ -1,2 +1 @@
* * * * * /usr/sbin/logrotate /app/logrotate.conf
1 0 * * * /usr/local/bin/python /app/manage.py remove_completed

View file

@ -16,7 +16,5 @@ def perform_tasks_on_login(sender, user, request, **kwargs):
check_all_outputs(user.id)
if Label.objects.filter(labelbase__user_id=user.id).exists():
messages.info(request, "<strong>Sync in progress:</strong> We are checking your unspent transaction outputs now.")
# Store nearest price information.
HistoricalPrice.get_or_create_from_api(-1)

View file

@ -1,5 +1,8 @@
import logging
from background_task import background
from background_task.management.commands.remove_completed import _remove_completed_task
from labelbase.models import Label
from finances.electrum import checkup_label
@ -18,7 +21,8 @@ def check_all_outputs(user_id, labelbase_id=None, loop=None):
if label.type == "output":
logger.debug("check_spent, label.id {}".format(label.id))
check_spent(label.id)
# Cleanup
_remove_completed_task()
@background(schedule={'run_at': 0}, remove_existing_tasks=True)
def check_spent(label_id, loop=None):

View file

@ -2,4 +2,4 @@ from django import forms
class JSONUploadForm(forms.Form):
json_file = forms.FileField() # in memory upload
json_file = forms.FileField(label="Knowledge Base, JSON file format") # in memory upload

View file

@ -110,6 +110,7 @@ INSTALLED_APPS = [
"connectrum",
"knowledge_base",
"hashtags",
"statusapp",
]
@ -146,6 +147,7 @@ TEMPLATES = [
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"sekizai.context_processors.sekizai",
"statusapp.context_processors.latest_status_message",
],
},
},

View file

View file

@ -0,0 +1,8 @@
from django.contrib import admin
from .models import StatusMessage
class StatusMessageAdmin(admin.ModelAdmin):
list_display = ['message', 'created_at']
search_fields = ['message']
admin.site.register(StatusMessage, StatusMessageAdmin)

6
django/statusapp/apps.py Normal file
View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class StatusappConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'statusapp'

View file

@ -0,0 +1,8 @@
from .models import StatusMessage
def latest_status_message(request):
try:
latest_message = StatusMessage.objects.latest('created_at')
except StatusMessage.DoesNotExist:
latest_message = None
return {'latest_status_message': latest_message}

View file

@ -0,0 +1,23 @@
# Generated by Django 3.2.24 on 2024-03-19 22:33
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='StatusMessage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('message', models.CharField(max_length=1000)),
('color', models.CharField(max_length=20)),
('created_at', models.DateTimeField(auto_now_add=True)),
],
),
]

View file

View file

@ -0,0 +1,9 @@
from django.db import models
class StatusMessage(models.Model):
message = models.CharField(max_length=1000)
color = models.CharField(max_length=20)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.message

View file

@ -0,0 +1,3 @@
from django.test import TestCase

View file

@ -0,0 +1 @@
from django.shortcuts import render

View file

@ -15,7 +15,7 @@
<link href="{% static 'css/dataTables.bootstrap5.min.css' %}" rel="stylesheet">
<link href="{% static 'css/labelbase.css' %}" rel="stylesheet">
{% block extra_media %}{% endblock %}
<style media="screen">
{% block css %}{% endblock %}
{% render_block "css" %}
@ -26,10 +26,12 @@
<body>
<!-- v1 -->
<div style="background: goldenrod;z-index: 999999;position: relative;" class="d-block px-3 py-1 text-center text-bold skippy">
<a href="#" class="text-white text-decoration-none">Run your self-hosted Labelbase! <u>Learn more</u></a>
</div>
{% if latest_status_message %}
<div style="background: {{ latest_status_message.color }}; z-index: 999999;position: relative;" class="d-block px-3 py-1 text-center text-bold skippy">
<a href="#" class="text-white text-decoration-none">{{ latest_status_message.message }}</a>
</div>
{% endif %}
<!-- end v1 -->
<!-- v2 --
<div style="background: rgb(33, 37, 41);z-index: 999999;position: relative;/*! padding: 11px; */" class="d-block px-3 py-1 text-center text-bold skippy">
@ -181,9 +183,11 @@
<span>Community</span>
</h6>
{% comment %}
<li class="nav-item ">
<a class="nav-link" href="{% url "donate" %}"><span data-feather="heart" class="align-text-bottom"></span> Donate</a>
<li>
{% endcomment %}
<li class="nav-item ">
<a class="nav-link" href="https://labelbase.space/newsletter/"><span data-feather="mail" class="align-text-bottom"></span> Newsletter</a>

View file

@ -8,9 +8,9 @@
{% block content %}
<div class=" p-3 pb-md-4 mx-auto text-left">
<h2 class="display-8 fw-normal">Support Labelbase: Make a Difference with Your Donation</h2>
<h2 class="display-8 fw-normal">Support Labelbase: Make a difference with your donation</h2>
<br>
<p class="fs-5 text-muted">Every Contribution Helps Us Build a Better Bitcoin Labeling Experience</p>
<p class="fs-5 text-muted">Every contribution helps us build a better Bitcoin labeling experience.</p>
<p class="fs-5 text-muted">
Choose the amount you'd like to donate to Labelbase.
</p> <p class="fs-5 text-muted">
@ -20,32 +20,7 @@ Your generosity ensures that our project continues to thrive and evolve, directl
Thank you for your support!
</p>
<style>
{% addtoblock "css" %}
.btcpay-form { display: inline-flex; align-items: center; justify-content: center; } .btcpay-form--inline { flex-direction: row; } .btcpay-form--block { flex-direction: column; } .btcpay-form--inline .submit { margin-left: 15px; } .btcpay-form--block select { margin-bottom: 10px; } .btcpay-form .btcpay-custom-container{ text-align: center; }.btcpay-custom { display: flex; align-items: center; justify-content: center; } .btcpay-form .plus-minus { cursor:pointer; font-size:25px; line-height: 25px; background: #DFE0E1; height: 30px; width: 45px; border:none; border-radius: 60px; margin: auto 5px; display: inline-flex; justify-content: center; } .btcpay-form select { -moz-appearance: none; -webkit-appearance: none; appearance: none; color: currentColor; background: transparent; border:1px solid transparent; display: block; padding: 1px; margin-left: auto; margin-right: auto; font-size: 11px; cursor: pointer; } .btcpay-form select:hover { border-color: #ccc; } .btcpay-form option { color: #000; background: rgba(0,0,0,.1); } .btcpay-input-price { -moz-appearance: textfield; border: none; box-shadow: none; text-align: center; font-size: 25px; margin: auto; border-radius: 5px; line-height: 35px; background: #fff; }.btcpay-input-price::-webkit-outer-spin-button, .btcpay-input-price::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
{% endaddtoblock %}
</style>
<form method="POST" action="http://178.194.166.112:3003/api/v1/invoices" class="btcpay-form btcpay-form--block">
<input type="hidden" name="storeId" value="77qPp3s5bGFQMfuStNWuWXmdKfn4t4tSQpvucQ4UdzKx" />
<input type="hidden" name="jsonResponse" value="true" />
<input type="hidden" name="notifyEmail" value="labelbase.space@proton.me" />
<div class="btcpay-custom-container">
<div class="btcpay-custom">
<input class="btcpay-input-price" type="number" name="price" min="1" max="210000" step="1" value="1" data-price="1" style="width:2em;" />
</div>
<select name="currency">
<option value="USD" selected>USD</option>
<option value="GBP">GBP</option>
<option value="EUR">EUR</option>
<option value="BTC">BTC</option>
</select>
</div>
<button type="submit" class="submit" name="submit" style="min-width:146px;min-height:40px;border-radius:4px;border-style:none;background-color:#0f3b21;cursor:pointer;" title="Pay with BTCPay Server, a Self-Hosted Bitcoin Payment Processor"><span style="color:#fff">Donate with</span>
<img src="http://178.194.166.112:3003/img/paybutton/logo.svg" style="height:40px;display:inline-block;padding:5% 0 5% 5px;vertical-align:middle;">
</button></form>
@ -53,69 +28,5 @@ Thank you for your support!
{% addtoblock "js" %}
if (!window.btcpay) {
var script = document.createElement('script');
script.src = "http://178.194.166.112:3003/modal/btcpay.js";
document.getElementsByTagName('head')[0].append(script);
}
function handleFormSubmit(event) {
event.preventDefault();
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200 && this.responseText) {
window.btcpay.appendInvoiceFrame(JSON.parse(this.responseText).invoiceId);
}
};
xhttp.open('POST', event.target.getAttribute('action'), true);
xhttp.send(new FormData(event.target));
}
document.querySelectorAll(".btcpay-form").forEach(function(el) {
if (!el.dataset.initialized) {
el.addEventListener('submit', handleFormSubmit);
el.dataset.initialized = true;
}
});
function handlePlusMinus(event) {
event.preventDefault();
const root = event.target.closest('.btcpay-form');
const el = root.querySelector('.btcpay-input-price');
const step = parseInt(event.target.dataset.step) || 1;
const min = parseInt(event.target.dataset.min) || 1;
const max = parseInt(event.target.dataset.max);
const type = event.target.dataset.type;
const price = parseInt(el.value) || min;
if (type === '-') {
el.value = price - step < min ? min : price - step;
} else if (type === '+') {
el.value = price + step > max ? max : price + step;
}
}
document.querySelectorAll(".btcpay-form .plus-minus").forEach(function(el) {
if (!el.dataset.initialized) {
el.addEventListener('click', handlePlusMinus);
el.dataset.initialized = true;
}
});
function handlePriceInput(event) {
event.preventDefault();
const root = event.target.closest('.btcpay-form');
const price = parseInt(event.target.dataset.price);
if (isNaN(event.target.value)) root.querySelector('.btcpay-input-price').value = price;
const min = parseInt(event.target.getAttribute('min')) || 1;
const max = parseInt(event.target.getAttribute('max'));
if (event.target.value < min) {
event.target.value = min;
} else if (event.target.value > max) {
event.target.value = max;
}
}
document.querySelectorAll(".btcpay-form .btcpay-input-price").forEach(function(el) {
if (!el.dataset.initialized) {
el.addEventListener('input', handlePriceInput);
el.dataset.initialized = true;
}
});
{% endaddtoblock %}
{% endblock %}

View file

@ -1,14 +1,31 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload JSON</title>
</head>
<body>
<h1>Upload JSON File</h1>
{% extends "_base.html" %}
{% load sekizai_tags %}
{% load i18n %}
{% load bootstrap %}
{% block title %}Upload Knowledge Base{% endblock %}
{% block nav_home %}active{% endblock %}
{% block content %}
<div class=" p-3 pb-md-4 mx-auto text-left">
<h2 class="display-8 fw-normal">Upload Knowledge Base (JSON file)</h2>
<br>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload and import</button>
{{ form|bootstrap }}
<br>
<button type="submit" class="btn btn-primary ">Upload and import</button>
</form>
</body>
</html>
</p>
</div>
{% addtoblock "js" %}
{% endaddtoblock %}
{% endblock %}