catchup
This commit is contained in:
parent
68e48ec6ac
commit
0bddc4a3e4
37
courses.py
37
courses.py
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
193
degrees.py
193
degrees.py
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
18
ssb.py
18
ssb.py
|
|
@ -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'])
|
||||
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}")
|
||||
|
||||
|
|
|
|||
26
tasks.py
26
tasks.py
|
|
@ -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])
|
||||
try:
|
||||
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())
|
||||
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]):
|
||||
|
|
|
|||
76
users.py
76
users.py
|
|
@ -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],
|
||||
|
|
|
|||
Loading…
Reference in New Issue