Skip to content

Commit

Permalink
Resolve more linter issues (#352)
Browse files Browse the repository at this point in the history
* Resolve more linter issues

* Resolve more linter issues + ignore some + enable flake8!!

* Add migration due to fixed typo

* mypy

* fix

* more mypy

* more mypy
  • Loading branch information
james-otten authored Apr 25, 2024
1 parent a6a01f6 commit 07a4bc1
Show file tree
Hide file tree
Showing 32 changed files with 181 additions and 92 deletions.
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[flake8]
max-line-length = 120
extend-ignore = E203
per-file-ignores = **/__init__.py:F401,F403,src/meshapi_hooks/models.py:F401,F403
per-file-ignores = **/__init__.py:F401,F403,src/meshapi_hooks/models.py:F401,F403,src/*/migrations/*.py:E501,src/*/tests/*.py:E501,src/meshapi_hooks/tests/test_webhooks.py:E402
exclude = src/meshdb/utils/spreadsheet_import
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies = [
"django-dbbackup@git+https://github.com/willnilges/django-dbbackup.git@62048411ff5beac4beac1578f686824214f1f33a",
"django-storages==1.14.*",
"boto3==1.34.*",
"six==1.16.0",
]

[project.optional-dependencies]
Expand All @@ -49,6 +50,8 @@ dev = [
"flask == 3.0.*",
"django-cprofile-middleware==1.0.5",
"django-silk==5.1.0",
"types-requests==2.31.*",
"types-six==1.16.0.*",
]

[project.scripts]
Expand All @@ -75,6 +78,7 @@ exclude = [
"^src/meshdb/utils/spreadsheet_import/.*",
"^src/meshapi/tests/.*",
"^src/meshapi_hooks/tests/.*",
"^src/meshapi/migrations/.*",
]


Expand Down
2 changes: 1 addition & 1 deletion src/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
load_dotenv()


def main():
def main() -> None:
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "meshdb.settings")
try:
Expand Down
4 changes: 2 additions & 2 deletions src/meshapi/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class BetterInline(admin.TabularInline):
can_delete = False
template = "admin/install_tabular.html"

def has_add_permission(self, request, obj):
def has_add_permission(self, request, obj) -> bool:
return False

class Media:
Expand All @@ -32,7 +32,7 @@ class BetterNonrelatedInline(NonrelatedTabularInline):
can_delete = False
template = "admin/install_tabular.html"

def has_add_permission(self, request, obj):
def has_add_permission(self, request, obj) -> bool:
return False

class Media:
Expand Down
6 changes: 4 additions & 2 deletions src/meshapi/management/commands/create_groups.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
from argparse import ArgumentParser

from django.contrib.auth.models import Group, Permission
from django.core.management.base import BaseCommand


class Command(BaseCommand):
help = "Creates basic MeshDB groups"

def add_arguments(self, parser):
def add_arguments(self, parser: ArgumentParser) -> None:
pass

def handle(self, *args, **options):
def handle(self, *args, **options) -> None:
models = [
"building",
"member",
Expand Down
7 changes: 4 additions & 3 deletions src/meshapi/management/commands/scramble_members.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from datetime import datetime, timedelta
from argparse import ArgumentParser
from datetime import timedelta
from random import randint, randrange

from django.core.management.base import BaseCommand
from django.db import transaction
from faker import Faker

from meshapi.models import Install, Member, install
from meshapi.models import Install, Member
from meshapi.models.building import Building
from meshapi.models.devices.device import Device
from meshapi.models.link import Link
Expand All @@ -18,7 +19,7 @@
class Command(BaseCommand):
help = "Updates all members with fake name, email, and phone number. Clears notes."

def add_arguments(self, parser):
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument(
"--skip-members",
action="store_true",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 4.2.11 on 2024-04-21 23:56

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("meshapi", "0007_alter_node_network_number"),
]

operations = [
migrations.AlterField(
model_name="building",
name="notes",
field=models.TextField(
blank=True,
help_text="A free-form text description of this building, to track any additional information. For Buidings imported from the spreadsheet, this starts with a formatted block of information about the import process and original spreadsheet data. However this structure can be changed by admins at any time and should not be relied on by automated systems. ",
null=True,
),
),
migrations.AlterField(
model_name="install",
name="notes",
field=models.TextField(
blank=True,
default=None,
help_text="A free-form text description of this Install, to track any additional information. For Installs imported from the spreadsheet, this starts with a formatted block of information about the import process and original spreadsheet data. However this structure can be changed by admins at any time and should not be relied on by automated systems. ",
null=True,
),
),
migrations.AlterField(
model_name="member",
name="notes",
field=models.TextField(
blank=True,
default=None,
help_text="A free-form text description of how to contact this member, to track any additional information. For Members imported from the spreadsheet, this starts with a formatted block of information about the import process and original spreadsheet data. However this structure can be changed by admins at any time and should not be relied on by automated systems. ",
null=True,
),
),
migrations.AlterField(
model_name="node",
name="notes",
field=models.TextField(
blank=True,
help_text="A free-form text description of this Node, to track any additional information. For Nodes imported from the spreadsheet, this starts with a formatted block of information about the import process and original spreadsheet data. However this structure can be changed by admins at any time and should not be relied on by automated systems. ",
null=True,
),
),
]
16 changes: 9 additions & 7 deletions src/meshapi/models/building.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Any

from django.contrib.postgres.fields import ArrayField
from django.core.validators import MinValueValidator
from django.db import models
Expand Down Expand Up @@ -49,8 +51,8 @@ class Building(models.Model):
choices=list((src.value, src.name) for src in AddressTruthSource),
),
help_text="A list of strings that answers the question: How was the content of"
"the street address, city, state, and ZIP fields determined? This is useful in understanding the level of validation "
"applied to spreadsheet imported data. Possible values are: "
"the street address, city, state, and ZIP fields determined? This is useful in "
"understanding the level of validation applied to spreadsheet imported data. Possible values are: "
f"{', '.join(src.value for src in AddressTruthSource)}. Check the import script for details",
)

Expand All @@ -64,9 +66,9 @@ class Building(models.Model):
blank=True,
null=True,
help_text="A free-form text description of this building, to track any additional information. "
"For Buidings imported from the spreadsheet, this starts with a formatted block of information about the import process"
"and original spreadsheet data. However this structure can be changed by admins at any time and should not be relied on"
"by automated systems. ",
"For Buidings imported from the spreadsheet, this starts with a formatted block of information about "
"the import process and original spreadsheet data. However this structure can be changed by admins at "
"any time and should not be relied on by automated systems. ",
)
panoramas = JSONFormArrayField(
models.URLField(),
Expand Down Expand Up @@ -96,14 +98,14 @@ class Building(models.Model):
related_name="buildings",
)

def save(self, *args, **kwargs):
def save(self, *args: Any, **kwargs: Any) -> None:
super().save(*args, **kwargs)

# Ensure primary_node is always contained in nodes
if self.primary_node and self.primary_node not in self.nodes.all():
self.nodes.add(self.primary_node)

def __str__(self):
def __str__(self) -> str:
if self.street_address:
addr_str = str(self.street_address)
if self.city:
Expand Down
2 changes: 1 addition & 1 deletion src/meshapi/models/devices/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class DeviceType(models.TextChoices):
protocol="ipv4",
)

def __str__(self):
def __str__(self) -> str:
if self.name:
return self.name
return f"MeshDB Device ID {self.id}"
2 changes: 1 addition & 1 deletion src/meshapi/models/devices/sector.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Sector(Device):
],
)

def __str__(self):
def __str__(self) -> str:
if self.name:
return self.name
return f"MeshDB Sector ID {self.id}"
8 changes: 4 additions & 4 deletions src/meshapi/models/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ class InstallStatus(models.TextChoices):
blank=True,
null=True,
help_text="A free-form text description of this Install, to track any additional information. "
"For Installs imported from the spreadsheet, this starts with a formatted block of information about the import process"
"and original spreadsheet data. However this structure can be changed by admins at any time and should not be relied on"
"by automated systems. ",
"For Installs imported from the spreadsheet, this starts with a formatted block of information about the "
"import process and original spreadsheet data. However this structure can be changed by admins at any time "
"and should not be relied on by automated systems. ",
)
diy = models.BooleanField(
default=None,
Expand All @@ -117,5 +117,5 @@ class Meta:
("update_panoramas", "Can update panoramas"),
]

def __str__(self):
def __str__(self) -> str:
return f"#{str(self.install_number)}"
2 changes: 1 addition & 1 deletion src/meshapi/models/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class LinkType(models.TextChoices):
default=None, blank=True, null=True, help_text="The UUID used to indentify this link in UISP (if applicable)"
)

def __str__(self):
def __str__(self) -> str:
if self.from_device.node.network_number and self.to_device.node.network_number:
return f"NN{self.from_device.node.network_number} → NN{self.to_device.node.network_number}"
return f"MeshDB Link ID {self.id}"
6 changes: 3 additions & 3 deletions src/meshapi/models/member.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ class Member(models.Model):
blank=True,
null=True,
help_text="A free-form text description of how to contact this member, to track any additional information. "
"For Members imported from the spreadsheet, this starts with a formatted block of information about the import process"
"and original spreadsheet data. However this structure can be changed by admins at any time and should not be relied on"
"by automated systems. ",
"For Members imported from the spreadsheet, this starts with a formatted block of information about the "
"import process and original spreadsheet data. However this structure can be changed by admins at any "
"time and should not be relied on by automated systems. ",
)

def __str__(self) -> str:
Expand Down
14 changes: 8 additions & 6 deletions src/meshapi/models/node.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Any

from django.core.validators import MaxValueValidator
from django.db import models

Expand Down Expand Up @@ -75,22 +77,22 @@ class NodeType(models.TextChoices):
blank=True,
null=True,
help_text="A free-form text description of this Node, to track any additional information. "
"For Nodes imported from the spreadsheet, this starts with a formatted block of information about the import process"
"and original spreadsheet data. However this structure can be changed by admins at any time and should not be relied on"
"by automated systems. ",
"For Nodes imported from the spreadsheet, this starts with a formatted block of information about the import "
"process and original spreadsheet data. However this structure can be changed by admins at any time and "
"should not be relied on by automated systems. ",
)

def save(self, *args, **kwargs):
def save(self, *args: Any, **kwargs: Any) -> None:
if not self.network_number:
self.network_number = get_next_available_network_number()

super().save(*args, **kwargs)

def __str__(self):
def __str__(self) -> str:
if self.name:
return f"NN{str(self.network_number)} ({str(self.name)})"

return f"NN{str(self.network_number)}"

def __network_number__(self):
def __network_number__(self) -> str:
return f"NN{str(self.network_number)}"
14 changes: 10 additions & 4 deletions src/meshapi/models/util/custom_many_to_many.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"""

from functools import partial
from typing import Any

from django.core.exceptions import ImproperlyConfigured
from django.db.models import ManyToManyField
Expand All @@ -37,7 +38,8 @@ def set_managed(model, related, through):
name = "%s_%s" % (klass._meta.object_name, field.name)
lazy_related_operation(set_managed, klass, to_model, name)

# CHANGED FROM ORIGINAL: The following two lines are changed from the original django create_many_to_many_intermediary_model function
# CHANGED FROM ORIGINAL: The following two lines are changed from the original django
# screate_many_to_many_intermediary_model function
to = getattr(field, "_to_column_name") or make_model_tuple(to_model)[1]
from_ = getattr(field, "_from_column_name") or klass._meta.model_name
if to == from_:
Expand Down Expand Up @@ -87,10 +89,13 @@ def set_managed(model, related, through):

class CustomColumnNameManyToManyField(ManyToManyField):
# CHANGED FROM ORIGINAL: The following init method was added from original ManyToManyField() class
def __init__(self, *args, db_from_column_name=None, db_to_column_name=None, **kwargs):
def __init__(
self, *args: Any, db_from_column_name: str | None = None, db_to_column_name: str | None = None, **kwargs: Any
):
if db_from_column_name is None or db_to_column_name is None:
raise ImproperlyConfigured(
"CustomColumnNameManyToManyField requires that you specify either db_from_column_name, db_to_column_name, or both."
"CustomColumnNameManyToManyField requires that you specify either db_from_column_name, "
"db_to_column_name, or both."
)
self._from_column_name = db_from_column_name
self._to_column_name = db_to_column_name
Expand Down Expand Up @@ -135,7 +140,8 @@ def resolve_through_model(_, model, field):
lazy_related_operation(resolve_through_model, cls, self.remote_field.through, field=self)
elif not cls._meta.swapped:
self.remote_field.through = (
# CHANGED FROM ORIGINAL: The following line was changed from the original django ManyToManyField class
# CHANGED FROM ORIGINAL: The following line was changed from the original
# django ManyToManyField class
create_custom_column_name_many_to_many_intermediary_model(self, cls)
)

Expand Down
15 changes: 8 additions & 7 deletions src/meshapi/permissions.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import json
from typing import Optional
from typing import Any, Optional

from django.conf import os
from django.contrib.auth import PermissionDenied
from django.contrib.auth.models import User
from django.db.models import Model
from rest_framework import permissions
from rest_framework.permissions import BasePermission
from rest_framework.request import Request


class IsReadOnly(BasePermission):
Expand All @@ -16,14 +17,14 @@ class IsReadOnly(BasePermission):
permission_classes = [permissions.DjangoModelPermissions | IsReadOnly]
"""

def has_permission(self, request, view):
def has_permission(self, request: Request, view: Any) -> bool:
return bool(request.method in permissions.SAFE_METHODS)


class HasDjangoPermission(BasePermission):
django_permission = None
django_permission: str | None = None

def has_permission(self, request, view):
def has_permission(self, request: Request, view: Any) -> bool:
if not self.django_permission:
raise NotImplementedError(
"You must subclass HasDjangoPermission and specify the django_permission attribute"
Expand All @@ -41,7 +42,7 @@ class HasPanoramaUpdatePermission(HasDjangoPermission):

# Janky
class LegacyMeshQueryPassword(permissions.BasePermission):
def has_permission(self, request, view):
def has_permission(self, request: Request, view: Any) -> bool:
if (
request.headers["Authorization"]
and request.headers["Authorization"] == f"Bearer {os.environ.get('QUERY_PSK')}"
Expand All @@ -52,15 +53,15 @@ def has_permission(self, request, view):


class LegacyNNAssignmentPassword(permissions.BasePermission):
def has_permission(self, request, view):
def has_permission(self, request: Request, view: Any) -> bool:
request_json = json.loads(request.body)
if "password" in request_json and request_json["password"] == os.environ.get("NN_ASSIGN_PSK"):
return True

raise PermissionDenied("Authentication Failed.")


def check_has_model_view_permission(user: Optional[User], model: Model):
def check_has_model_view_permission(user: Optional[User], model: Model) -> bool:
if not user:
# Unauthenticated requests do not have permission by default
return False
Expand Down
Loading

0 comments on commit 07a4bc1

Please sign in to comment.