diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8e4c2d..fe0b770 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,11 +16,11 @@ repos: # - id: check-added-large-files - repo: https://github.com/psf/black - rev: 23.11.0 + rev: 24.1.1 hooks: - id: black - repo: https://github.com/pre-commit/mirrors-prettier - rev: 'v4.0.0-alpha.4' # Use the sha / tag you want to point at + rev: 'v4.0.0-alpha.8' # Use the sha / tag you want to point at hooks: - id: prettier files: "^frontend/" @@ -29,13 +29,13 @@ repos: - "prettier-plugin-svelte@latest" - "prettier@latest" - repo: https://github.com/pycqa/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 # flake8 is passed in all tracked python files # so --exclude in .flake8 does not work exclude: ^migrations/versions/ - repo: https://github.com/fsfe/reuse-tool - rev: v2.1.0 + rev: v3.0.1 hooks: - id: reuse diff --git a/classquiz/helpers/__init__.py b/classquiz/helpers/__init__.py index 0cdefc1..f4b0a1d 100644 --- a/classquiz/helpers/__init__.py +++ b/classquiz/helpers/__init__.py @@ -70,9 +70,10 @@ async def generate_spreadsheet(quiz_results: dict, quiz: Quiz, player_fields: di worksheet.write(i + 1, 1, question["time"]) try: - async with ClientSession() as session, session.get( - f"{settings.root_address}/api/v1/storage/download/{question['image']}" - ) as response: + async with ( + ClientSession() as session, + session.get(f"{settings.root_address}/api/v1/storage/download/{question['image']}") as response, + ): if "image" in response.headers.get("Content-Type"): img_data = BytesIO(await response.read()) worksheet.insert_image(i + 1, 2, question["image"], {"image_data": img_data}) @@ -226,13 +227,16 @@ async def telemetry_ping(): except ormar.exceptions.NoMatch: instance_data = InstanceData() await instance_data.save() - async with ClientSession() as session, session.post( - f"https://cit.mawoka.eu.org/public/{instance_data.instance_id}", - json={ - "public_quizzes": await Quiz.objects.filter(public=True).count(), - "private_quizzes": await Quiz.objects.filter(public=False).count(), - "users": await User.objects.count(), - }, + async with ( + ClientSession() as session, + session.post( + f"https://cit.mawoka.eu.org/public/{instance_data.instance_id}", + json={ + "public_quizzes": await Quiz.objects.filter(public=True).count(), + "private_quizzes": await Quiz.objects.filter(public=False).count(), + "users": await User.objects.count(), + }, + ), ): return diff --git a/classquiz/helpers/pixabay.py b/classquiz/helpers/pixabay.py index bcf07ae..cbc193e 100644 --- a/classquiz/helpers/pixabay.py +++ b/classquiz/helpers/pixabay.py @@ -125,9 +125,10 @@ class NotFoundError(Exception): async def get_images(api_key: str, params: GetImagesParams) -> GetImagesResponse: - async with ClientSession() as session, session.get( - "https://pixabay.com/api/", params={"key": api_key, **params.dict()} - ) as resp: + async with ( + ClientSession() as session, + session.get("https://pixabay.com/api/", params={"key": api_key, **params.dict()}) as resp, + ): if resp.status == 200: return GetImagesResponse.parse_obj(await resp.json()) else: diff --git a/classquiz/kahoot_importer/get.py b/classquiz/kahoot_importer/get.py index 4aa02dc..96a1819 100644 --- a/classquiz/kahoot_importer/get.py +++ b/classquiz/kahoot_importer/get.py @@ -15,9 +15,10 @@ class _Response(BaseModel): async def get(game_id: str) -> _Response | int: - async with ClientSession() as session, session.get( - f"https://create.kahoot.it/rest/kahoots/{game_id}/card/?includeKahoot=true" - ) as response: + async with ( + ClientSession() as session, + session.get(f"https://create.kahoot.it/rest/kahoots/{game_id}/card/?includeKahoot=true") as response, + ): if response.status == 200: return _Response(**await response.json()) else: diff --git a/classquiz/kahoot_importer/search.py b/classquiz/kahoot_importer/search.py index 1f263d7..201dac7 100644 --- a/classquiz/kahoot_importer/search.py +++ b/classquiz/kahoot_importer/search.py @@ -35,7 +35,10 @@ async def search( :param limit: Less or equals 100 :return: """ - async with ClientSession() as session, session.get( - f"https://create.kahoot.it/rest/kahoots/?query={query}&limit={limit}&cursor={cursor}&searchCluster={search_cluster}&includeExtendedCounters=false&inventoryItemId={inventory_item_id}" # noqa : E501 - ) as response: + async with ( + ClientSession() as session, + session.get( + f"https://create.kahoot.it/rest/kahoots/?query={query}&limit={limit}&cursor={cursor}&searchCluster={search_cluster}&includeExtendedCounters=false&inventoryItemId={inventory_item_id}" # noqa : E501 + ) as response, + ): return _Response(**await response.json()) diff --git a/classquiz/routers/eximport.py b/classquiz/routers/eximport.py index 641be11..6c289d3 100644 --- a/classquiz/routers/eximport.py +++ b/classquiz/routers/eximport.py @@ -62,9 +62,10 @@ async def export_quiz(quiz_id: uuid.UUID, user: User = Depends(get_current_user) for image_key in image_urls: bin_data = bin_data + image_delimiter + str(image_key).encode("utf-8") + image_index_delimiter image_data = None - async with ClientSession() as session, session.get( - f"{settings.root_address}/api/v1/storage/download/{image_urls[image_key]}" - ) as resp: + async with ( + ClientSession() as session, + session.get(f"{settings.root_address}/api/v1/storage/download/{image_urls[image_key]}") as resp, + ): image_data = await resp.read() bin_data = bin_data + image_data diff --git a/frontend/src/lib/i18n/locales/fr.json b/frontend/src/lib/i18n/locales/fr.json index b4fa602..b139ce2 100644 --- a/frontend/src/lib/i18n/locales/fr.json +++ b/frontend/src/lib/i18n/locales/fr.json @@ -130,7 +130,7 @@ "index_page": { "slogan": "La plateforme de quiz open-source !", "meta": { - "description": "ClassQuiz est une application de quiz permettant d'apprendre de manière interactive pour les étudiants, libre et open-source", + "description": "ClassQuiz est une application d'apprentissage interactif destiné aux étudiants, elle est open-source et gratuite", "title": "Accueil" }, "features_description": { @@ -139,7 +139,7 @@ "3": "L'éditeur et la fonction d'export des résultats de quiz en format Excel sont des fonctionnalités remarquables du logiciel." }, "stats": "Il y a déjà {{user_count}} utilisateurs et {{quiz_count}} quiz sur ClassQuiz.", - "see_what_true_and_false": "Voir ce qui est vrai ou faux", + "see_what_true_and_false": "Voir ce qui était vrai ou faux", "see_how_many_true_and_false": "Voir combien avaient vrai ou faux", "create_or_import": "Créer ou Importer", "see_all_quizzes": "Voir tous vos quiz", diff --git a/frontend/src/lib/i18n/locales/ja.json b/frontend/src/lib/i18n/locales/ja.json new file mode 100644 index 0000000..c3f4ba6 --- /dev/null +++ b/frontend/src/lib/i18n/locales/ja.json @@ -0,0 +1,162 @@ +{ + "index_page": { + "slogan": "オープンソースのクイズプラットフォーム!", + "stats": "ClassQuizには {{user_count}} のユーザと {{quiz_count}} のクイズがあります.", + "see_what_true_and_false": "正しいか、間違っていたかを見る", + "see_all_quizzes": "あなたのクイズをすべて見る", + "teachers_site": "教師のサイト", + "students_site": "学生のサイト", + "user_friendly": "ユーザーフレンドリー", + "quiz_results_downloadable": "Quizの結果をダウンロードできます", + "select_answer": "回答を選択", + "choose_answer_wisely": "回答を賢く選択", + "view_results": "結果を見る", + "get_ranking_and_winners": "ランキングを取得し、誰が勝ったかを確認する", + "completely_free_content": "ClassQuizは、有料プランはありません。迷惑なアップグレードページへのリダイレクトもなく、完全に無料ですが、 寄付は非常に嬉しいです。", + "download_quizzes": "クイズをダウンロード", + "community_driven": "コミュニティ主導", + "german_server_content": "ClassQuizのサーバーはドイツにあり、netcupでホストされています.", + "meta": { + "description": "ClassQuizはオープンソースかつ無料で利用できる学生のための対話型学習クイズアプリです", + "title": "ホーム" + }, + "see_how_many_true_and_false": "何が正しいか間違っていたかを見る", + "create_or_import": "作成またはインポート", + "no_tracking": "追跡なし", + "self_hostable": "セルフホスト可能", + "german_server": "ドイツのサーバー", + "completely_free": "完全に費用は無料", + "get_a_quiz": "1. クイズを入手", + "create_a_quiz_from_scratch": "写真などを含むクイズを最初からエディターで作成", + "download_quizzes_content": "クイズは1つのファイルとしてダウンロードし、いつでもインポートすることができます。 また、クイズを他のClassQuizインスタンスに移動することもできます.", + "community_driven_content": "ClassQuizは、資金調達、テストアイデア、機能リクエスト、翻訳などのコミュニティに依存しています。 あなたもClassQuizコミュニティの一員になることもできます!", + "how_does_classquiz_work": "ClassQuizはどのように機能しますか?", + "no_player_limit": "プレーヤーの制限なし", + "no_player_limit_content": "ClassQuizは意図的なプレーヤーの上限はありません。 理論的には1000人以上のプレーヤーが同じクイズを再生することができます。但し1つのクイズにつき最大300人でテストされています.", + "check_if_chosen_wisely": "賢く選んだら、チェック", + "list_winners": "受賞者一覧", + "why_classquiz": "なぜ ClassQuizなのか?", + "self_hostable_content": "ClassQuizは簡単にセルフホストすることができます。つまりデータはあなたがだけが管理できます!", + "user_friendly_content": "ClassQuizはシンプルであることを目指しているので、誰もが使うことができます.", + "quiz_results_downloadable_content": "Quiz-resultsはExcelスプレッドシートに簡単にエクスポートできます.", + "multilingual_content": "ClassQuizは、英語、フランス語、ドイツ語、イタリア語、ノルウェーのBokmål、トルコ語、一部インドネシア語、カタロニア語で利用できます.", + "multilingual": "多言語に対応", + "dark_mode": "ダークモード" + }, + "edit_page": { + "success_update_title": "クイズを更新しました.", + "success_update_body": "だれでも不満を期待しません." + }, + "create_page": { + "success": { + "title": "作成したクイズ.", + "body": "ゲームを始めましょう." + } + }, + "login_page": { + "modal": { + "error": { + "description": { + "unexpected": "古い予期しないエラーがあった!", + "wrong_creds": "パスワードとメールアドレスが正しいことを確認してください." + }, + "wrong_creds": "メールアドレスまたはパスワードが間違っています." + }, + "success": { + "description": { + "success_check_mail": "リンクからログインして、メールの受信トレイを確認してください." + } + } + }, + "use_backup_code": "バックアップコードを使用する", + "login_or_create_account": "ログインまたはアカウントを作成する" + }, + "import_page": { + "download_template_here": "テンプレートをダウンロード", + "classquiz_quiz": "ClassQuizクイズ", + "upload_file_ending": ".cqa または .xlsx で終わるファイルをアップロードする", + "this_side_classquiz": "ここでは、ClassQuizからエクスポートされたクイズをインポートできます." + }, + "admin_page": { + "already_registered_as_admin": "すでにこのゲームには登録済みの管理者がいます.", + "get_results": "結果を得る", + "show_next_question": "次の質問を表示", + "no_answers": "回答なし!", + "stop_time": "停止時間", + "enter_answer_into_field": "入力欄に回答を入力してください!", + "request_export_results": "リクエスト結果のダウンロード", + "export_results": "エクスポート結果", + "start_game": "ゲーム開始", + "get_results_and_stop_time": "結果を取得し、時間を停止する", + "get_final_results": "最終結果を得る", + "start_by_showing_first_question": "最初の質問を表示し始めます.", + "next_question": "次の質問 ({{question}})", + "show_results": "結果を表示", + "stop_time_and_solutions": "時間を停止し、解決を示す", + "answers_submitted": "{{answer_count}} 提出された回答" + }, + "settings_page": { + "delete_this_session": "このセッションを削除", + "this_session?": "このセッションは?", + "change_avatar": "アバターの変更", + "last_seen": "最終確認", + "add_api_key": "APIキーの追加" + }, + "explore_page": { + "made_by": "作者:" + }, + "play_page": { + "1st_place": "第1位", + "2nd_place": "第2位", + "3rd place": "第3位", + "with_out_of": "{{total_question_count}}問中 {{correct_questions}} 問", + "your_score": "あなたのスコア: {{score}}", + "your_place": "あなたは第 {{place}}位!", + "join_by_entering_code": "以下のコードを入力してください", + "points_added": "ポイント追加" + }, + "editor_page": { + "add_an_answer": "回答を追加する", + "right_click_to_delete": "右クリックで回答削除!" + }, + "footer": { + "more_details_here": "詳細はこちら", + "self_ads": "{{mawoka_link}} と {{others_link}} の助けを借りて ❤️ で作られました." + }, + "uploader": { + "upload_video": "アップロードされた動画", + "images_by_pixabay": "Pixabayが提供する画像", + "search_english_only": "検索は英語のみ機能", + "select_upload_type": "アップロードタイプを選択", + "upload_a_video": "動画をアップロード", + "upload_video_popup_notice": "ポップアップが開きます。 詳細については、それを見てください" + }, + "avatar_settings": { + "skin_color": "肌の色", + "hair_color": "髪の色", + "hat_color": "帽子の色", + "clothe_color": "衣装の色" + }, + "results_page": { + "date_played": "プレイされた日付", + "quiz_title": "クイズタイトル", + "player_count": "プレイヤー数" + }, + "overview_page": { + "created_at": "によって作成されました", + "question_count": "質問のカウント", + "no_quizzes": "「作成」ボタンをクリックするか、クイズをインポートして下さい." + }, + "words": { + "game_pin": "ゲームPIN", + "backup_code": "バックアップコード" + }, + "search_page": { + "at_least_3_characters": "少なくとも3文字を入力してください.", + "nothing_here": "ここには何もない..." + }, + "dashboard": { + "search_for_own_quizzes": "あなた自身のクイズを検索", + "commandpalette_notice": "Ctr+kまたはMeta+kを押してコマンドパレットを開く" + } +} diff --git a/frontend/src/lib/i18n/locales/pl.json b/frontend/src/lib/i18n/locales/pl.json index a705dfd..79b1b35 100644 --- a/frontend/src/lib/i18n/locales/pl.json +++ b/frontend/src/lib/i18n/locales/pl.json @@ -49,7 +49,9 @@ "multilingual_content": "ClassQuiz jest dostępny w języku angielskim, francuskim, niemieckim, włoskim, norweskim bokmål, tureckim oraz częściowo indonezyjskim i katalońskim.", "quiz_results_downloadable_content": "Wyniki quizu można łatwo wyeksportować do arkusza kalkulacyjnego Excel. (Nie wiem, dlaczego inni nie mogli tego zrobić)", "dark_mode_content": "Jedna z najważniejszych funkcji, jakie może mieć strona internetowa!", - "how_does_classquiz_work": "Jak działa ClassQuiz?" + "how_does_classquiz_work": "Jak działa ClassQuiz?", + "no_player_limit": "Brak limitu graczy", + "no_player_limit_content": "ClassQuiz nie ma sztucznego limitu graczy. Teoretycznie w ten sam quiz może zagrać ponad 1000 graczy, ale w testach wzięło udział maksymalnie 300 graczy." }, "overview_page": { "question_count": "Liczba pytań", @@ -197,7 +199,7 @@ "analytics": "Analityka", "dislike_plural": "Nie lubię", "rating": "Ocena", - "like": "" + "like": "Lubię" }, "settings_page": { "last_seen": "Ostatnio widziany", @@ -226,7 +228,7 @@ "unknown_error_text": "To nie powinno się zdarzyć. To pewnie moja wina, nie twoja, ale może masz magiczną moc niszczenia rzeczy..." }, "uploader": { - "add_image": "Dodaj obraz", + "add_image": "Dodaj media", "select_upload_type": "Wybierz typ przesyłania", "upload_a_video": "Prześlij film", "upload_video_popup_notice": "Wyskakujące okienko jest otwarte; spójrz na nie, aby uzyskać więcej informacji", @@ -261,7 +263,12 @@ "check_choice_description": "Aby zdobyć punkty, należy wybrać wszystkie poprawne odpowiedzi", "order_description": "Odpowiedzi można ustawić w odpowiedniej kolejności", "text_description": "Gracze mogą wprowadzać tekst", - "range_description": "Zakres liczbowy można wybrać za pomocą suwaka" + "range_description": "Zakres liczbowy można wybrać za pomocą suwaka", + "enter_answer": "Wpisz odpowiedź", + "enable_reorder": "Włącz tryb zmiany kolejności", + "need_more_help": "Potrzebujesz pomocy?", + "visit_docs": "Odwiedź dokumentację.", + "disable_reorder": "Wyłącz tryb zmiany kolejności" }, "import_page": { "need_help": "Potrzebujesz pomocy?", @@ -273,7 +280,7 @@ "a_kahoot_quiz": "Quiz Kahoot", "classquiz_quiz": "Quiz ClassQuiz", "download_template_here": "Pobierz szablon", - "this_side_classquiz_excel": "Możesz również przesłać pliki Excel, jeśli wypełniłeś poniższy szablon." + "this_side_classquiz_excel": "Możesz również przesłać pliki Excel, jeśli wypełniłeś poniższy szablon. Jeśli quiz o danym tytule już istnieje, pytania zostaną dołączone do istniejącego quizu." }, "admin_page": { "time_left": "Pozostały czas", @@ -313,7 +320,10 @@ "join_description": "Dołącz na {{url}} i wpisz {{pin}}.", "join_by_entering_code": "Dołącz do nas wpisując następujący kod", "points_added": "Dodane punkty", - "final_result_rank": "{{place}}: {{username}} z {{points}} punktów" + "final_result_rank": "{{place}}: {{username}} z {{points}} punktów", + "your_place": "Jesteś na miejscu {{place}}!", + "players_waiting": "{{count}} graczy oczekuje", + "players_waiting_plural": "{{count}} graczy czeka" }, "explore_page": { "made_by": "Wykonane przez", @@ -358,7 +368,7 @@ "search_for_own_quizzes": "Wyszukaj własne quizy", "views_n_plays": "Widoki i gry", "info_analytics": "\"Odtworzenia\" pokazują tylko, jak często quiz był uruchamiany (w tym ty), podczas gdy \"Wyświetlenia\" liczą, jak często strona \"Wyświetlenia\" była odwiedzana, więc liczy również boty (przepraszam za to).", - "commandpalette_notice": "" + "commandpalette_notice": "Wciśnij Ctr+k lub Meta+k aby otworzyć paletę poleceń" }, "navbar": { "donate": "Darowizna" @@ -384,11 +394,11 @@ "add_security_key": "Dodaj Security-Key", "webauthn_available": "Webauthn jest dostępny", "webauthn": "Webauthn", - "enable_totp": "", - "disable_totp": "", + "enable_totp": "Włącz Totp", + "disable_totp": "Wyłączenie Totp", "totp": "", - "totp_unavailable": "", - "totp_available": "" + "totp_unavailable": "Totp nie jest dostępny", + "totp_available": "Totp jest dostępny" }, "view_quiz_page": { "made_by": "Wykonane przez", @@ -437,18 +447,19 @@ }, "public_user_page": { "joined_on": "Dołączył {{date}}", - "no_original_quizzes": "Ten użytkownik nie ma jeszcze żadnych oryginalnych quizów" + "no_original_quizzes": "Ten użytkownik nie ma jeszcze żadnych oryginalnych quizów", + "user_not_found": "Nie znaleziono użytkownika" }, "file_dashboard": { "not_available": "Niedostępne", - "size": "Rozmiar: {{size}} Mib", + "size": "Rozmiar: {{size}} MiB", "uploaded": "Przesłano: {{date}}", "Imported": "Importowane: {{yes_or_no}}", "edit_details": "Edytuj szczegóły", "delete_image": "Usuń obraz", "edit_the_image": "Edytuj obraz", "filename_word": "Nazwa pliku", - "storage_usage": "Użyłeś {{used}} Mib z {{łącznie}} MiB pamięci. Odpowiada to {{percent}}% twojej przestrzeni dyskowej.", + "storage_usage": "Użyłeś {{used}} MiB z {{łącznie}} MiB pamięci. Odpowiada to {{percent}}% twojej przestrzeni dyskowej.", "imported": "Importowane: {{yes_or_no}}", "missing": "ZAGINIONY!", "unset": "Nieustawiony", @@ -470,5 +481,11 @@ "cancel_update": "Anuluj aktualizację!", "update_from_to": "Aktualizacja z {{current_version}} do {{newest_version}}", "already_latest_version": "Masz już najnowszą wersję" + }, + "downloader": { + "select_download_type": "Wybierz typ pobierania", + "own_format": "Własny format", + "excel_format": "Format Excel", + "help": "Własny format (.cqa) zawiera również obrazki i obsługuje wszystkie typy pytań, podczas gdy format excel obsługuje tylko głosowanie i pytania wielokrotnego wyboru." } }