Skip to content

Commit

Permalink
1665 unified grants stamp (#445)
Browse files Browse the repository at this point in the history
* feat(api): add address field to contribution index

* refactor(api): update cgrants api to return contributions from protocol and cgrants

* feat(api): update rescoring command to propogate new weights

* fix(api): adjust tests to match settings update and query bug
  • Loading branch information
tim-schultz authored Oct 27, 2023
1 parent fd99efb commit 610345a
Show file tree
Hide file tree
Showing 13 changed files with 889 additions and 800 deletions.
1 change: 1 addition & 0 deletions api/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ gql = "*"
requests-toolbelt = "*"
pyarrow = "*"
pynacl = "*"
faker = "*"

[dev-packages]
black = "*"
Expand Down
564 changes: 269 additions & 295 deletions api/Pipfile.lock

Large diffs are not rendered by default.

97 changes: 43 additions & 54 deletions api/cgrants/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
from ninja.security import APIKeyHeader
from ninja_extra import NinjaExtraAPI
from ninja_schema import Schema
from ninja_schema.orm.utils.converter import Decimal
from registry.api.v1 import is_valid_address
from registry.exceptions import InvalidAddressException

from .models import (
Contribution,
Expand Down Expand Up @@ -57,57 +60,36 @@ class IdentifierType(Enum):
GITHUB_ID = "github_id"


def _get_contributor_statistics_for_cgrants(
identifier: str, identifier_type: IdentifierType
) -> dict:
if identifier_type == IdentifierType.HANDLE:
identifier_query = Q(profile__handle=identifier)
else:
identifier_query = Q(profile__github_id=identifier)
def _get_contributor_statistics_for_cgrants(address: str) -> dict:
identifier_query = Q(contributor_address=address)

contributions = GrantContributionIndex.objects.filter(
identifier_query, contribution__success=True
)

if contributions.count() == 0:
return {
"num_grants_contribute_to": 0,
"total_contribution_amount": 0,
}

# Get number of grants the user contributed to

num_grants_contribute_to = (
GrantContributionIndex.objects.filter(identifier_query)
.order_by("grant_id")
.values("grant_id")
.distinct()
.count()
)

# Get number of rounds the user contributed to
num_rounds_contribute_to = (
GrantContributionIndex.objects.filter(identifier_query, round_num__isnull=False)
.order_by("round_num")
.values("round_num")
.distinct()
.count()
contributions.order_by("grant_id").values("grant_id").distinct().count()
)

# Get the total contribution amount
total_contribution_amount = GrantContributionIndex.objects.filter(
identifier_query
).aggregate(total_contribution_amount=Sum("amount"))["total_contribution_amount"]
total_contribution_amount = contributions.aggregate(
total_contribution_amount=Sum("amount")
)["total_contribution_amount"]

if total_contribution_amount is None:
total_contribution_amount = 0

# GR14 contributor (and not squelched by FDD)
profile_squelch = SquelchProfile.objects.filter(
identifier_query, active=True
).values_list("profile_id", flat=True)

num_gr14_contributions = (
GrantContributionIndex.objects.filter(identifier_query, round_num=14)
.exclude(profile_id__in=profile_squelch)
.count()
)

return {
"num_grants_contribute_to": num_grants_contribute_to,
"num_rounds_contribute_to": num_rounds_contribute_to,
"total_contribution_amount": total_contribution_amount,
"num_gr14_contributions": num_gr14_contributions,
}


Expand All @@ -116,20 +98,15 @@ def _get_contributor_statistics_for_protocol(address: str) -> dict:
contributor=address, amount__gte=0.95
)
total_amount_usd = protocol_filter.aggregate(Sum("amount"))["amount__sum"]
num_rounds = protocol_filter.aggregate(Count("round", distinct=True))[
"round__count"
]
num_projects = protocol_filter.aggregate(Count("project", distinct=True))[
"project__count"
]

return {
"num_grants_contribute_to": num_projects if num_projects is not None else 0,
"num_rounds_contribute_to": num_rounds if num_rounds is not None else 0,
"total_valid_contribution_amount": round(total_amount_usd, 3)
"total_contribution_amount": round(total_amount_usd, 3)
if total_amount_usd is not None
else 0,
"num_gr14_contributions": 0,
}


Expand All @@ -139,26 +116,38 @@ def _get_contributor_statistics_for_protocol(address: str) -> dict:
auth=cg_api_key,
)
def contributor_statistics(
request, handle: str | None = None, github_id: str | None = None
request, address: str | None = None, github_id: str | None = None
):
if not handle and not github_id:
if not address:
return JsonResponse(
{
"error": "Bad request, 'handle' and 'github_id' parameter is missing or invalid. Either one is required."
"error": "Bad request, 'address' is missing or invalid. A valid address is required."
},
status=400,
)

if handle:
response = _get_contributor_statistics_for_cgrants(
handle, IdentifierType.HANDLE
)
else:
response = _get_contributor_statistics_for_cgrants(
github_id, IdentifierType.GITHUB_ID
if not is_valid_address(address):
raise InvalidAddressException()

address = address.lower()

cgrants_contributions = _get_contributor_statistics_for_cgrants(address)
protocol_contributions = _get_contributor_statistics_for_protocol(address)

combined_contributions = {
key: float(
round(
(
protocol_contributions.get(key, 0)
+ cgrants_contributions.get(key, 0)
),
2,
)
)
for key in set(protocol_contributions) | set(cgrants_contributions)
}

return JsonResponse(response)
return JsonResponse(combined_contributions)


@api.get(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from cgrants.models import Contribution, GrantContributionIndex, Profile
from django.core.management.base import BaseCommand
from tqdm import tqdm


class Command(BaseCommand):
help = "This command will update the contributor_address field in GrantContributionIndex"

batch_size = 1000

def handle(self, *args, **options):
self.stdout.write("Adding contributor_address to GrantContributionIndex")
last_id = None
while True:
contributions = self.get_data(last_id)

with tqdm(
unit="records",
unit_scale=True,
desc=f"Aggregating cGrants contributions by address",
) as progress_bar:
if contributions:
progress_bar.update(len(contributions))
for contribution_index in contributions:
last_id = contribution_index.pk
try:
address = contribution_index.contribution.data["fields"][
"originated_address"
]
except:
self.stdout.write(f"Error parsing address: {last_id}")
address = None
contribution_index.contributor_address = address

GrantContributionIndex.objects.bulk_update(
contributions, ["contributor_address"]
)
else:
break # No more data to process

def get_data(self, last_id):
q = (
GrantContributionIndex.objects.select_related("contribution")
.all()
.order_by("id")
)

if last_id:
q = q.filter(id__gt=last_id)

data = q[: self.batch_size]

return data
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.6 on 2023-10-26 16:23

import account.models
from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("cgrants", "0006_profile_github_id_profile_notes_alter_profile_handle"),
]

operations = [
migrations.AddField(
model_name="grantcontributionindex",
name="contributor_address",
field=account.models.EthAddressField(blank=True, max_length=100, null=True),
),
]
4 changes: 3 additions & 1 deletion api/cgrants/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ class GrantContributionIndex(models.Model):
"""
Stores data brought over from cgrants. Original model: https://github.com/gitcoinco/web/blob/master/app/grants/models/grant_contribution_index.py
Stores the grants and round number to shich a user contributed to.
Stores the grants and round number to which a user contributed to.
The purpose of this table is to allow a a fast query. This will be used from
the `contributor_statistics` API"""

Expand Down Expand Up @@ -221,6 +221,8 @@ class GrantContributionIndex(models.Model):
help_text=_("The USD amount contributed"),
)

contributor_address = EthAddressField(null=True, blank=True, max_length=100)


class ProtocolContributions(models.Model):
"""
Expand Down
Loading

0 comments on commit 610345a

Please sign in to comment.