def __init__(self, filename): """Opens file named filename and reads all configuration data""" self.confData = {} # We read all of the configuration data (without taking care of the type) f = openWithErrorManagement(filename, "r", encoding="utf8") nbLinesRead = [0] confKeys = floatConfKeys + intConfKeys + strConfKeys line = lookForNonBlankLine( f, nbLinesRead, True, """Une ligne de la forme "Cle = Valeur" """) while (line != ""): pos = line.find("=") if pos < 0: sys.exit( """ERREUR: Dans le fichier "{}", à la ligne {}, cette ligne contient "{}" qui ne respecte pas le format "Clé = Valeur" """ .format(f.name, nbLinesRead[0], line)) key = line[:pos].strip(" \t\n") value = line[pos + 1:].strip(" \t\n") if key not in confKeys: sys.exit( """ERREUR: Dans le fichier "{}", à la ligne {}, cette ligne contient "{}" qui définit la clé "{}" qui est inconnue (problème d'orthographe ?)""" .format(f.name, nbLinesRead[0], line, key)) self.confData[key] = value line = lookForNonBlankLine( f, nbLinesRead, True, """Une ligne de la forme "Cle = Valeur" """) f.close() # We check that we found all keys in file for key in confKeys: try: self.confData[key] except KeyError: sys.exit( """ERREUR: Il manque la clé "{}" dans le fichier de configuration "{}".""" .format(key, filename)) # We take care of float data for key in floatConfKeys: try: result = float(self.confData[key]) except ValueError: sys.exit( """ERREUR: Dans le fichier "{}", la clé "{}" a pour valeur "{}" qui n'est pas un flottant.""" .format(f.name, key, confData[key])) self.confData[key] = result # We take care of int data for key in intConfKeys: try: result = int(self.confData[key]) except ValueError: sys.exit( """ERREUR: Dans le fichier "{}", la clé "{}" a pour valeur "{}" qui n'est pas un entier.""" .format(f.name, key, confData[key])) self.confData[key] = result # Initialize opinionType2CommentBound self.opinionType2CommentBound = ( self.confData["negativeCommentBound"], "Erreur (Valeur non utilisée dans self.opinionType2CommentBound)", self.confData["positiveCommentBound"]) # If necessary, we add a '/' at the end of self.confData["rootDirectory"] rootDir = self.confData["rootDirectory"] if rootDir != "": if rootDir[len(rootDir) - 1] != '/': self.confData["rootDirectory"] += '/'
def main(): """Main entry point of the application""" # # Call parameter analysis # if len(sys.argv) != 3: print("ERREUR: Pas assez de parametres") usage() return if sys.argv[1][0] != '-': print( """ERREUR: Le premier parametre devrait etre "-1" ou "-2" (sans les guillemets)""" ) usage() return if len(sys.argv[1]) == 1 or sys.argv[1][1] not in "13": print( """ERREUR: Le premier parametre devrait etre "-1" ou "-3" (sans les guillemets)""" ) usage() return # # We display cos version before starting the processing # print() print("cos version 2.0.2") print() # # Read all configuration data # conf = Conf(sys.argv[2]) # # Read the list of defenses # defenses = [] f = openWithErrorManagement(key2inputFileName("defensesFilename", conf), "r", encoding=conf.get("encoding")) nbLinesRead = [0] name = lookForNonBlankLine(f, nbLinesRead, True, "Nom soutenance") while name != "": defenses.append(Defense(name)) name = lookForNonBlankLine(f, nbLinesRead, True, "Nom soutenance") f.close() # # Read the list of students and the title of their defense # students = [] f = openWithErrorManagement(key2inputFileName("studentsFilename", conf), "r", encoding=conf.get("encoding")) nbLinesRead = [0] lookForNonBlankLine( f, nbLinesRead, True, "Nom soutenance") # We ignore the line giving the title of the columns studentLine = lookForNonBlankLine(f, nbLinesRead, True, "Nom soutenance") while studentLine != "": info = splitCsvLine(studentLine, conf.get("csvSeparator")) # We look for info in the list of defense names found = False for defense in defenses: if info[1] == defense.name: # OK, this defense name is known students.append(Student(info[0], defense, defenses)) found = True break if not found: sys.exit( """ERREUR: Dans le fichier "{}", la ligne {} ("{}") fait référence à une soutenance intitulée "{}" qui n'apparaît pas dans le fichier "{}".""" .format(conf.get("studentsFilename"), nbLinesRead[0], studentLine, info[1], conf.get("defensesFilename"))) studentLine = lookForNonBlankLine(f, nbLinesRead, True, "Nom soutenance") f.close() # # Read the list of criteria types # criteriaTypes = [] f = openWithErrorManagement(key2inputFileName("criteriaTypesFilename", conf), "r", encoding=conf.get("encoding")) nbLinesRead = [0] criteriaType = lookForNonBlankLine(f, nbLinesRead, True, "Type de critère") while criteriaType != "": criteriaTypes.append(criteriaType) criteriaType = lookForNonBlankLine(f, nbLinesRead, True, "Type de critère") f.close() # # Read the list of criterias # criterias = [] f = openWithErrorManagement(key2inputFileName("criteriasFilename", conf), "r", encoding=conf.get("encoding")) nbLinesRead = [0] lookForNonBlankLine( f, nbLinesRead, True, "Nom critère") # We ignore the line giving the title of the columns criteriaLine = lookForNonBlankLine(f, nbLinesRead, True, "Nom critère") while criteriaLine != "": info = splitCsvLine(criteriaLine, conf.get("csvSeparator")) # We look for info[0] in the list of criteria types found = False for criteriaType in criteriaTypes: if info[0] == criteriaType: found = True break if not found: sys.exit( """ERREUR: Dans fichier "{}", la ligne {} ("{}") fait référence à un type de critère intitulée "{}" qui n'apparaît pas dans le fichier "{}".""" .format(f.name, nbLinesRead[0], criteriaLine, info[0], conf.get("criteriaTypesFilename"))) # OK, this citeriaType is known try: floatValue = float(info[2]) except ValueError: sys.exit( """ERREUR: Dans fichier "{}", la ligne {} ("{}") a son 3ème champ ("{}") qui n'est pas un flottant.""" .format(f.name, nbLinesRead[0], criteriaLine, info[2])) criterias.append( Criteria(info[0], info[1], floatValue, conf.get("ratioCriteriaKO"), conf.get("ratioCriteriaOK"))) criteriaLine = lookForNonBlankLine(f, nbLinesRead, True, "Nom critère") f.close() # # Prepare dateTime string which may be used for names of output files # date = datetime.datetime.now() s = str(date) dateTime = s[:s.find('.')] # # Remaining work depends on what the user asks for # if sys.argv[1][1] == '1': generateModels(conf, defenses, students, criteriaTypes, criterias, dateTime) else: analyzeTeacherData(conf, defenses, criteriaTypes, criterias) analyzeStudentsData(conf, defenses, students, criteriaTypes, criterias) generateResults(conf, defenses, students, criteriaTypes, criterias, dateTime) # # We display an end of execution message # if sys.argv[1][1] == '1': print( """OK, exécution de la phase {} terminée : les fichiers "{}", "{}" et "{}" ont été générés.""" .format( sys.argv[1][1], key2ouputFileName("nominativeSheetsFilename", conf, dateTime), key2ouputFileName("genericSheetFilename", conf, dateTime), key2ouputFileName("genericTeacherMarksFilename", conf, dateTime))) else: print( """OK, exécution de la phase {} terminée : les fichiers "{}", "{}" et "{}" ont été générés.""" .format( sys.argv[1][1], key2ouputFileName("synthesisCommentsFilename", conf, dateTime), key2ouputFileName("evaluationCommentsFilename", conf, dateTime), key2ouputFileName("studentsMarksSheetFilename", conf, dateTime))) print() return
def generateResults(conf, defenses, students, criteriaTypes, criterias, dateTime): """ Generates files conf.get("synthesisCommentsFilename"), conf.get("evaluationCommentsFilename") and conf.get("studentsMarksSheetFilename") Parameters ---------- conf : Conf Configuration information f : file File on which to write defenses : list of Defense List of defenses students : liste of Student List of students criteriaTypes : liste of str List of criteria types criterias : liste of Criteria List of criterias dateTime : str String containing date and time to be used in name of output files Returns ------- void """ # # Generate conf.get("synthesisCommentsFilename") # f = openWithErrorManagement(key2ouputFileName("synthesisCommentsFilename", conf, dateTime), "w", encoding=conf.get("encoding")) for defenseIndex in list(range(len(defenses))): # We cound how many '+' and '-' there are for each criteria nbPosNeg = [[], [], []] for criteriaIndex in list(range(len(criterias))): for opinionType in list_opinions: nbPosNeg[opinionType].append([0, criteriaIndex]) for student in students: for opinionType in list_opinions: if student.opinionsPerDefense[defenseIndex][ opinionType].criteriaIndex >= 0: nbPosNeg[opinionType][student.opinionsPerDefense[ defenseIndex][opinionType].criteriaIndex][0] += 1 # We get a sorted version of nbPositive and nbNegative sortedNbPosNeg = [ sorted(nbPosNeg[NEGATIVE_OPINION], key=itemgetter(0)), sorted(nbPosNeg[AVERAGE_OPINION], key=itemgetter(0)), sorted(nbPosNeg[POSITIVE_OPINION], key=itemgetter(0)) ] # We display the project name and the general comment on the defense f.write("{}\n{}\n{}\n".format(conf.get("defenseBound"), defenses[defenseIndex].name, conf.get("defenseBound"))) if defenses[defenseIndex].generalComment == "": f.write("Pas de commentaire général de {}\n\n".format( conf.get("teacherName"))) else: f.write("Commentaire général de {} :\n".format( conf.get("teacherName"))) for lineComment in defenses[defenseIndex].generalComment.split( "\\n"): f.write(lineComment) f.write("\n") f.write("\n") # We display the results for positive criteria and negative criteria for opinionType in list_opinions: for index in list(range(len(criterias) - 1, -1, -1)): criteriaIndex = sortedNbPosNeg[opinionType][index][1] teacherOpinion = defenses[ defenseIndex].teacherOpinionsPerCriteria[criteriaIndex] if ((sortedNbPosNeg[opinionType][index][0] > 0) or (teacherOpinion.opinionType == opinionType) or ((opinionType == AVERAGE_OPINION) and (opnionType == POSITIVE_OPINION))): f.write( """({}) Cité {:2} fois par les étudiants : {} - Note {} = {}/{} (correspondant à "{}")""" .format(opinionType2sign[opinionType], sortedNbPosNeg[opinionType][index][0], criterias[criteriaIndex].name, conf.get("teacherName"), teacherOpinion.mark, criterias[criteriaIndex].maxPoints, opinionType2sign[teacherOpinion.opinionType])) if defenses[defenseIndex].teacherOpinionsPerCriteria[ criteriaIndex].comment != "": f.write( ", " + defenses[defenseIndex]. teacherOpinionsPerCriteria[criteriaIndex].comment) f.write("\n") for student in students: if (student.opinionsPerDefense[defenseIndex] [opinionType].criteriaIndex == criteriaIndex and student.opinionsPerDefense[defenseIndex] [opinionType].comment != ""): f.write("\t* {}\n".format( student.opinionsPerDefense[defenseIndex] [opinionType].comment)) f.write("\n") f.close() # # Generate conf.get("evaluationCommentsFilename") # f = openWithErrorManagement(key2ouputFileName("evaluationCommentsFilename", conf, dateTime), "w", encoding=conf.get("encoding")) for student in students: f.write("{}\n{} ({})\n{}\n".format(conf.get("studentBound"), student.name, student.defense.name, conf.get("studentBound"))) for defenseIndex in list(range(len(defenses))): defense = defenses[defenseIndex] if defense != student.defense: f.write("{}\n{}\n{}\n".format(conf.get("defenseBound"), defense.name, conf.get("defenseBound"))) for opinionType in list_opinions: criteriaIndex = student.opinionsPerDefense[defenseIndex][ opinionType].criteriaIndex if criteriaIndex >= 0: f.write( "{} {}\n".format(opinionType2sign[opinionType], criterias[criteriaIndex].name)) teacherOpinion = defense.teacherOpinionsPerCriteria[ criteriaIndex] f.write( """ Evaluation de {} = {}/{} (correspondant à "{}")\n""" .format( conf.get("teacherName"), teacherOpinion.mark, criterias[criteriaIndex].maxPoints, opinionType2sign[teacherOpinion.opinionType])) else: f.write( "{} non évalué, car non fourni (ou alors manque de commentaire)\n" .format(opinionType2sign[opinionType])) f.write("\n") f.close() # # Generate conf.get("studentsMarksSheetFilename") # f = openWithErrorManagement(key2ouputFileName("studentsMarksSheetFilename", conf, dateTime), "w", encoding=conf.get("encoding")) f.write( "Nom etudiant{}Note donnee par {} a la soutenance{}Bonus opinion{}Note finale module\n" .format(conf.get("csvSeparator"), conf.get("teacherName"), conf.get("csvSeparator"), conf.get("csvSeparator"))) for student in students: f.write("{}{}{}{}{}{}{}\n".format( str2csvStr(student.name, conf.get("csvSeparator")), conf.get("csvSeparator"), float2str(student.defense.teacherFinalMark, conf), conf.get("csvSeparator"), float2str(student.bonus, conf), conf.get("csvSeparator"), float2str(student.defense.teacherFinalMark + student.bonus, conf))) f.close()
def analyzeStudentsData(conf, defenses, students, criteriaTypes, criterias): """ Enrich students information with the contents of conf.get("filledNominativeSheetsFilename") Parameters ---------- conf : Conf Configuration information f : file File on which to write defenses : list of Defense List of defenses students : liste of Student List of students criteriaTypes : liste of str List of criteria types criterias : liste of Criteria List of criterias Returns ------- void """ # # Analyze the contents of conf.get("filledNominativeSheetsFilename"), # f = openWithErrorManagement(key2inputFileName( "filledNominativeSheetsFilename", conf), "r", encoding=conf.get("encoding")) nbLinesRead = [0] while readLineWithSpecificContents(f, conf.get("studentBound"), nbLinesRead, True) != "": # Determine student index in students studentLine = lookForNonBlankLine(f, nbLinesRead, False, "Ligne contenant un nom d'etudiant") studentLine = studentLine[:studentLine.rfind( "(" )] # To suppress the name of the defense which is between parenthesis at the end of the line studentIndex = findName(studentLine, students, nbLinesRead, f, conf.get("studentsFilename")) if students[studentIndex].alreadyProcessed: sys.exit( """ERREUR: Dans le fichier "{}", derriere la ligne {}, l'etudiant ("{}") apparaît pour la deuxième fois.""" .format(conf.get("filledNominativeSheetsFilename"), nbLinesRead[0], studentLine)) students[studentIndex].alreadyProcessed = True # Skip delimiter of student name readLineWithSpecificContents(f, conf.get("studentBound"), nbLinesRead, False) # Analyze answers for the different defenses evaluated by the student # NB: There is one less defense evaluated by the student, as he does not # evalkuate his own defense. for unusedIndex in list(range(len(defenses) - 1)): # Skip delimiter of defense name readLineWithSpecificContents(f, conf.get("defenseBound"), nbLinesRead, False) # Determine defense index in defenses defenseLine = lookForNonBlankLine( f, nbLinesRead, False, "Ligne contenant un nom de projet") defenseIndex = findName(defenseLine, defenses, nbLinesRead, f, conf.get("defensesFilename")) if defenses[defenseIndex] == students[studentIndex].defense: sys.exit( """ERREUR: Dans le fichier "{}", derriere la ligne {}, l'étudiant "{}"a un commentaire de son propre projet "{}" : tentative de triche ?.""" .format(conf.get("filledNominativeSheetsFilename"), nbLinesRead[0], students[studentIndex].name, defenses[defenseIndex].name)) # Skip delimiter of defense name readLineWithSpecificContents(f, conf.get("defenseBound"), nbLinesRead, False) # For each criteria type for criteriaType in criteriaTypes: # Skip criteria type readLineWithSpecificContents(f, criteriaType, nbLinesRead, False) # Handle each critera in this criteria type for criteria in criterias: if criteria.criteriaType == criteriaType: criteriaLine = lookForNonBlankLine( f, nbLinesRead, False, "Ligne contenant un critère") if criteriaLine[0] in "+-": # We skip '+' or '-' sign which is at the beginning of the line criteriaIndex = findName( criteriaLine[1:].strip(" \t"), criterias, nbLinesRead, f, conf.get("criteriasFilename")) elif criteriaLine[-1] in "+-": # We skip '+' or '-' sign which is at the end of the line criteriaIndex = findName( criteriaLine[:-1].strip(" \t"), criterias, nbLinesRead, f, conf.get("criteriasFilename")) else: criteriaIndex = findName( criteriaLine, criterias, nbLinesRead, f, conf.get("criteriasFilename")) if criteriaLine[0] == '+' or criteriaLine[-1] == '+': students[studentIndex].opinionsPerDefense[ defenseIndex][ POSITIVE_OPINION].criteriaIndex = criteriaIndex students[studentIndex].opinionsPerDefense[ defenseIndex][ POSITIVE_OPINION].nbCriteriaIndex += 1 elif criteriaLine[0] == '-' or criteriaLine[-1] == '-': students[studentIndex].opinionsPerDefense[ defenseIndex][ NEGATIVE_OPINION].criteriaIndex = criteriaIndex students[studentIndex].opinionsPerDefense[ defenseIndex][ NEGATIVE_OPINION].nbCriteriaIndex += 1 # We now take care of opinion comments for opinionType in list_opinions: # Skip line introducing comment readLineWithSpecificContents(f, conf.getCommentBound(opinionType), nbLinesRead, False) # Take care of comment s = f.readline() nbLinesRead[0] += 1 if s == "": sys.exit( """ERREUR: Dans le fichier "{}", derriere la ligne {}, il devrait y avoir un commentaire {} et non la fin du fichier.""" .format(conf.get("filledNominativeSheetsFilename"), nbLinesRead[0], opinionType2str[opinionType])) s = removeComment(s) s = s.strip(" \t\n") if s == "": # It may happen that a student fills up the second line of the comment instead of the first line. # If the first line is empty, we read the second line in case. s = f.readline() nbLinesRead[0] += 1 if s == "": sys.exit( """ERREUR: Dans le fichier "{}", derriere la ligne {}, il devrait y avoir un commentaire {} et non la fin du fichier.""" .format(conf.get("filledNominativeSheetsFilename"), nbLinesRead[0], opinionType2str[opinionType])) s = removeComment(s) s = s.strip(" \t\n") students[studentIndex].opinionsPerDefense[defenseIndex][ opinionType].comment = s # Compute bonus for comment if students[studentIndex].opinionsPerDefense[defenseIndex][ opinionType].nbCriteriaIndex > 1: # Student has given a "+" (or a "-") to several criteras ==> # We cannot say for which criteria is this comment ==> # We ignore this comment. print( """ATTENTION: Pour la soutenance "{}", l'étudiant "{}" a mis le commentaire ({})\n"{}"\nMais, il a mis le signe "{}" sur plusieurs critères\n==> COS ne peut donc pas prendre en compte ce commentaire\n==> Regardez si vous pouvez ne garder qu'un "{}" dans "{}" qui correspondrait à ce commentaire.\n""" .format( defenses[defenseIndex].name, studentLine, opinionType2str[opinionType], students[studentIndex].opinionsPerDefense[ defenseIndex][opinionType].comment, opinionType2sign[opinionType], opinionType2sign[opinionType], f.name)) students[studentIndex].opinionsPerDefense[defenseIndex][ opinionType].comment = "" if students[studentIndex].opinionsPerDefense[defenseIndex][ opinionType].comment != "": criteriaIndex = students[studentIndex].opinionsPerDefense[ defenseIndex][opinionType].criteriaIndex if criteriaIndex < 0: print( """ATTENTION: Pour la soutenance "{}", l'étudiant "{}" a mis un commentaire {} sans sélectionner de critère {}\n==> Regardez si vous pouvez mettre un "{}" dans "{}" qui correspondrait à ce commentaire.\n""" .format(defenses[defenseIndex].name, studentLine, opinionType2str[opinionType], opinionType2str[opinionType], opinionType2sign[opinionType], f.name)) students[studentIndex].opinionsPerDefense[ defenseIndex][opinionType].comment = "" elif defenses[defenseIndex].teacherOpinionsPerCriteria[ criteriaIndex].opinionType == opinionType: # Student gave the same mak as the teacher ==> bonus students[studentIndex].bonus += conf.get( "bonusCriteriaOK") elif (((opinionType == POSITIVE_OPINION and defenses[defenseIndex].teacherBestOpinionType == AVERAGE_OPINION) or (opinionType == NEGATIVE_OPINION and defenses[defenseIndex].teacherWorstOpinionType == AVERAGE_OPINION)) and defenses[defenseIndex]. teacherOpinionsPerCriteria[criteriaIndex].opinionType == AVERAGE_OPINION): # The teacher found no criteria with opinionType and, for this criteria which index is criteriaIndex, # the teacher gave a mark signifying AVERAGE_OPINION. As the student cannot give an average opinion, # we consider that this is a good answer. students[studentIndex].bonus += conf.get( "bonusCriteriaOK") f.close()
def analyzeTeacherData(conf, defenses, criteriaTypes, criterias): """ Enrich defenses information with the contents of conf.get("teacherMarksFilename") Parameters ---------- conf : Conf Configuration information f : file File on which to write defenses : list of Defense List of defenses students : liste of Student List of students criteriaTypes : liste of str List of criteria types criterias : liste of Criteria List of criterias Returns ------- void """ # # Analyze the contents of conf.get("teacherMarksFilename"), # f = openWithErrorManagement(key2inputFileName("teacherMarksFilename", conf), "r", encoding=conf.get("encoding")) nbLinesRead = [0] lookForNonBlankLine(f, nbLinesRead, False, "Ligne de titre des colonnesNom soutenance" ) # We ignore the line giving the title of the columns # We deal with each criteria for criteria in criterias: # Read the marks for this criteria lineMarks = lookForNonBlankLine( f, nbLinesRead, False, "Ligne contenant les notes pour un critère donné") marks = splitCsvLine(lineMarks, conf.get("csvSeparator")) # Read the comments for this criteria lineComments = lookForNonBlankLine( f, nbLinesRead, False, "Ligne contenant les commentaires pour un critère donné") comments = splitCsvLine(lineComments, conf.get("csvSeparator")) # Add (mark, comment) to each defense column = 3 # We set column to 3 in order to skip : # - column 0 which contains title of the line, # - column 1 which contains value for maxCriteriaKO # - column 2 which contains value for minCriteriaOK for defense in defenses: marks[column] = marks[column].replace(conf.get("decimalSeparator"), '.') try: mark = float(marks[column]) except ValueError: sys.exit( """Dans le fichier "{}", la soutenance "{}" a son critère "{}" qui a reçu la note "{}" qui n'est n'est pas un flottant compris entre 0 et {}.""" .format(f.name, defense.name, criteria.name, marks[column], criteria.maxPoints)) if (mark < 0 or mark > criteria.maxPoints): sys.exit( """Dans le fichier "{}", la soutenance "{}" a son critère "{}" qui a reçu la note de {} qui n'est pas comprise entre 0 et {}.""" .format(f.name, defense.name, criteria.name, mark, criteria.maxPoints)) defense.addMarkComment( TeacherOpinion(mark, comments[column], criteria)) column += 1 lookForNonBlankLine(f, nbLinesRead, False, "Ligne de titre des colonnesNom soutenance" ) # We ignore the line left intentionally blank lineGeneralComments = lookForNonBlankLine( f, nbLinesRead, False, "Ligne contenant les commentaires généraux de chaque projet") generalComments = splitCsvLine(lineGeneralComments, conf.get("csvSeparator")) column = 3 # We set column to 3 in order to skip column 0 which contains title of the line, # column 1 which contains Note max critere KO, and column 2 which contains # Note min critere OK for defense in defenses: defense.generalComment = generalComments[column] column += 1 f.close() # # Update each defense, now that we know all the marks given by teacher # for defense in defenses: defense.update()
def generateModels(conf, defenses, students, criteriaTypes, criterias, dateTime): """ Generates models in conf.get("nominativeSheetsFilename"), conf.get("genericSheetFilename") and conf.get("genericTeacherMarksFilename") Parameters ---------- conf : Conf Configuration information defenses : list of Defense List of defenses students : liste of Student List of students criteriaTypes : liste of str List of criteria types criterias : liste of Criteria List of criterias dateTime : str String containing date and time to be used in name of output files Returns ------- void """ # # Generate conf.get("nominativeSheetsFilename") # f = openWithErrorManagement(key2ouputFileName("nominativeSheetsFilename", conf, dateTime), "w", encoding=conf.get("encoding")) for student in students: f.write("{}\n{} ({})\n{}\n".format(conf.get("studentBound"), student.name, student.defense.name, conf.get("studentBound"))) for defense in defenses: if defense != student.defense: writeDefenseSheet(conf, f, defense, criteriaTypes, criterias) f.close() # # Generate conf.get("genericSheetFilename") # f = openWithErrorManagement(key2ouputFileName("genericSheetFilename", conf, dateTime), "w", encoding=conf.get("encoding")) f.write( "{}\n\nNOM Prénom : Soutenance :\n\n{}\n\n" .format(conf.get("studentBound"), conf.get("studentBound"))) for defense in defenses: writeDefenseSheet(conf, f, defense, criteriaTypes, criterias) f.close() # # Generate conf.get("genericTeacherMarksFilename") # f = openWithErrorManagement(key2ouputFileName( "genericTeacherMarksFilename", conf, dateTime), "w", encoding=conf.get("encoding")) # Generate the title of the columns f.write("{}Note max critere KO{}Note min critere OK".format( conf.get("csvSeparator"), conf.get("csvSeparator"))) for defense in defenses: f.write("{}{}".format( conf.get("csvSeparator"), str2csvStr(defense.name, conf.get("csvSeparator")))) f.write("\n") # For each criteria, generate a line for the mark and a line for the comment for criteria in criterias: s = str2csvStr( "{} / {} ({} points)".format(criteria.criteriaType, criteria.name, criteria.maxPoints), conf.get("csvSeparator")) f.write(s) f.write(conf.get("csvSeparator")) f.write(float2str(criteria.maxCriteriaKO, conf)) f.write(conf.get("csvSeparator")) f.write(float2str(criteria.minCriteriaOK, conf)) f.write(conf.get("csvSeparator") * len(defenses)) f.write("\n") f.write("Commentaire de ce critère") f.write( conf.get("csvSeparator") * 2 ) # For the 2 columns corresponding to criteria.maxCriteriaKO and criteria.minCriteriaOK f.write(conf.get("csvSeparator") * len(defenses)) f.write("\n") f.write( str2csvStr("Ligne inutilisée (par exemple, pour y mettre des totaux)", conf.get("csvSeparator"))) f.write(conf.get("csvSeparator") * len(defenses)) f.write("\n") f.write(str2csvStr("Commentaires généraux", conf.get("csvSeparator"))) f.write(conf.get("csvSeparator") * len(defenses)) f.close()