diff --git a/backend/SermoIA/myapp/views/upload_json.py b/backend/SermoIA/myapp/views/upload_json.py index 90fcbee9e0a5ad1f480e4cb677ef66f105d5834f..b3851f6b7a9f695460f3ddbd200787cc9fb7e2cb 100644 --- a/backend/SermoIA/myapp/views/upload_json.py +++ b/backend/SermoIA/myapp/views/upload_json.py @@ -336,22 +336,58 @@ def correct_questions(language, questions): print() return questions +def initialize_options(options): + option_bools = { + "checkFormat": False, # Check Questionnaire + "correctSpelling": False, + "showTypes": False, + "checkConsistency": False, + "addQuestion": False, + "translate": False, + "gender": False, + "reformulateLinkers": False, + "addOptions": False, + "correctionFinale": False, + "validateAudio": False, # Check User Answer + "generalCorrection": False, + "validateSyntax": False, + "simplify": False, + "checkContext": False, + "checkOptions": False, + "sentimentAnalysis": False, + "checkScale": False, + "generateNewQuestion": False + } + + for key in option_bools.keys(): + if key in options: + option_bools[key] = options[key] + + language = options.get('language', 'fr') # Default to 'FR' if not provided + gender = options.get('gender', 'male') # Default to 'Male' if not provided + + return option_bools, language.lower(), gender.lower() + @csrf_exempt def upload_json(request): if request.method == 'POST': try: clear_tts_outputs() - json_data = json.loads(request.body) + data = json.loads(request.body) + json_data = data.get('jsonData') + options = data.get('options') + option_bools, target_language, target_gender = initialize_options(options) # Phase de vérification du fichier JSON reçu (envoyé par l'utilisateur). # 1) Vérification du format JSON reçu (en fonction du template) -> jsonschema. - start_time = time.time() - is_valid, error_message = validate_json(json_data, get_schema()) - end_time = time.time() - print() - print_text(f"Execution time: {end_time - start_time} seconds") - if not is_valid: - return JsonResponse({'message': f'Invalid JSON: {error_message}'}, status=400) + if option_bools["checkFormat"]: + start_time = time.time() + is_valid, error_message = validate_json(json_data, get_schema()) + end_time = time.time() + print() + print_text(f"Execution time: {end_time - start_time} seconds") + if not is_valid: + return JsonResponse({'message': f'Invalid JSON: {error_message}'}, status=400) # Création du questionnaire à partir du fichier JSON questionnaire = Questionnaire.objects.create( @@ -377,83 +413,88 @@ def upload_json(request): print_text(f"Topic: {topic}") print_text(f"Language: {language}") - # # 2) Corrections orthographiques -> LanguageTool => Secondaire (et ne marche pas super bien) - # corrections = check_questionnaire(json_data.get('questionnaire', {})) - # output_path = os.path.join(default_storage.location, 'language_tool/corrections.json') - # with open(output_path, 'w', encoding='utf-8') as output_file: - # json.dump(corrections, output_file, indent=2, ensure_ascii=False) - - # # # 3a) Affichage simple des types de questions (par la suite tri des questions importantes et inutiles): - # start_time = time.time() - # count_questions_by_category(json_data.get('questionnaire', {}).get('questions', [])) - # end_time = time.time() - # print() - # print_text(f"Execution time: {end_time - start_time} seconds") - - # # # 3b) Tri des questions incohérentes avec le contexte du questionnaire: => Secondaire (marche super bien) - # start_time = time.time() - # questionnaire.data['questionnaire']['questions'] = find_out_of_context_questions(topic, questionnaire.data['questionnaire']['questions']) - # end_time = time.time() - # print() - # print_text(f"Execution time: {end_time - start_time} seconds") - - # # # 4) Ajout de questions pertinentes (avec ordre / liaison et continuité / cohérence avec les questions précédentes) -> GPT-3. - # start_time = time.time() - # # questionnaire.data['questionnaire']['questions'] = generate_new_questions(topic, questions) - # questionnaire.data['questionnaire']['questions'] = generate_new_questions(topic, questionnaire.data['questionnaire']['questions']) - # end_time = time.time() - # print() - # print_text(f"Execution time: {end_time - start_time} seconds") - # num_questions += NEW_QUESTIONS_GENERATED - # questionnaire.initial_size += NEW_QUESTIONS_GENERATED - - # # # 5) Tri des questions selon paramètres de base fournis (âge, langue, sexe, lieu de résidence) -> GPT. - # # # - Filtre - Censuration => Secondaire (pas implémenté) - - # target_language = 'fr' - # if target_language != questionnaire.language: - # # - Traduction JSON - # print_title(f"Translation of the questionnaire with GPT-3.5-turbo ({target_language})") - # start_time = time.time() - # questionnaire_data = translate_questionnaire(questionnaire_data, target_language) - # end_time = time.time() - # print() - # print_text(f"Execution time: {end_time - start_time} seconds") - # questionnaire.data['questionnaire']['language'] = target_language - # questionnaire.language = target_language - - # target_gender = "female" # "male" - # if (target_language == 'fr'): - # # - Genrer le texte (male / female) - # start_time = time.time() - # questionnaire_data = gender_questionnaire(questionnaire_data, target_gender) - # end_time = time.time() - # print() - # print_text(f"Execution time: {end_time - start_time} seconds") - - # # # 6) Reformulation (familier ou formel) avec liaisons / métaphores / décoration de texte - # start_time = time.time() - # questionnaire.data['questionnaire']['questions'] = link_questions_gpt(questionnaire.data['questionnaire']['questions']) - # end_time = time.time() - # print() - # print_text(f"Execution time: {end_time - start_time} seconds") - - # # # 7) Modification des questions qui proposent des choix / réponses afin de compléter la question. - # # les questions de type : multiple_choice, single_choice et ranking doivent être complétées à partir de leurs réponses proposées - # start_time = time.time() - # questionnaire.data['questionnaire']['questions'] = complete_questions_with_options(questionnaire_data) - # end_time = time.time() - # print_text(f"Execution time: {end_time - start_time} seconds") - - # # # 8) Correction finale de toutes les questions - # start_time = time.time() - # questionnaire.data['questionnaire']['questions'] = correct_questions(language, questionnaire.data['questionnaire']['questions']) - # end_time = time.time() - # print_text(f"Execution time: {end_time - start_time} seconds") - # questionnaire.save() - - # threading.Thread(target=tts_questionnaire, args=(questionnaire,)).start() - threading.Thread(target=process_questionnaire, args=(questionnaire,)).start() + # 2) Corrections orthographiques -> LanguageTool => Secondaire (et ne marche pas super bien) + if option_bools["correctSpelling"]: + corrections = check_questionnaire(json_data.get('questionnaire', {})) + output_path = os.path.join(default_storage.location, 'language_tool/corrections.json') + with open(output_path, 'w', encoding='utf-8') as output_file: + json.dump(corrections, output_file, indent=2, ensure_ascii=False) + + # # 3a) Affichage simple des types de questions (par la suite tri des questions importantes et inutiles): + if option_bools["showTypes"]: + start_time = time.time() + count_questions_by_category(json_data.get('questionnaire', {}).get('questions', [])) + end_time = time.time() + print() + print_text(f"Execution time: {end_time - start_time} seconds") + + # # 3b) Tri des questions incohérentes avec le contexte du questionnaire: => Secondaire (marche super bien) + if option_bools["checkConsistency"]: + start_time = time.time() + questionnaire.data['questionnaire']['questions'] = find_out_of_context_questions(topic, questionnaire.data['questionnaire']['questions']) + end_time = time.time() + print() + print_text(f"Execution time: {end_time - start_time} seconds") + + # # 4) Ajout de questions pertinentes (avec ordre / liaison et continuité / cohérence avec les questions précédentes) -> GPT-3. + if option_bools["addQuestion"]: + start_time = time.time() + # questionnaire.data['questionnaire']['questions'] = generate_new_questions(topic, questions) + questionnaire.data['questionnaire']['questions'] = generate_new_questions(topic, questionnaire.data['questionnaire']['questions']) + end_time = time.time() + print() + print_text(f"Execution time: {end_time - start_time} seconds") + num_questions += NEW_QUESTIONS_GENERATED + questionnaire.initial_size += NEW_QUESTIONS_GENERATED + + # # 5) Tri des questions selon paramètres de base fournis (âge, langue, sexe, lieu de résidence) -> GPT. + if option_bools["translate"]: + if target_language != questionnaire.language: + # - Traduction JSON + print_title(f"Translation of the questionnaire with GPT-3.5-turbo ({target_language})") + start_time = time.time() + questionnaire_data = translate_questionnaire(questionnaire_data, target_language) + end_time = time.time() + print() + print_text(f"Execution time: {end_time - start_time} seconds") + questionnaire.data['questionnaire']['language'] = target_language + questionnaire.language = target_language + + if option_bools["gender"]: + if (target_gender == 'female'): + # - Genrer le texte (male / female) + start_time = time.time() + questionnaire_data = gender_questionnaire(questionnaire_data, target_gender) + end_time = time.time() + print() + print_text(f"Execution time: {end_time - start_time} seconds") + + # # 6) Reformulation (familier ou formel) avec liaisons / métaphores / décoration de texte + if option_bools["reformulateLinkers"]: + start_time = time.time() + questionnaire.data['questionnaire']['questions'] = link_questions_gpt(questionnaire.data['questionnaire']['questions']) + end_time = time.time() + print() + print_text(f"Execution time: {end_time - start_time} seconds") + + # # 7) Modification des questions qui proposent des choix / réponses afin de compléter la question. + # les questions de type : multiple_choice, single_choice et ranking doivent être complétées à partir de leurs réponses proposées + if option_bools["addOptions"]: + start_time = time.time() + questionnaire.data['questionnaire']['questions'] = complete_questions_with_options(questionnaire_data) + end_time = time.time() + print_text(f"Execution time: {end_time - start_time} seconds") + + # # 8) Correction finale de toutes les questions + if option_bools["correctionFinale"]: + start_time = time.time() + questionnaire.data['questionnaire']['questions'] = correct_questions(language, questionnaire.data['questionnaire']['questions']) + end_time = time.time() + print_text(f"Execution time: {end_time - start_time} seconds") + + questionnaire.save() + + # threading.Thread(target=process_questionnaire, args=(questionnaire,)).start() # 9) Corriger des audios qui auraient eu un problème (trop long ou bugs de parole) # ... diff --git a/frontend/src/app/game-menu/game-menu.component.css b/frontend/src/app/game-menu/game-menu.component.css index 785d1c8dc42b0e27909f8e44ec88872ab7f96697..29e060fcd259a5fe37ef36a57d4951a48ad62a03 100644 --- a/frontend/src/app/game-menu/game-menu.component.css +++ b/frontend/src/app/game-menu/game-menu.component.css @@ -134,6 +134,7 @@ body { background-color: var(--_btn-bg); color: var(--_btn-text); text-shadow: 0 1px 1px var(--_btn-text-shadow); + width: 300px; font-size: min(5vmin, 2rem); font-family: Audiowide; diff --git a/frontend/src/app/game-menu/game-menu.component.ts b/frontend/src/app/game-menu/game-menu.component.ts index 054de856d9207d4e7cf551ceff5c1501fa52703c..ed223eb7ce4b7d6fc7314e497512d6dc78920e2d 100644 --- a/frontend/src/app/game-menu/game-menu.component.ts +++ b/frontend/src/app/game-menu/game-menu.component.ts @@ -15,6 +15,7 @@ export class GameMenuComponent implements OnInit, AfterViewInit { numQuestions: number = 0; selectedAudioId: number | null = null; errorMessage: string | null = null; + selectedOptions: any = {}; constructor( private jsonUploadService: JsonUploadService, @@ -23,6 +24,9 @@ export class GameMenuComponent implements OnInit, AfterViewInit { ngOnInit(): void { this.initializeArrowKeySupport(); + this.sharedService.currentOptions.subscribe(options => { + this.selectedOptions = options; + }); } ngAfterViewInit(): void { @@ -80,7 +84,13 @@ export class GameMenuComponent implements OnInit, AfterViewInit { reader.onload = (e: any) => { try { const jsonData = JSON.parse(e.target.result); - this.jsonUploadService.uploadJson(jsonData).subscribe( + + const payload = { + jsonData, + options: this.selectedOptions + }; + + this.jsonUploadService.uploadJson(payload).subscribe( numQuestions => { this.numQuestions = numQuestions.num_questions; this.sharedService.setJsonUploaded(true); diff --git a/frontend/src/app/options-column/options-column.component.css b/frontend/src/app/options-column/options-column.component.css index 387297c6bb540d00bb41718c76c2b8949305ce94..9b5731e126d429465a606e49ecd07a886e12004a 100644 --- a/frontend/src/app/options-column/options-column.component.css +++ b/frontend/src/app/options-column/options-column.component.css @@ -9,15 +9,15 @@ overflow: auto; background-color: rgb(0,0,0); background-color: rgba(0,0,0,0.4); - padding-top: 10%; + padding-top: 5%; } .modal-content { - background-color: #2e2e2ee7; - margin: 15% auto; + background-color: #2e2e2ef6; + margin: 5% auto; padding: 20px; border: 1px solid #888; - width: 50%; + width: 40%; border-radius: 5%; } @@ -63,16 +63,50 @@ label, h3 { align-items: center; } -.option-item input[type="checkbox"] { - margin: 2px 8px; +.option-item input { + margin-right: 10px; +} + +.option-item input[type="checkbox"]:checked + label { + color: hsl(44, 53%, 50%); +} + +.option-item input[type="checkbox"]:checked { + accent-color: hsl(44, 53%, 50%); } .options-button { + margin: 8%; + color: white; + background-color: rgba(0, 0, 0, 0); + padding: 5px 10px; +} + +.save-button { margin-top: 20px; + color: white; + background-color: rgba(0, 0, 0, 0); + width: 80%; + height: 30px; +} + +.save-button:hover { + background-color: hsl(44, 53%, 50%); +} + +.select-item { + margin: 20px; +} + +.select-item label { + padding-right: 20px; } @media (max-width: 800px) { .modal-content { width: 80%; } + .modal { + padding-top: 20%; + } } \ No newline at end of file diff --git a/frontend/src/app/options-column/options-column.component.html b/frontend/src/app/options-column/options-column.component.html index 5d7cb202b71872447dbac0908a56b34b4a5c581c..fb1876990b8cf548dbf9c9656bd24e08604fbe66 100644 --- a/frontend/src/app/options-column/options-column.component.html +++ b/frontend/src/app/options-column/options-column.component.html @@ -10,8 +10,7 @@ <input type="checkbox" formControlName="{{option.controlName}}"> {{option.label}} </label> - </div> - <button (click)="toggleAll(optionsForm1, allChecked1)" class="options-button">{{ allChecked1 ? 'Uncheck All' : 'Check All' }}</button> + </div> </form> </div> @@ -24,9 +23,50 @@ {{option.label}} </label> </div> - <button (click)="toggleAll(optionsForm2, allChecked2)" class="options-button">{{ allChecked2 ? 'Uncheck All' : 'Check All' }}</button> </form> </div> </div> + <div class="button-container"> + <button (click)="toggleAll(optionsForm1, allChecked1)" class="options-button">{{ allChecked1 ? 'Uncheck All' : 'Check All' }}</button> + <button (click)="toggleAll(optionsForm2, allChecked2)" class="options-button">{{ allChecked2 ? 'Uncheck All' : 'Check All' }}</button> + </div> +<!-- + <div class="select-container"> + <label for="languageSelect">Language:</label> + <select id="languageSelect"> + <option value="FR">FR</option> + <option value="EN">EN</option> + </select> + </div> + + <div class="select-container"> + <label for="genderSelect">Gender:</label> +¨ <select id="genderSelect"> + <option value="Male">Male</option> + <option value="Female">Female</option> + </select> + </div> --> + + <div class="select-container"> + <h3>Preferences</h3> + <form [formGroup]="optionsForm3"> + <div class="select-item"> + <label for="languageSelect">Language:</label> + <select id="languageSelect" formControlName="language"> + <option value="FR">FR</option> + <option value="EN">EN</option> + </select> + </div> + <div class="select-item"> + <label for="genderSelect">Gender:</label> + <select id="genderSelect" formControlName="gender"> + <option value="Male">Male</option> + <option value="Female">Female</option> + </select> + </div> + </form> + </div> + + <button (click)="saveOptions()" class="save-button">Save</button> </div> -</div> \ No newline at end of file +</div> diff --git a/frontend/src/app/options-column/options-column.component.ts b/frontend/src/app/options-column/options-column.component.ts index 252492913f9ec12379637ff469632a9c035fac51..84e92d731779f9824dbb5e72191efa00fb12df22 100644 --- a/frontend/src/app/options-column/options-column.component.ts +++ b/frontend/src/app/options-column/options-column.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, FormControl } from '@angular/forms'; +import { SharedService } from '../shared.service'; @Component({ selector: 'app-options-column', @@ -9,6 +10,7 @@ import { FormBuilder, FormGroup, FormControl } from '@angular/forms'; export class OptionsColumnComponent implements OnInit { optionsForm1: FormGroup; optionsForm2: FormGroup; + optionsForm3: FormGroup; options1 = [ // Check Questionnaire { label: 'Check Format', controlName: 'checkFormat' }, @@ -38,7 +40,10 @@ export class OptionsColumnComponent implements OnInit { allChecked1 = false; allChecked2 = false; - constructor(private fb: FormBuilder) { + constructor( + private fb: FormBuilder, + private sharedService: SharedService + ) { this.optionsForm1 = this.fb.group({ checkFormat: [true], reformulateLinkers: [true], @@ -52,6 +57,11 @@ export class OptionsColumnComponent implements OnInit { sentimentAnalysis: [true], checkOptions: [true], }); + + this.optionsForm3 = this.fb.group({ + language: ['FR'], + gender: ['Male'] + }); } openOptions(): void { @@ -64,6 +74,10 @@ export class OptionsColumnComponent implements OnInit { modal.style.display = 'none'; } + saveOptions(): void { + this.closeOptions(); + } + ngOnInit(): void { this.options1.forEach(option => { this.optionsForm1.addControl(option.controlName, new FormControl(false)); @@ -77,6 +91,11 @@ export class OptionsColumnComponent implements OnInit { options!.addEventListener('click', () => { this.openOptions() }); + + this.updateOptions() + this.optionsForm1.valueChanges.subscribe(value => this.updateOptions()); + this.optionsForm2.valueChanges.subscribe(value => this.updateOptions()); + this.optionsForm3.valueChanges.subscribe(value => this.updateOptions()); } toggleAll(form: FormGroup, allChecked: boolean): void { @@ -91,4 +110,11 @@ export class OptionsColumnComponent implements OnInit { this.allChecked2 = newValue; } } + + updateOptions(): void { + const optionsData1 = this.optionsForm1.value; + const optionsData2 = this.optionsForm2.value; + const optionsData3 = this.optionsForm3.value; + this.sharedService.updateOptions({ ...optionsData1, ...optionsData2, ...optionsData3 }); + } } diff --git a/frontend/src/app/shared.service.ts b/frontend/src/app/shared.service.ts index fcb577f96618f280eb3485a0e10efdcc9cd0314a..7fdcc6e40a6a35d2a43bbb7b45979dbae4a82a05 100644 --- a/frontend/src/app/shared.service.ts +++ b/frontend/src/app/shared.service.ts @@ -12,6 +12,10 @@ export class SharedService { private numQuestions = new BehaviorSubject<number>(0); numQuestions$ = this.numQuestions.asObservable(); + // Options (checkbox) + private optionsSource = new BehaviorSubject<any>({}); + currentOptions = this.optionsSource.asObservable(); + // Audio from User private audioListSource = new BehaviorSubject<{ id: string, urls: string[] }[]>([]); @@ -64,4 +68,8 @@ export class SharedService { setNumQuestions(num: number): void { this.numQuestions.next(num); } + + updateOptions(options: any) { + this.optionsSource.next(options); + } }