re-added importer

This commit is contained in:
Xavier Fiechter 2024-01-07 07:56:45 +01:00
parent 57f0bc492f
commit 1aed9938cb
13 changed files with 351 additions and 20 deletions

View file

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

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ImporterConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "importer"

17
django/importer/forms.py Normal file
View file

@ -0,0 +1,17 @@
from django import forms
IMPORTER_CHOICES = (
("BIP-0329", "BIP-329 .jsonl"),
#("BIP-0329-7z-enc" , "BIP-329 (encrypted) .7z"),
("csv-bluewallet", "BlueWallet .csv"),
("csv-bitbox", "BitBox .csv"),
("pocket-accointing", "Pocket Accointing .csv")
)
class UploadFileForm(forms.Form):
labelbase_id = forms.IntegerField(widget=forms.HiddenInput())
import_type = forms.ChoiceField(
choices=IMPORTER_CHOICES #, widget=forms.HiddenInput()
)
file = forms.FileField()

49
django/importer/pocket.py Normal file
View file

@ -0,0 +1,49 @@
import csv
import json
from labelbase.models import Label
from labelbase.models import Labelbase
def validate_csv_format(csv_file_path):
expected_sequence = ['withdraw', 'order', 'deposit']
with open(csv_file_path, newline='') as csvfile:
csv_reader = csv.reader(csvfile)
next(csv_reader) # Skip the header
for row in csv_reader:
if len(row) != 11 or row[0] not in expected_sequence:
return False
expected_index = expected_sequence.index(row[0])
if expected_index == 0 and expected_index != 0:
return False
expected_sequence.append(expected_sequence.pop(0))
return True
def parse_csv_to_json(csv_file_path):
json_list = []
with open(csv_file_path, newline='') as csvfile:
csv_reader = csv.reader(csvfile)
next(csv_reader) # Skip the header
group = []
for row in csv_reader:
if row[0] == 'withdraw':
if group:
json_list.append(group)
group = []
data = {
'transactionType': row[0],
'date': row[1],
'inBuyAmount': row[2],
'inBuyAsset': row[3],
'outSellAmount': row[4],
'outSellAsset': row[5],
'feeAmount': row[6] if row[6] else "",
'feeAsset': row[7] if row[7] else "",
'classification': row[8] if row[8] else "",
'operationId': row[9] if row[9] else "",
'comments': row[10] if row[10] else ""
}
group.append(data)
if group:
json_list.append(group)
return json_list

View file

View file

@ -0,0 +1,34 @@
from django import template
from importer.forms import UploadFileForm
register = template.Library()
@register.simple_tag
def genericlabeluploadform(labelbase_id):
form = UploadFileForm()
form.fields["labelbase_id"].initial = labelbase_id
form.fields["import_type"].initial = "BIP-0329"
return form
@register.simple_tag
def bip0329labeluploadform(labelbase_id):
form = UploadFileForm()
form.fields["labelbase_id"].initial = labelbase_id
form.fields["import_type"].initial = "BIP-0329"
return form
@register.simple_tag
def csvBlueWalletlabeluploadform(labelbase_id):
form = UploadFileForm()
form.fields["labelbase_id"].initial = labelbase_id
form.fields["import_type"].initial = "csv-bluewallet"
return form
@register.simple_tag
def csvBitBoxLabeluploadform(labelbase_id):
form = UploadFileForm()
form.fields["labelbase_id"].initial = labelbase_id
form.fields["import_type"].initial = "csv-bitbox"
return form

184
django/importer/views.py Normal file
View file

@ -0,0 +1,184 @@
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
import os
import json
from labelbase.models import Labelbase
from labelbase.serializers import LabelSerializer
from django.shortcuts import get_object_or_404
from django.contrib import messages
import decimal
from .forms import UploadFileForm
from tempfile import NamedTemporaryFile
EOLSTOP = [b"", "", None, "\n"]
def handle_uploaded_file(f):
fp = NamedTemporaryFile(delete=False)
for chunk in f.chunks():
fp.write(chunk)
return fp
@login_required
def upload_labels(request):
"""
Used to import labels manually using files.
"""
if request.method == "POST":
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
imported_lables = 0
labelbase = get_object_or_404(
Labelbase,
id=form.cleaned_data.get("labelbase_id", ""),
user_id=request.user.id,
)
fp = handle_uploaded_file(request.FILES["file"])
fp.seek(0)
# BIP-0329
if form.cleaned_data.get("import_type", "") == "BIP-0329":
while True:
buf = fp.readline()
if buf in EOLSTOP:
break
data = json.loads(buf)
data["labelbase"] = labelbase.id
serializer = LabelSerializer(data=data)
if serializer.is_valid():
serializer.save()
imported_lables += 1
elif form.cleaned_data.get("import_type", "") == "BIP-0329-7z-enc":
# TODO: Implement
pass
# Bitbox App
elif form.cleaned_data.get("import_type", "") == "csv-bitbox":
while True:
buf = fp.readline()
if buf in EOLSTOP:
break
try:
buf = str(buf.decode("utf-8"))
sbuf = buf.split(",")
# Time,Type,Amount,Unit,Fee,Address,Transaction ID,Note
for elem in [("tx", 6), ("addr", 5)]:
data = {
"type": elem[0],
"ref": sbuf[elem[1]],
"label": " ".join(sbuf[7:]),
}
data["labelbase"] = labelbase.id
serializer = LabelSerializer(data=data)
if serializer.is_valid():
serializer.save()
imported_lables += 1
else:
messages.add_message(
request,
messages.ERROR,
'Could not process line "{}".'.format(buf),
)
except Exception as ex:
messages.add_message(
request,
messages.ERROR,
'Could not process line "{}", {}.'.format(buf, ex),
)
# Pocket Accointing
elif form.cleaned_data.get("import_type", "") == "pocket-accointing":
fp.close()
csv_file_path = fp.name
mempool_api = labelbase.get_mempool_api()
from .pocket import validate_csv_format, parse_csv_to_json
if validate_csv_format(csv_file_path):
for item in parse_csv_to_json(csv_file_path):
#label = "Got {} {} for {} {} in tx=\"{}\" ref=\"{} #Pocket\" ".format(item[0].get('outSellAmount'), item[1].get('inBuyAsset') , item[2].get('inBuyAmount'), item[2].get('inBuyAsset') , item[0].get('operationId'), item[1].get('operationId'))
label = "Got {} {} for {:.2f} {} with reference: {} #Pocket".format(item[0].get('outSellAmount'), item[1].get('inBuyAsset'), decimal.Decimal(item[2].get('inBuyAmount')), item[2].get('inBuyAsset'), item[1].get('operationId'))
txid = item[0].get('operationId')
tx = mempool_api.get_transaction(txid)
potential_utxos = []
vouts = tx.get("vout", [])
for i in range(len(vouts)):
if vouts[i].get('value', 0) == decimal.Decimal(item[0].get('outSellAmount'))*100000000:
potential_utxos.append("{}:{}".format(txid, i ))
data = {}
if len(potential_utxos) == 1:
# label UTXO/output of tx
data = {
"type": "output",
"ref": potential_utxos[0],
"label": label,
}
if len(potential_utxos) > 1:
# mark tx, add warning tag
data = {
"type": "tx",
"ref": txid,
"label": "{} {}".format(label, "#W001_UTXO_NOT_FOUND"),
}
if data:
data["labelbase"] = labelbase.id
serializer = LabelSerializer(data=data)
if serializer.is_valid():
serializer.save()
imported_lables += 1
else:
messages.add_message(
request,
messages.ERROR,
'Could not process record "{}".'.format(item),
)
else:
print("ERROR") # TODO
# BlueWallet
elif form.cleaned_data.get("import_type", "") == "csv-bluewallet":
while True:
buf = fp.readline()
if buf in EOLSTOP:
break
try:
buf = str(buf.decode("utf-8"))
sbuf = buf.split(",")
data = {
"type": "tx",
"ref": sbuf[1],
"label": " ".join(sbuf[3:]),
}
data["labelbase"] = labelbase.id
serializer = LabelSerializer(data=data)
if serializer.is_valid():
serializer.save()
imported_lables += 1
else:
messages.add_message(
request,
messages.ERROR,
'Could not process line "{}".'.format(buf),
)
except Exception as ex:
messages.add_message(
request,
messages.ERROR,
'Could not process line "{}", {}.'.format(buf, ex),
)
else:
fp.close()
os.unlink(fp.name)
return HttpResponseRedirect("/failed/url/")
fp.close()
os.unlink(fp.name)
if imported_lables:
messages.add_message(
request,
messages.INFO,
"Processed and imported {} labels.".format(imported_lables),
)
return HttpResponseRedirect(labelbase.get_absolute_url())
else:
form = UploadFileForm()
return render(request, "upload.html", {"form": form})

View file

@ -386,7 +386,6 @@
$(document).ready(function () {
// Disable the Origin field by default if not Tx
{% if label.type == "tx" %}
{% else %}

View file

@ -17,17 +17,15 @@
<div id="export-label-modal-body">
{% labelbaseform_export as export_form %}
{{ export_form|bootstrap }}
<div id="export_enc_warning" class="alert alert-light" role="alert" style="margin-top:1.2rem;">
<small>Labels are encrypted using our <a href="https://github.com/Labelbase/python-bip329">Python library for BIP-329</a>. <br>
Always keep your passphrase secret and secure! </small>
</div>
</div>
<div id="successMessage" class="alert alert-success d-none" role="alert" style="margin-top:1.2rem;">
<strong>Success!</strong> Your export has been completed.
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>

View file

@ -281,6 +281,28 @@
</div>
</div>
<div class="col d-flex align-items-start">
<div>
<h3 class="fw-bold mb-0 fs-4 text-body-emphasis">Automated Output Management</h3>
<p>We utilize Electrum servers to automatically update your spendable outputs. Labelbase keeps the state of your spendable outputs up to date, helping you keep track of your labels and funds.</p>
</div>
</div>
<div class="col d-flex align-items-start">
<div>
<h3 class="fw-bold mb-0 fs-4 text-body-emphasis">Automated Fiat Currency Evaluation</h3>
<p>Labelbase converts the value of your spendable outputs to fiat currency, assisting you in keeping track of your portfolio value measured in your chosen currency. </p>
</div>
</div>
<div class="col d-flex align-items-start">
<div>
<h3 class="fw-bold mb-0 fs-4 text-body-emphasis"><br> <br> </h3>
<p>.. and much more.</p>
</div>
</div>
</div>
</div>
@ -293,10 +315,10 @@
<div class="text-center mt-5">
<p class="lb-header mx-auto text-center fs-5 text-muted">
Ready to optimize your Bitcoin labeling for optimal efficiency?
<br><br>
Discover the power of streamlined label management with Labelbase.
<br><br>
Join Labelbase now and unlock the full potential of your Bitcoin experience!
<br><br>
Discover the power of streamlined label management with Labelbase.
<br><br>
Join Labelbase now and unlock the full potential of your Bitcoin experience!
</p>
@ -306,12 +328,15 @@ Join Labelbase now and unlock the full potential of your Bitcoin experience!
{% endif %}
<!-- Add: What people are saying -->
<!-- Add:
Our supporters
Our supporters:
Special Thanks! <3 to OpenSats !
Thank you for your support!
-->
-->
{% endblock %}

View file

@ -10,24 +10,32 @@
<div class=" p-3 pb-md-4 mx-auto text-left">
<h2 class="display-8 fw-normal">Privacy Policy</h2>
<small>The privacy policy on this website specifically applies to the labelbase.space web application, which is hosted by the Labelbase team.<br>Last updated: July 26<sup>th</sup> 2023.</small>
<p>
The privacy policy applies to labelbase.space hosted by Labelbase. For self-hosted versions, variations or customizations may exist.
Users are responsible for reviewing any specific privacy considerations or customizations made to their instance.<br><br>
Last updated: December 19<sup>th</sup> 2023.</p>
<p class="fs-5 text-muted">
<ol style="line-height:2em; ">
<li>We do not use any analytics or tracking tools that collect personal information, but {# offer an optional opt-in to use #} we use Sentry for anonymized error tracking, ensuring users are not associated with reported errors.</li>
<li>This website does not use cookies, apart from the login session. However, your IP address may be collected as part of basic webserver logs necessary for systems administration purposes. </li>
<li>We do not use any analytics or tracking tools that collect personal information, but {# offer an optional opt-in to use #} we use <a href="https://sentry.io/">Sentry</a> for anonymized error tracking, ensuring users are not associated with reported errors.</li>
<li>This website does not use cookies, except for the login session and other essential cookies required for the functionality of the web application. However, your IP address may be collected as part of basic webserver logs necessary for systems administration purposes. </li>
<li>We do not collect or store information, expect the information that is attached directly to labels. Deleting your Labelbase removes the labels attached to it. </li>
<li>We do not share any information with third parties including advertisers. See 6. for interated APIs and services.</li>
<li>We cannot access your funds.</li>
<li>We are and act as your data processor. In other words: You are responsible for the labelbases and labels you create on or upload to Labelbase, which we store and process to provide our services to you. </li>
<li>This is not a wallet software. We cannot access your funds.</li>
<li>We are and act as your data processor. In other words: You are responsible for the labelbases, labels and additional date you create on or upload to Labelbase, which we store and process to provide our services to you. </li>
<li>"Labelbase" uses the following 3<sup>rd</sup> party APIs and services:
<ul>
<li>The addresses and transactions are provided with hyperlinks to mempool.space. These links must be actively clicked.</li>
<li>We have no influence over wallets and services that implement our API.</li>
<li>We connect and may get data (like exchange rates) from mempool.space</li>
<li>We connect and may get data from some "community trusted" Electrum servers.</li>
</ul>
We have no influence over wallets and services that implement our API.
</li>
</ol>
</p>
</div>
{% endblock %}

View file

@ -9,9 +9,9 @@
<div class=" p-3 pb-md-4 mx-auto text-left">
<h2 class="display-8 fw-normal">Terms of Service</h2>
<small>February 10<sup>th</sup> 2023</small>
<small>December 19<sup>th</sup> 2023</small>
<p class="fs-5 text-muted">
"LABELBASE" (labelbase.space) is provided without any guarantees of anything, not
"LABELBASE" (labelbase.space, but also the self-hosted versions) is provided without any guarantees of anything, not
even that it will do its job properly. If you have any doubts, please use an
alternate service.
</p>
@ -107,5 +107,5 @@ FIRE, FLOODS, ACCIDENTS, SERVICE OUTAGES RESULTING FROM EQUIPMENT AND/OR SOFTWAR
</div>
{% endblock %}

View file

@ -0,0 +1,11 @@
{% extends "_base.html" %}
{% load i18n %}
{% load bootstrap %}
{% block content %}
<h2 style="padding-top: 1em;">Import labels</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" value="OK">
</form>
{% endblock %}