This commit is contained in:
Peter Howell 2025-07-24 10:53:43 -07:00
parent 68e48ec6ac
commit 0bddc4a3e4
8 changed files with 398 additions and 59 deletions

View File

@ -247,7 +247,7 @@ def users_in_by_depts_live(depts=[], termid='181'):
print("Getting enrollments for %s" % c['course_code'])
if d in courses_by_by_dept: courses_by_by_dept[d].append(c)
else: courses_by_by_dept[d] = [ c, ]
for u in course_enrollment_with_faculty(c['id'],1).values():
for u in course_enrollment_with_faculty(c['id'],0).values():
#if u['type'] != "StudentEnrollment": continue
if not (d in students_by_by_dept):
students_by_by_dept[d] = set()
@ -829,9 +829,10 @@ def course_term_summary_3():
# check number of students and publish state of all shells in a term
def all_semester_course_sanity_check():
outputfile = 'cache/courses_checker.csv'
t = 287
term = "sp25"
term = "su25"
target_start = "6-14"
outputfile = f'cache/courses_checker_{term}.csv'
t = 288
c = getCoursesInTerm(t,1,0)
sched1 = requests.get(f"http://gavilan.cc/schedule/{term}_sched_expanded.json").json()
sched = { x['crn']: x for x in sched1 }
@ -840,7 +841,7 @@ def all_semester_course_sanity_check():
#output.write( ",".join(['what','id','parent_course_id','sis_course_id','name']) + "\n" )
output2 = codecs.open(outputfile,'w','utf-8')
output2.write( ",".join(['id','sis_course_id','name','state','mode','startdate','students']) + "\n" )
htmlout = codecs.open('cache/courses_checker.html','w','utf-8')
htmlout = codecs.open(f'cache/courses_checker_{term}.html','w','utf-8')
htmlout.write('<html><body><table>\n')
htmlout.write(f'<tr><td><b>Name</b></td><td><b>SIS ID</b></td><td><b>State</b></td><td><b>Mode</b></td><td><b>Start Date</b></td><td><b># Stu</b></td></tr>\n')
html_sections = []
@ -859,10 +860,11 @@ def all_semester_course_sanity_check():
ctype = sched[crn]['type']
cstart = sched[crn]['start']
ts = sched[crn]['act']
teacher = sched[crn]['teacher']
info = [ 'course', course['id'], '', course['sis_course_id'], course['name'], course['workflow_state'], ts ]
info = list(map(str,info))
info2 = [ course['id'], course['sis_course_id'], course['name'], course['workflow_state'], ctype, cstart, ts ]
info2 = [ course['id'], course['sis_course_id'], course['name'], course['workflow_state'], ctype, cstart, ts, teacher ]
info2 = list(map(str,info2))
output2.write( ",".join(info2) + "\n" )
output2.flush()
@ -870,8 +872,8 @@ def all_semester_course_sanity_check():
#output.write( ",".join(info) + "\n" )
uu = f"https://ilearn.gavilan.edu/courses/{course['id']}"
if course["workflow_state"]=='unpublished' and ctype=='online' and cstart=="1-27":
html_sections.append(f'<!--{course["name"]}--><tr><td><a href="{uu}" target="_blank">{course["name"]}</a></td><td>{course["sis_course_id"]}</td><td>{course["workflow_state"]}</td><td>{ctype}</td><td>{cstart}</td><td>{ts}</td></tr>\n')
if course["workflow_state"]=='unpublished' and ctype=='online' and cstart==target_start:
html_sections.append(f'<!--{course["name"]}--><tr><td><a href="{uu}" target="_blank">{course["name"]}</a></td><td>{course["sis_course_id"]}</td><td>{course["workflow_state"]}</td><td>{ctype}</td><td>{cstart}</td><td>{ts}</td><td>{teacher}</td></tr>\n')
#uu = url + '/api/v1/courses/%s/sections' % str(course['id'])
#course['sections'] = fetch(uu)
#s_info = [ [ 'section', y['id'], y['course_id'], y['sis_course_id'], y['name'], y['total_students'] ] for y in course['sections'] ]
@ -1047,7 +1049,7 @@ def enroll_id_list_to_shell(id_list, shell_id, v=0):
# multiple semesters
def enroll_stem_students_live():
semesters = [286,287]
semesters = [288,289]
for S in semesters:
enroll_stem_students_live_semester(S)
@ -1249,8 +1251,11 @@ def enroll_gott_workshops():
# date, title, shell_id
#['2025-02-23 16:00:00', 'GOTT 6: Intro to Synchronous Teaching (Sync/Hyflex)', 21835],
['2025-03-14 17:00:00', 'GOTT 5: The Essentials of Blended Learning (Hybrid) ', '21886'],
#['2025-03-14 17:00:00', 'GOTT 5: The Essentials of Blended Learning (Hybrid) ', '21886'],
#['2025-02-23 16:00:00', 'GOTT 1: Intro to Teaching Online (2 week, async)', 21874]
['2025-05-26 17:00:00', 'GOTT 2: Introduction to Asynchronous Teaching and Learning', 23015],
['2025-06-01 17:00:00', 'GOTT 1: Intro to Teaching Online', 23083],
['2025-06-01 17:00:00', 'GOTT 4: Assessments in Digital Learning', 21898],
]
#print(json.dumps(signups,indent=4))
#print(json.dumps(by_email,indent=4))
@ -1271,6 +1276,11 @@ def enroll_gott_workshops():
'lorrmay36@mac.com': 'llevy@gavilan.edu',
'gkalu1@gmail.com': 'gkalu@gavilan.edu',
'rpotter@gav.edu': 'rpotter@gavilan.edu',
'ally162@qq.com': 'aao@gavilan.edu',
'davidamancio791@gmail.com': 'damancio@gavilan.edu',
'carissaamunoz83@gmail.com': 'amunoz@gavilan.edu',
'jasonwcpa@yahoo.com': 'jwolowitz@gavilan.edu',
}
for each_workshop in workshop_ids:
@ -1792,6 +1802,9 @@ def teacher_to_many_shells():
import os, pickle
def create_sandboxes():
## TODO: read all student names and determine ahead of time if initials conflict. deal with them
courses_to_sandbox = [ #(20567, ' Sandbox GOTT1 SU24'),
#(20575, ' Sandbox GOTT2 SU24'),
#(20600, ' Sandbox GOTT4 SU24'),
@ -1800,7 +1813,9 @@ def create_sandboxes():
#(20761, ' Sandbox GOTT1 FA24'),
#(21770, ' Sandbox GOTT1 WI25'),
#(21772, ' Sandbox GOTT2 WI25'),
(21874, ' Sandbox GOTT1 SP25'),
(23083, ' Sandbox GOTT1 SU25'),
(23015, ' Sandbox GOTT2 SU25'),
(21898, ' Sandbox GOTT4 SU25'),
]
filepath = 'cache/sandbox_courses.pkl'

View File

@ -10,6 +10,7 @@ import pandas as pd
debug_out = codecs.open("cache/degrees_debug.txt", "w", "utf-8")
def d(s):
global debug_out
#if type(s) == tuple or type(s) == list:
# debug_out.write(" ".join(str(s)) + "\n")
debug_out.write(str(s) + "\n")
@ -112,6 +113,13 @@ class DSLTransformer(Transformer):
d("\ncourse_declaration")
d(name)
d(units)
units_value = float(units.children[0]) # Handle "3", "3.0", etc.
#prereq_list = [p.value for p in prereqs if p is not None]
course = Course(name.value)
course.units = units_value
#course.prereqs = prereq_list
self.courses[name.value] = course
return course
def take_all(self):
d("\ntake_all")
@ -141,7 +149,10 @@ class DSLTransformer(Transformer):
def course(self, c):
d("\ncourse")
d(c)
return Course(c.value)
name = c.value
if name not in self.courses:
self.courses[name] = Course(name)
return self.courses[name]
def course_or(self, *items):
d("\ncourse_or")
@ -330,32 +341,40 @@ rule_lookup = {
'Choose (one) or more',
'Choose (one|two|three) of the classes listed',
'Choose (one|two|three)',
'Choose (4) classes (12 units) from the following list:',
'Choose ([\d\w]+) courses from',
'Choose (\w+) of the following',
'Please select (ONE) course from the options below:',
'LIST [AB]: Select (\d)',
'Select (1)',
'Select (one) of the following REQUIRED CORE',
'SELECT (ONE|TWO) OF THE FOLLOWING', 'Select (one|two|three)',
'Select (\d+) courses', ],
'take at least n units': [ '(\d+) units total',
'take at least n units': [ '(\d+) units total', '(\d+) Unit Minimum',
'Any combination totaling (\d+) units',
'Choose (eight|\d+) units',
'Choose (\w+) units from classes listed',
'Choose a minimum of ([\w\d]+) units from',
'Choose a minimum of (\d+) units',
'Choose at least ([\d|\.]+) units',
'Choose eighteen \((18)\) units from the following list:',
'Choose any combination of courses for a minimum of ([\w\d]+) units\:?',
'Choose any combination of the following courses for a minimum of (\d+) units:',
'Choose courses for at least ([\w\d]+) units',
'LIST A \((\d+) units\)',
'LIST B \((\d+) units\)',
'LIST C \- Any course .*\((\d+) units\)',
'Select (\d+) units',
'Select any (\d+)\-\d+ units from the following',],
'electives': ['Electives', 'Recommended electives?:', ],
'take_all': ['RN PROGRAM', 'REQUIRED CORE', 'CORE COURSES', 'ADDITIONAL REQUIREMENTS','REQUIREMENTS:',
'Requirements', 'Core Requirements',
'electives': ['Electives', 'Recommended electives?:', 'Recommended Elective'],
'take_all': ['RN PROGRAM', 'REQUIRED CORE', 'CORE COURSES', 'CORE Courses', 'Core Major Courses', 'Core Courses :',
'ADDITIONAL REQUIREMENTS', 'REQUIREMENTS:', 'REQUIREMENTS', 'Required:',
'Requirements', 'Core Requirements', 'Required Courses',
'Required Core', 'REQUIRED', 'LVN PROGRAM', 'Student Teaching Practicum', '^LIST A:?$', '^LIST B:$',
'Complete the following courses:', 'Complete the following required courses for 6 units:', 'Complete all of the following courses:',
'Complete all of the following General Education (Cal-GETC) courses:',
'Program Requirements', 'Required Courses:', 'PROGRAM REQUIREMENTS (5 Units)',
'PROGRAM REQUIREMENTS (162 Hours)',
'PROGRAM REQUIREMENTS (162 Hours)', 'PROGRAM REQUIREMENTS (5 Units):', 'PROGRAM REQUIREMENTS (162 Hours):',
],
}
@ -373,6 +392,7 @@ def lookup_rule(line):
pass
if verbose: print(f"line: {line} matched: {each} with {num}")
return key,num
print(f"** failed to lookup rule: {line}")
return None,None
@ -394,23 +414,153 @@ def num_units(s):
def examine5(li,award, verbose=0):
summary = [x[0] for x in li]
if summary[1] in ['and','or'] and summary[3] in ['and','or']:
if verbose: print(" - ", summary)
return summary[1]
return False
def examine(li,award, verbose=0):
summary = [x[0] for x in li]
if summary[1] in ['and','or']:
if verbose: print(" - ", summary)
return summary[1]
return False
# given list of courses, pbd like: [c1, c2, c3, or, c4, c5, c6, or, c7, or, c8, c9],
# return [c1, c2, [or, c3, c4], c5, [or, c6, c7, c8], c9]
# given [c1, 'or', c2, 'or', c3, 'or', c4, 'and', c5, 'and', c6]
# return ['or', c1, c2, c3, [ 'and', c4, c5, c6 ]]
def check_ands_ors_pbd(award, pbd, verbose=0):
verbose = 0
if not pbd:
return []
i = 0
or_group = []
and_group = []
while i < len(pbd):
token = pbd[i][0]
if token == 'or':
i += 1 # skip 'or', next item is part of or_group
continue # will be handled by next loop iteration
elif token == 'and':
# Start or continue and_group
i += 1
if i < len(pbd):
and_group.append(pbd[i])
i += 1
else:
# If previous token was not 'and', treat as part of or_group
if i == 0 or pbd[i-1] != 'and':
or_group.append(token)
i += 1
result = ['or'] + or_group
if and_group:
result.append(['and'] + and_group)
return result
def check_ands_ors_pbd2(award, pbd, verbose=0):
summary = [x[0] for x in pbd]
#if verbose: print(" ", summary)
for i,x in enumerate(summary):
desc = ''
if x == 'course': desc = f"\t{pbd[i][2]['code']} {pbd[i][2]['name']}"
elif x == 'or': desc = ''
elif x == 'and': desc = ''
else: desc = pbd[i][2]
print(f"{x}\t{desc}")
result = []
i = 0
while i < len(pbd):
#if pbd[i][1]=='21' and pbd[i][2]=='Political Science':
# print('here')
if pbd[i][0] == 'or':
# special case: math 233 or higher
if i < len(pbd) and pbd[i+1][2] == 'higher':
result[-1][2]['name'] += "OR HIGHER"
i += 2
continue
# Take last item from result
left = result.pop()
group = ['or', left, pbd[i+1]]
i += 2
if i < len(pbd) and pbd[i][0] == 'or':
group.append(pbd[i+1])
i += 2
result.append(group)
elif pbd[i][0] == 'and':
# Take last item from result
left = result.pop()
group = ['and', left, pbd[i+1]]
i += 2
if i < len(pbd) and pbd[i][0] == 'and':
group.append(pbd[i+1])
i += 2
result.append(group)
else:
result.append(pbd[i])
i += 1
#print(result)
return result
def check_ands_ors_pbd1(award, pbd, verbose=0):
verbose = 1
return_list = []
if verbose: print(f"check_ands_ors_pbd({award}, ...)")
summary = [x[0] for x in pbd]
if verbose: print(" ", summary)
#if verbose: print(" ", summary)
for i,x in enumerate(summary):
desc = ''
if x == 'course': desc = f"\t{pbd[i][2]['code']} {pbd[i][2]['name']}"
elif x == 'or': desc = ''
else: desc = pbd[i][2]
print(f"{x}\t{desc}")
# iterate through in groups of 5, from 0/1/2/3/4 up to 6/7/8/9/10
# (for length 1. length n: n-2, n-1, n)
skip = 0
for i in range(len(pbd)-4):
if skip>0:
skip -= 1
continue
cmd = examine5(pbd[i:i+5], award, verbose)
if cmd:
skip = 4
return_list.append( [cmd, pbd[i], pbd[i+2], pbd[i+4]] )
else:
return_list.append( pbd[i] )
# iterate through in groups of 3, from 0/1/2 up to 3/4/5
# (for length 6. length n: n-2, n-1, n)
skip = 0
for i in range(len(pbd)-2):
examine(pbd[i:i+3], award, verbose)
if verbose: print()
if skip>0:
skip -= 1
continue
cmd = examine(pbd[i:i+3], award, verbose)
if cmd:
skip = 2
return_list.append( [cmd, pbd[i], pbd[i+2]] )
else:
return_list.append( pbd[i] )
#if verbose: print(return_list)
return return_list
def build_program_rules(verbose=0):
@ -467,13 +617,21 @@ def build_program_rules(verbose=0):
# Each 'program block definition'
# 1st is dict with unit totals, rest are lists.
check_ands_ors_pbd( award, sorted( r[k][1:], key=lambda x: float(x[1])) )
for each_r in sorted( r[k][1:], key=lambda x: float(x[1])):
requirements_inorder = sorted( r[k][1:], key=lambda x: float(x[1]))
reqs_with_or_blocks = check_ands_ors_pbd( award, requirements_inorder )
for each_r in reqs_with_or_blocks:
if each_r[0] in ['and','or']:
#print(' ', each_r[0],each_r[1])
pass
if isinstance(each_r, list):
if each_r[0] == 'or':
if not this_rule:
this_rule = "take_all from "
this_rule += ' or '.join( [x[2]['code'] for x in each_r[1:] ] ) # each_r[1][2]['code']} or {each_r[2][2]['code']}, "
if each_r[0] == 'and':
if not this_rule:
this_rule = "take_all from "
this_rule += f"{each_r[1][2]['code']} and {each_r[2][2]['code']}, "
elif isinstance(each_r, list):
#print(each_r)
if each_r[0] == 'h3' or (each_r[0]=='noncourse' and is_noncourse_new_section(each_r[2])):
# This is a rule title
@ -533,6 +691,7 @@ def build_program_rules(verbose=0):
if ok:
okay.append(p)
verbose = 1
if verbose:
print("\n\n\n\nThese programs are okay:")
for p in okay:

View File

@ -11,6 +11,7 @@ from os.path import exists, getmtime
#from pipelines import sync_non_interactive, url, header, gp, dean
from tabulate import tabulate
from semesters import short_to_sis
from canvas_secrets import postgres_database, postgres_password, postgres_port, postgres_user, postgres_host
#########
@ -448,6 +449,94 @@ def courses_to_sched():
conn.close()
# Populate schedule table and correlate to courses table
def refresh_semester_schedule_db(term="fa25"):
# TODO: fix units when they are variable... change to float in between range. round to 0.5 unit.
EXECUTE = 1
last_time = 0
sis_code = short_to_sis(term)
conn,cur = db()
drop_query = f"DELETE FROM canvas.schedule WHERE sem='{sis_code}'"
print(f"executing: {drop_query}")
cur.execute(drop_query)
# get all ilearn courses
query = """SELECT c.id, c.workflow_state, c.sis_source_id, c.course_code, c.enrollment_term_id, t.name
FROM canvas.courses c
JOIN canvas.enrollment_terms t ON c.enrollment_term_id = t.id
ORDER BY c.sis_source_id, c.course_code;"""
cur.execute(query)
conn.commit()
sis_to_sched = {}
for row in cur.fetchall():
sis_source_id = row[2] # c.sis_source_id
sis_to_sched.setdefault(sis_source_id, []).append(row)
vals_cache = []
i = 0
print(sis_code)
sched = 0
try:
sched = requests.get(f"http://gavilan.cc/schedule/{term}_sched_expanded.json").json()
except Exception as e:
print(e)
query = "INSERT INTO canvas.schedule (canvascourse, crn, code, units, teacher, start,\"end\", type, loc, site, partofday, cap, act, sem) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);"
if sched:
for c in sched:
try:
pod = ''
if 'partofday' in c: pod = c['partofday']
#print(c['cred'])
cred_match = re.search(r'(\d+\.\d+)\-(\d+\.\d+)',c['cred'])
if cred_match:
#print(f"matched range: {cred_match.groups}")
cred_start = float(cred_match.group(1))
cred_end = float(cred_match.group(2))
mid = float(int( (cred_end-cred_start)/2 + cred_start ))
c['cred'] = str(mid)
#print(f"middle cred is {c['cred']}")
full_sis_code = sis_code+'-'+c['crn']
if full_sis_code in sis_to_sched:
print(c['cred'])
q = [sis_to_sched[full_sis_code][0][0], c['crn'], c['code'], c['cred'], c['teacher'], c['start'], c['end'], c['type'], c['loc'], c['site'], pod, int(c['cap']), int(c['act']), sis_code]
vals_cache.append( q ) # [ str(x) for x in q ] )
#print(f"{i}: {q}")
i += 1
if i % 100 == 0:
if EXECUTE:
cur.executemany(query, vals_cache)
conn.commit()
vals_cache = []
t = time.process_time()
delta = t - last_time
last_time = t
print(f"Loop {i} - committed to db in %0.3fs. " % delta, flush=True)
else:
print(f"{full_sis_code} not in canvas courses.")
except Exception as e:
print(e)
if EXECUTE:
cur.executemany(query, vals_cache)
conn.commit()
cur.close()
conn.close()
def student_count(courseid):
conn,cursor = db()
q = f"""SELECT COUNT(u.id) AS student_count FROM canvas.courses AS c
@ -498,6 +587,7 @@ if __name__ == "__main__":
4: ['new students this semester', users_new_this_semester],
5: ['all semester courses + teachers', all_sem_courses_teachers],
6: ['Populate schedule table and correlate to courses table', courses_to_sched],
7: ['refresh db schedule 1 semester', refresh_semester_schedule_db],
}

View File

@ -124,7 +124,7 @@ HTM,skill,cwp'''
areas_csv = '''Area,AreaCode,DeanCode,DeanName
Nursing and Allied Health,nah,et,[Dean: Nursing/Allied Health]
Career Education and Workforce Pathways,cwp,ss,Vins Chacko
Career Education and Workforce Pathways,cwp,vc,Vins Chacko
Arts Humanities and Social Sciences,ahss,nl,[Dean: AHSS]
Counseling,c,de,Diego Espinoza
Student Support and Special Programs,sssp,de,Diego Espinoza
@ -1084,6 +1084,8 @@ def find_goo_for_name(name_dict,name):
return ''
# All teachers' teaching history
# need schedule files to exist and be current here:
# \canvasapp\cache\schedules
def teachers_history():
# Let's just make a giant dict of names we know about -> G numbers
@ -1129,8 +1131,6 @@ def teachers_history():
all_sections_by_goo = defaultdict(list)
#non_matches_file = codecs.open('cache/nonmatches.txt','w','utf-8')
# Define the regex pattern
pattern = re.compile(r"(\w\w\d\d)_sched_expanded\.json")

View File

@ -24,6 +24,7 @@ def short_to_sis(s):
return "20" + s[2:5] + s_to_n[season]
# go from sp20 to 2020spring
def short_to_long(s):
parts = re.search(r'(\w\w)(\d\d)', s)

22
ssb.py
View File

@ -337,6 +337,9 @@ def scrape_schedule(short_sem, semester_label):
writepage(driver.page_source)
print(driver.title)
# switch to new tab
driver.switch_to.window(driver.window_handles[1])
driver.find_element(By.LINK_TEXT,"Registration").click()
print('registration')
driver.implicitly_wait(5)
@ -421,6 +424,7 @@ def scrape_schedule(short_sem, semester_label):
except Exception as e:
print("Couldn't Diff.")
print("Got an exception: ", e)
return 2
# fname = pathlib.Path('test.py')
# assert fname.exists(), f'No such file: {fname}' # check that the file exists
# print(fname.stat())
@ -438,14 +442,14 @@ def scrape_schedule(short_sem, semester_label):
except Exception as e:
print("Got an exception: ", e)
#print("There was an error: " + e.args[0] + ". The line where the code failed was " + str(traceback.extract_stack()))
return 1
finally:
driver.quit()
return 0
def expanded(as_dict, short_sem):
#as_dict = scrape_schedule()
def expanded(short_sem):
course_to_gp, course_to_area, areacode_to_area, area_to_dean, course_to_dean, dean_code_to_name = schedules.campus_dept_hierarchy()
expanded = list_latestarts(short_sem)
@ -582,9 +586,11 @@ with open('cache/to_scrape.json', 'r') as f:
# Loop through each item and call the function
for item in semesters:
as_dict = scrape_schedule(item['short_sem'], item['sem'])
ex = expanded(as_dict, item['short_sem'])
print(f"Done with {item['sem']}. Sleeping 45 seconds.")
time.sleep(45)
result = scrape_schedule(item['short_sem'], item['sem'])
if result == 0:
ex = expanded(item['short_sem'])
print(f"Done with {item['sem']}. Sleeping 45 seconds.")
time.sleep(45)
else:
print(f"Stopped due to error: {result}")

View File

@ -20,7 +20,7 @@ from collections import defaultdict
from time import gmtime, strftime
from time import mktime
from semesters import sem_to_short
from semesters import human_to_short
from canvas_secrets import badgr_target, badgr_hd
@ -167,6 +167,7 @@ def send_email(fullname, firstname, addr, subj, content):
def convert_to_pdf(name1, name2):
wd = 'C:\\Users\\peter\\Documents\\gavilan\\canvasapp\\'
wd = 'I:/canvasapp/'
wd = 'C:/Users/phowell/source/repos/canvasapp/'
print( wd + name1 )
try:
word = win32.DispatchEx("Word.Application")
@ -182,16 +183,27 @@ def convert_to_pdf(name1, name2):
# Build (docx/pdf) certificates for gott graduates
def certificates_gott_build():
course = "gott_1_su25"
coursedate = "Summer 2025"
certificate = "gott 1 template.docx"
#course = "gott_4_su25"
#certificate = "gott 4 template.docx"
i = 0
for row in csv.reader( open('cache/completers_gott2_wi24.csv','r'), delimiter=','):
for row in csv.reader( open(f'cache/completers_{course}.csv','r'), delimiter=','):
i += 1
if i < 3: continue
if i < 2: continue
print(row[0])
lname,fname = row[0].split(",")
name = fname.strip() + " " + lname.strip()
doc = DocxTemplate("cache/certificates/gott 2 template.docx")
doc.render({ 'name' : row[0] })
fn = "cache/certificates/gott_2_wi24%s." % re.sub('\s', '_', name.lower())
try:
lname,fname = row[0].split(",")
name = fname.strip() + " " + lname.strip()
except Exception as e:
name = row[0].strip()
doc = DocxTemplate(f"cache/certificates/{certificate}")
doc.render({ 'name' : name, 'coursedate': coursedate })
name_as_filename = re.sub('\s', '_', name.lower())
fn = f"cache/certificates/{course}_{name_as_filename}."
print(fn+'docx')
doc.save(fn+'docx')
convert_to_pdf(fn+'docx', fn+'pdf')
@ -1309,7 +1321,7 @@ if __name__ == "__main__":
15: ['create a week calendar in word (general purpose)', word_calendar_v2],
16: ['create GOTT certificates', certificates_gott_build],
20: ['build_quiz', build_quiz],
21: ['certificates_gott_build, certificates_gott_build']
#21: ['certificates_gott_build, certificates_gott_build']
}
if len(sys.argv) > 1 and re.search(r'^\d+',sys.argv[1]):

View File

@ -1,4 +1,5 @@
from importlib import reload
import json, codecs, requests, re, pdb, csv, textdistance, collections
import sys, csv, string, funcy, math, shutil, os
import pytz, time
@ -20,6 +21,7 @@ from schedules import campus_dept_hierarchy
from util import dept_from_name, most_common_item
from os.path import exists, getmtime
from localcache2 import refresh_semester_schedule_db
from canvas_secrets import url
from dateutil import parser
@ -2206,10 +2208,11 @@ def cross_ref_training():
wb = load_workbook("C:/Users/phowell/Downloads/GOTT_Completion_masterlist 2023 DEC.xlsx")
print(wb.sheetnames)
term = "202570"
# Fetch from Canvas DB. Make sure its recently updated.
# Also relies on schedule being in database. Run localcache2.courses_to_sched()
courses = all_2x_sem_courses_teachers('202550', '202570') #
#courses = all_sem_courses_teachers('202470')
#courses = all_2x_sem_courses_teachers('202550', '202570') #
courses = all_sem_courses_teachers(term)
# report for email
@ -2220,6 +2223,13 @@ def cross_ref_training():
ask = input('download new list of teachers? (y/n) ')
if ask.strip()=='y': RELOAD_TEACHERS = 1
RELOAD_SCHEDULE = 0
ask2 = input('refresh schedule db? (y/n) ')
if ask2.strip()=='y': RELOAD_SCHEDULE = 1
if RELOAD_SCHEDULE:
refresh_semester_schedule_db(term)
if RELOAD_TEACHERS:
teacherRolesUpdateCache()
@ -2257,7 +2267,9 @@ def cross_ref_training():
if record[0]=='Name': continue
print(f" {goo}")
try:
if record[4]=="None":
if len(record)<3:
by_goo[goo][course] = "ok"
elif record[4]=="None":
by_goo[goo][course] = "ok"
else:
by_goo[goo][course] = record[4] # record the end date
@ -2275,11 +2287,11 @@ def cross_ref_training():
for c in courses:
print(c)
try:
goo = c[8]
crn = c[4]
goo = c[6] ## is this changing? c[8]
crn = c[8]
name = c[1] # full course name
teacher = c[6] # last, first
ctype = c[3]
teacher = c[4] # last, first
ctype = c[7]
dept1 = re.search(r'([A-Z]+)(\d+)',c[2].split(' ')[0]).group(1)
alldepts.add(dept1)
d = list(c)
@ -2302,7 +2314,7 @@ def cross_ref_training():
deptfont = Font(bold=True)
flagfont = PatternFill("solid", fgColor="00FFFFCC")
for thedean in ['et','nl','ss','jn', 'de']:
for thedean in ['et','nl','vc','jn', 'de']:
sheet.cell(row=r, column=1).value = dean_code_to_name[thedean]
sheet.cell(row=r, column=1).font = deptfont
r += 2
@ -2325,8 +2337,8 @@ def cross_ref_training():
waived = 0
sects = teachers[t]
print(f"Sections for {t}: {sects}")
goo = sects[0][8]
course_mode = sects[0][3]
goo = sects[0][6]
course_mode = sects[0][7]
print(t)
sheet.cell(row=r, column=1).value = f"{t}"
sheet.cell(row=r, column=2).value = f"{goo}"
@ -2573,6 +2585,48 @@ def get_port_pages(id=0):
p = fetch(f"{url}/api/v1/eportfolios/{id}/pages")
print(json.dumps(p, indent=2))
def set_email_skip_confirm():
user_id = '76741'
old_email = 'scalhoun@gavilan.edu'
new_email = 'scalhoun@hartnell.edu'
create_url = url + f'/api/v1/users/{user_id}/communication_channels'
print(create_url)
list_channels = fetch(create_url)
print(json.dumps(list_channels,indent=2))
for ch in list_channels:
id = ch['id']
result = requests.delete(create_url+f"/{id}", headers=header)
print(f"deleting id {id}")
print(result.content)
print('after deleting:')
list_channels = fetch(create_url)
print(json.dumps(list_channels,indent=2))
print('\n\n')
body = {
'communication_channel[address]' : old_email,
'communication_channel[type]' : 'email',
'skip_confirmation': True
}
response = requests.post(create_url, headers=header, data = body)
print(response.json()) #confirm that the channel was created
body = {
'communication_channel[address]' : new_email,
'communication_channel[type]' : 'email',
'skip_confirmation': True
}
response = requests.post(create_url, headers=header, data = body)
print(response.json()) #confirm that the channel was created
print('after creating:')
list_channels = fetch(create_url)
print(json.dumps(list_channels,indent=2))
print('\n\n')
if __name__ == "__main__":
@ -2607,6 +2661,8 @@ if __name__ == "__main__":
30: ['get portfolios for user id', get_portfolios],
31: ['get portfolio pages for portfolio id', get_port_pages],
40: ['reset user email without confirmation', set_email_skip_confirm]
#3: ['Main index, 1 year, teachers and their classes', getAllTeachersInTerm],
#5: ['Match names in schedule & ilearn', match_usernames],
#6: ['Create Dept\'s ZTC list', create_ztc_list],