From 6fc0edaaac205386a136ba6841fb3d8bf4ba6a4f Mon Sep 17 00:00:00 2001 From: Julian B Date: Tue, 30 Jul 2024 15:08:41 +0200 Subject: [PATCH] lint repository using pre-commit hook --- .github/workflows/build-container.yml | 4 +- .github/workflows/codeql-analysis.yml | 73 +++-- .github/workflows/tests.yml | 194 +++++++------ .pre-commit-config.yaml | 18 +- myhpi/core/models.py | 1 - myhpi/core/templates/core/minutes_list.html | 87 ++++++ myhpi/core/templatetags/core_extras.py | 1 - myhpi/polls/models.py | 3 +- myhpi/static/css/myHPI_admin.css | 44 +-- myhpi/static/css/text_array_widget.css | 44 +-- myhpi/static/js/admin/easymde_custom.js | 301 ++++++++++---------- myhpi/static/js/admin/text_array_widget.js | 96 ++++--- myhpi/static/js/print_processor.js | 100 +++---- myhpi/static/js/search.js | 42 +-- myhpi/static/js/sidebar.js | 4 +- myhpi/static/js/utils.js | 22 +- myhpi/static/scss/myHPI.scss | 8 +- myhpi/tenca_django/views.py | 2 +- myhpi/tests/core/test_email_utils.py | 1 - myhpi/tests/core/test_widgets.py | 2 - 20 files changed, 577 insertions(+), 470 deletions(-) diff --git a/.github/workflows/build-container.yml b/.github/workflows/build-container.yml index 754a6ede..468524a8 100644 --- a/.github/workflows/build-container.yml +++ b/.github/workflows/build-container.yml @@ -3,9 +3,9 @@ name: build-container on: push: branches: - - 'main' + - "main" tags: - - 'v*' + - "v*" jobs: docker: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 34297c03..dd659c78 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,12 +2,12 @@ name: "CodeQL" on: push: - branches: [ main ] + branches: [main] pull_request: # The branches below must be a subset of the branches above - branches: [ main ] + branches: [main] schedule: - - cron: '44 16 * * 0' + - cron: "44 16 * * 0" jobs: analyze: @@ -21,41 +21,40 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'javascript', 'python' ] + language: ["javascript", "python"] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5ec1fa0b..a7b87c72 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,51 +2,55 @@ name: tests on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: lint: runs-on: ubuntu-20.04 strategy: matrix: - python-version: [ 3.11 ] + python-version: [3.11] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install poetry - run: curl -sSL https://install.python-poetry.org | python3 - --version 1.3.2 - - uses: actions/cache@v1 - with: - path: ~/.cache/pypoetry/virtualenvs - key: myHPI-${{ runner.os }}-poetry-py${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }} - restore-keys: | - myHPI-${{ runner.os }}-poetry-py${{ matrix.python-version }}- - - name: Install dependencies - run: | - poetry install - - name: check format with black - run: | - poetry run black --version - poetry run black --check . - - name: check import order with isort - run: | - poetry run isort --version - poetry run isort -c . - - name: Lint with pylint - run: | - poetry run pylint --version - poetry run pylint --fail-under=9 myhpi + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install poetry + run: curl -sSL https://install.python-poetry.org | python3 - --version 1.3.2 + - uses: actions/cache@v1 + with: + path: ~/.cache/pypoetry/virtualenvs + key: myHPI-${{ runner.os }}-poetry-py${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }} + restore-keys: | + myHPI-${{ runner.os }}-poetry-py${{ matrix.python-version }}- + - name: Install dependencies + run: | + poetry install + - name: check format with black + run: | + poetry run black --version + poetry run black --check . + - name: check import order with isort + run: | + poetry run isort --version + poetry run isort -c . + - name: Lint with pylint + run: | + poetry run pylint --version + poetry run pylint --fail-under=9 myhpi + - name: Lint templates with djhtml + run: | + poetry run djhtml --version + poetry run djhtml --check myhpi test: runs-on: ubuntu-20.04 strategy: matrix: - python-version: [ 3.11, 3.12 ] - database: [ sqlite, postgres ] + python-version: [3.11, 3.12] + database: [sqlite, postgres] include: - database: sqlite database_url: "sqlite:///data/db.sqlite3" @@ -56,70 +60,70 @@ jobs: DJANGO_SETTINGS_MODULE: myhpi.tests.settings steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - #- uses: Gr1N/setup-poetry@v4 - #- uses: actions/cache@v1 - # with: - # path: ~/.cache/pypoetry/virtualenvs - # key: myHPI-${{ runner.os }}-poetry-py${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }} - # restore-keys: | - # myHPI-${{ runner.os }}-poetry-py${{ matrix.python-version }}- - - name: Setup python venv and poetry - run: | - #sudo apt-get install python-venv - python -m venv env - source env/bin/activate - curl -sSL https://install.python-poetry.org | python3 - --version 1.3.2 - - name: Install dependencies - # always install all -E extras to use a single cache - run: | - sudo apt-get install gettext - source env/bin/activate - poetry run python -m pip install setuptools -U - poetry install -E mysql -E pgsql - python tools/install_bootstrap.py - - name: Prepare files for test run - continue-on-error: true - run: | - cp .env.example .env - source env/bin/activate - poetry run python manage.py compilemessages --settings myhpi.settings - poetry run python manage.py collectstatic --settings myhpi.settings + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + #- uses: Gr1N/setup-poetry@v4 + #- uses: actions/cache@v1 + # with: + # path: ~/.cache/pypoetry/virtualenvs + # key: myHPI-${{ runner.os }}-poetry-py${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }} + # restore-keys: | + # myHPI-${{ runner.os }}-poetry-py${{ matrix.python-version }}- + - name: Setup python venv and poetry + run: | + #sudo apt-get install python-venv + python -m venv env + source env/bin/activate + curl -sSL https://install.python-poetry.org | python3 - --version 1.3.2 + - name: Install dependencies + # always install all -E extras to use a single cache + run: | + sudo apt-get install gettext + source env/bin/activate + poetry run python -m pip install setuptools -U + poetry install -E mysql -E pgsql + python tools/install_bootstrap.py + - name: Prepare files for test run + continue-on-error: true + run: | + cp .env.example .env + source env/bin/activate + poetry run python manage.py compilemessages --settings myhpi.settings + poetry run python manage.py collectstatic --settings myhpi.settings - - name: Setup postgres - uses: harmon758/postgresql-action@v1 - with: - postgresql version: '11' # See https://hub.docker.com/_/postgres for available versions - postgresql db: myHPI - postgresql user: user - postgresql password: pass - if: matrix.database == 'postgres' + - name: Setup postgres + uses: harmon758/postgresql-action@v1 + with: + postgresql version: "11" # See https://hub.docker.com/_/postgres for available versions + postgresql db: myHPI + postgresql user: user + postgresql password: pass + if: matrix.database == 'postgres' - - name: Setup postgres dependency - run: | - source env/bin/activate - poetry run python -m pip install psycopg2 - if: matrix.database == 'postgres' + - name: Setup postgres dependency + run: | + source env/bin/activate + poetry run python -m pip install psycopg2 + if: matrix.database == 'postgres' - #- name: Migrate db - # run: | - # source env/bin/activate - # # python manage.py migrate --run-syncdb + #- name: Migrate db + # run: | + # source env/bin/activate + # # python manage.py migrate --run-syncdb - - name: Test apps - env: - DATABASE_URL: ${{ matrix.database_url }} - run: | - source env/bin/activate - poetry run coverage run --source=myhpi manage.py test myhpi.tests - - name: Coveralls - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - source env/bin/activate - pip install coveralls - coveralls --service=github - if: matrix.python-version == '3.9' && matrix.database == 'sqlite' + - name: Test apps + env: + DATABASE_URL: ${{ matrix.database_url }} + run: | + source env/bin/activate + poetry run coverage run --source=myhpi manage.py test myhpi.tests + - name: Coveralls + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + source env/bin/activate + pip install coveralls + coveralls --service=github + if: matrix.python-version == '3.9' && matrix.database == 'sqlite' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a2c04548..b530472f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,16 +20,16 @@ repos: hooks: - id: black - repo: https://github.com/rtts/djhtml - rev: '3.0.6' + rev: "3.0.6" hooks: - id: djhtml - id: djcss - id: djjs - - repo: local - hooks: - - id: prettier-eslint - name: Prettier and ESLint - entry: prettier-eslint --write --list-different - language: node - types_or: [javascript, css, markdown, yaml] - additional_dependencies: ["prettier-eslint-cli@8.0.1"] +# - repo: local +# hooks: +# - id: prettier-eslint +# name: Prettier and ESLint +# entry: prettier-eslint --write --list-different +# language: node +# types_or: [javascript, css, markdown, yaml] +# additional_dependencies: ["prettier-eslint-cli@8.0.1"] diff --git a/myhpi/core/models.py b/myhpi/core/models.py index be5c5e01..80c20be6 100644 --- a/myhpi/core/models.py +++ b/myhpi/core/models.py @@ -1,4 +1,3 @@ -from collections import defaultdict from datetime import date from django import forms diff --git a/myhpi/core/templates/core/minutes_list.html b/myhpi/core/templates/core/minutes_list.html index e69de29b..9175c232 100644 --- a/myhpi/core/templates/core/minutes_list.html +++ b/myhpi/core/templates/core/minutes_list.html @@ -0,0 +1,87 @@ +{% extends "base.html" %} +{% load bootstrap_icons %} +{% load wagtailcore_tags %} +{% load i18n %} + +{% block content %} +

+ + {{ page.title }} {{ selected_year }} + +

+ {% if minutes %} + + {% for minute in minutes %} + + + + + + + + {% endfor %} +
{{ minute.date|date:"d.m.Y" }} + {% if minute.has_unpublished_changes %} + + {% endif %} + + {% include "core/label.html" with minutes=minute %} + {{ minute.title }} + {% if minute.attachments.count > 0 %} + + {% bs_icon "paperclip" %} + + {% endif %} +
+ {% else %} +

+ {% translate "No minutes available for this year." %} +

+ {% endif %} + {% if all_years|length > 1 %} + + {% endif %} +{% endblock %} diff --git a/myhpi/core/templatetags/core_extras.py b/myhpi/core/templatetags/core_extras.py index 2ca5cb17..db4ab154 100644 --- a/myhpi/core/templatetags/core_extras.py +++ b/myhpi/core/templatetags/core_extras.py @@ -3,7 +3,6 @@ from django import template from django.template import Context, Template -from myhpi import settings from myhpi.core.markdown.utils import render_markdown from myhpi.core.models import Footer diff --git a/myhpi/polls/models.py b/myhpi/polls/models.py index d1ee293b..3eac974b 100644 --- a/myhpi/polls/models.py +++ b/myhpi/polls/models.py @@ -1,11 +1,10 @@ import datetime import heapq -import math from django.contrib import messages from django.contrib.auth.models import Group, User from django.db import DatabaseError, IntegrityError, models, transaction -from django.db.models import F, Sum +from django.db.models import Sum from django.shortcuts import redirect from django.utils.safestring import mark_safe from django.utils.translation import gettext as _ diff --git a/myhpi/static/css/myHPI_admin.css b/myhpi/static/css/myHPI_admin.css index 39433823..f9cd6656 100644 --- a/myhpi/static/css/myHPI_admin.css +++ b/myhpi/static/css/myHPI_admin.css @@ -1,26 +1,32 @@ -.action-set-privacy, .privacy-indicator { - display: none; +.action-set-privacy, +.privacy-indicator { + display: none; } -.editor-toolbar.fullscreen, .CodeMirror-fullscreen { - left: 200px !important ; - bottom: 40px !important; +.editor-toolbar.fullscreen, +.CodeMirror-fullscreen { + left: 200px !important ; + bottom: 40px !important; } -@media(prefers-color-scheme: dark) { - .select2-container--default .select2-selection--single .select2-selection__rendered { - color: white !important; - background-color: inherit !important; - } +@media (prefers-color-scheme: dark) { + .select2-container--default + .select2-selection--single + .select2-selection__rendered { + color: white !important; + background-color: inherit !important; + } - .select2-container--default .select2-selection--single, - .select2-container--default .select2-selection--multiple, - .select2-container--default .select2-selection--multiple .select2-selection__choice, - .select2-container--default .select2-results__option[aria-selected="true"] { - background-color: inherit !important; - } + .select2-container--default .select2-selection--single, + .select2-container--default .select2-selection--multiple, + .select2-container--default + .select2-selection--multiple + .select2-selection__choice, + .select2-container--default .select2-results__option[aria-selected="true"] { + background-color: inherit !important; + } - .select2-dropdown { - background-color: black !important; - } + .select2-dropdown { + background-color: black !important; + } } diff --git a/myhpi/static/css/text_array_widget.css b/myhpi/static/css/text_array_widget.css index 83a7547d..7d440665 100644 --- a/myhpi/static/css/text_array_widget.css +++ b/myhpi/static/css/text_array_widget.css @@ -1,28 +1,28 @@ -.text-array>div { - display: flex; - align-items: center; - margin: 5px 0; +.text-array > div { + display: flex; + align-items: center; + margin: 5px 0; } -.text-array>div>span { - background-color: palevioletred; - color: white; - height: 2em; - width: 2em; - border-radius: 1em; - margin-left: 8px; - display: flex; - align-items: center; - justify-content: center; +.text-array > div > span { + background-color: palevioletred; + color: white; + height: 2em; + width: 2em; + border-radius: 1em; + margin-left: 8px; + display: flex; + align-items: center; + justify-content: center; } .text-array-button { - background-color: limegreen; - color: white; - height: 2em; - width: 2em; - border-radius: 1em; - display: flex; - align-items: center; - justify-content: center; + background-color: limegreen; + color: white; + height: 2em; + width: 2em; + border-radius: 1em; + display: flex; + align-items: center; + justify-content: center; } diff --git a/myhpi/static/js/admin/easymde_custom.js b/myhpi/static/js/admin/easymde_custom.js index 4671aac6..2d914283 100644 --- a/myhpi/static/js/admin/easymde_custom.js +++ b/myhpi/static/js/admin/easymde_custom.js @@ -1,177 +1,188 @@ -window.wagtailMarkdown = {}; +window.wagtailMarkdown = {} window.wagtailMarkdown.options = { - spellChecker: false, - toolbar: ["bold", "italic", "heading-1", "heading-2", "unordered-list", - { - name: "start meeting", - action: startMeeting, - className: "fa fa-play", // Look for a suitable icon - title: "Start or continue meeting (Ctrl/Cmd-Alt-R)", + spellChecker: false, + toolbar: [ + "bold", + "italic", + "heading-1", + "heading-2", + "unordered-list", + { + name: "start meeting", + action: startMeeting, + className: "fa fa-play", // Look for a suitable icon + title: "Start or continue meeting (Ctrl/Cmd-Alt-R)", + }, + { + name: "end meeting", + action: endMeeting, + className: "fa fa-stop", + title: "End meeting", + }, + { + name: "pause", + action: pauseMeeting, + className: "fa fa-pause", + title: "Pause meeting", + }, + { + name: "enter", + action: enterMeeting, + className: "fa fa-user-plus", + title: "Enter the meeting", + }, + { + name: "leave", + action: leaveMeeting, + className: "fa fa-user-times", + title: "Leave the meeting", + }, + { + name: "quorum", + action: addQuorum, + className: "fa fa-users", + title: "Add quorum text", + }, + { + name: "resolution", + action: addResolution, + className: "fa fa-euro", + title: "Add resolution", + }, + { + name: "Internal link", + action: function (editor) { + ModalWorkflow({ + onError: function (error) { + console.log(error) + }, + url: "/admin/choose-page/", + onload: PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS, + responses: { + pageChosen: function (t) { + editor.codemirror.replaceSelection( + "[" + t.title + "](page:" + t.id + ")", + ) }, - { - name: "end meeting", - action: endMeeting, - className: "fa fa-stop", - title: "End meeting" + }, + }) + }, + className: "fa fa-link", + title: "Add internal link", + }, + { + name: "Image", + action: function (editor) { + ModalWorkflow({ + onError: function (error) { + console.log(error) + }, + url: "/admin/images/chooser/", + onload: IMAGE_CHOOSER_MODAL_ONLOAD_HANDLERS, + responses: { + chosen: function (t) { + editor.codemirror.replaceSelection( + "![" + + t.title + + "](image:" + + t.id + + ",class=rendered-image,filter=width-800)", + ) }, - { - name: "pause", - action: pauseMeeting, - className: "fa fa-pause", - title: "Pause meeting" - }, - { - name: "enter", - action: enterMeeting, - className: "fa fa-user-plus", - title: "Enter the meeting" - }, - { - name: "leave", - action: leaveMeeting, - className: "fa fa-user-times", - title: "Leave the meeting" - }, - { - name: "quorum", - action: addQuorum, - className: "fa fa-users", - title: "Add quorum text" - }, - { - name: "resolution", - action: addResolution, - className: "fa fa-euro", - title: "Add resolution" - }, - { - name: "Internal link", - action: function (editor) { - ModalWorkflow({ - onError: function (error) { - console.log(error) - }, - url: "/admin/choose-page/", - onload: PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS, - responses: { - pageChosen: function (t) { - editor.codemirror.replaceSelection("[" + t.title + "](page:" + t.id + ")"); - } - }, - - }) - }, - className: "fa fa-link", - title: "Add internal link" - }, - { - name: "Image", - action: function (editor) { - ModalWorkflow({ - onError: function (error) { - console.log(error) - }, - url: "/admin/images/chooser/", - onload: IMAGE_CHOOSER_MODAL_ONLOAD_HANDLERS, - responses: { - chosen: function (t) { - editor.codemirror.replaceSelection("![" + t.title + "](image:" + t.id + ",class=rendered-image,filter=width-800)"); - } - }, - - }) - }, - className: "fa fa-image", - title: "Add image" - }, - "fullscreen" - ], - + }, + }) + }, + className: "fa fa-image", + title: "Add image", + }, + "fullscreen", + ], } - // convenience function -function getCurrentTime(){ - return new Date().toLocaleTimeString("de-DE", {timeStyle: 'short'}) +function getCurrentTime() { + return new Date().toLocaleTimeString("de-DE", { timeStyle: "short" }) } // Custom button actions function startMeeting(editor) { - const cm = editor.codemirror; - let output = ''; - const currentTime = getCurrentTime() - - const unfinishedBreak = cm.getValue().match(/\|break\|\((\d+):(\d+)\)\(\)/); - const startedMeeting = cm.getValue().match(/\|start\|\((\d+):(\d+)\)/); - - if (unfinishedBreak) { - const cursor = cm.getSearchCursor(/\|break\|\((\d+):(\d+)\)\(\)/); - cursor.findNext(); - const unfinishedBreakPosition = cursor.from(); - const relativeInsertPosition = unfinishedBreak[0].search(/\(\)/) + 1; - - // set cursor position to absolute insert position - cm.setCursor({line: unfinishedBreakPosition.line, ch: unfinishedBreakPosition.ch + relativeInsertPosition}); - - output = currentTime; - cm.replaceSelection(output); - } - else if (!startedMeeting) { - output = "\n|start|(" + getCurrentTime() + ")"; - cm.replaceSelection(output); - } + const cm = editor.codemirror + let output = "" + const currentTime = getCurrentTime() + + const unfinishedBreak = cm.getValue().match(/\|break\|\((\d+):(\d+)\)\(\)/) + const startedMeeting = cm.getValue().match(/\|start\|\((\d+):(\d+)\)/) + + if (unfinishedBreak) { + const cursor = cm.getSearchCursor(/\|break\|\((\d+):(\d+)\)\(\)/) + cursor.findNext() + const unfinishedBreakPosition = cursor.from() + const relativeInsertPosition = unfinishedBreak[0].search(/\(\)/) + 1 + + // set cursor position to absolute insert position + cm.setCursor({ + line: unfinishedBreakPosition.line, + ch: unfinishedBreakPosition.ch + relativeInsertPosition, + }) + + output = currentTime + cm.replaceSelection(output) + } else if (!startedMeeting) { + output = "\n|start|(" + getCurrentTime() + ")" + cm.replaceSelection(output) + } } function endMeeting(editor) { - const cm = editor.codemirror; - let output = ''; + const cm = editor.codemirror + let output = "" - output = "\n|end|(" + getCurrentTime() + ")"; - cm.replaceSelection(output); + output = "\n|end|(" + getCurrentTime() + ")" + cm.replaceSelection(output) } -function pauseMeeting(editor){ - const cm = editor.codemirror; - let output = ''; +function pauseMeeting(editor) { + const cm = editor.codemirror + let output = "" - output = "\n|break|(" + getCurrentTime() + ")()"; - cm.replaceSelection(output); + output = "\n|break|(" + getCurrentTime() + ")()" + cm.replaceSelection(output) } -function enterMeeting(editor){ - const cm = editor.codemirror; - let output = ''; +function enterMeeting(editor) { + const cm = editor.codemirror + let output = "" - output = "\n|enter|(" + getCurrentTime() + ")()"; - cm.replaceSelection(output); + output = "\n|enter|(" + getCurrentTime() + ")()" + cm.replaceSelection(output) } -function leaveMeeting(editor){ - const cm = editor.codemirror; - let output = ''; +function leaveMeeting(editor) { + const cm = editor.codemirror + let output = "" - output = "\n|leave|(" + getCurrentTime() + ")()"; - cm.replaceSelection(output); + output = "\n|leave|(" + getCurrentTime() + ")()" + cm.replaceSelection(output) } -function addQuorum(editor){ - const cm = editor.codemirror; - let output = ''; +function addQuorum(editor) { + const cm = editor.codemirror + let output = "" - output = "\n|quorum|(/)"; - cm.replaceSelection(output); + output = "\n|quorum|(/)" + cm.replaceSelection(output) } -function addResolution(editor){ - const cm = editor.codemirror; +function addResolution(editor) { + const cm = editor.codemirror - const output = "\n|resolution|()()() [||]"; - cm.replaceSelection(output); + const output = "\n|resolution|()()() [||]" + cm.replaceSelection(output) - // move the cursor to the first missing field - const position = cm.getCursor(); - position.ch = "|resolution|(".length; - cm.focus(); - cm.setCursor(position); + // move the cursor to the first missing field + const position = cm.getCursor() + position.ch = "|resolution|(".length + cm.focus() + cm.setCursor(position) } diff --git a/myhpi/static/js/admin/text_array_widget.js b/myhpi/static/js/admin/text_array_widget.js index 5cd07745..2d2530a6 100644 --- a/myhpi/static/js/admin/text_array_widget.js +++ b/myhpi/static/js/admin/text_array_widget.js @@ -1,51 +1,55 @@ -const addTextArrayItem = function(id) { - // create a new item - const container = document.getElementById(`text-array-${id}`); - const wrapper = document.createElement('div'); - const input = document.createElement('input'); - const remover = document.createElement('span'); - wrapper.append(input); - wrapper.append(remover); - container.append(wrapper); - input.type = 'text'; - // prevent submission on enter - input.addEventListener('keydown', function(event) { - onTextArrayItemInputKeyDown(id, event, this); - }); - // synchronize AFTER the character has been written - input.addEventListener('keyup', function() { - synchronizeTextArray(id); - }); - remover.innerText = 'X'; - remover.addEventListener('click', function() { - removeTextArrayItem(id, this); - }); - synchronizeTextArray(id); - return input; -}; +const addTextArrayItem = function (id) { + // create a new item + const container = document.getElementById(`text-array-${id}`) + const wrapper = document.createElement("div") + const input = document.createElement("input") + const remover = document.createElement("span") + wrapper.append(input) + wrapper.append(remover) + container.append(wrapper) + input.type = "text" + // prevent submission on enter + input.addEventListener("keydown", function (event) { + onTextArrayItemInputKeyDown(id, event, this) + }) + // synchronize AFTER the character has been written + input.addEventListener("keyup", function () { + synchronizeTextArray(id) + }) + remover.innerText = "X" + remover.addEventListener("click", function () { + removeTextArrayItem(id, this) + }) + synchronizeTextArray(id) + return input +} const removeTextArrayItem = function (id, inputNode) { - inputNode.parentNode.remove(); - synchronizeTextArray(id); -}; + inputNode.parentNode.remove() + synchronizeTextArray(id) +} -const onTextArrayItemInputKeyDown = function(id, event, inputNode) { - if (event.key === 'Enter') { - // prevent form from being submitted - event.preventDefault(); - // add new item - addTextArrayItem(id).focus(); - } - synchronizeTextArray(id); -}; +const onTextArrayItemInputKeyDown = function (id, event, inputNode) { + if (event.key === "Enter") { + // prevent form from being submitted + event.preventDefault() + // add new item + addTextArrayItem(id).focus() + } + synchronizeTextArray(id) +} -const synchronizeTextArray = function(id) { - const container = document.getElementById(`text-array-${id}`); - const syncInput = document.getElementById(`id-${id}`); - const inputs = container.querySelectorAll('input'); - // get all items, filter for empty inputs and write serialized list to dedicated input - const items = Array.from(inputs) - .map(function (input) { return input.value.trim() }) - .filter(function (item) { return item.length !== 0 }); - syncInput.value = JSON.stringify(items); +const synchronizeTextArray = function (id) { + const container = document.getElementById(`text-array-${id}`) + const syncInput = document.getElementById(`id-${id}`) + const inputs = container.querySelectorAll("input") + // get all items, filter for empty inputs and write serialized list to dedicated input + const items = Array.from(inputs) + .map(function (input) { + return input.value.trim() + }) + .filter(function (item) { + return item.length !== 0 + }) + syncInput.value = JSON.stringify(items) } diff --git a/myhpi/static/js/print_processor.js b/myhpi/static/js/print_processor.js index 91090e0c..17e502f4 100644 --- a/myhpi/static/js/print_processor.js +++ b/myhpi/static/js/print_processor.js @@ -1,67 +1,69 @@ - function processPageForPrinting() { - moveLinksToFooter(); - expandAbbreviations(); + moveLinksToFooter() + expandAbbreviations() } function moveLinksToFooter() { - let content = document.getElementsByClassName("minutes-text")[0]; - if (!content) return; + let content = document.getElementsByClassName("minutes-text")[0] + if (!content) return - let footer = document.getElementById("minutes-footer"); - let linkList = document.createElement("ol"); + let footer = document.getElementById("minutes-footer") + let linkList = document.createElement("ol") - let articleLinks = content.getElementsByTagName("a"); - for (let i = 0; i < articleLinks.length; i++) { - const articleLink = articleLinks[i]; - articleLink.innerHTML += "[" + (i + 1) + "]"; - let footnote = document.createElement("li"); - footnote.innerText = articleLink.href; - linkList.appendChild(footnote); - } - footer.appendChild(linkList); + let articleLinks = content.getElementsByTagName("a") + for (let i = 0; i < articleLinks.length; i++) { + const articleLink = articleLinks[i] + articleLink.innerHTML += + "[" + (i + 1) + "]" + let footnote = document.createElement("li") + footnote.innerText = articleLink.href + linkList.appendChild(footnote) + } + footer.appendChild(linkList) } function expandAbbreviations() { - let content = document.getElementsByClassName("minutes-text")[0]; - if (!content) return; - let abbreviations = content.getElementsByTagName("abbr"); - for (let i = 0; i < abbreviations.length; i++) { - let short = abbreviations[i].innerText; - let long = abbreviations[i].getAttribute("title"); + let content = document.getElementsByClassName("minutes-text")[0] + if (!content) return + let abbreviations = content.getElementsByTagName("abbr") + for (let i = 0; i < abbreviations.length; i++) { + let short = abbreviations[i].innerText + let long = abbreviations[i].getAttribute("title") - let replacement = document.createElement("span"); - replacement.classList.add("print-expanded-abbr"); - replacement.setAttribute("short", short); - replacement.setAttribute("long", long); - replacement.innerText = long; - abbreviations[i].parentNode.replaceChild(replacement, abbreviations[i]); - } + let replacement = document.createElement("span") + replacement.classList.add("print-expanded-abbr") + replacement.setAttribute("short", short) + replacement.setAttribute("long", long) + replacement.innerText = long + abbreviations[i].parentNode.replaceChild(replacement, abbreviations[i]) + } } function removePrintingProcessing() { - let content = document.getElementsByClassName("minutes-text")[0]; - if (!content) return; - let footer = document.getElementById("minutes-footer"); - footer.innerText = ""; + let content = document.getElementsByClassName("minutes-text")[0] + if (!content) return + let footer = document.getElementById("minutes-footer") + footer.innerText = "" - let generated = document.getElementsByClassName('print-generated-tag'); - while (generated.length > 0) { - generated[0].remove(); - } + let generated = document.getElementsByClassName("print-generated-tag") + while (generated.length > 0) { + generated[0].remove() + } - let abbreviationReplacements = content.getElementsByClassName("print-expanded-abbr"); - for (let i = 0; i < abbreviationReplacements.length; i++) { - let replacement = abbreviationReplacements[i]; - let short = replacement.getAttribute("short"); - let long = replacement.getAttribute("long"); + let abbreviationReplacements = content.getElementsByClassName( + "print-expanded-abbr", + ) + for (let i = 0; i < abbreviationReplacements.length; i++) { + let replacement = abbreviationReplacements[i] + let short = replacement.getAttribute("short") + let long = replacement.getAttribute("long") - let abbr = document.createElement("abbr"); - abbr.setAttribute("title", long); - abbr.innerText = short; - replacement.parentNode.replaceChild(abbr, replacement); - } + let abbr = document.createElement("abbr") + abbr.setAttribute("title", long) + abbr.innerText = short + replacement.parentNode.replaceChild(abbr, replacement) + } } -addEventListener("beforeprint", processPageForPrinting); -addEventListener("afterprint", removePrintingProcessing); +addEventListener("beforeprint", processPageForPrinting) +addEventListener("afterprint", removePrintingProcessing) diff --git a/myhpi/static/js/search.js b/myhpi/static/js/search.js index b6607fc8..e8adf248 100644 --- a/myhpi/static/js/search.js +++ b/myhpi/static/js/search.js @@ -1,28 +1,28 @@ const initializeSearch = () => { - const searchModal = document.querySelector("#searchModal") + const searchModal = document.querySelector("#searchModal") - /** - * Focus input in search modal when showing. - * We use both events: - * - `show.bs.modal` focuses immediately (with a short delay), so the user can start typing before the popup is completely visible - * - `shown.bs.modal` refocuses the input once the popup is completely visible. Without, the input would loose its focus at that point - */ - searchModal.addEventListener("show.bs.modal", () => - setTimeout(() => document.querySelector("#searchInput").focus(), 200) - ) - searchModal.addEventListener("shown.bs.modal", () => - document.querySelector("#searchInput").focus() - ) + /** + * Focus input in search modal when showing. + * We use both events: + * - `show.bs.modal` focuses immediately (with a short delay), so the user can start typing before the popup is completely visible + * - `shown.bs.modal` refocuses the input once the popup is completely visible. Without, the input would loose its focus at that point + */ + searchModal.addEventListener("show.bs.modal", () => + setTimeout(() => document.querySelector("#searchInput").focus(), 200), + ) + searchModal.addEventListener("shown.bs.modal", () => + document.querySelector("#searchInput").focus(), + ) - document.addEventListener("keydown", (event) => { - if (event.ctrlKey && event.key === "k") { - showSearchModal() - event.preventDefault() - } - }) + document.addEventListener("keydown", (event) => { + if (event.ctrlKey && event.key === "k") { + showSearchModal() + event.preventDefault() + } + }) } const showSearchModal = () => { - const searchModal = document.querySelector("#searchModal") - bootstrap.Modal.getOrCreateInstance(searchModal).show() + const searchModal = document.querySelector("#searchModal") + bootstrap.Modal.getOrCreateInstance(searchModal).show() } diff --git a/myhpi/static/js/sidebar.js b/myhpi/static/js/sidebar.js index 880f199f..af71bd35 100644 --- a/myhpi/static/js/sidebar.js +++ b/myhpi/static/js/sidebar.js @@ -24,9 +24,9 @@ const initializeSidebar = () => { const applyTocOffcanvasBehaviour = () => { const anchors = document.querySelectorAll("#sidebar-offcanvas a") const offcanvas = new bootstrap.Offcanvas( - document.querySelector("#sidebar-offcanvas") + document.querySelector("#sidebar-offcanvas"), ) anchors.forEach((anchor) => - anchor.addEventListener("click", () => offcanvas.hide()) + anchor.addEventListener("click", () => offcanvas.hide()), ) } diff --git a/myhpi/static/js/utils.js b/myhpi/static/js/utils.js index bac68b0c..47312736 100644 --- a/myhpi/static/js/utils.js +++ b/myhpi/static/js/utils.js @@ -1,11 +1,11 @@ -/** - * Converts a given rem value into pixel. - * - * @param {number} rem Rem value to convert. - * @returns Number of pixel the given rem value corresponds to. - * - * Source: https://stackoverflow.com/questions/36532307/rem-px-in-javascript - */ -function remToPx(rem) { - return rem * parseFloat(getComputedStyle(document.documentElement).fontSize) -} +/** + * Converts a given rem value into pixel. + * + * @param {number} rem Rem value to convert. + * @returns Number of pixel the given rem value corresponds to. + * + * Source: https://stackoverflow.com/questions/36532307/rem-px-in-javascript + */ +function remToPx(rem) { + return rem * parseFloat(getComputedStyle(document.documentElement).fontSize) +} diff --git a/myhpi/static/scss/myHPI.scss b/myhpi/static/scss/myHPI.scss index 4d1414fa..65dbbbed 100644 --- a/myhpi/static/scss/myHPI.scss +++ b/myhpi/static/scss/myHPI.scss @@ -215,7 +215,7 @@ h1.side-panel-title::after { .search-result { h5 { - hyphens: auto; + hyphens: auto; } transition: all 0.25s; @@ -270,7 +270,7 @@ img { align-items: center; &:hover { - color: white; + color: white; } } @@ -313,7 +313,7 @@ img { .side-panel:first-of-type { border-top: none; padding-top: 0; - + .side-panel-title { display: none; } @@ -345,7 +345,7 @@ img { .page:not(.navbar-myhpi) .page-content { padding-top: 1.5rem; // default padding without height of navbar } - + .sidebar-container ul li>ul { margin-left: 0.5rem; } diff --git a/myhpi/tenca_django/views.py b/myhpi/tenca_django/views.py index 8bfad31f..31096248 100644 --- a/myhpi/tenca_django/views.py +++ b/myhpi/tenca_django/views.py @@ -4,7 +4,7 @@ import tenca.templates from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin -from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseNotFound +from django.http import Http404 from django.shortcuts import redirect from django.urls import reverse from django.utils.translation import gettext as _ diff --git a/myhpi/tests/core/test_email_utils.py b/myhpi/tests/core/test_email_utils.py index 5ac5d74f..d24e411b 100644 --- a/myhpi/tests/core/test_email_utils.py +++ b/myhpi/tests/core/test_email_utils.py @@ -1,6 +1,5 @@ from unittest import TestCase -from myhpi import settings from myhpi.core.utils import ( alternative_emails, email_belongs_to_domain, diff --git a/myhpi/tests/core/test_widgets.py b/myhpi/tests/core/test_widgets.py index 85e69e73..b51a16ec 100644 --- a/myhpi/tests/core/test_widgets.py +++ b/myhpi/tests/core/test_widgets.py @@ -1,5 +1,3 @@ -from dataclasses import dataclass - from django.forms.models import ModelChoiceIteratorValue from myhpi.core.widgets import AttachmentSelectWidget