From e32826472410c2f178394f29e78c854f3a76e4d0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 21:30:11 +0000 Subject: [PATCH 1/4] build(pre-commit): pre-commit autoupdate by ci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.2 → v0.9.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.2...v0.9.4) - [github.com/pre-commit/mirrors-eslint: v9.14.0 → v9.19.0](https://github.com/pre-commit/mirrors-eslint/compare/v9.14.0...v9.19.0) - [github.com/crate-ci/typos: v1.27.0 → typos-dict-v0.12.4](https://github.com/crate-ci/typos/compare/v1.27.0...typos-dict-v0.12.4) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6e57cc161..c8e622da3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,12 +22,12 @@ repos: exclude: \.dot$ - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.2 + rev: v0.9.4 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/pre-commit/mirrors-eslint - rev: v9.14.0 + rev: v9.19.0 hooks: - id: eslint args: [--fix, --color] @@ -36,7 +36,7 @@ repos: - eslint-plugin-react@7.37.0 - react@18.3.1 - repo: https://github.com/crate-ci/typos - rev: v1.27.0 + rev: typos-dict-v0.12.4 hooks: - id: typos exclude: | From 4ef054f8f9f973464b0513772f2dbd7c2010e607 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 6 Feb 2025 09:36:38 +0100 Subject: [PATCH 2/4] style(core, commands): update code style Signed-off-by: David Wallace --- .../management/commands/find_spam_users.py | 178 ++++++++---------- rdmo/core/management/commands/find_users.py | 143 ++++++-------- 2 files changed, 133 insertions(+), 188 deletions(-) diff --git a/rdmo/core/management/commands/find_spam_users.py b/rdmo/core/management/commands/find_spam_users.py index 0ff7e6b21..0410dd1e7 100644 --- a/rdmo/core/management/commands/find_spam_users.py +++ b/rdmo/core/management/commands/find_spam_users.py @@ -9,144 +9,118 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument( - '-t', '--timespan', default=2, type=int, - help='timespan in seconds between two joining users, ' + - 'less than the given value is considered to be suspicious ' + - ', default is 2' + "-t", "--timespan", + default=2, type=int, + help=( + "timespan in seconds between two joining users, " + "less than the given value is considered to be suspicious, " + "default is 2" + ) ) parser.add_argument( - '-n', '--occurrence', default=3, type=int, - help='number of sequentially occurring timespan ' + - 'violations at which users are put into the ' + - 'potential spam users list, default is 3' + "-n", "--occurrence", + default=3, type=int, + help=( + "number of sequentially occurring timespan " + "violations at which users are put into the " + "potential spam users list, default is 3" + ) ) parser.add_argument( - '-p', '--print', action='store_true', - help='print found users, don\'t save them to csv' + "-p", "--print", + action="store_true", + help="print found users, don't save them to csv" ) parser.add_argument( - '-o', '--output_file', default='potential_spam_users.csv', - help='output file, default is \'potential_spam_users.csv\'' + "-o", "--output_file", + default="potential_spam_users.csv", + help="output file, default is 'potential_spam_users.csv'" ) def save_csv(self, data, filename): - if len(data) > 0: - data_file = open(filename, 'w') - csv_writer = csv.writer(data_file) - csv_writer.writerow(list(data[0].keys())) - for user in data: - csv_writer.writerow(user.values()) - print('List written to ' + filename) + if data: + with open(filename, "w", newline="", encoding="utf-8") as data_file: + csv_writer = csv.writer(data_file) + csv_writer.writerow(list(data[0].keys())) + for user in data: + csv_writer.writerow(user.values()) + print(f"List written to {filename}") def print_file(self, filename): - f = open(filename) - content = f.read() - print(content) - f.close() + with open(filename) as f: + content = f.read() + print(content) def get_users_having_projects(self): - arr = [] - memberships = Membership.objects.all().values_list('user') - for mem in memberships: - arr.append( - User.objects.filter(id=mem[0]).values('id')[0]['id'] - ) - return arr + memberships = Membership.objects.all().values_list("user", flat=True) + return list(memberships) - def append_to_group( - self, group_list, group_count, user, list_users_having_projects - ): - date_string = '%Y-%m-%dT%H:%M:%S.%f' - last_login = user['last_login'] - if last_login is not None: - last_login = last_login.strftime(date_string) + def append_to_group(self, group_list, group_count, user, list_users_having_projects): + date_string = "%Y-%m-%dT%H:%M:%S.%f" + last_login = user["last_login"].strftime(date_string) if user["last_login"] else None - has_project = user['id'] in list_users_having_projects + has_project = user["id"] in list_users_having_projects group_list[group_count].append( { - 'id': user['id'], - 'email': user['email'], - 'username': user['username'], - 'first_name': user['first_name'], - 'last_name': user['last_name'], - 'date_joined': user['date_joined'].strftime(date_string), - 'last_login': last_login, - 'has_project': has_project, + "id": user["id"], + "email": user["email"], + "username": user["username"], + "first_name": user["first_name"], + "last_name": user["last_name"], + "date_joined": user["date_joined"].strftime(date_string), + "last_login": last_login, + "has_project": has_project, } ) return group_list def find_potential_spam_users(self, timespan, occurrence): list_users_having_projects = self.get_users_having_projects() - arr = [] - for idx, user in enumerate(User.objects.all().order_by('date_joined')): - arr.append( - { - 'id': user.id, - 'username': user.username, - 'first_name': user.first_name, - 'last_name': user.last_name, - 'date_joined': user.date_joined, - 'unix_joined': user.date_joined.timestamp(), - 'email': user.email, - 'last_login': user.last_login, - } - ) + arr = [ + { + "id": user.id, + "username": user.username, + "first_name": user.first_name, + "last_name": user.last_name, + "date_joined": user.date_joined, + "unix_joined": user.date_joined.timestamp(), + "email": user.email, + "last_login": user.last_login, + } + for user in User.objects.all().order_by("date_joined") + ] grouped = {} group_count = 0 for idx, user in enumerate(arr): - prev = None - diff = timespan - if idx > 0: - prev = arr[idx-1] - diff = user['unix_joined'] - prev['unix_joined'] + prev = arr[idx - 1] if idx > 0 else None + diff = user["unix_joined"] - prev["unix_joined"] if prev else timespan - if prev is not None and diff >= timespan: + if prev and diff >= timespan: group_count += 1 - try: - grouped[group_count] - except KeyError: - grouped[group_count] = [] + grouped.setdefault(group_count, []) + grouped = self.append_to_group(grouped, group_count, user, list_users_having_projects) - grouped = self.append_to_group( - grouped, group_count, user, list_users_having_projects - ) - - no_potential_spam_users = 0 - grouped_clean = {} - for group_id in grouped: - group = grouped[group_id] - if len(group) > occurrence: - no_potential_spam_users += len(group) - grouped_clean[group_id] = group - - potential_spam_users = [] - for group in grouped_clean: - for user in grouped_clean[group]: - potential_spam_users.append(user) + grouped_clean = {k: v for k, v in grouped.items() if len(v) > occurrence} + potential_spam_users = [user for group in grouped_clean.values() for user in group] - return (potential_spam_users, len(list_users_having_projects)) + return potential_spam_users, len(list_users_having_projects) def handle(self, *args, **options): + no_total_users = User.objects.count() + print(f"Total no of users: {no_total_users}") - no_total_users = User.objects.all().count() - print('Total no of users: %d' % (no_total_users)) - potential_spam_users, no_users_having_projects =\ - self.find_potential_spam_users( - options['timespan'], options['occurrence'] - ) + potential_spam_users, no_users_having_projects = self.find_potential_spam_users( + options["timespan"], options["occurrence"] + ) + percentage = (100 / no_total_users) * len(potential_spam_users) if no_total_users else 0 print( - 'Potential spam users: %d %.2f%% / of which have at least one project %d' - % ( - len(potential_spam_users), - (100/no_total_users)*len(potential_spam_users), - no_users_having_projects - ) + f"Potential spam users: {len(potential_spam_users)} {percentage:.2f}% / " + f"of which have at least one project {no_users_having_projects}" ) - self.save_csv(potential_spam_users, options['output_file']) - if options['print'] is True: - self.print_file(options['output_file']) + self.save_csv(potential_spam_users, options["output_file"]) + if options["print"]: + self.print_file(options["output_file"]) diff --git a/rdmo/core/management/commands/find_users.py b/rdmo/core/management/commands/find_users.py index c67903624..f85fce0a2 100644 --- a/rdmo/core/management/commands/find_users.py +++ b/rdmo/core/management/commands/find_users.py @@ -9,107 +9,78 @@ class Command(BaseCommand): def add_arguments(self, parser): - parser.add_argument( - '--id', default='.*', - help='find users by id' - ) - parser.add_argument( - '--email', default='.*', - help='find users by email' - ) - parser.add_argument( - '--username', default='.*', - help='find users by username' - ) - parser.add_argument( - '--first_name', default='.*', - help='find users by first name' - ) - parser.add_argument( - '--last_name', default='.*', - help='find users by last name' - ) - parser.add_argument( - '-p', '--print', action='store_true', - help='print found users, don\'t save them to csv' - ) - parser.add_argument( - '-o', '--output_file', default='found_users.csv', - help='output file, default is \'found_users.csv\'' - ) + parser.add_argument("--id", + default=".*", + help="find users by id") + parser.add_argument("--email", + default=".*", + help="find users by email") + parser.add_argument("--username", + default=".*", + help="find users by username") + parser.add_argument("--first_name", + default=".*", + help="find users by first name") + parser.add_argument("--last_name", + default=".*", + help="find users by last name") + parser.add_argument("-p", "--print", + action="store_true", + help="print found users, don't save them to csv") + parser.add_argument("-o", "--output_file", + default="found_users.csv", + help="output file, default is 'found_users.csv'") def save_csv(self, data, filename): - if len(data) > 0: - data_file = open(filename, 'w') - csv_writer = csv.writer(data_file) - csv_writer.writerow(list(data[0].keys())) - for user in data: - csv_writer.writerow(user.values()) - print('List written to ' + filename) + if data: + with open(filename, "w", newline="") as data_file: + csv_writer = csv.writer(data_file) + csv_writer.writerow(list(data[0].keys())) + for user in data: + csv_writer.writerow(user.values()) + print(f"List written to {filename}") def print_file(self, filename): - f = open(filename) - content = f.read() - print(content) - f.close() + with open(filename) as f: + content = f.read() + print(content) def get_users_having_projects(self): - arr = [] - memberships = Membership.objects.all().values_list('user') - for mem in memberships: - arr.append( - User.objects.filter(id=mem[0]).values('id')[0]['id'] - ) - return arr + return list(Membership.objects.all().values_list("user", flat=True)) def rx_match(self, regex, s): return bool(re.search(regex, str(s))) def check_match(self, user, options): - if self.rx_match(options['id'], user.id) is False: - return False - if self.rx_match(options['email'], user.email) is False: - return False - if self.rx_match(options['username'], user.username) is False: - return False - if self.rx_match(options['last_name'], user.last_name) is False: - return False - if self.rx_match(options['first_name'], user.first_name) is False: - return False - return True + return all( + self.rx_match(options[key], getattr(user, key)) + for key in ["id", "email", "username", "last_name", "first_name"] + ) def find_users(self, options): - found_users = [] - for _, user in enumerate(User.objects.all().order_by('date_joined')): - m = self.check_match(user, options) - if m is True: - found_users.append( - { - 'id': user.id, - 'username': user.username, - 'first_name': user.first_name, - 'last_name': user.last_name, - 'date_joined': user.date_joined, - 'unix_joined': user.date_joined.timestamp(), - 'email': user.email, - 'last_login': user.last_login, - } - ) - return found_users + return [ + { + "id": user.id, + "username": user.username, + "first_name": user.first_name, + "last_name": user.last_name, + "date_joined": user.date_joined, + "unix_joined": user.date_joined.timestamp(), + "email": user.email, + "last_login": user.last_login, + } + for user in User.objects.all().order_by("date_joined") + if self.check_match(user, options) + ] def handle(self, *args, **options): - no_total_users = User.objects.all().count() - print('Total no of users: %d' % (no_total_users)) + no_total_users = User.objects.count() + print(f"Total no of users: {no_total_users}") found_users = self.find_users(options) - print( - 'Matching the filter: %d %.2f%%' - % ( - len(found_users), - (100/no_total_users)*len(found_users) - ) - ) + percentage = (100 / no_total_users) * len(found_users) if no_total_users else 0 + print(f"Matching the filter: {len(found_users)} {percentage:.2f}%") - self.save_csv(found_users, options['output_file']) - if options['print'] is True: - self.print_file(options['output_file']) + self.save_csv(found_users, options["output_file"]) + if options["print"]: + self.print_file(options["output_file"]) From 47fd438df6992b50198fd5c2bed74d5739cf0979 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 6 Feb 2025 09:41:53 +0100 Subject: [PATCH 3/4] style(core, utils): update code style Signed-off-by: David Wallace --- rdmo/core/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rdmo/core/utils.py b/rdmo/core/utils.py index 0813b05c7..e4b2bd7ad 100644 --- a/rdmo/core/utils.py +++ b/rdmo/core/utils.py @@ -131,12 +131,11 @@ def get_model_field_meta(model): return meta -def get_languages(): +def get_languages() -> list[tuple]: languages = [] for i in range(5): try: - language = settings.LANGUAGES[i][0], settings.LANGUAGES[i][1],\ - 'lang%i' % (i + 1) + language = (settings.LANGUAGES[i][0], settings.LANGUAGES[i][1], f"lang{i + 1}") languages.append(language) except IndexError: pass From d94b05c5e285aac82a99a0a377d9dde162fc157f Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 6 Feb 2025 09:44:05 +0100 Subject: [PATCH 4/4] style(tests): remove deprecated PT004 rule Signed-off-by: David Wallace --- conftest.py | 2 +- rdmo/management/tests/e2e/conftest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conftest.py b/conftest.py index 5dbd3652e..7a2d7ec8d 100644 --- a/conftest.py +++ b/conftest.py @@ -34,7 +34,7 @@ def fixtures(): @pytest.fixture(scope='session') -def django_db_setup(django_db_setup, django_db_blocker, fixtures): # noqa: PT004 - pytest-django requires this name "django_db_setup" +def django_db_setup(django_db_setup, django_db_blocker, fixtures): """Populate database with test data from fixtures directories.""" with django_db_blocker.unblock(): call_command('loaddata', *fixtures) diff --git a/rdmo/management/tests/e2e/conftest.py b/rdmo/management/tests/e2e/conftest.py index 44d1321a2..15f4ae807 100644 --- a/rdmo/management/tests/e2e/conftest.py +++ b/rdmo/management/tests/e2e/conftest.py @@ -22,7 +22,7 @@ def _set_django_allow_async_unsafe(): @pytest.fixture -def django_db_setup(django_db_setup, django_db_blocker, fixtures): # noqa: PT004 - pytest-django requires this name "django_db_setup" +def django_db_setup(django_db_setup, django_db_blocker, fixtures): """Set up database and populate with fixtures, that get restored for every test case. This fixture overrides the django_db_setup in the main conftest.py, this only applies to the e2e tests