1
0
mirror of https://github.com/peeringdb/peeringdb.git synced 2024-05-11 05:55:09 +00:00

Support 202104 (#980)

* Add migration for service level and terms

* Add service level and terms to UI and serializer, as well as data/enum

* Wire up data/enum endpoint and loader

* remove proto_ from ix UI

* derive fields for proto_unicast and proto_ipv6

* update tests for readonly fields

* Fix query for protocols

* Fix api bug with protocol

* add readonly fields to django admin

* rename readonly fields

* Add translation to names

* Add pdb api test for suggested facility re-add

* Add printing debuggin test

* add printing debugging serializer

* Update _undelete with _reapprove to handle pending cases

* Update tests (one is still failing)

* adjust suggest test

* Add ix_count to fac (834)

* Add test for ix_count on fac (834)

* Add fac_count to IX (836)

* add ix_count and fac_count to Network

* Refactor ix net_count filtering

* Add filtering for 834, 835, 836

* Remove duplicates from the Network's ix_count

* Setup Network for ix_count and fac_count (835)

* initial obj_counts for Facilities and Exchanges

* Add signals for updates to all counts

* add migration

* Add print statements to test

* introduce reversion to tests

* rename network count to net count across codebase

* fix network_count typo

* add migration to set default vals

* fix filter tests for obj_counts

* speed up migration

* fix failing tests

* fix final test

* sort out migration tree and add fac offered fields

* update frontend for facility dropdown offered_resilience

* First pass at advanced api search for user story 1

* melissa geo lookup first steps

* fix migration hierarchy

* working melissa integration

* begin ending filters for api endpoints

* add more org_present endpoints

* add search for IXs that match multiple networks

* extend logic to facility

* Add service level and terms to advanced search

* use address2 field for lookup

* melissa tests

* cleanup and docs

* uncomment offered_power

* developed offered_power component

* fix geo normalize existing cmd
normalize state

* change migration to match django-peeringdb

* add offered_space field

* Fill out remaining api filter fields

* Add org_not_present endpoint filter

* fix unit input ux

* more ux fixes

* remove merge cruft

* google for geocoding
various melissa improvements (consider result quality)

* fix tests

* refactor org_preset and org_not_present queries

* ix capacity api filters

* ix capacity filters for #802
advanced search ux for #802

* finalize advanced search UX for #802

* css fixes

* remove cruft

* fix net_count fac_count queries

* add new fields to create facility (#800)
tests for #802 and #800

* fix tests

* remove #800 changes

* fix capacity search

* more #800 changes to remove

* django-peeringdb 2.7.0 and pipenv relock

* black format

* pin black version

Co-authored-by: Elliot Frank <elliot@20c.com>
Co-authored-by: Stefan Pratter <stefan@20c.com>
This commit is contained in:
Matt Griswold
2021-05-19 09:11:30 -04:00
committed by GitHub
parent dddbb4e34a
commit fbc72ea682
36 changed files with 2531 additions and 501 deletions

View File

@@ -154,8 +154,8 @@ class GeocodeSerializerMixin(object):
if not geosync_info_present:
return False
# We do not need to resync if floor, suite, or address2 are changed
ignored_fields = ["floor", "suite", "address2"]
# We do not need to resync if floor, suite and geo coords are changed
ignored_fields = ["floor", "suite", "latitude", "longitude"]
geocode_fields = [
f for f in AddressSerializer.Meta.fields if f not in ignored_fields
]
@@ -207,6 +207,9 @@ class GeocodeSerializerMixin(object):
a address suggestion should be provided to the user.
"""
if not suggested_address:
return False
for key in ["address1", "city", "state", "zipcode"]:
suggested_val = suggested_address.get(key, None)
instance_val = getattr(instance, key, None)
@@ -232,10 +235,8 @@ class GeocodeSerializerMixin(object):
return instance
if need_geosync:
print("Normalizing geofields")
try:
suggested_address = instance.normalize_api_response()
print(suggested_address)
suggested_address = instance.process_geo_location()
if self.needs_address_suggestion(suggested_address, instance):
self._add_meta_information(
@@ -263,7 +264,7 @@ class GeocodeSerializerMixin(object):
if self._geosync_information_present(instance, validated_data):
try:
suggested_address = instance.normalize_api_response()
suggested_address = instance.process_geo_location()
if self.needs_address_suggestion(suggested_address, instance):
self._add_meta_information(
@@ -1157,7 +1158,14 @@ class ModelSerializer(serializers.ModelSerializer):
if request.method == "POST":
self.instance = instance
self._undelete = True
if type(instance) in QUEUE_ENABLED:
self._reapprove = True
self._undelete = False
else:
self._reapprove = False
self._undelete = True
elif request.method == "PUT":
for field in filters.keys():
if field == "status":
@@ -1184,7 +1192,11 @@ class ModelSerializer(serializers.ModelSerializer):
"""
instance = super().save(**kwargs)
if instance.status == "deleted" and getattr(self, "_undelete", False):
if instance.status == "deleted" and getattr(self, "_reapprove", False):
instance.status = "pending"
instance.save()
elif instance.status == "deleted" and getattr(self, "_undelete", False):
instance.status = "ok"
instance.save()
@@ -1300,8 +1312,6 @@ class FacilitySerializer(GeocodeSerializerMixin, ModelSerializer):
org = serializers.SerializerMethodField()
net_count = serializers.SerializerMethodField()
suggest = serializers.BooleanField(required=False, write_only=True)
website = serializers.URLField()
@@ -1340,6 +1350,7 @@ class FacilitySerializer(GeocodeSerializerMixin, ModelSerializer):
"npanxx",
"notes",
"net_count",
"ix_count",
"suggest",
"sales_email",
"sales_phone",
@@ -1361,7 +1372,9 @@ class FacilitySerializer(GeocodeSerializerMixin, ModelSerializer):
qset = qset.select_related("org")
filters = get_relation_filters(
["net_id", "net", "ix_id", "ix", "org_name", "net_count"], cls, **kwargs
["net_id", "net", "ix_id", "ix", "org_name", "ix_count", "net_count"],
cls,
**kwargs,
)
for field, e in list(filters.items()):
@@ -1373,27 +1386,92 @@ class FacilitySerializer(GeocodeSerializerMixin, ModelSerializer):
if field == "org_name":
flt = {"org__name__%s" % (e["filt"] or "icontains"): e["value"]}
qset = qset.filter(**flt)
elif field == "network_count":
if e["filt"]:
flt = {"net_count_a__%s" % e["filt"]: e["value"]}
else:
flt = {"net_count_a": e["value"]}
qset = qset.annotate(
net_count_a=Sum(
Case(
When(netfac_set__status="ok", then=1),
default=0,
output_field=IntegerField(),
)
)
).filter(**flt)
if field == "network_count":
if e["filt"]:
flt = {"net_count__%s" % e["filt"]: e["value"]}
else:
flt = {"net_count": e["value"]}
qset = qset.filter(**flt)
if "asn_overlap" in kwargs:
asns = kwargs.get("asn_overlap", [""])[0].split(",")
qset = cls.Meta.model.overlapping_asns(asns, qset=qset)
filters.update({"asn_overlap": kwargs.get("asn_overlap")})
if "org_present" in kwargs:
org_list = kwargs.get("org_present")[0].split(",")
fac_ids = []
# relation through netfac
fac_ids.extend(
[
netfac.facility_id
for netfac in NetworkFacility.objects.filter(
network__org_id__in=org_list
)
]
)
# relation through ixfac
fac_ids.extend(
[
ixfac.facility_id
for ixfac in InternetExchangeFacility.objects.filter(
ix__org_id__in=org_list
)
]
)
qset = qset.filter(id__in=set(fac_ids))
filters.update({"org_present": kwargs.get("org_present")[0]})
if "org_not_present" in kwargs:
org_list = kwargs.get("org_not_present")[0].split(",")
fac_ids = []
# relation through netfac
fac_ids.extend(
[
netfac.facility_id
for netfac in NetworkFacility.objects.filter(
network__org_id__in=org_list
)
]
)
# relation through ixfac
fac_ids.extend(
[
ixfac.facility_id
for ixfac in InternetExchangeFacility.objects.filter(
ix__org_id__in=org_list
)
]
)
qset = qset.exclude(id__in=set(fac_ids))
filters.update({"org_not_present": kwargs.get("org_not_present")[0]})
if "all_net" in kwargs:
network_id_list = [
int(net_id) for net_id in kwargs.get("all_net")[0].split(",")
]
qset = cls.Meta.model.related_to_multiple_networks(
value_list=network_id_list, qset=qset
)
filters.update({"all_net": kwargs.get("all_net")})
if "not_net" in kwargs:
networks = kwargs.get("not_net")[0].split(",")
qset = cls.Meta.model.not_related_to_net(
filt="in", value=networks, qset=qset
)
filters.update({"not_net": kwargs.get("not_net")})
return qset, filters
def to_internal_value(self, data):
@@ -1403,14 +1481,12 @@ class FacilitySerializer(GeocodeSerializerMixin, ModelSerializer):
# this happens here so it is done before the validators run
if "suggest" in data and (not self.instance or not self.instance.id):
data["org_id"] = settings.SUGGEST_ENTITY_ORG
return super().to_internal_value(data)
def get_org(self, inst):
return self.sub_serializer(OrganizationSerializer, inst.org)
def get_net_count(self, inst):
return inst.net_count
def validate(self, data):
try:
data["tech_phone"] = validate_phonenumber(
@@ -1980,6 +2056,8 @@ class NetworkSerializer(ModelSerializer):
"info_multicast",
"info_ipv6",
"info_never_via_route_servers",
"ix_count",
"fac_count",
"notes",
"netixlan_updated",
"netfac_updated",
@@ -2033,6 +2111,8 @@ class NetworkSerializer(ModelSerializer):
"netfac",
"fac",
"fac_id",
"fac_count",
"ix_count",
],
cls,
**kwargs,
@@ -2045,6 +2125,13 @@ class NetworkSerializer(ModelSerializer):
qset = fn(qset=qset, field=field, **e)
break
if field == "facility_count":
if e["filt"]:
flt = {"fac_count__%s" % e["filt"]: e["value"]}
else:
flt = {"fac_count": e["value"]}
qset = qset.filter(**flt)
if "name_search" in kwargs:
name = kwargs.get("name_search", [""])[0]
qset = qset.filter(Q(name__icontains=name) | Q(aka__icontains=name))
@@ -2386,8 +2473,6 @@ class InternetExchangeSerializer(ModelSerializer):
getter="facility",
)
net_count = serializers.SerializerMethodField()
# suggest = serializers.BooleanField(required=False, write_only=True)
ixf_net_count = serializers.IntegerField(read_only=True)
@@ -2415,6 +2500,9 @@ class InternetExchangeSerializer(ModelSerializer):
write_only=True,
)
proto_unicast = serializers.SerializerMethodField()
proto_ipv6 = serializers.SerializerMethodField()
validators = [
RequiredForMethodValidator("prefix", ["POST"]),
SoftRequiredValidator(
@@ -2451,13 +2539,18 @@ class InternetExchangeSerializer(ModelSerializer):
# "suggest",
"prefix",
"net_count",
"fac_count",
"ixf_net_count",
"ixf_last_import",
"service_level",
"terms",
] + HandleRefSerializer.Meta.fields
_ref_tag = model.handleref.tag
related_fields = ["org", "fac_set", "ixlan_set"]
list_exclude = ["org"]
read_only_fields = ["proto_multicast"]
@classmethod
def prepare_query(cls, qset, **kwargs):
@@ -2474,12 +2567,15 @@ class InternetExchangeSerializer(ModelSerializer):
"net_id",
"net",
"net_count",
"fac_count",
"capacity",
],
cls,
**kwargs,
)
for field, e in list(filters.items()):
for valid in ["ixlan", "ixfac", "fac", "net"]:
if validate_relation_filter_field(field, valid):
fn = getattr(cls.Meta.model, "related_to_%s" % valid)
@@ -2487,7 +2583,21 @@ class InternetExchangeSerializer(ModelSerializer):
break
if field == "network_count":
qset = cls.Meta.model.filter_net_count(qset=qset, **e)
if e["filt"]:
flt = {"net_count__%s" % e["filt"]: e["value"]}
else:
flt = {"net_count": e["value"]}
qset = qset.filter(**flt)
if field == "facility_count":
if e["filt"]:
flt = {"fac_count__%s" % e["filt"]: e["value"]}
else:
flt = {"fac_count": e["value"]}
qset = qset.filter(**flt)
if field == "capacity":
qset = cls.Meta.model.filter_capacity(qset=qset, **e)
if "ipblock" in kwargs:
qset = cls.Meta.model.related_to_ipblock(
@@ -2496,15 +2606,90 @@ class InternetExchangeSerializer(ModelSerializer):
filters.update({"ipblock": kwargs.get("ipblock")})
if "name_search" in kwargs:
name = kwargs.get("name_search", [""])[0]
qset = qset.filter(Q(name__icontains=name) | Q(name_long__icontains=name))
filters.update({"name_search": kwargs.get("name_search")})
if "asn_overlap" in kwargs:
asns = kwargs.get("asn_overlap", [""])[0].split(",")
qset = cls.Meta.model.overlapping_asns(asns, qset=qset)
filters.update({"asn_overlap": kwargs.get("asn_overlap")})
if "all_net" in kwargs:
network_id_list = [
int(net_id) for net_id in kwargs.get("all_net")[0].split(",")
]
qset = cls.Meta.model.related_to_multiple_networks(
value_list=network_id_list, qset=qset
)
filters.update({"all_net": kwargs.get("all_net")})
if "not_net" in kwargs:
networks = kwargs.get("not_net")[0].split(",")
qset = cls.Meta.model.not_related_to_net(
filt="in", value=networks, qset=qset
)
filters.update({"not_net": kwargs.get("not_net")})
if "org_present" in kwargs:
org_list = kwargs.get("org_present")[0].split(",")
ix_ids = []
# relation through netixlan
ix_ids.extend(
[
netixlan.ixlan_id
for netixlan in NetworkIXLan.objects.filter(
network__org_id__in=org_list
)
]
)
# relation through ixfac
ix_ids.extend(
[
ixfac.ix_id
for ixfac in InternetExchangeFacility.objects.filter(
facility__org_id__in=org_list
)
]
)
qset = qset.filter(id__in=set(ix_ids))
filters.update({"org_present": kwargs.get("org_present")[0]})
if "org_not_present" in kwargs:
org_list = kwargs.get("org_not_present")[0].split(",")
ix_ids = []
# relation through netixlan
ix_ids.extend(
[
netixlan.ixlan_id
for netixlan in NetworkIXLan.objects.filter(
network__org_id__in=org_list
)
]
)
# relation through ixfac
ix_ids.extend(
[
ixfac.ix_id
for ixfac in InternetExchangeFacility.objects.filter(
facility__org_id__in=org_list
)
]
)
qset = qset.exclude(id__in=set(ix_ids))
filters.update({"org_not_present": kwargs.get("org_not_present")[0]})
return qset, filters
def validate_create(self, data):
@@ -2580,8 +2765,11 @@ class InternetExchangeSerializer(ModelSerializer):
def get_org(self, inst):
return self.sub_serializer(OrganizationSerializer, inst.org)
def get_net_count(self, inst):
return inst.network_count
def get_proto_ipv6(self, inst):
return inst.derived_proto_ipv6
def get_proto_unicast(self, inst):
return inst.derived_proto_unicast
def validate(self, data):
try: