Skip to content

Commit

Permalink
feat(app): adding models and admin for address lists (#612)
Browse files Browse the repository at this point in the history
* feat(app): adding models and admin for address lists

* manual csv import

* feat(api): working csv import, moved to account

* feat(api): added allowlists to customizaion weights

* cleanup
  • Loading branch information
lucianHymer authored Jun 12, 2024
1 parent 4dc618b commit 8cc7faf
Show file tree
Hide file tree
Showing 12 changed files with 628 additions and 413 deletions.
748 changes: 343 additions & 405 deletions api/Pipfile.lock

Large diffs are not rendered by default.

74 changes: 72 additions & 2 deletions api/account/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,20 @@
from rest_framework_api_key.admin import APIKeyAdmin
from scorer.scorer_admin import ScorerModelAdmin
from scorer_weighted.models import Scorer

from .models import Account, AccountAPIKey, Community, Customization
from django.urls import path
from django.shortcuts import render, redirect
import csv
import codecs

from .models import (
Account,
AccountAPIKey,
AllowList,
Community,
Customization,
AddressList,
AddressListMember,
)


@admin.register(Account)
Expand Down Expand Up @@ -193,10 +205,16 @@ class Meta:
fields = "__all__"


class AllowListInline(admin.TabularInline):
model = AllowList
extra = 0


@admin.register(Customization)
class CustomizationAdmin(ScorerModelAdmin):
form = CustomizationForm
raw_id_fields = ["scorer"]
inlines = [AllowListInline]
fieldsets = [
(
None,
Expand Down Expand Up @@ -254,3 +272,55 @@ def save_model(self, request, obj, form, change):
# Perform some validation...
pass
super().save_model(request, obj, form, change)


class AddressListMemberInline(admin.TabularInline):
model = AddressListMember
extra = 0


class AddressListCsvImportForm(forms.Form):
list = forms.ModelChoiceField(queryset=AddressList.objects.all(), required=True)
csv_file = forms.FileField()


@admin.register(AddressList)
class AddressListAdmin(ScorerModelAdmin):
list_display = ["name", "address_count"]
inlines = [AddressListMemberInline]
change_list_template = "account/addresslist_changelist.html"

def address_count(self, obj):
return obj.addresses.count()

def get_urls(self):
return [
path("import-csv/", self.import_csv),
] + super().get_urls()

def import_csv(self, request):
if request.method == "POST":
csv_file = request.FILES["csv_file"]
reader = csv.reader(codecs.iterdecode(csv_file, "utf-8"))
list_id = request.POST.get("list")
address_list = AddressList.objects.get(id=list_id)
duplicate_count = 0
success_count = 0
for row in reader:
address = row[0].strip()
try:
AddressListMember.objects.create(address=address, list=address_list)
success_count += 1
except Exception:
duplicate_count += 1
continue

self.message_user(
request,
"Imported %d addresses, skipped %d duplicates"
% (success_count, duplicate_count),
)
return redirect("..")
form = AddressListCsvImportForm()
payload = {"form": form}
return render(request, "account/address_list_csv_import_form.html", payload)
5 changes: 5 additions & 0 deletions api/account/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,11 @@ def get_account_customization(request, dashboard_path: str):
elif scorer and getattr(scorer, "binaryweightedscorer", None):
weights = scorer.binaryweightedscorer.weights

for allow_list in customization.allow_lists.all():
weights[f"AllowList#{allow_list.address_list.name}"] = str(
allow_list.weight
)

return dict(
key=customization.path,
useCustomDashboardPanel=customization.use_custom_dashboard_panel,
Expand Down
58 changes: 58 additions & 0 deletions api/account/migrations/0025_addresslist_addresslistmember.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Generated by Django 4.2.6 on 2024-06-11 22:12

import account.models
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
("account", "0024_customization_scorer_panel_text"),
]

operations = [
migrations.CreateModel(
name="AddressList",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(db_index=True, max_length=100, unique=True)),
],
),
migrations.CreateModel(
name="AddressListMember",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"address",
account.models.EthAddressField(db_index=True, max_length=100),
),
(
"list",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="addresses",
to="account.addresslist",
),
),
],
options={
"unique_together": {("address", "list")},
},
),
]
20 changes: 20 additions & 0 deletions api/account/migrations/0026_alter_customization_scorer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.2.6 on 2024-06-11 22:13

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
("account", "0025_addresslist_addresslistmember"),
]

operations = [
migrations.AlterField(
model_name="customization",
name="scorer",
field=models.OneToOneField(
on_delete=django.db.models.deletion.PROTECT, to="account.community"
),
),
]
44 changes: 44 additions & 0 deletions api/account/migrations/0027_allowlist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Django 4.2.6 on 2024-06-11 22:35

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
("account", "0026_alter_customization_scorer"),
]

operations = [
migrations.CreateModel(
name="AllowList",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("weight", models.FloatField(default=0.0)),
(
"address_list",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="allow_lists",
to="account.addresslist",
),
),
(
"customization",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="allow_lists",
to="account.customization",
),
),
],
),
]
17 changes: 17 additions & 0 deletions api/account/migrations/0028_alter_allowlist_weight.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.6 on 2024-06-12 15:39

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("account", "0027_allowlist"),
]

operations = [
migrations.AlterField(
model_name="allowlist",
name="weight",
field=models.DecimalField(decimal_places=4, default=0.0, max_digits=7),
),
]
35 changes: 34 additions & 1 deletion api/account/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,27 @@ async def aget_scorer(self) -> Scorer:
return await BinaryWeightedScorer.objects.aget(scorer_ptr_id=scorer.id)


class AddressList(models.Model):
name = models.CharField(max_length=100, db_index=True, unique=True)

def __str__(self):
return f"AllowList - {self.name}"


class AddressListMember(models.Model):
address = EthAddressField(null=False, blank=False, max_length=100, db_index=True)
list = models.ForeignKey(
AddressList,
related_name="addresses",
on_delete=models.CASCADE,
null=False,
db_index=True,
)

class Meta:
unique_together = ["address", "list"]


class Customization(models.Model):
class CustomizationLogoBackgroundType(models.TextChoices):
DOTS = "DOTS"
Expand All @@ -405,7 +426,7 @@ class CustomizationLogoBackgroundType(models.TextChoices):
blank=True,
unique=False,
)
scorer = models.ForeignKey(Community, on_delete=models.PROTECT)
scorer = models.OneToOneField(Community, on_delete=models.PROTECT)
use_custom_dashboard_panel = models.BooleanField(default=False)

# CustomizationTheme
Expand Down Expand Up @@ -452,3 +473,15 @@ class CustomizationLogoBackgroundType(models.TextChoices):
blank=True,
null=True,
)


class AllowList(models.Model):
address_list = models.ForeignKey(
AddressList, on_delete=models.PROTECT, related_name="allow_lists"
)

customization = models.ForeignKey(
Customization, on_delete=models.CASCADE, related_name="allow_lists"
)

weight = models.DecimalField(default=0.0, max_digits=7, decimal_places=4)
14 changes: 14 additions & 0 deletions api/account/templates/account/address_list_csv_import_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% extends 'admin/base.html' %}

{% block content %}
<div>
<form action="." method="POST" enctype="multipart/form-data">
{{ form.as_p }}
{% csrf_token %}

<button type="submit">Upload CSV</button>
</form>
</div>
<br />

{% endblock %}
11 changes: 11 additions & 0 deletions api/account/templates/account/addresslist_changelist.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% extends 'admin/change_list.html' %}

{% block object-tools %}
<a style="padding: 8px; margin: 20px; font-weight: bold; text-decoration: none; border-radius: 12px; background-color: blue; color: white;"
href="import-csv/">
Import CSV 📁
</a>
<br />
<br />
{{ block.super }}
{% endblock %}
11 changes: 9 additions & 2 deletions api/registry/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
from django.contrib import admin, messages
from registry.api.schema import SubmitPassportPayload
from registry.api.v1 import ahandle_submit_passport
from registry.models import Event, GTCStakeEvent, HashScorerLink, Passport, Score, Stamp
from registry.models import (
Event,
GTCStakeEvent,
HashScorerLink,
Passport,
Score,
Stamp,
)
from scorer.scorer_admin import ScorerModelAdmin


Expand All @@ -24,7 +31,7 @@ def recalculate_user_score(modeladmin, request, queryset):
)
async_to_sync(ahandle_submit_passport)(sp, c.account)
rescored_ids.append(score.id)
except Exception as e:
except Exception:
print(f"Error for {scorer_id} and {address}")
failed_rescoring.append(score.id)

Expand Down
4 changes: 1 addition & 3 deletions api/registry/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import json

from account.models import Community, EthAddressField
from django.db import models
from django.db.models.signals import pre_save
Expand Down Expand Up @@ -89,7 +87,7 @@ def score_updated(sender, instance, **kwargs):
address=instance.passport.address,
community=instance.passport.community,
data={
"score": float(instance.score) if instance.score != None else 0,
"score": float(instance.score) if instance.score is not None else 0,
"evidence": instance.evidence,
},
)
Expand Down

0 comments on commit 8cc7faf

Please sign in to comment.