def test_unique_path(): assert_equals(qs.unique_path('._qstest.txt'), '._qstest(1).txt') assert_equals(qs.unique_path('._qstest(3).txt'), '._qstest(3).txt') assert_equals(qs.unique_path('._qstest(5).txt'), '._qstest(6).txt') assert_equals(qs.unique_path('._qstest.txt', suffix='suf'), '._qstestsuf(1).txt') assert_in('_qstest', qs.unique_path('._qstest.txt', use_random=True))
def _get_output_filepath(self, filepath, overwrite, new_extension): filepath = filepath or self.filepath original_extension = os.path.splitext(self.filepath)[1] if overwrite: return self.filepath.replace(original_extension, new_extension) else: return qs.unique_path(self.filepath, extension=new_extension)
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] identifier = sys.argv[2] filename = sys.argv[3] q = qs.API(schoolcode) csv_report_card_data = qs.CSV(filename) student_report_card_data = {} if 'Student ID' not in csv_report_card_data.cols: raise ValueError("'Student ID' column is required.") if 'Report Cycle ID' not in csv_report_card_data.cols: raise ValueError("'Report Cycle ID' column is required.") qs.logger.info( 'GETting all report card data for each student enrollment...', cc_print=True) for csv_student in qs.bar(csv_report_card_data): student_id = csv_student['Student ID'] section_id = csv_student['Section ID'] report_cycle_id = csv_student['Report Cycle ID'] report_card_data = q.get_report_card(student_id, report_cycle_id)['sectionLevel'] csv_student[identifier] = report_card_data[section_id][identifier] qs.logger.info('Values retrieved for identifier:', identifier, cc_print=True) filepath = qs.unique_path(csv_report_card_data.filepath, suffix="-values") csv_report_card_data.save(filepath)
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] filename = sys.argv[2] ignore_case = bool(sys.argv[3]) if len(sys.argv) > 3 else True enrolled_only = bool(sys.argv[4]) if len(sys.argv) > 4 else False csv_students = qs.CSV(filename) q = qs.API(schoolcode) if not ('Full Name' in csv_students.cols or ('First' in csv_students.cols and 'Last' in csv_students.cols)): raise ValueError('"Full Name" or "First" and "Last" columns required.') if enrolled_only is True: db_students = q.get_students() elif enrolled_only is False: db_students = q.get_students(show_has_left=True, show_deleted=True, ignore_deleted_duplicates=True) db_duplicates = qs.find_dups_in_dict_list(db_students, 'fullName') if db_duplicates: qs.logger.critical('Student names are not unique in DB, duplicates:', db_duplicates) else: qs.logger.info('Student names are unique.') if ignore_case is True: for student in db_students: student['fullName'] = student['fullName'].lower() db_by_name = qs.dict_list_to_dict(db_students, 'fullName') student_names_not_matched = set() for csv_student in csv_students: if 'Full Name' in csv_student: csv_full_name = csv_student['Full Name'] else: csv_full_name = '{}, {}'.format(csv_student['Last'], csv_student['First']) csv_original_full_name = csv_full_name if ignore_case: csv_full_name = csv_full_name.lower() db_match = db_by_name.get(csv_full_name) if db_match: csv_student['Student ID'] = db_match['id'] else: student_names_not_matched.add(csv_original_full_name) if student_names_not_matched: qs.logger.warning(('{} students in the file were not found in the db' ''.format(len(student_names_not_matched))), student_names_not_matched) else: qs.logger.info('All students were matched in the db.', cc_print=True) filepath = qs.unique_path(csv_students.filepath, suffix="with student IDs") csv_students.save(filepath)
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] filename = sys.argv[2] ignore_case = bool(sys.argv[3]) if len(sys.argv) > 3 else True csv_classes = qs.CSV(filename) q = qs.API(schoolcode) if not ('Class Name' in csv_classes.cols or ('Grade' in csv_classes.cols) or ('Grade Level' in csv_classes.cols)): raise ValueError('Class Name, Grade Level, or Grade column required.') db_classes = q.get_classes() db_duplicates = qs.find_dups_in_dict_list(db_classes, 'name') if db_duplicates: qs.logger.critical('Class Names are not unique in DB, duplicates:', db_duplicates) else: qs.logger.info('class names are unique.') if ignore_case is True: for class_name in db_classes: class_name['name'] = class_name['name'].lower() db_by_name = qs.dict_list_to_dict(db_classes, 'name') class_names_not_matched = set() for csv_class in csv_classes: if 'Class Name' in csv_class: csv_class_name = csv_class['Class Name'] elif 'Grade' in csv_class: csv_class_name = csv_class['Grade'] else: csv_class_name = csv_class['Grade Level'] csv_orginal_class_name = csv_class_name if ignore_case: csv_class_name = csv_class_name.lower() db_match = db_by_name.get(csv_class_name) if db_match: csv_class['Class ID'] = db_match['id'] else: class_names_not_matched.add(csv_orginal_class_name) if class_names_not_matched: qs.logger.warning(('{} classes in the file were not found in the db' ''.format(len(class_names_not_matched))), class_names_not_matched) else: qs.logger.info('All classes were matched in the db.', cc_print=True) filepath = qs.unique_path(csv_classes.filepath, suffix="with student IDs") csv_classes.save(filepath)
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] server = sys.argv[2] identifier = sys.argv[3] level = sys.argv[4] filename = sys.argv[5] q = qs.API(schoolcode, server) csv_transcript_data = qs.CSV(filename) student_transcript_data = {} if 'Student ID' not in csv_transcript_data.cols: raise ValueError("'Student ID' column is required.") if not (len(sys.argv) == 6): raise ValueError("Incorrect number of params. Please review.") if ('Section ID' not in csv_transcript_data.cols and level == "section"): raise ValueError("'Section ID' column is required.") if (level != "transcript" and level != "section"): raise ValueError( "'Level' param not defined properly. Please chose 'section' or 'transcript'." ) qs.logger.info( 'GETting all transcript data for each student enrollment...', cc_print=True) for csv_student in qs.bar(csv_transcript_data): student_id = csv_student['Student ID'] if level == "section": section_id = csv_student['Section ID'] transcript_data = q.get_transcript(student_id)['sectionLevel'] if section_id in transcript_data: if identifier in transcript_data[section_id]: csv_student[identifier] = transcript_data[section_id][ identifier] else: csv_student[identifier] = None if level == "transcript": transcript_data = q.get_transcript(student_id)['transcriptLevel'] if identifier in transcript_data: csv_student[identifier] = transcript_data[identifier] else: csv_student[identifier] = None else: qs.logger.critical('Transcript level param incorrect.') qs.logger.info('Values retrieved for identifier:', identifier, cc_print=True) filepath = qs.unique_path(csv_transcript_data.filepath, suffix="-values") csv_transcript_data.save(filepath)
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] section_id = sys.argv[2] filename = sys.argv[3] q = qs.API(schoolcode) csv_grades = qs.CSV(filename) section_name = q.get_section(section_id)['sectionName'] matched_categories = list() unmatched_categories = list() if section_name is None: raise ValueError("Section doesn't exist") if not ('Category Name') in csv_grades.cols: raise ValueError("'Category Name' column required") qs.logger.info( "GETting grading categories based of the gradebook in section: " + section_name, cc_print=True) categories = q.get_grade_category_ids(section_id) for csv_grade in qs.bar(csv_grades): category_name = csv_grade['Category Name'] if category_name in categories: csv_grade['Category ID'] = categories[category_name] matched_categories.append(category_name) else: unmatched_categories.append(category_name) # Return new csv if successful if 'Category ID' in csv_grade: qs.logger.info( "Completed getting category ids. {} categories matched.".format( len(matched_categories)), cc_print=True) qs.logger.info("{} outstanding unmatched categories\n".format( len(unmatched_categories)), cc_print=True) filepath = qs.unique_path(csv_grades.filepath, suffix="w category IDs") csv_grades.save(filepath) else: qs.logger.info("Outstanding categories ({}) are unmatched\n".format( len(unmatched_categories)), cc_print=True) for unmatched_category in unmatched_categories: qs.logger.info(unmatched_category, cc_print=True)
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] filename = sys.argv[2] q = qs.API(schoolcode) csv_semesters = qs.CSV(filename) # Check CSV for proper columns and verify unique semester names in db if not ("Semester" in csv_semesters.cols): raise ValueError("'Semester' column required") db_semesters = q.get_semesters() db_duplicates = qs.find_dups_in_dict_list(db_semesters, "semesterName") if db_duplicates: qs.logger.critical('Semester names are not unique in DB. Duplicates:', db_duplicates) else: qs.logger.info('Semester names are unique. Matching...', cc_print=True) db_semesters_by_name = qs.dict_list_to_dict(db_semesters, "semesterName") # Check for semesters in CSV that are not in db semester_names_not_matched = set() for csv_semester in csv_semesters: csv_semester_name = csv_semester['Semester'] csv_original_semester_name = csv_semester_name db_match = db_semesters_by_name.get(csv_semester_name) if db_match: csv_semester['Semester ID'] = db_match['id'] else: semester_names_not_matched.add(csv_original_semester_name) # Match semesters if all are matched if semester_names_not_matched: qs.logger.info('Semester Names do not match DB:', semester_names_not_matched, cc_print=True) else: qs.logger.info('All semester names matched', cc_print=True) filepath = qs.unique_path(csv_semesters.filepath) csv_semesters.save(filepath)
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] filename = sys.argv[2] ignore_case = bool(sys.argv[3]) if len(sys.argv) > 3 else True csv_teachers = qs.CSV(filename) q = qs.API(schoolcode) if not ('Teacher Name' in csv_teachers.cols): raise ValueError('Teacher Name column required.') db_teachers = q.get_teachers() db_duplicates = qs.find_dups_in_dict_list(db_teachers, 'fullName') if db_duplicates: qs.logger.critical('Teacher names are not unique in DB, duplicates:', db_duplicates) else: qs.logger.info('Teacher names are unique.') if ignore_case is True: for teacher in db_teachers: teacher['fullName'] = teacher['fullName'].lower() db_by_name = qs.dict_list_to_dict(db_teachers, 'fullName') teacher_names_not_matched = set() for csv_teacher in csv_teachers: csv_full_name = csv_teacher['Teacher Name'] if ignore_case: csv_full_name = csv_full_name.lower() db_match = db_by_name.get(csv_full_name) if db_match: csv_teacher['Teacher ID'] = db_match['id'] else: teacher_names_not_matched.add(csv_full_name) if teacher_names_not_matched: qs.logger.warning(('{} Teachers in the file were not found in the db' ''.format(len(teacher_names_not_matched))), teacher_names_not_matched) else: qs.logger.info('All teachers were matched in the db.', cc_print=True) filepath = qs.unique_path(csv_teachers.filepath, suffix="with teacher IDs") csv_teachers.save(filepath)
def write_csv(rows, filepath, overwrite=False, column_headers=None): """Write a CSV to disk. Takes a list of dicts, where keys map to columns, and values map to row cells. If a key exists in any dict, it'll be added to the CSV headers, so in general all dicts should have the same keys. By default, the column headers will be alphabetically sorted. To specify custom column headers/sorting, supply the column_headers arg By default, the filepath is made unique using the default behavior of qs.unique_path. Args: rows: a list of dicts, each dict corresponding to a single row in the CSV. filepath: path to write the CSV to overwrite: by default, this function finds a unique file path based on the filepath supplied. To overwrite the file at that path, set overwrite to True column_headers: Supply a list of column headers to use in the CSV. This is useful both for order and for specifying a specific subset of headers to use instead of all keys in the rows array. """ filepath = os.path.expanduser(filepath) if column_headers is None: column_headers = set() for row in rows: column_headers.update(row.keys()) column_headers = sorted(list(column_headers)) if overwrite is False: filepath = qs.unique_path(filepath, extension='csv') for row in rows: _sanitized_row_for_csv(row) with open(filepath, 'w') as f: writer = csv.DictWriter(f, column_headers) writer.writeheader() for row in rows: writer.writerow(row)
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] filename = sys.argv[2] q = qs.API(schoolcode) csv_report_cycles = qs.CSV(filename) if 'Report Cycle' not in csv_report_cycles.cols: raise ValueError("'Report Cycle' column required") db_report_cycles = q.get_report_cycles() db_duplicates = qs.find_dups_in_dict_list(db_report_cycles, 'name') db_by_name = qs.dict_list_to_dict(db_report_cycles, 'name') if db_duplicates: qs.logger.info('Report Cycle names are not unique', cc_print=True) else: qs.logger.info('Report Cycle names are unique', cc_print=True) report_cycle_names_not_matched = set() for csv_report_cycle in csv_report_cycles: csv_report_cycle_name = csv_report_cycle['Report Cycle'] if csv_report_cycle_name in db_duplicates: qs.logger.info('Report Cycle name has multiple matches', cc_print=True) db_match = db_by_name.get(csv_report_cycle_name) if db_match: csv_report_cycle['Report Cycle ID'] = db_match['id'] else: report_cycle_names_not_matched.add(csv_report_cycle_name) if report_cycle_names_not_matched: qs.logger.warning( ('{} Report Cycles in the file were not found in the db' ''.format(len(report_cycle_names_not_matched))), report_cycle_names_not_matched) else: qs.logger.info('All Report Cycles were matched in the db.', cc_print=True) filepath = qs.unique_path(csv_report_cycles.filepath, suffix="with-report-cycle-IDs") csv_report_cycles.save(filepath)
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] server = sys.argv[2] filename = sys.argv[3] q = qs.API(schoolcode, server) csv_student_report_cycles = qs.CSV(filename) tr_enrollments = list() if 'Student ID' not in csv_student_report_cycles.cols: raise ValueError("'Student ID' column required.") for csv_student in qs.bar(csv_student_report_cycles): student_id = csv_student['Student ID'] if 'Full Name' in csv_student: full_name = csv_student['Full Name'] else: full_name = None tr_data = q.get_transcript(student_id)['sectionLevel'] for section in tr_data: section_id = section if full_name: tr_enrollments.append({'Student ID': student_id, 'Full Name': full_name, 'Section ID': section_id}) else: tr_enrollments.append({'Student ID': student_id, 'Section ID': section_id}) if tr_enrollments: qs.logger.info('Transcript section enrollment retrieved', cc_print=True) filepath = qs.unique_path(csv_student_report_cycles.filepath) qs.write_csv(tr_enrollments, filepath) else: qs.logger.info('No enrollments found for these students', cc_print=True)
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] filename = sys.argv[2] silent = bool(sys.argv[3]) if len(sys.argv) > 3 else False csv_sections = qs.CSV(filename) q = qs.API(schoolcode) row_num = 1 if 'Semester ID' in csv_sections.cols: qs.logger.info('Matching with db by Semester ID...', cc_print=True) for csv_section_info in qs.bar(csv_sections): section_name = csv_section_info['Section Name'] semester_id = csv_section_info['Semester ID'] if 'Section Code' in csv_sections.cols: section_code = csv_section_info['Section Code'] section = {'sectionCode': section_code} matched_section = q.match_section( section, fail_silent=silent, match_code=True, target_semester_id=semester_id) else: matched_section = q.match_section( identifier=section_name, fail_silent=silent, target_semester_id=semester_id) if matched_section is "FALSE": section_id = "FALSE" else: section_id = matched_section['id'] csv_section_info['Section ID'] = section_id elif 'Student ID' in csv_sections.cols: qs.logger.info('Matching with db by Student ID...', cc_print=True) for csv_section_info in qs.bar(csv_sections): section_name = csv_section_info['Section Name'] student = csv_section_info['Student ID'] row_num += 1 section = q.match_section(identifier=section_name, student_id=student, match_name=True) section_id = section['id'] csv_section_info['Section ID'] = section_id elif 'Section Code' in csv_sections.cols: qs.logger.info('Matching with db by Section Code...', cc_print=True) sections = {} sections_with_id = {} row_num = 1 for csv_section_info in csv_sections: section_name = csv_section_info['Section Name'] section_code = csv_section_info['Section Code'] row_num += 1 if section_code not in sections: sections[section_code] = {'sectionCode': section_code} qs.logger.info('GETting matching section ids by Section Code...', cc_print=True) for section_code in sections: matched_section = q.match_section(sections[section_code], match_code=True) section_id = matched_section['id'] if section_id not in sections_with_id: sections[section_code] = {'section_id': section_id} qs.logger.info('Matching section ids to csv section data...', cc_print=True) for csv_section_info in csv_sections: section_code = csv_section_info['Section Code'] csv_section_info['Section ID'] = sections[section_code][ 'section_id'] else: qs.logger.critical( '"Student ID", "Semester ID", or "Section Code" columns required. Current columns:', csv_sections.cols) # Make CSV only if successful if 'Section ID' in csv_section_info: filepath = qs.unique_path(csv_sections.filepath, suffix="with section IDs") csv_sections.save(filepath)
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] filename = sys.argv[2] q = qs.API(schoolcode) csv_student_report_cycles = qs.CSV(filename) rc_enrollments = list() if 'Student ID' not in csv_student_report_cycles.cols: raise ValueError("'Student ID' column required.") if 'Report Cycle ID' not in csv_student_report_cycles.cols: raise ValueError("'Report Cycle ID' column required.") for csv_student in qs.bar(csv_student_report_cycles): student_id = csv_student['Student ID'] report_cycle_id = csv_student['Report Cycle ID'] if 'Full Name' in csv_student: full_name = csv_student['Full Name'] else: full_name = None if 'Report Cycle' in csv_student: report_cycle = csv_student['Report Cycle'] else: report_cycle = None rc_section_data = q.get_report_card(student_id, report_cycle_id)['sectionLevel'] for section in rc_section_data: section_id = section if full_name: if report_cycle: rc_enrollments.append({ 'Student ID': student_id, 'Full Name': full_name, 'Report Cycle ID': report_cycle_id, 'Report Cycle': report_cycle, 'Section ID': section_id }) else: rc_enrollments.append({ 'Student ID': student_id, 'Full Name': full_name, 'Report Cycle ID': report_cycle_id, 'Section ID': section_id }) else: if report_cycle: rc_enrollments.append({ 'Student ID': student_id, 'Report Cycle ID': report_cycle_id, 'Report Cycle': report_cycle, 'Section ID': section_id }) else: rc_enrollments.append({ 'Student ID': student_id, 'Report Cycle ID': report_cycle_id, 'Section ID': section_id }) if rc_enrollments: qs.logger.info('Report Card section enrollment retrieved', cc_print=True) filepath = qs.unique_path(csv_student_report_cycles.filepath) qs.write_csv(rc_enrollments, filepath) else: qs.logger.info( 'No enrollments found for these students and report cylces', cc_print=True)
def save_tree(file_path, tree): """Save the tree at a unique path based on file_path""" output_path = qs.unique_path(file_path, extension='xml') tree.write(output_path) qs.print_wrapped(qs.messages.successful_save(output_path))
def main(): qs.logger.config(__file__) schoolcode = sys.argv[1] filename = sys.argv[2] q = qs.API(schoolcode) csv_grades = qs.CSV(filename) sections = {} grades = {} new_assignments = [] required_columns = ['Section ID', 'Student ID', 'Total Pts', 'Category ID', 'Marks', 'Grading Scale ID'] missing_columns = [] for required_column in required_columns: if required_column not in csv_grades.cols: missing_columns.append(required_column) if missing_columns: for missing_column in missing_columns: print "Column Missing: {}" . format(missing_column) sys.tracebacklimit = 0 raise ValueError("Columns are missing from CSV. See above for details.") # Make dict of students' assignment grades and assignment metadata for student_section_record in csv_grades: if 'Assignment Name' in student_section_record: assign_name = student_section_record[u'Assignment Name'] else: assign_name = 'Grade Import' if 'Assignment Date' in student_section_record: assign_date = student_section_record['Assignment Date'] else: assign_date = qs.today() section = student_section_record['Section ID'] cat_id = student_section_record['Category ID'] total = student_section_record['Total Pts'] grade_scale = student_section_record['Grading Scale ID'] marks = student_section_record['Marks'] student = student_section_record['Student ID'] # List out students' assignment grades if section not in grades: grades[section] = dict() if assign_name not in grades[section]: grades[section][assign_name] = list() grades[section][assign_name].append({'studentId': student, 'marks': marks}) # List out assignment metadata if section not in sections: sections[section] = dict() sections[section][assign_name] = {'cat_id': cat_id, 'total': total, 'grade_scale': grade_scale, 'assign_date': assign_date, 'assign_name': assign_name, 'section_id': section, 'grades_data': []} for section in sections: for assign_name in sections[section]: sections[section][assign_name]['grades_data'] = grades[section][assign_name] qs.logger.info(sections) # POST assignment and POST grades to it qs.logger.info('POSTing assignments...', cc_print=True) for section in qs.bar(sections): for assign_name in sections[section]: assign_data = sections[section][assign_name] section_id = assign_data['section_id'] new_grade = q.post_assignment_with_grades(section, assign_data['assign_name'], assign_data['assign_date'], assign_data['total'], assign_data['cat_id'], assign_data['grade_scale'], assign_data['grades_data']) qs.logger.info('--> NEW ASSIGNMENT ID: ', new_grade) qs.logger.info('--> NEW SECTION ID: ', section_id) new_assignments.append({'Assignment ID': new_grade, 'Section ID': section_id}) qs.logger.info('All Assignments are posted....', cc_print=True) filepath = qs.unique_path(csv_grades.filepath, suffix='_posted_asignments') qs.write_csv(new_assignments, filepath)