diff --git a/pyproject.toml b/pyproject.toml index 90ed17fba..b875787b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,7 +115,8 @@ markers = [ omit = ["*/migrations/*", "*/test/*", "e2e_tests/*"] [tool.ruff] -extend-select = ["B", "S"] +src = ["src"] +extend-select = ["B", "S", "I"] ignore = ["B904", "E501", "N818", "S101"] extend-exclude = [ "manage.py", diff --git a/src/config/celery.py b/src/config/celery.py index ec4a0cb1b..2557b4714 100644 --- a/src/config/celery.py +++ b/src/config/celery.py @@ -3,6 +3,7 @@ from celery import Celery from celery.schedules import crontab + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.prod") celery_app = Celery("DjangoCelery") diff --git a/src/config/settings/test.py b/src/config/settings/test.py index ea71000c4..8c8006d60 100644 --- a/src/config/settings/test.py +++ b/src/config/settings/test.py @@ -2,6 +2,7 @@ from .base import * # noqa + APP_ENV = "test" DEBUG = True TEMPLATE_DEBUG = True diff --git a/src/config/urls.py b/src/config/urls.py index 62d65d33f..4aec88eda 100644 --- a/src/config/urls.py +++ b/src/config/urls.py @@ -13,6 +13,7 @@ from core.urls import urlpatterns as core_urlpatterns from peoplefinder.urls import api_urlpatterns, people_urlpatterns, teams_urlpatterns + urlpatterns = [ # URLs for Staff SSO Auth broker path("auth/", include(authbroker_client_urls)), diff --git a/src/config/wsgi.py b/src/config/wsgi.py index e06aed09d..2ad0bbf00 100644 --- a/src/config/wsgi.py +++ b/src/config/wsgi.py @@ -12,6 +12,7 @@ from django.core.wsgi import get_wsgi_application from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.prod") application = get_wsgi_application() diff --git a/src/content/management/commands/create_groups.py b/src/content/management/commands/create_groups.py index a503ba121..8cb916317 100644 --- a/src/content/management/commands/create_groups.py +++ b/src/content/management/commands/create_groups.py @@ -14,6 +14,7 @@ WorkingAtDITHome, ) + TOP_LEVEL_PAGE_TYPES = [ AboutUsHome, HowDoIHome, diff --git a/src/content/models.py b/src/content/models.py index f19977a4b..0b9d84702 100644 --- a/src/content/models.py +++ b/src/content/models.py @@ -24,12 +24,13 @@ from content import blocks from content.utils import manage_excluded, manage_pinned, truncate_words_and_chars from core.utils import set_seen_cookie_banner -from extended_search.index import Indexed from extended_search.index import DWIndexedField as IndexedField +from extended_search.index import Indexed from peoplefinder.widgets import PersonChooser from search.utils import split_query from user.models import User as UserModel + User = get_user_model() RICH_TEXT_FEATURES = [ diff --git a/src/core/models.py b/src/core/models.py index 320cecb6d..36f6be522 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -2,9 +2,9 @@ from django.db import models from simple_history import register from simple_history.models import HistoricalRecords +from waffle.models import AbstractUserFlag from wagtail.documents.models import Document from wagtail.snippets.models import register_snippet -from waffle.models import AbstractUserFlag @register_snippet diff --git a/src/core/urls.py b/src/core/urls.py index 41f13a663..72a4575e5 100644 --- a/src/core/urls.py +++ b/src/core/urls.py @@ -6,4 +6,5 @@ urlpatterns = [ path("problem-found/", views.page_problem_found, name="page_problem_found"), path("deactivated/", views.deactivated, name="deactivated"), + path("report/user-groups", views.user_groups_report, name="report:user-groups"), ] diff --git a/src/core/views.py b/src/core/views.py index cc5c22f2b..7484744b8 100644 --- a/src/core/views.py +++ b/src/core/views.py @@ -1,11 +1,17 @@ +import csv import logging from django.conf import settings +from django.contrib.postgres.aggregates import StringAgg +from django.core.exceptions import PermissionDenied +from django.http import HttpRequest, HttpResponse from django.template.response import TemplateResponse +from django.views.decorators.http import require_GET from notifications_python_client.notifications import NotificationsAPIClient from sentry_sdk import capture_message from core.forms import PageProblemFoundForm +from user.models import User logger = logging.getLogger(__name__) @@ -86,3 +92,31 @@ def page_problem_found(request): "core/page_problem_found.html", {"form": form, "message_sent": message_sent}, ) + + +@require_GET +def user_groups_report(request: HttpRequest, *args, **kwargs) -> HttpResponse: + if not request.user.is_superuser: + raise PermissionDenied + + header = ["ID", "Email", "First name", "Last name", "Groups"] + + qs = ( + User.objects.filter(groups__isnull=False) + .annotate(group_names=StringAgg("groups__name", delimiter=", ")) + .values_list("id", "email", "first_name", "last_name", "group_names") + ) + + filename = "user_groups.csv" + + response = HttpResponse( + content_type="text/csv", + headers={"Content-Disposition": f'attachment; filename="{filename}"'}, + ) + + writer = csv.writer(response) + writer.writerow(header) + for obj in qs: + writer.writerow(obj) + + return response diff --git a/src/e2e_tests/conftest.py b/src/e2e_tests/conftest.py index dfefff6ce..bb390013f 100644 --- a/src/e2e_tests/conftest.py +++ b/src/e2e_tests/conftest.py @@ -31,6 +31,7 @@ recreate_db, ) + os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" diff --git a/src/extended_search/admin.py b/src/extended_search/admin.py index d9f0fea19..3235a0c98 100644 --- a/src/extended_search/admin.py +++ b/src/extended_search/admin.py @@ -2,8 +2,8 @@ from django.contrib import admin from core.admin import admin_site -from extended_search.models import Setting from extended_search import settings +from extended_search.models import Setting class SettingAdminForm(forms.ModelForm): diff --git a/src/extended_search/backends/backend.py b/src/extended_search/backends/backend.py index 6464f9e16..57832deb5 100644 --- a/src/extended_search/backends/backend.py +++ b/src/extended_search/backends/backend.py @@ -9,8 +9,8 @@ from wagtail.search.index import SearchField from wagtail.search.query import MATCH_NONE, Fuzzy, MatchAll, Not, Phrase, PlainText -from extended_search.query import Filtered, Nested, OnlyFields from extended_search.index import RelatedFields +from extended_search.query import Filtered, Nested, OnlyFields class FilteredSearchMapping(Elasticsearch7Mapping): diff --git a/src/extended_search/index.py b/src/extended_search/index.py index b45221408..19dbc3551 100644 --- a/src/extended_search/index.py +++ b/src/extended_search/index.py @@ -13,6 +13,7 @@ from extended_search.types import AnalysisType + logger = logging.getLogger(__name__) diff --git a/src/extended_search/query_builder.py b/src/extended_search/query_builder.py index 2b592194d..1b15454c6 100644 --- a/src/extended_search/query_builder.py +++ b/src/extended_search/query_builder.py @@ -21,6 +21,7 @@ from extended_search.query import Filtered, Nested, OnlyFields from extended_search.types import AnalysisType, SearchQueryType + logger = logging.getLogger(__name__) diff --git a/src/extended_search/settings.py b/src/extended_search/settings.py index e35757c64..d0944b99e 100644 --- a/src/extended_search/settings.py +++ b/src/extended_search/settings.py @@ -17,6 +17,7 @@ get_indexed_models, ) + env_file_path = os.path.join( os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), ".env", diff --git a/src/extended_search/signals.py b/src/extended_search/signals.py index b49a33571..10419277e 100644 --- a/src/extended_search/signals.py +++ b/src/extended_search/signals.py @@ -1,7 +1,7 @@ -from django.db.models.signals import post_save, post_delete +from django.db.models.signals import post_delete, post_save -from extended_search.models import Setting from extended_search import settings +from extended_search.models import Setting def update_searchsetting_queryset(sender, **kwargs): diff --git a/src/extended_search/tests/test_backend.py b/src/extended_search/tests/test_backend.py index 6905965c6..ed0a5ff85 100644 --- a/src/extended_search/tests/test_backend.py +++ b/src/extended_search/tests/test_backend.py @@ -8,10 +8,10 @@ from wagtail.search.query import ( MATCH_NONE, Fuzzy, - Phrase, - PlainText, MatchAll, Not, + Phrase, + PlainText, SearchQuery, ) diff --git a/src/extended_search/tests/test_query.py b/src/extended_search/tests/test_query.py index 4df00285c..95239bf51 100644 --- a/src/extended_search/tests/test_query.py +++ b/src/extended_search/tests/test_query.py @@ -1,7 +1,7 @@ import pytest +from wagtail.search.query import PlainText from extended_search.query import Filtered, Nested, OnlyFields -from wagtail.search.query import PlainText class TestOnlyFields: diff --git a/src/extended_search/tests/test_types.py b/src/extended_search/tests/test_types.py index e6af96a31..c4a57c474 100644 --- a/src/extended_search/tests/test_types.py +++ b/src/extended_search/tests/test_types.py @@ -1,7 +1,8 @@ -import pytest from enum import Enum -from extended_search.types import SearchQueryType, AnalysisType +import pytest + +from extended_search.types import AnalysisType, SearchQueryType class TestAnalysisType: diff --git a/src/home/util.py b/src/home/util.py index b4a6f598f..098c53664 100644 --- a/src/home/util.py +++ b/src/home/util.py @@ -1,7 +1,7 @@ import tweepy from django.conf import settings -from tweepy.auth import OAuthHandler from sentry_sdk import capture_exception +from tweepy.auth import OAuthHandler def get_tweets(): diff --git a/src/news/models.py b/src/news/models.py index 55b259aea..5de1eebc9 100644 --- a/src/news/models.py +++ b/src/news/models.py @@ -17,6 +17,7 @@ from news.forms import CommentForm from working_at_dit.models import PageWithTopics + UserModel = get_user_model() diff --git a/src/patch/__init__.py b/src/patch/__init__.py index cf58023a9..806893c90 100644 --- a/src/patch/__init__.py +++ b/src/patch/__init__.py @@ -1,6 +1,8 @@ -from extended_search.index import get_indexed_models, class_is_indexed from wagtail.search import index +from extended_search.index import class_is_indexed, get_indexed_models + + index.get_indexed_models = get_indexed_models index.class_is_indexed = class_is_indexed diff --git a/src/peoplefinder/forms/profile.py b/src/peoplefinder/forms/profile.py index 21b2f19d1..a4e442f6b 100644 --- a/src/peoplefinder/forms/profile.py +++ b/src/peoplefinder/forms/profile.py @@ -4,6 +4,7 @@ from django.contrib.auth import get_user_model from django.core.validators import ValidationError + User = get_user_model() diff --git a/src/peoplefinder/legacy_migration/migrate_legacy.py b/src/peoplefinder/legacy_migration/migrate_legacy.py index 497597a38..57a2a1db1 100644 --- a/src/peoplefinder/legacy_migration/migrate_legacy.py +++ b/src/peoplefinder/legacy_migration/migrate_legacy.py @@ -56,6 +56,7 @@ from peoplefinder.services.team import TeamService from user.models import User + BATCH_SIZE = 100 logger = logging.getLogger(__name__) diff --git a/src/peoplefinder/management/commands/create_test_teams.py b/src/peoplefinder/management/commands/create_test_teams.py index 793115dc0..333e49d45 100644 --- a/src/peoplefinder/management/commands/create_test_teams.py +++ b/src/peoplefinder/management/commands/create_test_teams.py @@ -3,6 +3,7 @@ from peoplefinder.models import Team from peoplefinder.services.team import TeamService + TREE_HELP = """Team tree: . └── SpaceX diff --git a/src/peoplefinder/models.py b/src/peoplefinder/models.py index 27cb8e7ec..74ceaf81e 100644 --- a/src/peoplefinder/models.py +++ b/src/peoplefinder/models.py @@ -22,6 +22,7 @@ from extended_search.index import DWIndexedField as IndexedField from extended_search.index import Indexed, RelatedFields + # United Kingdom DEFAULT_COUNTRY_PK = "CTHMTC00260" diff --git a/src/peoplefinder/services/person.py b/src/peoplefinder/services/person.py index c98a4b298..1ddf511bb 100644 --- a/src/peoplefinder/services/person.py +++ b/src/peoplefinder/services/person.py @@ -30,6 +30,7 @@ from peoplefinder.types import EditSections, ProfileSections from user.models import User + logger = logging.getLogger(__name__) LEFT_DIT_LOG_MESSAGE = """People Finder deletion request: {profile_name} diff --git a/src/peoplefinder/templatetags/markdown.py b/src/peoplefinder/templatetags/markdown.py index 1a3fe2751..a7d9fe129 100644 --- a/src/peoplefinder/templatetags/markdown.py +++ b/src/peoplefinder/templatetags/markdown.py @@ -3,6 +3,7 @@ from django.utils.html import conditional_escape from django.utils.safestring import mark_safe + register = template.Library() diff --git a/src/peoplefinder/templatetags/profile.py b/src/peoplefinder/templatetags/profile.py index 6ed0d7738..fec9bd38a 100644 --- a/src/peoplefinder/templatetags/profile.py +++ b/src/peoplefinder/templatetags/profile.py @@ -6,6 +6,7 @@ from peoplefinder.models import Person + register = template.Library() diff --git a/src/peoplefinder/urls.py b/src/peoplefinder/urls.py index dc488aca1..263ef8ad1 100644 --- a/src/peoplefinder/urls.py +++ b/src/peoplefinder/urls.py @@ -38,6 +38,7 @@ TeamTreeView, ) + people_urlpatterns = [ path("", PeopleHome.as_view(), name="people-home"), path( diff --git a/src/peoplefinder/views/profile.py b/src/peoplefinder/views/profile.py index 27c583d6c..9001ce3e0 100644 --- a/src/peoplefinder/views/profile.py +++ b/src/peoplefinder/views/profile.py @@ -48,6 +48,7 @@ from .base import HtmxFormView, PeoplefinderView + User = get_user_model() diff --git a/src/peoplefinder/views/team.py b/src/peoplefinder/views/team.py index 2d78f65cf..424de5c62 100644 --- a/src/peoplefinder/views/team.py +++ b/src/peoplefinder/views/team.py @@ -15,6 +15,7 @@ from .base import PeoplefinderView + # TODO: Potential to refactor for the common parts. diff --git a/src/search/templatetags/search.py b/src/search/templatetags/search.py index 0fe9f8b4f..66f725cab 100644 --- a/src/search/templatetags/search.py +++ b/src/search/templatetags/search.py @@ -6,6 +6,7 @@ from search import search as search_vectors + # from search.utils import has_only_bad_results # from silk.profiling.profiler import silk_profile diff --git a/src/search/templatetags/search_explore.py b/src/search/templatetags/search_explore.py index d9044e235..625d64841 100644 --- a/src/search/templatetags/search_explore.py +++ b/src/search/templatetags/search_explore.py @@ -6,6 +6,7 @@ from search import search as search_vectors from search.templatetags import search as main_search + register = template.Library() diff --git a/src/search/urls.py b/src/search/urls.py index ba3f887a9..0801547d4 100644 --- a/src/search/urls.py +++ b/src/search/urls.py @@ -1,7 +1,7 @@ from django.urls import path from django.views.generic import RedirectView -from .views import explore, search, autocomplete +from .views import autocomplete, explore, search app_name = "search" diff --git a/src/search/utils.py b/src/search/utils.py index cee4d75d1..c0053f7fb 100644 --- a/src/search/utils.py +++ b/src/search/utils.py @@ -5,10 +5,10 @@ from django.conf import settings from wagtail.search.query import Fuzzy, Or, Phrase, PlainText +from extended_search import settings as search_settings from extended_search.index import Indexed from extended_search.query import OnlyFields from extended_search.query_builder import CustomQueryBuilder -from extended_search import settings as search_settings def sanitize_search_query(query: Optional[str] = None) -> str: diff --git a/src/search/views.py b/src/search/views.py index eb8ff4835..1017fe7fd 100644 --- a/src/search/views.py +++ b/src/search/views.py @@ -16,6 +16,7 @@ from search.templatetags import search as search_template_tag from search.utils import get_query_info_for_model + logger = logging.getLogger(__name__) diff --git a/src/tools/admin.py b/src/tools/admin.py index bc2d8bd77..cedfc9edb 100644 --- a/src/tools/admin.py +++ b/src/tools/admin.py @@ -1,6 +1,7 @@ from core.admin import admin_site from tools.models import IrapToolData, IrapToolDataImport, Tool + # Really crude admin, to be used for development admin_site.register(IrapToolData) diff --git a/src/tools/models.py b/src/tools/models.py index 8808df13d..0d6391c1a 100644 --- a/src/tools/models.py +++ b/src/tools/models.py @@ -1,10 +1,11 @@ -from content.models import ContentPage from django import forms from django.core.serializers.json import DjangoJSONEncoder from django.db import models from django.shortcuts import redirect -from extended_search.index import DWIndexedField as IndexedField from wagtail.admin.panels import FieldPanel + +from content.models import ContentPage +from extended_search.index import DWIndexedField as IndexedField from working_at_dit.models import PageWithTopics