mirror of
https://github.com/Labelbase/Labelbase.git
synced 2026-08-13 12:33:23 +02:00
.
This commit is contained in:
parent
fc903d105f
commit
402c38719b
38 changed files with 1148 additions and 237 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,6 +2,7 @@
|
|||
db.sqlite3
|
||||
config.ini
|
||||
labelbase.log
|
||||
labelbase.log.*
|
||||
bgt.log
|
||||
db/
|
||||
*.pyc
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import random
|
||||
import time
|
||||
from django.db import close_old_connections
|
||||
from django.core.management.base import BaseCommand
|
||||
import asyncio
|
||||
|
||||
from background_task.tasks import tasks, autodiscover
|
||||
from background_task.utils import SignalManager
|
||||
from django.db import close_old_connections
|
||||
from background_task.recovery import start_recovery
|
||||
|
||||
import logging
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from background_task.models import CompletedTask
|
||||
|
||||
logger = logging.getLogger('labelbase')
|
||||
|
||||
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()
|
||||
self.stdout.write(self.style.SUCCESS(f"Deleted {deleted_count} completed tasks."))
|
||||
except Exception as ex:
|
||||
logger.exception("Error occurred while deleting completed tasks")
|
||||
self.stdout.write(self.style.ERROR("An error occurred while deleting completed tasks."))
|
||||
|
|
@ -57,6 +57,79 @@ def is_valid_output_ref(ref):
|
|||
return False
|
||||
|
||||
def checkup_label(label_id, loop):
|
||||
if label_id and loop:
|
||||
try:
|
||||
elem = Label.objects.get(id=label_id)
|
||||
output = OutputStat.objects.filter(user=elem.labelbase.user,
|
||||
type_ref_hash=elem.type_ref_hash,
|
||||
network=elem.labelbase.network).last()
|
||||
if not output:
|
||||
output = OutputStat(user=elem.labelbase.user,
|
||||
type_ref_hash=elem.type_ref_hash,
|
||||
network=elem.labelbase.network, value=0)
|
||||
|
||||
if elem.type == "output" and is_valid_output_ref(elem.ref) and \
|
||||
(output.spent is not True or output.confirmed_at_block_time == 0):
|
||||
electrum_hostname = elem.labelbase.user.profile.electrum_hostname or "electrum.emzy.de"
|
||||
electrum_ports = elem.labelbase.user.profile.electrum_ports or "s50002"
|
||||
server_info = ServerInfo(electrum_hostname, electrum_hostname, ports=(electrum_ports))
|
||||
|
||||
conn = StratumClient()
|
||||
utxo = elem.ref
|
||||
utxo_resp = loop.run_until_complete(interact(conn, server_info, "blockchain.transaction.get", utxo))
|
||||
|
||||
if utxo_resp:
|
||||
txid, index, address, value, blocktime = utxo_resp
|
||||
if blocktime:
|
||||
HistoricalPrice.get_or_create_from_api(timestamp=blocktime)
|
||||
|
||||
unspents = loop.run_until_complete(interact_addr(conn, server_info, "blockchain.address.listunspent", address))
|
||||
|
||||
|
||||
utxo_value = 0
|
||||
utxo_height = 0
|
||||
|
||||
if unspents:
|
||||
for unspent in unspents:
|
||||
if unspent.get('tx_hash') == txid and \
|
||||
unspent.get('tx_pos') == int(index) and \
|
||||
unspent.get('height') > 0 and \
|
||||
unspent.get('value') > 0: # Output is confirmed, but not spent yet
|
||||
output.spent = False
|
||||
utxo_value = unspent.get('value')
|
||||
utxo_height = unspent.get('height')
|
||||
|
||||
output.network = elem.labelbase.network
|
||||
if utxo_height:
|
||||
output.confirmed_at_block_height = utxo_height
|
||||
if blocktime:
|
||||
output.confirmed_at_block_time = blocktime
|
||||
if utxo_value:
|
||||
output.value = utxo_value
|
||||
elif value:
|
||||
output.value = value
|
||||
break
|
||||
#
|
||||
elif conn.last_error:
|
||||
output.last_error = conn.last_error
|
||||
else:
|
||||
output.last_error = {}
|
||||
output.save()
|
||||
try:
|
||||
conn.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Error processing label {}: {}".format(label_id, e))
|
||||
else:
|
||||
if not label_id:
|
||||
logger.error("Can't get label_id! {}".format(label_id))
|
||||
if not loop:
|
||||
logger.error("Can't get loop!")
|
||||
|
||||
|
||||
def checkup_label_buggy(label_id, loop):
|
||||
if label_id and loop:
|
||||
elem = Label.objects.get(id=label_id)
|
||||
output = OutputStat.objects.filter(user=elem.labelbase.user,
|
||||
|
|
@ -69,7 +142,8 @@ def checkup_label(label_id, loop):
|
|||
network=elem.labelbase.network, value=0)
|
||||
print("Using OutputStat id {}".format(output))
|
||||
print("elem.type {} {} {} {}".format(elem.type, is_valid_output_ref(elem.ref), elem.ref, output.spent))
|
||||
if elem.type == "output" and is_valid_output_ref(elem.ref) and output.spent is not True:
|
||||
if elem.type == "output" and is_valid_output_ref(elem.ref) and \
|
||||
(output.spent is not True or output.confirmed_at_block_time == 0):
|
||||
electrum_hostname = elem.labelbase.user.profile.electrum_hostname
|
||||
if not electrum_hostname:
|
||||
electrum_hostname = "electrum.emzy.de"
|
||||
|
|
@ -101,6 +175,7 @@ def checkup_label(label_id, loop):
|
|||
utxo_value = 0
|
||||
utxo_height = 0
|
||||
print("unspents: {}".format(unspents))
|
||||
|
||||
if unspents:
|
||||
for unspent in unspents:
|
||||
if unspent.get('tx_hash') == tx_hash and \
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import logging
|
|||
logger = logging.getLogger('labelbase')
|
||||
|
||||
|
||||
|
||||
class OutputStat(models.Model):
|
||||
"""
|
||||
These fields are unencrypted. Why?
|
||||
|
|
|
|||
0
django/hashtags/__init__.py
Normal file
0
django/hashtags/__init__.py
Normal file
5
django/hashtags/admin.py
Normal file
5
django/hashtags/admin.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import Hashtag
|
||||
|
||||
admin.site.register(Hashtag)
|
||||
6
django/hashtags/apps.py
Normal file
6
django/hashtags/apps.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class HashtagsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'hashtags'
|
||||
26
django/hashtags/migrations/0001_initial.py
Normal file
26
django/hashtags/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Generated by Django 3.2.24 on 2024-03-12 10:43
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django_cryptography.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('labelbase', '0010_alter_label_label'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Hashtag',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', django_cryptography.fields.encrypt(models.CharField(blank=True, default='', max_length=160))),
|
||||
('description', django_cryptography.fields.encrypt(models.TextField(blank=True, default=''))),
|
||||
('labelbase', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='labelbase.labelbase')),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
django/hashtags/migrations/__init__.py
Normal file
0
django/hashtags/migrations/__init__.py
Normal file
19
django/hashtags/models.py
Normal file
19
django/hashtags/models.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django_cryptography.fields import encrypt
|
||||
from django.urls import reverse
|
||||
from labelbase.models import Labelbase
|
||||
|
||||
class Hashtag(models.Model):
|
||||
labelbase = models.ForeignKey(Labelbase, on_delete=models.CASCADE)
|
||||
name = encrypt(
|
||||
models.CharField(
|
||||
max_length=160,
|
||||
default="",
|
||||
blank=True,
|
||||
)
|
||||
)
|
||||
description = encrypt(models.TextField(default="", blank=True))
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('hashtag_edit', kwargs={'pk': self.pk})
|
||||
20
django/hashtags/tasks.py
Normal file
20
django/hashtags/tasks.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import re
|
||||
from .models import Hashtag
|
||||
from labelbase.models import Label
|
||||
from background_task import background
|
||||
|
||||
@background(schedule=1)
|
||||
def store_hashtags_as_objects(labelbase_id, loop=None):
|
||||
for obj in Label.objects.filter(labelbase_id=labelbase_id):
|
||||
hashtags = re.findall(r'#\w+', obj.label)
|
||||
for tag in hashtags:
|
||||
clean_tag = re.search(r'#(\w+)', tag).group(1)
|
||||
hts = Hashtag.objects.filter(labelbase_id=labelbase_id)
|
||||
found = False
|
||||
for ht in hts:
|
||||
if ht.name == clean_tag:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
hashtag = Hashtag(name=clean_tag, labelbase_id=labelbase_id)
|
||||
hashtag.save()
|
||||
71
django/hashtags/views.py
Normal file
71
django/hashtags/views.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
from django.views.generic import ListView
|
||||
from django.views.generic.edit import UpdateView
|
||||
from django.views import View
|
||||
from django.http import HttpResponseForbidden, HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.contrib import messages
|
||||
|
||||
from labelbase.models import Labelbase
|
||||
from .tasks import store_hashtags_as_objects
|
||||
from .models import Hashtag
|
||||
|
||||
|
||||
class LabelbaseProxyView(View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
labelbase_id = kwargs.get('labelbase_id')
|
||||
labelbase = get_object_or_404(Labelbase, id=labelbase_id,
|
||||
user_id=request.user.id)
|
||||
if request.GET.get('sync', 'no') == 'yes':
|
||||
store_hashtags_as_objects(labelbase_id)
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.INFO,
|
||||
"<strong>Processing Hashtags!</strong> Please wait briefly and refresh your page.",
|
||||
)
|
||||
return HttpResponseRedirect(labelbase.get_hashtags_url())
|
||||
|
||||
class HashtagListView(ListView):
|
||||
model = Hashtag
|
||||
template_name = 'hashtags/labelbase_list.html'
|
||||
|
||||
def get_queryset(self):
|
||||
labelbase_id = self.kwargs['labelbase_id']
|
||||
labelbase = get_object_or_404(
|
||||
Labelbase, id=labelbase_id, user_id=self.request.user.id
|
||||
)
|
||||
queryset = Hashtag.objects.filter(labelbase_id=labelbase_id,
|
||||
labelbase__user_id=self.request.user.id).order_by('-name')
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(HashtagListView, self).get_context_data(**kwargs)
|
||||
context["active_labelbase_id"] = self.kwargs['labelbase_id']
|
||||
context["labelbase"] = get_object_or_404(
|
||||
Labelbase, id=self.kwargs['labelbase_id'], user_id=self.request.user.id
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class HashtagUpdateView(UpdateView):
|
||||
model = Hashtag
|
||||
fields = ['name', 'description']
|
||||
template_name = 'hashtags/labelbase_edit.html'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if self.request.user.id != self.get_object().labelbase.user_id:
|
||||
return HttpResponseForbidden("You do not have permission to edit this hashtag.")
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_success_url(self):
|
||||
labelbase_id = self.object.labelbase_id
|
||||
return reverse('labelbase_hashtags', kwargs={'labelbase_id': labelbase_id})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
labelbase_id = self.get_object().labelbase.id
|
||||
context = super(HashtagUpdateView, self).get_context_data(**kwargs)
|
||||
context["active_labelbase_id"] = labelbase_id
|
||||
context["labelbase"] = get_object_or_404(
|
||||
Labelbase, id=labelbase_id, user_id=self.request.user.id
|
||||
)
|
||||
return context
|
||||
28
django/knowledge_base/migrations/0004_auto_20240314_0939.py
Normal file
28
django/knowledge_base/migrations/0004_auto_20240314_0939.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 3.2.24 on 2024-03-14 09:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('knowledge_base', '0003_auto_20240308_2231'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='order',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='desc',
|
||||
field=models.CharField(default='', max_length=280),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='order',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
]
|
||||
|
|
@ -14,6 +14,8 @@ class ExportSnapshot(models.Model):
|
|||
|
||||
class Category(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
desc = models.CharField(max_length=280, default="")
|
||||
order = models.IntegerField(default=0)
|
||||
slug = models.SlugField(unique=True, max_length=100)
|
||||
exclude_from_export = models.BooleanField(default=False)
|
||||
|
||||
|
|
@ -27,6 +29,7 @@ class Category(models.Model):
|
|||
class Article(models.Model):
|
||||
title = models.CharField(max_length=200)
|
||||
content = models.TextField()
|
||||
order = models.IntegerField(default=0)
|
||||
category = models.ForeignKey(Category, on_delete=models.CASCADE)
|
||||
slug = models.SlugField(unique=True, max_length=100)
|
||||
exclude_from_export = models.BooleanField(default=False)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,5 @@ urlpatterns = [
|
|||
path('article/<slug:slug>/', ArticleDetailView.as_view(), name='article_detail'),
|
||||
#TODO: staff only,
|
||||
path('export', ExportJSONView.as_view(), name='export_json'),
|
||||
#TODO: staff only,
|
||||
path('import', ImportJSONView.as_view(), name='import_json'),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ class ExportJSONView(View):
|
|||
for category in categories:
|
||||
articles = list(category.article_set.all().exclude(exclude_from_export=True).values())
|
||||
data[category.name] = articles
|
||||
|
||||
export_snapshot = ExportSnapshot.objects.create(
|
||||
exported_data=data
|
||||
)
|
||||
|
|
@ -37,7 +36,8 @@ class ImportJSONView(View):
|
|||
try:
|
||||
category = Category.objects.get(slug=slugify(category_name))
|
||||
except Category.DoesNotExist:
|
||||
category = Category.objects.create(name=category_name, slug=slugify(category_name))
|
||||
category = Category.objects.create(name=category_name,
|
||||
slug=slugify(category_name))
|
||||
|
||||
for article_data in articles:
|
||||
try:
|
||||
|
|
@ -47,8 +47,10 @@ class ImportJSONView(View):
|
|||
article.category = category
|
||||
article.save()
|
||||
except Article.DoesNotExist:
|
||||
Article.objects.create(title=article_data['title'], content=article_data['content'], category=category, slug=article_data['slug'])
|
||||
|
||||
Article.objects.create(title=article_data['title'],
|
||||
content=article_data['content'],
|
||||
category=category,
|
||||
slug=article_data['slug'])
|
||||
return JsonResponse({'message': 'Import successful'})
|
||||
except json.JSONDecodeError:
|
||||
return JsonResponse({'error': 'Invalid JSON format'}, status=400)
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ class Labelbase(models.Model):
|
|||
def get_absolute_url(self):
|
||||
return reverse("labelbase", args=[self.id])
|
||||
|
||||
def get_hashtags_url(self):
|
||||
return reverse('labelbase_hashtags', kwargs={'labelbase_id': self.id})
|
||||
|
||||
class Label(models.Model):
|
||||
"""
|
||||
|
|
@ -193,6 +195,7 @@ class Label(models.Model):
|
|||
"""
|
||||
return self.labelbase.get_absolute_url()
|
||||
|
||||
|
||||
def get_mempool_url(self):
|
||||
if self.labelbase and self.labelbase.network != "mainnet":
|
||||
mempool_endpoint = \
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
|
|||
--bd-callout-border: var(--bs-warning-border-subtle);
|
||||
}
|
||||
|
||||
.bd-callout-good {
|
||||
.bd-callout-good {
|
||||
--bd-callout-color: var(--bs-success-text-emphasis);
|
||||
--bd-callout-bg: #d9fbd0;
|
||||
--bd-callout-border: #bee8be;
|
||||
|
|
@ -285,17 +285,10 @@ box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
|
|||
}
|
||||
|
||||
|
||||
.modal-footer {
|
||||
border-top: 0;
|
||||
}
|
||||
.modal-header {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
<style>
|
||||
.help-block {
|
||||
display: none;
|
||||
.modal-footer {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
</style>*/
|
||||
.modal-header {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ INSTALLED_APPS = [
|
|||
"background_task",
|
||||
"connectrum",
|
||||
"knowledge_base",
|
||||
"hashtags",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ from userprofile.views import (ProfileView,
|
|||
ElectrumInfoUpdateView)
|
||||
from userprofile.views import APIKeyView
|
||||
|
||||
from hashtags.views import HashtagListView, HashtagUpdateView, LabelbaseProxyView
|
||||
|
||||
|
||||
from .views import (
|
||||
LabelbaseView,
|
||||
|
|
@ -27,7 +29,7 @@ from .views import (
|
|||
RegistrationView,
|
||||
LabelbaseFormView,
|
||||
LabelbaseUpdateView,
|
||||
FaqView,
|
||||
DonationView,
|
||||
AboutView,
|
||||
EncryptionView,
|
||||
InteroperationalView,
|
||||
|
|
@ -37,6 +39,7 @@ from .views import (
|
|||
FixAndMergeLabelsView,
|
||||
LabelbaseDatatableView,
|
||||
LabelbasePortfolioView,
|
||||
OutputStatUpdateRedirectView,
|
||||
)
|
||||
|
||||
from importer.views import upload_labels
|
||||
|
|
@ -99,6 +102,21 @@ urlpatterns = [
|
|||
login_required(LabelbaseView.as_view()),
|
||||
name="labelbase"
|
||||
),
|
||||
path(
|
||||
"labelbase/<int:labelbase_id>/hashtags/",
|
||||
login_required(HashtagListView.as_view()),
|
||||
name="labelbase_hashtags"
|
||||
),
|
||||
path(
|
||||
"labelbase/<int:labelbase_id>/hashtags/proxy/",
|
||||
login_required(LabelbaseProxyView.as_view()),
|
||||
name="labelbase_hashtags_proxy"
|
||||
),
|
||||
path(
|
||||
"labelbase/hashtag/<int:pk>/edit/",
|
||||
login_required(HashtagUpdateView.as_view()),
|
||||
name="hashtag_edit"
|
||||
),
|
||||
path(
|
||||
"labelbase/<int:labelbase_id>/data/",
|
||||
login_required(LabelbaseDatatableView.as_view()),
|
||||
|
|
@ -206,9 +224,9 @@ urlpatterns = [
|
|||
name="terms"
|
||||
),
|
||||
path(
|
||||
"faq",
|
||||
FaqView.as_view(),
|
||||
name="faq"
|
||||
"donate",
|
||||
DonationView.as_view(),
|
||||
name="donate"
|
||||
),
|
||||
path(
|
||||
"about",
|
||||
|
|
@ -225,6 +243,11 @@ urlpatterns = [
|
|||
InteroperationalView.as_view(),
|
||||
name="interoperational"
|
||||
),
|
||||
path(
|
||||
"outputstat/<int:output_stats_id>/update/<int:label_id>/",
|
||||
login_required(OutputStatUpdateRedirectView.as_view()),
|
||||
name='outputstat_update_redirect'
|
||||
),
|
||||
path(
|
||||
"",
|
||||
HomeView.as_view(),
|
||||
|
|
|
|||
|
|
@ -12,24 +12,24 @@ from django.views.generic.list import ListView
|
|||
from django.views.generic.edit import DeleteView
|
||||
from django.views.generic.edit import UpdateView
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.http import FileResponse
|
||||
from django.urls import reverse
|
||||
from django.template.loader import render_to_string
|
||||
from django.contrib import messages
|
||||
from django_datatables_view.base_datatable_view import BaseDatatableView
|
||||
from two_factor.views import OTPRequiredMixin
|
||||
from two_factor.views.utils import class_view_decorator
|
||||
from rest_framework.authtoken.models import Token
|
||||
from bip329.bip329_writer import BIP329JSONLWriter, BIP329JSONLEncryptedWriter
|
||||
from labelbase.models import Label, Labelbase
|
||||
from labelbase.forms import LabelForm, LabelbaseForm
|
||||
from labelbase.forms import ExportLabelsForm
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.http import FileResponse
|
||||
from rest_framework.authtoken.models import Token
|
||||
from bip329.bip329_writer import BIP329JSONLWriter, BIP329JSONLEncryptedWriter
|
||||
from django.urls import reverse
|
||||
from django_datatables_view.base_datatable_view import BaseDatatableView
|
||||
from django.template.loader import render_to_string
|
||||
from django.contrib import messages
|
||||
|
||||
|
||||
from .utils import hashtag_to_badge, extract_fiat_value
|
||||
from finances.models import OutputStat
|
||||
from finances.tasks import check_all_outputs
|
||||
from .utils import hashtag_to_badge, extract_fiat_value
|
||||
|
||||
|
||||
|
||||
|
||||
class AboutView(TemplateView):
|
||||
|
|
@ -56,8 +56,8 @@ class TermsView(TemplateView):
|
|||
template_name = "terms.html"
|
||||
|
||||
|
||||
class FaqView(TemplateView):
|
||||
template_name = "faq.html"
|
||||
class DonationView(TemplateView):
|
||||
template_name = "donate.html"
|
||||
|
||||
|
||||
class LabelDeleteView(DeleteView):
|
||||
|
|
@ -648,3 +648,49 @@ class RegistrationCompleteView(TemplateView):
|
|||
@class_view_decorator(never_cache)
|
||||
class ExampleSecretView(OTPRequiredMixin, TemplateView):
|
||||
template_name = "secret.html"
|
||||
|
||||
|
||||
class OutputStatUpdateRedirectView(View):
|
||||
def get(self, request, output_stats_id, label_id):
|
||||
try:
|
||||
output_stat = OutputStat.objects.get(id=output_stats_id)
|
||||
except OutputStat.DoesNotExist:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
"<strong>Hmmmm....</srong> Could not modify label data."
|
||||
)
|
||||
return redirect('edit_label', pk=label_id)
|
||||
try:
|
||||
label = Label.objects.get(id=label_id, labelbase__user_id=self.request.user.id)
|
||||
except Label.DoesNotExist:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
"Can't find what you are looking for.."
|
||||
)
|
||||
return redirect("home")
|
||||
|
||||
spent = request.GET.get('force-spent', None)
|
||||
|
||||
if spent not in ["true", "false", "none"]:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
"<strong>Hmmmm....</srong> This action is unknown."
|
||||
)
|
||||
return redirect('edit_label', pk=label_id)
|
||||
if spent == "true":
|
||||
spent = True
|
||||
elif spent == "false":
|
||||
spent = False
|
||||
elif spent == "none":
|
||||
spent = None
|
||||
OutputStat.objects.filter(id=output_stat.id).update(spent=spent)
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.SUCCESS,
|
||||
"<strong>Okay!</strong> Verifying output status now."
|
||||
)
|
||||
label.save() # will trigger a check agains Electrum
|
||||
return redirect('edit_label', pk=label_id)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,11 @@
|
|||
<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" %}
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
|
|
@ -120,7 +123,7 @@
|
|||
<li><a class="nav-link" style="font-size: .875rem;"><span data-feather="dollar-sign" class="align-text-bottom"></span> Fiat Finances</a></li>
|
||||
{% endif %}
|
||||
{% if labelbase.user.profile.use_hashtags %}
|
||||
<li><a class="nav-link" style="font-size: .875rem;"><span data-feather="hash" class="align-text-bottom"></span> Hashtags</a></li>
|
||||
<li><a href="{{ labelbase.get_hashtags_url }}" class="nav-link" style="font-size: .875rem;"><span data-feather="hash" class="align-text-bottom"></span> Hashtags</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
|
|
@ -178,15 +181,17 @@
|
|||
<span>Community</span>
|
||||
</h6>
|
||||
|
||||
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link" href="{% url "donate" %}"><span data-feather="heart" class="align-text-bottom"></span> Donate</a>
|
||||
<li>
|
||||
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link" href="https://labelbase.space/newsletter/"><span data-feather="mail" class="align-text-bottom"></span> Newsletter</a>
|
||||
<li>
|
||||
|
||||
<li class="nav-item ">
|
||||
<!--li class="nav-item ">
|
||||
<a class="nav-link" href="https://labelbase.space/#/"><span data-feather="mic" class="align-text-bottom"></span> Podcast</a>
|
||||
<li>
|
||||
<li-->
|
||||
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link" href="https://github.com/Labelbase/Labelbase/"><span data-feather="github" class="align-text-bottom"></span> Code on GitHub</a>
|
||||
|
|
@ -205,7 +210,7 @@
|
|||
<li>
|
||||
-->
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link" href="{% url 'faq' %}"><span data-feather="book" class="align-text-bottom"></span> FAQ & Knowledge</a>
|
||||
<a class="nav-link" href="{% url 'kb-index' %}"><span data-feather="book" class="align-text-bottom"></span> Knowledge Base</a>
|
||||
<li>
|
||||
|
||||
<li class="nav-item ">
|
||||
|
|
@ -222,18 +227,10 @@
|
|||
|
||||
|
||||
</ul>
|
||||
{% comment %}
|
||||
<ul class="nav flex-column mb-2">
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link" href="{% url 'terms' %}"><span data-feather="heart" class="align-text-bottom" style="color:red;"></span> Special Thanks</a>
|
||||
<li>
|
||||
</ul>
|
||||
<br>
|
||||
{% endcomment %}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||
{% include "_messages.html" %}
|
||||
|
|
@ -453,7 +450,7 @@
|
|||
data: { toggleId: 'use_chatwoot', toggleValue: 'true' },
|
||||
success: function (response) {
|
||||
show_message("success", "<strong>Success!</strong> " + response.message)
|
||||
window.$chatwoot.toggle("open");
|
||||
window.$chatwoot.toggle("open");
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error(error);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<style>
|
||||
{% load sekizai_tags %}
|
||||
|
||||
<style>
|
||||
{% addtoblock "css" %}
|
||||
.carousel-control-prev {
|
||||
margin-left:-2rem;
|
||||
}
|
||||
|
|
@ -8,7 +10,6 @@
|
|||
margin-left: 0rem;
|
||||
}
|
||||
|
||||
|
||||
.carousel-control-prev-icon {
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%237e7e7e'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
|
@ -18,7 +19,6 @@
|
|||
margin-right: -4em;
|
||||
}
|
||||
|
||||
|
||||
.carousel-indicators {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
|
|
@ -28,34 +28,25 @@
|
|||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
/* .carousel-indicators li {
|
||||
background-color: #ccc !important;
|
||||
border: 2px solid #007bff;
|
||||
margin: 0 5px !important;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
cursor: pointer;
|
||||
|
||||
} */
|
||||
|
||||
.carousel-indicators .active {
|
||||
background-color: #7e7e7e; /* Adjust the color for the active indicator */
|
||||
background-color: #7e7e7e;
|
||||
border: 1px solid #7e7e7e;
|
||||
}
|
||||
|
||||
|
||||
.carousel-inner {
|
||||
padding-left: 2em;
|
||||
padding-right: 0em;
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
{% endaddtoblock %}
|
||||
</style>
|
||||
|
||||
<div class="modal fade" id="introTourModal" tabindex="-1" aria-labelledby="introTourModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="introTourModalLabel">Welcome to Labelbase!</h5>
|
||||
<h3 class="modal-title" id="introTourModalLabel">Welcome to Labelbase!</h3>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
|
||||
</div>
|
||||
|
|
@ -64,7 +55,7 @@
|
|||
Some key details to bring to your attention.
|
||||
</p>
|
||||
<br>
|
||||
<div id="carouselExample" class="carousel slide" data-bs-ride="carousel">
|
||||
<div id="carouselIntroduction" class="carousel slide" data-bs-ride="carousel">
|
||||
<div class="carousel-inner">
|
||||
|
||||
<div class="carousel-item active">
|
||||
|
|
@ -87,8 +78,8 @@
|
|||
</div>
|
||||
|
||||
<div class="carousel-item">
|
||||
<h5>Tree maps and Analytics</h5>
|
||||
<p>Access detailed tree maps and analytics to make data-driven decisions which UTXO to spend next.</p>
|
||||
<h5>Tree Maps and Analytics</h5>
|
||||
<p>Access detailed Tree Maps and analytics to make data-driven decisions which UTXO to spend next.</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -99,14 +90,21 @@
|
|||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="carousel-item">
|
||||
<h5>Donate</h5>
|
||||
<p>Before upgrading to a newer version, backup your data by exporting your labels as a BIP-329 file or an encrypted archive.</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<button class="carousel-control-prev" type="button" data-bs-target="#carouselExample" data-bs-slide="prev">
|
||||
<button class="carousel-control-prev" type="button" data-bs-target="#carouselIntroduction" data-bs-slide="prev">
|
||||
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">Previous</span>
|
||||
</button>
|
||||
|
||||
<button class="carousel-control-next" type="button" data-bs-target="#carouselExample" data-bs-slide="next">
|
||||
<button class="carousel-control-next" type="button" data-bs-target="#carouselIntroduction" data-bs-slide="next">
|
||||
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">Next</span>
|
||||
</button>
|
||||
|
|
@ -114,10 +112,10 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<ol class="carousel-indicators">
|
||||
<li data-bs-target="#carouselExample" data-bs-slide-to="0"></li>
|
||||
<li data-bs-target="#carouselExample" data-bs-slide-to="1"></li>
|
||||
<li data-bs-target="#carouselExample" data-bs-slide-to="2"></li>
|
||||
<li data-bs-target="#carouselExample" data-bs-slide-to="3"></li>
|
||||
<li data-bs-target="#carouselIntroduction" data-bs-slide-to="0"></li>
|
||||
<li data-bs-target="#carouselIntroduction" data-bs-slide-to="1"></li>
|
||||
<li data-bs-target="#carouselIntroduction" data-bs-slide-to="2"></li>
|
||||
<li data-bs-target="#carouselIntroduction" data-bs-slide-to="3"></li>
|
||||
</ol>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Get Started</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -55,5 +55,4 @@
|
|||
Your feedback is important to us. :)
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
api_token
|
||||
{% endblock %}
|
||||
|
|
|
|||
121
django/templates/donate.html
Normal file
121
django/templates/donate.html
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
{% extends "_base.html" %}
|
||||
{% load sekizai_tags %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Donate Support Labelbase{% 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">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">
|
||||
Choose the amount you'd like to donate to Labelbase.
|
||||
</p> <p class="fs-5 text-muted">
|
||||
|
||||
Your generosity ensures that our project continues to thrive and evolve, directly supporting our mission to make Bitcoin transactions more organized and transparent.
|
||||
</p> <p class="fs-5 text-muted">
|
||||
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>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{% 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 %}
|
||||
|
|
@ -5,13 +5,7 @@
|
|||
{% block nav_home %}active{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
|
||||
|
||||
.lb-header {
|
||||
max-width: 810px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="p-3 pb-md-4 mx-auto text-center">
|
||||
<h1 style="padding-top: 1.6em; padding-bottom: 0.9em; text-transform: uppercase;font-weight:800;">L<span style="height:1.1em; width:1em" data-feather="tag" class="align-text-bottom"></span>belbase</h1>
|
||||
<h2 class="display-6 fw-normal" style="padding-bottom: 0.9em;">All your labels in one place.</h2>
|
||||
|
|
|
|||
|
|
@ -1,75 +0,0 @@
|
|||
{% extends "_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Privacy{% 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">Frequently Asked Questions</h2>
|
||||
<p>
|
||||
</p>
|
||||
|
||||
<h3 class="display-10 fw-normal">How to import labels from my wallet into Labelbase?</h3>
|
||||
<p class="fs-5 text-muted">
|
||||
To import data from various wallet's export format into Labelbase and then export it as BIP-329 labels, you would need to follow these steps:
|
||||
<ol>
|
||||
<li>
|
||||
Export data from your wallet: The first step is to export your data from your wallet. The format of this export will depend on the wallet you are using, but it may include private keys, public keys, or other information.
|
||||
</li>
|
||||
<li>
|
||||
Import data into Labelbase: Next, you'll need to import the exported data into Labelbase. You can do this by using the import feature within your Labelbase. Choose the appropriate option based on the file format you want to import.
|
||||
Even through we do not store your uploaded files, <br>DO NOT UPLOAD ANY PRIVATE KEY MATERIAL TO LABELBASE.
|
||||
</li>
|
||||
<li>
|
||||
Modify and edit labels: Once your labels are imported into Labelbase, you can modify and edit your labels as needed. This may include changing the names of your addresses, grouping them into different categories, or adding additional information.
|
||||
</li>
|
||||
<li>
|
||||
Export as BIP-329 labels: Finally, you'll need to export your labels from Labelbase as BIP-329 labels. This will typically involve downloading a file in the BIP-329 format, which you can then import into your wallet.
|
||||
</li>
|
||||
<li>
|
||||
Use Labelbase through the API: Alternatively, you can use Labelbase through the API in your wallet. This will allow you to access your labels directly from within your wallet, without the need to export and import files. To do this, your wallet needs to integrate the Labelbase API into the wallet.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
</p>
|
||||
<!-- -->
|
||||
|
||||
<h3 class="display-10 fw-normal">How is the encrypted label file generated, and how can I decrypt it?
|
||||
</h3>
|
||||
<p class="fs-5 text-muted">
|
||||
<ul>
|
||||
<li>The encrypted label file is generated using the <a href="https://github.com/Labelbase/python-bip329">Labelbase Python library for BIP-329</a>. This library provides functionality for managing Bitcoin coin control information and allows you to export labels in an encrypted format.
|
||||
</li><li>
|
||||
|
||||
|
||||
To decrypt the BIP-329 label files manually, you can refer to <a href="https://github.com/Labelbase/python-bip329#decrypting-bip-329-label-files">the instructions provided</a> in the Labelbase Python library documentation. This documentation provides detailed steps and examples for decrypting label files using the library.
|
||||
</li><li>
|
||||
|
||||
Please note that the passphrase used for encryption is crucial for decryption. Ensure that you keep your passphrase secure and do not share it with anyone unauthorized, as it is required to access your encrypted label data.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
<h3 class="display-10 fw-normal">How is the encrypted label file generated, and how can I decrypt it?
|
||||
</h3>
|
||||
<p class="fs-5 text-muted">
|
||||
<ul>
|
||||
<li>
|
||||
The encrypted label file is generated using the <a href="https://github.com/Labelbase/python-bip329">Labelbase Python library for BIP-329</a>. This library provides functionality for managing Bitcoin coin control information and allows you to export labels in an encrypted format.
|
||||
</li>
|
||||
<li>
|
||||
To decrypt the BIP-329 label files manually, you can refer to <a href="https://github.com/Labelbase/python-bip329#decrypting-bip-329-label-files">the instructions provided</a> in the Labelbase Python library documentation. This documentation provides detailed steps and examples for decrypting label files using the library.
|
||||
</li>
|
||||
<li>
|
||||
Please note that the passphrase used for encryption is crucial for decryption. Ensure that you keep your passphrase secure and do not share it with anyone unauthorized, as it is required to access your encrypted label data.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
{% if labelbase %}
|
||||
<div class="row">
|
||||
<div class="col float-start">
|
||||
<h2 style="padding-top: 1em;">{% block title %}{{ labelbase.name }} {% endblock %}</h2>
|
||||
{% if labelbase.fingerprint %}<tt>{{ labelbase.fingerprint }} </tt>{% endif %}
|
||||
{% if labelbase.about %}
|
||||
<p>{{ labelbase.about}}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col float-end" >
|
||||
<div class="btn-group float-end" role="group" style="padding-top: 2em;">
|
||||
|
||||
{% comment %}
|
||||
<button type="button" class="rounded-start btn btn-sm btn-outline-secondary "
|
||||
data-bs-toggle="modal" data-bs-target="#addLabelModal">New Hashtag</button>
|
||||
{% endcomment %}
|
||||
|
||||
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="{#d-none d-md-block #} btn btn-sm btn-outline-secondary dropdown-toggle rounded-end me-2" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Hashtag Actions
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url "labelbase_hashtags_proxy" labelbase_id=labelbase.id %}?sync=yes">
|
||||
Collect & Sync Hashtags
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>There is no such labelbase.</p>
|
||||
{% endif %}
|
||||
32
django/templates/hashtags/labelbase_edit.html
Normal file
32
django/templates/hashtags/labelbase_edit.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{% extends "_base.html" %}
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
{% load sekizai_tags %}
|
||||
|
||||
{% block content %}
|
||||
<ul class="nav nav-tabs" style="padding-top: 2rem; ">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page">Hashtag Detail</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div style="padding-top: 2.1rem; padding-bottom: 2.1rem; " >
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|bootstrap }}
|
||||
<br>
|
||||
<div style="padding-top: 1em;">
|
||||
<a href="{{ form.instance.labelbase.get_hashtags_url }}"
|
||||
type="button" class="btn btn-link">Back to Hashtags</a>
|
||||
<!--a href="#" data-bs-toggle="modal" data-bs-target="#deleteHashtagModal"
|
||||
type="button" class="btn btn-danger ">Delete</a-->
|
||||
<input type="submit" class="btn btn-primary" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
{% addtoblock "js" %}
|
||||
{% endaddtoblock %}
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
38
django/templates/hashtags/labelbase_list.html
Normal file
38
django/templates/hashtags/labelbase_list.html
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{% extends "_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load labelbase_tags %}
|
||||
{% load importer_tags %}
|
||||
{% load sekizai_tags %}
|
||||
{% load bootstrap %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% include "hashtags/_labelbase_header_info_menu_hashtag_list.html" %}
|
||||
<h4>Known Hashtags</h4>
|
||||
|
||||
|
||||
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Hashtag</th>
|
||||
<th scope="col">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for hashtag in object_list %}
|
||||
<tr>
|
||||
<td><a href="{{ hashtag.get_absolute_url }}">{{ hashtag.pk }}{# <small>(edit)</small>#}</a></td>
|
||||
<td><a href="{% url "labelbase" labelbase_id=active_labelbase_id %}?tag={{ hashtag.name }}" class="badge badge-hashtag">{{ hashtag.name }}</a></td>
|
||||
<td>{{ hashtag.description }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
|
@ -333,6 +333,12 @@
|
|||
</p>
|
||||
<a href="{{ reg_url }}" type="button" class="btn btn-lg btn-primary" style="margin-bottom:6rem;">Get started</a>
|
||||
|
||||
|
||||
|
||||
<br><br>
|
||||
|
||||
Built with support from <a href="https://opensats.org/blog/bitcoin-grants-july-2023">OpenSats</a>.
|
||||
<br><br>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,7 @@
|
|||
{% block title %}Interoperability{% endblock %}
|
||||
{% block nav_home %}active{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
|
||||
|
||||
.lb-header {
|
||||
max-width: 810px;
|
||||
}
|
||||
</style>
|
||||
{% block content %}
|
||||
<div class="p-3 pb-md-4 mx-auto text-center">
|
||||
<h1 style="padding-top: 1.6em; padding-bottom: 0.9em; text-transform: uppercase;font-weight:800;">L<span style="height:1.1em; width:1em" data-feather="tag" class="align-text-bottom"></span>belbase</h1>
|
||||
<h2 class="display-6 fw-normal" style="padding-bottom: 0.9em;">All your labels in one place.</h2>
|
||||
|
|
|
|||
|
|
@ -3,14 +3,40 @@
|
|||
{% load breadcrumb_tags %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load sekizai_tags %}
|
||||
|
||||
{% block title %}{{ article.title }} | Labelbase{% endblock %}
|
||||
{#% block nav_home %}active{% endblock %#}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
{% addtoblock "css" %}
|
||||
pre {
|
||||
background-color: #f4f4f4;
|
||||
border: 1px solid #dddddd;
|
||||
border-left: 3px solid #ccc;
|
||||
color: #666;
|
||||
page-break-inside: avoid;
|
||||
font-family: monospace;
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1.6em;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
padding: 1em;
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
|
||||
|
||||
code {
|
||||
font-family: inherit;
|
||||
color: inherit;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
{% endaddtoblock %}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
{% block content %}
|
||||
|
||||
|
||||
|
||||
{% breadcrumbs_category category as current_breadcrumbs %}
|
||||
<nav style="padding-top: 0.76rem; --bs-breadcrumb-divider: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath d='M2.5 0L1 1.5 3.5 4 1 6.5 2.5 8l4-4-4-4z' fill='%236c757d'/%3E%3C/svg%3E");" aria-label="breadcrumb">
|
||||
|
||||
|
|
@ -24,17 +24,11 @@
|
|||
</ol>
|
||||
</nav>
|
||||
|
||||
|
||||
<!-- <ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="#">Home</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Library</li>
|
||||
</ol>
|
||||
</nav>
|
||||
-->
|
||||
<h2>{{ category.name }}</h2>
|
||||
<ul>
|
||||
<ul class="list-group list-group-flush">
|
||||
|
||||
{% for article in category.article_set.all %}
|
||||
<li><a href="{% url 'article_detail' article.slug %}">{{ article.title }}</a></li>
|
||||
<li class="list-group-item"><a href="{% url 'article_detail' article.slug %}">{{ article.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,81 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Knowledge Base</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Categories</h1>
|
||||
<ul>
|
||||
{% for category in categories %}
|
||||
<li><a href="{% url 'category_detail' category.slug %}">{{ category.name }}</a></li>
|
||||
{% empty %}
|
||||
Your knowledge base is empty. You can download the latest knowledge here and import it.
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
{% extends "_base.html" %}
|
||||
{% load markdown_extras %}
|
||||
{% load breadcrumb_tags %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load sekizai_tags %}
|
||||
{% block title %}Knowledge Base Categories | Labelbase{% endblock %}
|
||||
{#% block nav_home %}active{% endblock %#}
|
||||
|
||||
'kb-index'
|
||||
{% block content %}
|
||||
|
||||
<style>
|
||||
{% addtoblock "css" %}
|
||||
|
||||
.card:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.card-body:hover {
|
||||
background-color: #f8f9fa; /* or any light color of your choice */
|
||||
}
|
||||
{% endaddtoblock %}
|
||||
</style>
|
||||
|
||||
|
||||
<nav style="padding-top: 0.76rem; --bs-breadcrumb-divider: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath d='M2.5 0L1 1.5 3.5 4 1 6.5 2.5 8l4-4-4-4z' fill='%236c757d'/%3E%3C/svg%3E");" aria-label="breadcrumb">
|
||||
|
||||
<ol class="breadcrumb">
|
||||
|
||||
<li class="breadcrumb-item active" aria-current="page">Knowledge Base</li>
|
||||
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="p-3 pb-md-4 mx-auto text-center">
|
||||
<h2 class="display-8 fw-bold text-muted" style="padding-bottom: 0.9em;">Knowledge Base</h2>
|
||||
|
||||
<p class="lb-header mx-auto text-center fs-5 text-muted">Browse through the categories below.</p>
|
||||
<p class="lb-header mx-auto text-center fs-5 text-muted">If you have unanswered questions, are confused, or just want to understand
|
||||
Labelbase better – just get in touch using our {% if request.user.profile.use_chatwoot %}
|
||||
|
||||
<a href="JavaScript:void(0);" onclick="window.$chatwoot.toggle('open');">
|
||||
support chat</a>.
|
||||
{% else %}
|
||||
|
||||
<a href="JavaScript:void(0);" onclick="$('#chatModal').modal('show');">
|
||||
support chat</a>.
|
||||
|
||||
{% endif %}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
{% for category in categories %}
|
||||
{% if category.article_set.count %}
|
||||
<div class="col-sm-6">
|
||||
<a href="{% url 'category_detail' category.slug %}" style="text-decoration: none; color: inherit;">
|
||||
<div class="card" style="margin-bottom: 1rem !important; transition: transform 0.2s, box-shadow 0.2s;">
|
||||
<div class="card-body text-center" style="transition: background-color 0.2s;">
|
||||
<h5 class="card-title">{{ category.name }}</h5>
|
||||
<p class="card-text">{{ category.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{% empty %}
|
||||
Your knowledge base is empty. You can download the latest knowledge here and import it.
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -75,46 +75,47 @@
|
|||
|
||||
<div style="padding-top: 0rem; padding-bottom: 2.1rem; ">
|
||||
|
||||
|
||||
<div class="alert bd-callout bd-callout-danger ">
|
||||
<div class="btn-group" role="group" style="position: absolute; top: -8px; right: -4px; padding: 1.25rem 1rem;">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger dropdown-toggle " data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Actions
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="#">Reset output and check again</a></li>
|
||||
<li><a class="dropdown-item" href="#">Mark output as spent</a></li>
|
||||
<li><a class="dropdown-item" href="#">Mark output as unspent</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style=" padding-right: 1.8rem"><strong>Output spent! </strong> Blockchain records indicate that this output has been spent in another transaction.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% if output.last_error %}
|
||||
<div class="bd-callout bd-callout-danger">
|
||||
<div class="alert bd-callout bd-callout-danger">
|
||||
<div class="btn-group" role="group" style="position: absolute; top: -8px; right: -4px; padding: 1.25rem 1rem;">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger dropdown-toggle " data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Actions
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{% url 'outputstat_update_redirect' output_stats_id=output.id label_id=form.instance.id %}?force-spent=none">Verify output status</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'outputstat_update_redirect' output_stats_id=output.id label_id=form.instance.id %}?force-spent=true">Mark output as spent</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'outputstat_update_redirect' output_stats_id=output.id label_id=form.instance.id %}?force-spent=false">Mark output as unspent</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<strong>Ouch!</strong> Got "{{ output.last_error.message }}{% if output.last_error.code %} (code {{ output.last_error.code }}){% endif %}" from Electrum.
|
||||
</div>
|
||||
{% else %}
|
||||
{% if form.instance.type == "output" %}
|
||||
{% switch output.get_spent_status %}
|
||||
{% case "spent" %}
|
||||
<div class="bd-callout bd-callout-warning">
|
||||
<strong>Output spent!</strong> Blockchain records indicate that this output has been spent in another transaction.
|
||||
</div>
|
||||
{% case "unspent" %}
|
||||
<div class="bd-callout bd-callout-good">
|
||||
<strong>Output unspent!</strong> Blockchain records indicate that this output has not been spent yet.
|
||||
</div>
|
||||
{% case "unconfirmed" %}
|
||||
<div class="bd-callout bd-callout-info">
|
||||
<strong>Output unconfirmed!</strong> Blockchain records indicate that this output has not been confirmed yet.
|
||||
</div>
|
||||
{% endswitch %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if form.instance.type == "output" %}
|
||||
{% switch output.get_spent_status %}
|
||||
{% case "spent" %}
|
||||
<div class="bd-callout bd-callout-warning">
|
||||
<strong>Output spent!</strong> Blockchain records indicate that this output has been spent in another transaction.
|
||||
</div>
|
||||
{% case "unspent" %}
|
||||
<div class="bd-callout bd-callout-good">
|
||||
<strong>Output unspent!</strong> Blockchain records indicate that this output has not been spent yet.
|
||||
</div>
|
||||
{% case "unconfirmed" %}
|
||||
<div class="alert bd-callout bd-callout-info">
|
||||
<div class="btn-group" role="group" style="position: absolute; top: -8px; right: -4px; padding: 1.25rem 1rem;">
|
||||
<button type="button" class="btn btn-sm btn-outline-info dropdown-toggle " data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Actions
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{% url 'outputstat_update_redirect' output_stats_id=output.id label_id=form.instance.id %}?force-spent=none">
|
||||
Verify output status</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<strong>Output unconfirmed!</strong> Blockchain records indicate that this output has not been confirmed yet.
|
||||
</div>
|
||||
{% endswitch %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
<!--div class="bd-callout bd-callout-warning">
|
||||
<strong>Heads up!</strong> There are multiple records for this transaction output. <a href="">Review & merge</a>
|
||||
|
|
|
|||
343
django/templates/label_fix_and_merge.html
Normal file
343
django/templates/label_fix_and_merge.html
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
{% extends "_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load labelbase_tags %}
|
||||
{% load importer_tags %}
|
||||
{% load sekizai_tags %}
|
||||
{% load bootstrap %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{#% include "_labelbase_header_info_menu.html" %#}
|
||||
{# TODO: Models / Forms aren't loaded if included like that ^^^ – using a simplified header #}
|
||||
{# TODO: Fix & merge is a bit a mess, rework this ASAP. #}
|
||||
{% if labelbase %}
|
||||
<div class="row">
|
||||
<div class="col float-start">
|
||||
<h2 style="padding-top: 1em;">{% block title %}{{ labelbase.name }} {% endblock %}</h2>
|
||||
{% if labelbase.fingerprint %}<tt>{{ labelbase.fingerprint }} </tt>{% endif %}
|
||||
{% if labelbase.about %}
|
||||
<p>{{ labelbase.about}}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>There is no such labelbase.</p>
|
||||
{% endif %}
|
||||
{# simplified header #}
|
||||
|
||||
|
||||
<div class="col">
|
||||
|
||||
{% if fix_suggestions %}
|
||||
{% if fix_suggestions == 1 %}
|
||||
<strong>There is {{ fix_suggestions }} suggestsion!</strong>
|
||||
{% else %}
|
||||
<strong>There are {{ fix_suggestions }} suggestions!</strong>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<!--
|
||||
<div class="alert alert-dismissible bd-callout bd-callout-info ">
|
||||
<div style=" padding-right: 1.8rem"><strong>There are {{ fix_suggestions }} suggestions!</strong>
|
||||
|
||||
"Automatically Consolidate Duplicate Labels
|
||||
<i>Fix & manage</i> has identified multiple labels with identical type, reference, label, origin, and spendable attributes within the same labelbase. As an efficiency measure, we have automatically merged XX of these duplicate labels, reducing them to YY unique labels."
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-dismissible bd-callout bd-callout-info">
|
||||
<div style="padding-right: 1.8rem">
|
||||
<strong>{{ fix_suggestions }} suggestions available!</strong>
|
||||
<p>
|
||||
<i>Automatically Consolidate Duplicate Labels</i>
|
||||
</p>
|
||||
<p>
|
||||
<em>Fix & Manage</em> has identified multiple labels with identical type, reference, label, origin, and spendable attributes within the same labelbase. As an efficiency measure, we have automatically merged {{ duplicate_labels_count }} of these duplicate labels, reducing them to {{ unique_labels_count }} unique labels.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
-->
|
||||
|
||||
|
||||
{% comment %}
|
||||
{% if resulting_duplicates_all_identical_current_record_count %}
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Merge duplicate with identical attributes</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<p class="card-text">
|
||||
Multiple labels with identical <tt>type</tt>, <tt>ref</tt>, <tt>label</tt>, <tt>origin</tt> and <tt>spendable</tt> attributes exists within the same labelbase.
|
||||
</p>
|
||||
<p>
|
||||
Automatically merge {{ resulting_duplicates_all_identical_current_record_count }} labels into {{ resulting_duplicates_all_identical_final_record_count }} unique labels.
|
||||
|
||||
</p>
|
||||
|
||||
<tt>TODO: Show hint for auto cleanup (profile settings).</tt>
|
||||
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="d-flex justify-content-end">
|
||||
<a href="{% url "labelbase_merge" labelbase_id=labelbase.id %}?type={{ problem.type }}&ref={{ problem.ref }}&label={{ problem.label }}" class="btn btn-outline-primary">Merge</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endcomment %}
|
||||
|
||||
{% comment %}
|
||||
{% for problem in all_identical_records %}
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title"> all_identical_records Merge duplicate with identical type, ref and label</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<p class="card-text">
|
||||
Multiple labels with the same type <tt>'{{ problem.type }}'</tt>, ref <tt>'{{ problem.ref }}'</tt> and label <tt>'{{ problem.label }}'</tt> exist within the same labelbase.
|
||||
</p>
|
||||
<br>
|
||||
<tt>TODO: Depending on the attrs it's totally safe to automerge these recods.</tt>
|
||||
<br>
|
||||
<tt>TODO: Group those instead of listening one after the other, eg. 109 indentical records.</tt>
|
||||
<br>
|
||||
<tt>TODO: Show hint for auto cleanup (profile settings).</tt>
|
||||
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="d-flex justify-content-end">
|
||||
<a href="">Review & Merge</a> (<- this works in some cases , eg. addr,ref,label)
|
||||
<a href="{% url "labelbase_merge" labelbase_id=labelbase.id %}?type={{ problem.type }}&ref={{ problem.ref }}&label={{ problem.label }}" class="btn btn-outline-primary">Review & Merge</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endcomment %}
|
||||
|
||||
{% comment %}
|
||||
{% for problem in resulting_duplicates_type_and_ref_and_label %}
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Merge duplicate with identical type, ref and label</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<p class="card-text">
|
||||
Multiple labels with the same type <tt>'{{ problem.type }}'</tt>, ref <tt>'{{ problem.ref }}'</tt> and label <tt>'{{ problem.label }}'</tt> exist within the same labelbase.
|
||||
</p>
|
||||
<br>
|
||||
<tt>TODO: Depending on the attrs it's totally safe to automerge these recods.</tt>
|
||||
<br>
|
||||
<tt>TODO: Group those instead of listening one after the other, eg. 109 indentical records.</tt>
|
||||
<br>
|
||||
<tt>TODO: Show hint for auto cleanup (profile settings).</tt>
|
||||
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="d-flex justify-content-end">
|
||||
<a href="">Review & Merge</a> (<- this works in some cases , eg. addr,ref,label)
|
||||
<a href="{% url "labelbase_merge" labelbase_id=labelbase.id %}?type={{ problem.type }}&ref={{ problem.ref }}&label={{ problem.label }}" class="btn btn-outline-primary">Review & Merge</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endcomment %}
|
||||
|
||||
{% comment %}
|
||||
{% for problem in resulting_duplicates_type_and_ref %}
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Merge duplicate{# .. with different attributes" #}{# vs "Merge duplicate with identical attributes" #}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<p class="card-text">
|
||||
Multiple labels with the same type <tt>'{{ problem.type }}'</tt> and ref <tt>'{{ problem.ref }}'</tt> exist within the same labelbase.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="d-flex justify-content-end">
|
||||
<a href="{% url "labelbase_merge" labelbase_id=labelbase.id %}?type={{ problem.type }}&ref={{ problem.ref }}" class="btn btn-outline-primary">Review & Merge</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endcomment %}
|
||||
|
||||
{% if fix_suggestions %}
|
||||
{% else %}
|
||||
Well done! Nothing to complain.
|
||||
{% endif %}
|
||||
|
||||
{% comment %}
|
||||
<div class="card mt-3">
|
||||
<div class="card-header" data-bs-toggle="collapse" data-bs-target="#collapse1">
|
||||
<h5 class="mb-0 d-flex justify-content-between align-items-center">
|
||||
<span>Merge duplicate with identical attributes</span>
|
||||
<button class="btn btn-primary">Fix Now</button>
|
||||
</h5>
|
||||
</div>
|
||||
<div id="collapse1" class="collapse">
|
||||
<div class="card-body">
|
||||
{{ problem.type }} / {{ problem.ref }}
|
||||
<p>You have duplicated labels in your system. Please review and remove duplicates.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
|
||||
|
||||
{% comment %}
|
||||
<!-- Problem Card 1 - Collapsed -->
|
||||
<div class="card mt-3">
|
||||
<div class="card-header" data-bs-toggle="collapse" data-bs-target="#collapse1">
|
||||
<h5 class="mb-0 d-flex justify-content-between align-items-center">
|
||||
<span>Duplicated Labels</span>
|
||||
<button class="btn btn-primary">Fix Now</button>
|
||||
</h5>
|
||||
</div>
|
||||
<div id="collapse1" class="collapse">
|
||||
<div class="card-body">
|
||||
<p>You have duplicated labels in your system. Please review and remove duplicates.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
{% if resulting_empty_label_records %}
|
||||
<div class="card mt-3">
|
||||
<div class="card-header" data-bs-toggle="collapse" data-bs-target="#collapse_empty_labels">
|
||||
<h5 class="mb-0 d-flex justify-content-between align-items-center">
|
||||
<span>{{ resulting_empty_label_records|length }} Empty Labels</span>
|
||||
<button class="btn btn-primary">Fix Now</button>
|
||||
</h5>
|
||||
<p>There are empty label texts in your labelbase. Add a label text to these labels for better organization.</p>
|
||||
</div>
|
||||
<div id="collapse_empty_labels" class="collapse">
|
||||
<div class="card-body">
|
||||
<ul>
|
||||
{% for problem in resulting_empty_label_records %}
|
||||
<li>
|
||||
Record with type <tt>'{{ problem.type }}'</tt> and <br>
|
||||
ref <tt>'{{ problem.ref }}'</tt> has an empty label text.
|
||||
<span class="d-flex justify-content-end" style="margin-top: -2rem;">
|
||||
<a href="{% url "edit_label" problem.id %}"
|
||||
class="btn btn-outline-primary">Edit Label</a>
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if resulting_too_long_label_records %}
|
||||
<div class="card mt-3">
|
||||
<div class="card-header" data-bs-toggle="collapse" data-bs-target="#collapse_too_long_labels">
|
||||
<h5 class="mb-0 d-flex justify-content-between align-items-center">
|
||||
<span>{{ resulting_too_long_label_records|length }} Labels are too long</span>
|
||||
<button class="btn btn-primary">Fix Now</button>
|
||||
</h5>
|
||||
<p>There are too long label texts in your labelbase.</p>
|
||||
</div>
|
||||
<div id="collapse_too_long_labels" class="collapse">
|
||||
<div class="card-body">
|
||||
<p><strong>BIP-329 suggests:</strong> Importing wallet may ignore records it does not store, and truncate labels if necessary.
|
||||
A suggested default for maximum label length is 255 characters, and an importing wallet should consider warning the user if truncation is applied.
|
||||
</p>
|
||||
<ul>
|
||||
{% for problem in resulting_too_long_label_records %}
|
||||
<li>
|
||||
Record with type <tt>'{{ problem.type }}'</tt> and <br>
|
||||
ref <tt>'{{ problem.ref }}'</tt> has a too long label text.
|
||||
<span class="d-flex justify-content-end" style="margin-top: -2rem;">
|
||||
<a href="{% url "edit_label" problem.id %}"
|
||||
class="btn btn-outline-primary">Edit Label</a>
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
||||
{% comment %}
|
||||
<!-- Problem Card 2 - Collapsed -->
|
||||
<div class="card mt-3">
|
||||
<div class="card-header" data-bs-toggle="collapse" data-bs-target="#collapse_too_long_labels">
|
||||
<h5 class="mb-0 d-flex justify-content-between align-items-center">
|
||||
<span>Label too long</span>
|
||||
|
||||
</h5>
|
||||
</div>
|
||||
<div id="collapse_too_long_labels" class="collapse">
|
||||
<div class="card-body">
|
||||
|
||||
<p><!--Some labels exceed the maximum length of 255 characters.-->
|
||||
This label exceeds the maximum length of 255 characters for this record.
|
||||
</p>
|
||||
|
||||
|
||||
<button class="btn btn-primary">Fix Now (label edit page)</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
|
||||
|
||||
{% comment %}
|
||||
<!-- Problem Card 3 - Collapsed -->
|
||||
<div class="card mt-3">
|
||||
<div class="card-header" data-bs-toggle="collapse" data-bs-target="#collapse3">
|
||||
<h5 class="mb-0 d-flex justify-content-between align-items-center">
|
||||
<span>Incorrect Labels</span>
|
||||
<button class="btn btn-primary">Fix Now</button>
|
||||
</h5>
|
||||
</div>
|
||||
<div id="collapse3" class="collapse">
|
||||
<div class="card-body">
|
||||
<p>Some labels have incorrect information. Please update them for accuracy.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
|
||||
{% comment %}
|
||||
<!-- Problem Card 4 - Collapsed -->
|
||||
<div class="card mt-3">
|
||||
<div class="card-header" data-bs-toggle="collapse" data-bs-target="#collapse4">
|
||||
<h5 class="mb-0 d-flex justify-content-between align-items-center">
|
||||
<span>Missing Labels</span>
|
||||
<button class="btn btn-primary">Fix Now</button>
|
||||
</h5>
|
||||
</div>
|
||||
<div id="collapse4" class="collapse">
|
||||
<div class="card-body">
|
||||
<p>Some labels are missing in your addresses derived from the XPUB. Add them for better organization.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
|
||||
{% else %} {# fix_suggestions #}
|
||||
All good. Great job!
|
||||
{% endif %} {# no fix_suggestions #}
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
{% addtoblock "js" %}
|
||||
{% endaddtoblock %}
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue