From 6de7976dde861224708f61fc9c25e3c8ca10fc3b Mon Sep 17 00:00:00 2001 From: ARYAN-NIKNEZHAD Date: Sun, 18 Aug 2024 08:19:21 +0430 Subject: [PATCH] :hammer: refactor: solve-pylint-problems --- .pylintrc | 13 -- iranian_cities/admin.py | 143 ++++++++----- iranian_cities/apps.py | 6 +- iranian_cities/checks.py | 30 ++- iranian_cities/conf.py | 8 +- iranian_cities/exc.py | 15 +- iranian_cities/fields.py | 59 +++--- .../management/commands/generate_city.py | 194 ++++++++++-------- iranian_cities/mixins/__init__.py | 5 - iranian_cities/mixins/base_inline.py | 1 + iranian_cities/mixins/base_location.py | 8 +- iranian_cities/mixins/dynamic_permission.py | 17 +- iranian_cities/models.py | 120 ++++++----- poetry.lock | 34 ++- pyproject.toml | 26 ++- tox.ini | 1 + 16 files changed, 405 insertions(+), 275 deletions(-) delete mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 7713202..0000000 --- a/.pylintrc +++ /dev/null @@ -1,13 +0,0 @@ -[MASTER] -ignore=tests,docs,build,stubs -persistent=yes -ignore-patterns=^tests/.*, ^docs/.*, ^build/.*, ^stubs/.* - -[MESSAGES CONTROL] -disable=C0114,C0115,C0116,R0903 - -[FORMAT] -max-line-length=88 - -[DESIGN] -max-args=5 \ No newline at end of file diff --git a/iranian_cities/admin.py b/iranian_cities/admin.py index 8b5b98c..cd25e78 100644 --- a/iranian_cities/admin.py +++ b/iranian_cities/admin.py @@ -1,132 +1,177 @@ +from typing import Any, Optional + from django.contrib import admin from django.contrib.admin import widgets from django.db.models import ForeignKey from django.http import HttpRequest -from typing import Any, Optional -from iranian_cities.mixins.dynamic_permission import IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin + from iranian_cities.mixins.base_inline import BaseTabularInline +from iranian_cities.mixins.dynamic_permission import ( + DynamicInlineAdmin, + IranianCitiesAdminReadOnlyEnabled, +) from iranian_cities.models import ( - Province, County, District, - City, RuralDistrict, Village + City, + County, + District, + Province, + RuralDistrict, + Village, ) class IranianCitiesAdmin(admin.ModelAdmin): """Custom admin model for Iranian cities with a custom form field for foreign key.""" - def formfield_for_foreignkey(self, db_field: ForeignKey, request: HttpRequest, **kwargs: Any) -> Any: + def formfield_for_foreignkey( + self, db_field: ForeignKey, request: HttpRequest, **kwargs: Any + ) -> Any: """Override the default form field for foreign keys to use ForeignKeyRawIdWidget.""" - db: Optional[str] = kwargs.get('using') - kwargs['widget'] = widgets.ForeignKeyRawIdWidget( - db_field.remote_field, self.admin_site, using=db) + db: Optional[str] = kwargs.get("using") + kwargs["widget"] = widgets.ForeignKeyRawIdWidget( + db_field.remote_field, self.admin_site, using=db + ) - if 'queryset' not in kwargs: + if "queryset" not in kwargs: queryset = self.get_field_queryset(db, db_field, request) if queryset is not None: - kwargs['queryset'] = queryset + kwargs["queryset"] = queryset return db_field.formfield(**kwargs) class CountyInline(BaseTabularInline): """Inline admin model for County.""" + model = County extra = 1 - raw_id_fields = ['province'] + raw_id_fields = ["province"] max_num = 5 class DistrictInline(BaseTabularInline): """Inline admin model for District.""" + model = District extra = 1 - raw_id_fields = ['county', 'province'] + raw_id_fields = ["county", "province"] max_num = 5 class CityInline(BaseTabularInline): """Inline admin model for City.""" + model = City extra = 1 - raw_id_fields = ['district', 'county', 'province'] + raw_id_fields = ["district", "county", "province"] max_num = 5 class RuralDistrictInline(BaseTabularInline): """Inline admin model for RuralDistrict.""" + model = RuralDistrict extra = 1 - raw_id_fields = ['district', 'county', 'province'] + raw_id_fields = ["district", "county", "province"] max_num = 5 class VillageInline(BaseTabularInline): """Inline admin model for Village.""" + model = Village extra = 1 - raw_id_fields = ['rural_district', 'district', 'county', 'province'] + raw_id_fields = ["rural_district", "district", "county", "province"] max_num = 5 @admin.register(Province) -class ProvinceAdmin(IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin): +class ProvinceAdmin( + IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin +): """Admin model for Province.""" - list_display = ['name', 'code'] - search_fields = ['name', 'code'] + + list_display = ["name", "code"] + search_fields = ["name", "code"] @admin.register(County) -class CountyAdmin(IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin): +class CountyAdmin( + IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin +): """Admin model for County.""" - list_display = ['name', 'code', 'province'] - list_filter = ['province'] - search_fields = ['name', 'code', 'province__name'] - raw_id_fields = ['province'] + + list_display = ["name", "code", "province"] + list_filter = ["province"] + search_fields = ["name", "code", "province__name"] + raw_id_fields = ["province"] @admin.register(City) -class CityAdmin(IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin): +class CityAdmin( + IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin +): """Admin model for City.""" - list_display = [ - 'name', 'code', 'city_type', - 'district', 'county', 'province' - ] - list_filter = ['province'] + + list_display = ["name", "code", "city_type", "district", "county", "province"] + list_filter = ["province"] search_fields = [ - 'name', 'code', 'city_type', - 'district__name', 'county__name', 'province__name' + "name", + "code", + "city_type", + "district__name", + "county__name", + "province__name", ] - raw_id_fields = ['district', 'county', 'province'] + raw_id_fields = ["district", "county", "province"] @admin.register(District) -class DistrictAdmin(IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin): +class DistrictAdmin( + IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin +): """Admin model for District.""" - list_display = ['name', 'code', 'county', 'province'] - list_filter = ['province'] - search_fields = ['name', 'code', 'county__name', 'province__name'] - raw_id_fields = ['county', 'province'] + + list_display = ["name", "code", "county", "province"] + list_filter = ["province"] + search_fields = ["name", "code", "county__name", "province__name"] + raw_id_fields = ["county", "province"] @admin.register(RuralDistrict) -class RuralDistrictAdmin(IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin): +class RuralDistrictAdmin( + IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin +): """Admin model for RuralDistrict.""" - list_display = ['name', 'code', 'district', 'county', 'province'] - list_filter = ['province'] - search_fields = ['name', 'code', 'district__name', 'county__name', 'province__name'] - raw_id_fields = ['district', 'county', 'province'] + + list_display = ["name", "code", "district", "county", "province"] + list_filter = ["province"] + search_fields = ["name", "code", "district__name", "county__name", "province__name"] + raw_id_fields = ["district", "county", "province"] @admin.register(Village) -class VillageAdmin(IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin): +class VillageAdmin( + IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin, admin.ModelAdmin +): """Admin model for Village.""" + list_display = [ - 'name', 'code', 'village_type', - 'rural_district', 'district', 'county', 'province' + "name", + "code", + "village_type", + "rural_district", + "district", + "county", + "province", ] - list_filter = ['province'] + list_filter = ["province"] search_fields = [ - 'name', 'code', 'rural_district__name', - 'district__name', 'county__name', 'province__name' + "name", + "code", + "rural_district__name", + "district__name", + "county__name", + "province__name", ] - raw_id_fields = ['rural_district', 'district', 'county', 'province'] + raw_id_fields = ["rural_district", "district", "county", "province"] diff --git a/iranian_cities/apps.py b/iranian_cities/apps.py index 5d34a4f..60671b1 100644 --- a/iranian_cities/apps.py +++ b/iranian_cities/apps.py @@ -3,9 +3,9 @@ class IranianCitiesConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'iranian_cities' - verbose_name = _('Iranian Cities') + default_auto_field = "django.db.models.BigAutoField" + name = "iranian_cities" + verbose_name = _("Iranian Cities") def ready(self) -> None: from . import checks diff --git a/iranian_cities/checks.py b/iranian_cities/checks.py index ec6eab1..d49e639 100644 --- a/iranian_cities/checks.py +++ b/iranian_cities/checks.py @@ -1,14 +1,15 @@ +from typing import Any, Dict, List + from django.core.checks import Error, register -from typing import List, Dict, Any from iranian_cities.conf import sage_iranian_cities_settings -from iranian_cities.exc import ( - IranianCitiesConfigurationError, -) +from iranian_cities.exc import IranianCitiesConfigurationError @register() -def check_iranian_cities_config(app_configs: Dict[str, Any], **kwargs: Any) -> List[Error]: +def check_iranian_cities_config( + app_configs: Dict[str, Any], **kwargs: Any +) -> List[Error]: """ Check the Iranian Cities configuration for the application. @@ -43,10 +44,18 @@ def check_iranian_cities_config(app_configs: Dict[str, Any], **kwargs: Any) -> L def get_iranian_cities_settings() -> Dict[str, Any]: - sage_iranian_cities_admin_add_readonly_enabled: bool = sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_ADD_READONLY_ENABLED - sage_iranian_cities_admin_delete_readonly_enabled: bool = sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_DELETE_READONLY_ENABLED - sage_iranian_cities_admin_change_readonly_enabled: bool = sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_CHANGE_READONLY_ENABLED - sage_iranian_cities_admin_inline_enabled: bool = sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_INLINE_ENABLED + sage_iranian_cities_admin_add_readonly_enabled: bool = ( + sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_ADD_READONLY_ENABLED + ) + sage_iranian_cities_admin_delete_readonly_enabled: bool = ( + sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_DELETE_READONLY_ENABLED + ) + sage_iranian_cities_admin_change_readonly_enabled: bool = ( + sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_CHANGE_READONLY_ENABLED + ) + sage_iranian_cities_admin_inline_enabled: bool = ( + sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_INLINE_ENABLED + ) return { "IRANIAN_CITIES_ADMIN_ADD_READONLY_ENABLED": sage_iranian_cities_admin_add_readonly_enabled, @@ -55,9 +64,8 @@ def get_iranian_cities_settings() -> Dict[str, Any]: "IRANIAN_CITIES_ADMIN_INLINE_ENABLED": sage_iranian_cities_admin_inline_enabled, } - def check_missing_configs(settings: Dict[str, Any]) -> None: - missing: List[str] = [key for key, value in settings.items() if value == None] + missing: List[str] = [key for key, value in settings.items() if value is None] if missing: raise IranianCitiesConfigurationError( f"Iranian Cities configuration settings are missing: {', '.join(missing)}." diff --git a/iranian_cities/conf.py b/iranian_cities/conf.py index 271286b..817c14f 100644 --- a/iranian_cities/conf.py +++ b/iranian_cities/conf.py @@ -1,7 +1,10 @@ -from django.conf import settings from typing import Any, Dict -from iranian_cities.exc import IranianCitiesConfigurationError + +from django.conf import settings + from iranian_cities.constants import DEFAULT_SETTINGS +from iranian_cities.exc import IranianCitiesConfigurationError + class SageIranianCitiesSettings: _settings: Dict[str, bool] @@ -24,4 +27,5 @@ def __getattr__(self, item: str) -> bool: f"'{self.__class__.__name__}' object has no attribute '{item}'" ) + sage_iranian_cities_settings = SageIranianCitiesSettings() diff --git a/iranian_cities/exc.py b/iranian_cities/exc.py index ee3a4f1..f12106a 100644 --- a/iranian_cities/exc.py +++ b/iranian_cities/exc.py @@ -26,10 +26,17 @@ class SageError(Exception): default_code: str = "E5000" section_code: str = "SAGE" - def __init__(self, detail: Optional[str] = None, code: Optional[str] = None, section_code: Optional[str] = None): + def __init__( + self, + detail: Optional[str] = None, + code: Optional[str] = None, + section_code: Optional[str] = None, + ): self.detail: str = detail if detail is not None else self.default_detail self.code: str = code if code is not None else self.default_code - self.section_code: str = section_code if section_code is not None else self.section_code + self.section_code: str = ( + section_code if section_code is not None else self.section_code + ) self.error_id: str = str(uuid.uuid4()) def __str__(self) -> str: @@ -69,6 +76,8 @@ class IranianCitiesConfigurationError(IranianCitiesError): """ status_code: int = 400 - default_detail: str = "Invalid Sage Irianian Cities configuration. Please check your settings." + default_detail: str = ( + "Invalid Sage Irianian Cities configuration. Please check your settings." + ) default_code: str = "E4001" section_code: str = "CFG" diff --git a/iranian_cities/fields.py b/iranian_cities/fields.py index 9a6af01..f233a8d 100644 --- a/iranian_cities/fields.py +++ b/iranian_cities/fields.py @@ -1,81 +1,78 @@ from typing import Any + from django.db import models -from iranian_cities.models import Province, County, District, City, RuralDistrict, Village + +from iranian_cities.models import ( + City, + County, + District, + Province, + RuralDistrict, + Village, +) + class ProvinceField(models.ForeignKey): """A ForeignKey field for Iranian Provinces.""" - description: str = 'Iranian Province' + description: str = "Iranian Province" def __init__(self, *args: Any, **kwargs: Any) -> None: - defaults = { - 'to': Province, - 'on_delete': models.CASCADE - } + defaults = {"to": Province, "on_delete": models.CASCADE} defaults.update(kwargs) super().__init__(*args, **defaults) + class CountyField(models.ForeignKey): """A ForeignKey field for Iranian Counties.""" - description: str = 'Iranian County' + description: str = "Iranian County" def __init__(self, *args: Any, **kwargs: Any) -> None: - defaults = { - 'to': County, - 'on_delete': models.CASCADE - } + defaults = {"to": County, "on_delete": models.CASCADE} defaults.update(kwargs) super().__init__(*args, **defaults) + class DistrictField(models.ForeignKey): """A ForeignKey field for Iranian Districts.""" - description: str = 'Iranian District' + description: str = "Iranian District" def __init__(self, *args: Any, **kwargs: Any) -> None: - defaults = { - 'to': District, - 'on_delete': models.CASCADE - } + defaults = {"to": District, "on_delete": models.CASCADE} defaults.update(kwargs) super().__init__(*args, **defaults) + class CityField(models.ForeignKey): """A ForeignKey field for Iranian Cities.""" - description: str = 'Iranian City' + description: str = "Iranian City" def __init__(self, *args: Any, **kwargs: Any) -> None: - defaults = { - 'to': City, - 'on_delete': models.CASCADE - } + defaults = {"to": City, "on_delete": models.CASCADE} defaults.update(kwargs) super().__init__(*args, **defaults) + class RuralDistrictField(models.ForeignKey): """A ForeignKey field for Iranian Rural Districts.""" - description: str = 'Iranian Rural District' + description: str = "Iranian Rural District" def __init__(self, *args: Any, **kwargs: Any) -> None: - defaults = { - 'to': RuralDistrict, - 'on_delete': models.CASCADE - } + defaults = {"to": RuralDistrict, "on_delete": models.CASCADE} defaults.update(kwargs) super().__init__(*args, **defaults) + class VillageField(models.ForeignKey): """A ForeignKey field for Iranian Villages.""" - description: str = 'Iranian Village' + description: str = "Iranian Village" def __init__(self, *args: Any, **kwargs: Any) -> None: - defaults = { - 'to': Village, - 'on_delete': models.CASCADE - } + defaults = {"to": Village, "on_delete": models.CASCADE} defaults.update(kwargs) super().__init__(*args, **defaults) diff --git a/iranian_cities/management/commands/generate_city.py b/iranian_cities/management/commands/generate_city.py index ba7e9f4..4fc696d 100644 --- a/iranian_cities/management/commands/generate_city.py +++ b/iranian_cities/management/commands/generate_city.py @@ -1,22 +1,27 @@ -import os import csv import logging +import os +from typing import Dict, List, Tuple, Union + from django.core.management import BaseCommand -from iranian_cities import data -from iranian_cities.models import Province, County, District, City, RuralDistrict, Village -from typing import List, Dict, Tuple, Union +from iranian_cities import data +from iranian_cities.models import ( + City, + County, + District, + Province, + RuralDistrict, + Village, +) logger = logging.getLogger(__name__) + class Command(BaseCommand): """Management command to generate and populate database tables for Iranian cities.""" - help = 'Generate all data' - - def add_arguments(self, parser) -> None: - """Initialize command line arguments (currently none).""" - pass + help = "Generate all data" def read_csv(self, path: str) -> List[Dict[str, str]]: """ @@ -28,7 +33,7 @@ def read_csv(self, path: str) -> List[Dict[str, str]]: Returns: List[Dict[str, str]]: A list of dictionaries representing CSV rows. """ - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: logger.debug("Reading CSV file") csv_reader = csv.DictReader(f) for row in csv_reader: @@ -44,50 +49,59 @@ def prompt_user(self) -> Tuple[bool, str]: """ response: Union[None, str] = None logger.debug("Checking if the database is has data in the database") - existing_data: Dict[str, bool] = { + existing_data: Dict[str, bool] = { "Provinces": Province.objects.exists(), "Counties": County.objects.exists(), "Districts": District.objects.exists(), "Cities": City.objects.exists(), "Rural Districts": RuralDistrict.objects.exists(), - "Villages": Village.objects.exists() + "Villages": Village.objects.exists(), } if any(existing_data.values()): data_present = ", ".join(k for k, v in existing_data.items() if v) while True: try: - response = input( - f"Your database currently has objects for the following tables: {data_present}. " - "\nDo you want to flush the tables? Type 'yes' to flush or 'no' to cancel the operation: " - ).strip().lower() + response = ( + input( + f"Your database currently has objects for the following tables: {data_present}. " + "\nDo you want to flush the tables? Type 'yes' to flush or 'no' to cancel the operation: " + ) + .strip() + .lower() + ) - if response not in ['yes', 'no']: + if response not in ["yes", "no"]: print("Invalid response. Please type 'yes' or 'no'.") - elif response == 'yes': + elif response == "yes": state = "flush" self.flush_tables() break else: self.stdout.write( - self.style.ERROR('Operation canceled. No changes were made to the database.') + self.style.ERROR( + "Operation canceled. No changes were made to the database." + ) ) break except KeyboardInterrupt: state = "cancel" self.stdout.write( - self.style.ERROR('Operation interrupted by user. No changes were made to the database.') + self.style.ERROR( + "Operation interrupted by user. No changes were made to the database." + ) ) break - result = True if response == 'yes' or state == "flush" else False - response_by_user = "cancel" if response == 'no' or state == "cancel" else "yes" + result = True if response == "yes" or state == "flush" else False + response_by_user = ( + "cancel" if response == "no" or state == "cancel" else "yes" + ) return result, response_by_user result = False response_by_user = "no" return result, response_by_user - def flush_tables(self) -> None: """Delete all records from the relevant tables.""" logger.debug("flushing tables starting...") @@ -107,17 +121,16 @@ def generate_province(self, path: str) -> None: Args: path (str): The path to the CSV file containing province data. """ - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: data = csv.DictReader(f) province_objs = [ Province( - id=int(row.get('id')), - name=row.get('name'), - code=row.get('code') - ) for row in data + id=int(row.get("id")), name=row.get("name"), code=row.get("code") + ) + for row in data ] Province.objects.bulk_create(province_objs) - print('Province Objects Created Successfully') + print("Province Objects Created Successfully") def generate_county(self, path: str) -> None: """ @@ -126,18 +139,19 @@ def generate_county(self, path: str) -> None: Args: path (str): The path to the CSV file containing county data. """ - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: data = csv.DictReader(f) county_objs = [ County( - id=int(row.get('id')), - name=row.get('name'), - code=row.get('code'), - province_id=int(row.get('province')) - ) for row in data + id=int(row.get("id")), + name=row.get("name"), + code=row.get("code"), + province_id=int(row.get("province")), + ) + for row in data ] County.objects.bulk_create(county_objs) - print('County Objects Created Successfully') + print("County Objects Created Successfully") def generate_district(self, path: str) -> None: """ @@ -146,19 +160,20 @@ def generate_district(self, path: str) -> None: Args: path (str): The path to the CSV file containing district data. """ - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: data = csv.DictReader(f) district_objs = [ District( - id=int(row.get('id')), - name=row.get('name'), - code=row.get('code'), - province_id=int(row.get('province')), - county_id=int(row.get('county')) - ) for row in data + id=int(row.get("id")), + name=row.get("name"), + code=row.get("code"), + province_id=int(row.get("province")), + county_id=int(row.get("county")), + ) + for row in data ] District.objects.bulk_create(district_objs) - print('District Objects Created Successfully') + print("District Objects Created Successfully") def generate_city(self, path: str) -> None: """ @@ -167,21 +182,22 @@ def generate_city(self, path: str) -> None: Args: path (str): The path to the CSV file containing city data. """ - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: data = csv.DictReader(f) city_objs = [ City( - id=int(row.get('id')), - name=row.get('name'), - code=row.get('code'), - province_id=int(row.get('province')), - county_id=int(row.get('county')), - district_id=int(row.get('district')), - city_type=row.get('city_type') - ) for row in data + id=int(row.get("id")), + name=row.get("name"), + code=row.get("code"), + province_id=int(row.get("province")), + county_id=int(row.get("county")), + district_id=int(row.get("district")), + city_type=row.get("city_type"), + ) + for row in data ] City.objects.bulk_create(city_objs) - print('City Objects Created Successfully') + print("City Objects Created Successfully") def generate_rural_district(self, path: str) -> None: """ @@ -190,20 +206,21 @@ def generate_rural_district(self, path: str) -> None: Args: path (str): The path to the CSV file containing rural district data. """ - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: data = csv.DictReader(f) rural_district_objs = [ RuralDistrict( - id=int(row.get('id')), - name=row.get('name'), - code=row.get('code'), - province_id=int(row.get('province')), - county_id=int(row.get('county')), - district_id=int(row.get('district')) - ) for row in data + id=int(row.get("id")), + name=row.get("name"), + code=row.get("code"), + province_id=int(row.get("province")), + county_id=int(row.get("county")), + district_id=int(row.get("district")), + ) + for row in data ] RuralDistrict.objects.bulk_create(rural_district_objs) - print('RuralDistrict Objects Created Successfully') + print("RuralDistrict Objects Created Successfully") def generate_village(self, path: str) -> None: """ @@ -212,22 +229,23 @@ def generate_village(self, path: str) -> None: Args: path (str): The path to the CSV file containing village data. """ - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: data = csv.DictReader(f) village_objs = [ Village( - id=int(row.get('id')), - name=row.get('name'), - code=row.get('code'), - province_id=int(row.get('province')), - county_id=int(row.get('county')), - district_id=int(row.get('district')), - village_type=row.get('village_type'), - rural_district_id=int(row.get('rural_district')) - ) for row in data + id=int(row.get("id")), + name=row.get("name"), + code=row.get("code"), + province_id=int(row.get("province")), + county_id=int(row.get("county")), + district_id=int(row.get("district")), + village_type=row.get("village_type"), + rural_district_id=int(row.get("rural_district")), + ) + for row in data ] Village.objects.bulk_create(village_objs) - print('Village Objects Created Successfully') + print("Village Objects Created Successfully") def handle(self, *args, **options) -> None: """ @@ -240,12 +258,24 @@ def handle(self, *args, **options) -> None: result, response = self.prompt_user() if not result and response == "cancel": return - province_data_path = os.path.abspath(data.__file__).replace('__init__.py', 'province.csv') - county_data_path = os.path.abspath(data.__file__).replace('__init__.py', 'county.csv') - district_data_path = os.path.abspath(data.__file__).replace('__init__.py', 'district.csv') - city_data_path = os.path.abspath(data.__file__).replace('__init__.py', 'city.csv') - rural_district_data_path = os.path.abspath(data.__file__).replace('__init__.py', 'rural_district.csv') - village_data_path = os.path.abspath(data.__file__).replace('__init__.py', 'village.csv') + province_data_path = os.path.abspath(data.__file__).replace( + "__init__.py", "province.csv" + ) + county_data_path = os.path.abspath(data.__file__).replace( + "__init__.py", "county.csv" + ) + district_data_path = os.path.abspath(data.__file__).replace( + "__init__.py", "district.csv" + ) + city_data_path = os.path.abspath(data.__file__).replace( + "__init__.py", "city.csv" + ) + rural_district_data_path = os.path.abspath(data.__file__).replace( + "__init__.py", "rural_district.csv" + ) + village_data_path = os.path.abspath(data.__file__).replace( + "__init__.py", "village.csv" + ) self.generate_province(province_data_path) self.generate_county(county_data_path) @@ -254,6 +284,4 @@ def handle(self, *args, **options) -> None: self.generate_rural_district(rural_district_data_path) self.generate_village(village_data_path) - self.stdout.write( - self.style.SUCCESS('Data generated successfully.') - ) + self.stdout.write(self.style.SUCCESS("Data generated successfully.")) diff --git a/iranian_cities/mixins/__init__.py b/iranian_cities/mixins/__init__.py index 2e330b1..e69de29 100644 --- a/iranian_cities/mixins/__init__.py +++ b/iranian_cities/mixins/__init__.py @@ -1,5 +0,0 @@ -from iranian_cities.mixins.base_location import BaseLocation -from iranian_cities.mixins.dynamic_permission import IranianCitiesAdminReadOnlyEnabled - - -__all__ = ["BaseLocation", "IranianCitiesAdminReadOnlyEnabled"] diff --git a/iranian_cities/mixins/base_inline.py b/iranian_cities/mixins/base_inline.py index f71e715..0f1a510 100644 --- a/iranian_cities/mixins/base_inline.py +++ b/iranian_cities/mixins/base_inline.py @@ -3,5 +3,6 @@ class BaseTabularInline(admin.TabularInline): """Base class for inline models using a tabular layout.""" + extra = 1 max_num = 5 diff --git a/iranian_cities/mixins/base_location.py b/iranian_cities/mixins/base_location.py index 125e9f4..413922b 100644 --- a/iranian_cities/mixins/base_location.py +++ b/iranian_cities/mixins/base_location.py @@ -1,4 +1,4 @@ -from django.db.models import Model, Index, CharField, BigIntegerField +from django.db.models import BigIntegerField, CharField, Index, Model from django.utils.translation import gettext_lazy as _ @@ -6,20 +6,20 @@ class BaseLocation(Model): """ Abstract base model for location entities. """ + name = CharField( verbose_name=_("Name"), max_length=255, help_text=_("The name of the location."), - db_comment="This field stores the name of the location." + db_comment="This field stores the name of the location.", ) code = BigIntegerField( verbose_name=_("Code"), help_text=_("The code representing the location."), db_comment="This field stores the code for the location.", - unique=True + unique=True, ) - def __str__(self): return self.name diff --git a/iranian_cities/mixins/dynamic_permission.py b/iranian_cities/mixins/dynamic_permission.py index f026ab4..915ccf7 100644 --- a/iranian_cities/mixins/dynamic_permission.py +++ b/iranian_cities/mixins/dynamic_permission.py @@ -1,5 +1,6 @@ from iranian_cities.conf import sage_iranian_cities_settings + class IranianCitiesAdminReadOnlyEnabled: def has_add_permission(self, request): return sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_ADD_READONLY_ENABLED @@ -13,16 +14,20 @@ def has_change_permission(self, request, obj=None): class DynamicInlineAdmin: def get_dynamic_inlines(self, model): + """ + Returns the list of inlines based on the settings. + """ from iranian_cities.admin import ( - Province, County, District, + CityInline, + County, + CountyInline, + District, + DistrictInline, + Province, RuralDistrict, - CountyInline, DistrictInline, - CityInline, VillageInline + VillageInline, ) - """ - Returns the list of inlines based on the settings. - """ if sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_INLINE_ENABLED: if model == Province: return [CountyInline] diff --git a/iranian_cities/models.py b/iranian_cities/models.py index f8a26ab..c355ed0 100644 --- a/iranian_cities/models.py +++ b/iranian_cities/models.py @@ -1,14 +1,17 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from iranian_cities.mixins import BaseLocation + +from iranian_cities.mixins.base_location import BaseLocation + class Province(BaseLocation): """ Represents a province entity within the application. """ + class Meta(BaseLocation.Meta): - verbose_name = _('Province') - verbose_name_plural = _('Provinces') + verbose_name = _("Province") + verbose_name_plural = _("Provinces") db_table_comment = "Model representing a province." db_table = "sage_province" @@ -17,18 +20,19 @@ class County(BaseLocation): """ Represents a county within a province. """ + province = models.ForeignKey( Province, - verbose_name=_('Province'), - related_name='counties', + verbose_name=_("Province"), + related_name="counties", help_text=_("The province to which the county belongs."), db_comment="Foreign key to the Province model.", - on_delete=models.CASCADE + on_delete=models.CASCADE, ) class Meta(BaseLocation.Meta): - verbose_name = _('County') - verbose_name_plural = _('Counties') + verbose_name = _("County") + verbose_name_plural = _("Counties") db_table_comment = "Model representing a county within a province." db_table = "sage_county" @@ -37,26 +41,27 @@ class District(BaseLocation): """ Represents a district within a county. """ + province = models.ForeignKey( Province, - verbose_name=_('Province'), - related_name='districts', + verbose_name=_("Province"), + related_name="districts", on_delete=models.CASCADE, help_text=_("The province to which the district belongs."), - db_comment="Foreign key to the Province model." + db_comment="Foreign key to the Province model.", ) county = models.ForeignKey( County, - verbose_name=_('County'), - related_name='districts', + verbose_name=_("County"), + related_name="districts", on_delete=models.CASCADE, help_text=_("The county to which the district belongs."), - db_comment="Foreign key to the County model." + db_comment="Foreign key to the County model.", ) class Meta(BaseLocation.Meta): - verbose_name = _('District') - verbose_name_plural = _('Districts') + verbose_name = _("District") + verbose_name_plural = _("Districts") db_table_comment = "Model representing a district within a county." db_table = "sage_district" @@ -65,39 +70,40 @@ class City(BaseLocation): """ Represents a city within a district. """ + province = models.ForeignKey( Province, - verbose_name=_('Province'), - related_name='cities', + verbose_name=_("Province"), + related_name="cities", on_delete=models.CASCADE, help_text=_("The province to which the city belongs."), - db_comment="Foreign key to the Province model." + db_comment="Foreign key to the Province model.", ) county = models.ForeignKey( County, - verbose_name=_('County'), - related_name='cities', + verbose_name=_("County"), + related_name="cities", on_delete=models.CASCADE, help_text=_("The county to which the city belongs."), - db_comment="Foreign key to the County model." + db_comment="Foreign key to the County model.", ) district = models.ForeignKey( District, - verbose_name=_('District'), - related_name='cities', + verbose_name=_("District"), + related_name="cities", on_delete=models.CASCADE, help_text=_("The district to which the city belongs."), - db_comment="Foreign key to the District model." + db_comment="Foreign key to the District model.", ) city_type = models.IntegerField( - verbose_name=_('City Type'), + verbose_name=_("City Type"), help_text=_("The type of city."), - db_comment="Field to define the type of city." + db_comment="Field to define the type of city.", ) class Meta(BaseLocation.Meta): - verbose_name = _('City') - verbose_name_plural = _('Cities') + verbose_name = _("City") + verbose_name_plural = _("Cities") db_table_comment = "Model representing a city within a district." db_table = "sage_city" @@ -106,34 +112,35 @@ class RuralDistrict(BaseLocation): """ Represents a rural district within a district. """ + province = models.ForeignKey( Province, - verbose_name=_('Province'), - related_name='rural_districts', + verbose_name=_("Province"), + related_name="rural_districts", on_delete=models.CASCADE, help_text=_("The province to which the rural district belongs."), - db_comment="Foreign key to the Province model." + db_comment="Foreign key to the Province model.", ) county = models.ForeignKey( County, - verbose_name=_('County'), - related_name='rural_districts', + verbose_name=_("County"), + related_name="rural_districts", on_delete=models.CASCADE, help_text=_("The county to which the rural district belongs."), - db_comment="Foreign key to the County model." + db_comment="Foreign key to the County model.", ) district = models.ForeignKey( District, - verbose_name=_('District'), - related_name='rural_districts', + verbose_name=_("District"), + related_name="rural_districts", on_delete=models.CASCADE, help_text=_("The district to which the rural district belongs."), - db_comment="Foreign key to the District model." + db_comment="Foreign key to the District model.", ) class Meta(BaseLocation.Meta): - verbose_name = _('Rural District') - verbose_name_plural = _('Rural Districts') + verbose_name = _("Rural District") + verbose_name_plural = _("Rural Districts") db_table_comment = "Model representing a rural district within a district." db_table = "sage_rural_district" @@ -142,46 +149,47 @@ class Village(BaseLocation): """ Represents a village within a rural district. """ + province = models.ForeignKey( Province, - verbose_name=_('Province'), - related_name='villages', + verbose_name=_("Province"), + related_name="villages", on_delete=models.CASCADE, help_text=_("The province to which the village belongs."), - db_comment="Foreign key to the Province model." + db_comment="Foreign key to the Province model.", ) county = models.ForeignKey( County, - verbose_name=_('County'), - related_name='villages', + verbose_name=_("County"), + related_name="villages", on_delete=models.CASCADE, help_text=_("The county to which the village belongs."), - db_comment="Foreign key to the County model." + db_comment="Foreign key to the County model.", ) district = models.ForeignKey( District, - verbose_name=_('District'), - related_name='villages', + verbose_name=_("District"), + related_name="villages", on_delete=models.CASCADE, help_text=_("The district to which the village belongs."), - db_comment="Foreign key to the District model." + db_comment="Foreign key to the District model.", ) rural_district = models.ForeignKey( RuralDistrict, - verbose_name=_('Rural District'), - related_name='villages', + verbose_name=_("Rural District"), + related_name="villages", on_delete=models.CASCADE, help_text=_("The rural district to which the village belongs."), - db_comment="Foreign key to the Rural District model." + db_comment="Foreign key to the Rural District model.", ) village_type = models.IntegerField( - verbose_name=_('Village Type'), + verbose_name=_("Village Type"), help_text=_("The type of village."), - db_comment="Field to define the type of village." + db_comment="Field to define the type of village.", ) class Meta(BaseLocation.Meta): - verbose_name = _('Village') - verbose_name_plural = _('Villages') + verbose_name = _("Village") + verbose_name_plural = _("Villages") db_table_comment = "Model representing a village within a rural district." db_table = "sage_village" diff --git a/poetry.lock b/poetry.lock index 67b024d..438487b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -957,6 +957,38 @@ typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\"" spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] +[[package]] +name = "pylint-django" +version = "2.5.5" +description = "A Pylint plugin to help Pylint understand the Django web framework" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_django-2.5.5-py3-none-any.whl", hash = "sha256:5abd5c2228e0e5e2a4cb6d0b4fc1d1cef1e773d0be911412f4dd4fc1a1a440b7"}, + {file = "pylint_django-2.5.5.tar.gz", hash = "sha256:2f339e4bf55776958283395c5139c37700c91bd5ef1d8251ef6ac88b5abbba9b"}, +] + +[package.dependencies] +pylint = ">=2.0,<4" +pylint-plugin-utils = ">=0.8" + +[package.extras] +with-django = ["Django (>=2.2)"] + +[[package]] +name = "pylint-plugin-utils" +version = "0.8.2" +description = "Utilities and helpers for writing Pylint plugins" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, + {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, +] + +[package.dependencies] +pylint = ">=1.7" + [[package]] name = "pyproject-api" version = "1.7.1" @@ -1524,4 +1556,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.8,<4.0" -content-hash = "55a50e1f5ae79ea53907964814df99a7b237117ee92a78f21388137f58d77179" +content-hash = "fd3f555f36630cb18948b68c72bdb39242b7400fb3070e4ba63f3452a6a7b852" diff --git a/pyproject.toml b/pyproject.toml index e8a9444..a24ed58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ mypy = "^1.11.1" sphinx-rtd-theme = "^2.0.0" tox = "^4.16.0" pylint = "^3.2.6" +pylint-django = "^2.5.5" docformatter = "^1.7.5" commitizen = "^3.28.0" @@ -168,12 +169,24 @@ increment_types = ["feat", "fix"] [tool.pylint] disable = [ + "C0103", # Invalid constant name "C0114", # Missing module docstring "C0115", # Missing class docstring "C0116", # Missing function or method docstring - "E1101", # Instance of 'Foo' has no 'bar' member (Django dynamic attributes) - "W0212", # Access to a protected member _foo of a client class - "C0330", # Wrong hanging indentation before block (conflicts with Black) + "E1101", # Instance of 'Foo' has no 'bar' member + "W0212", # Access to a protected member + "C0301", # Line too long + "C0411", # Wrong import order + "W0611", # Unused imports + "W0613", # Unused arguments + "W0622", # Redefining built-in names + "R0903", # Too few public methods + "R0801", # Duplicate code + "W0621", + "C0415", + "R1719", # The if expression can be replaced with 'bool(test)' + "R1705", # Unnecessary "elif" after "return" + "R0401", ] max-line-length = 88 ignore = [ @@ -187,11 +200,12 @@ ignore = [ ".mypy_cache/*", ".pytest_cache/*" ] +django-settings-module= "kernel.settings" load-plugins = [ "pylint_django", "pylint.extensions.docparams", + ] -django-settings-module = "myproject.settings" good-names = [ "qs", # QuerySet abbreviation "pk", # Primary key abbreviation @@ -202,10 +216,6 @@ const-rgx = "([A-Z_][A-Z0-9_]*)|(__.*__)" attr-rgx = "[a-z_][a-z0-9_]{2,30}$" variable-rgx = "[a-z_][a-z0-9_]{2,30}$" argument-rgx = "[a-z_][a-z0-9_]{2,30}$" -argument-name-hint = [ - "cls", # class method first argument - "self", # instance method first argument -] method-rgx = "[a-z_][a-z0-9_]{2,30}$" function-rgx = "[a-z_][a-z0-9_]{2,30}$" class-rgx = "[A-Z_][a-zA-Z0-9]+$" diff --git a/tox.ini b/tox.ini index 6f3de57..a1de588 100644 --- a/tox.ini +++ b/tox.ini @@ -12,6 +12,7 @@ python = 3.12: py312 [testenv] +description = Run Pytest tests with multiple django versions usedevelop = True deps = django40: django>=4.2,<5.0