diff --git a/conditional/blueprints/dashboard.py b/conditional/blueprints/dashboard.py
index 527e8dd..1a75d46 100644
--- a/conditional/blueprints/dashboard.py
+++ b/conditional/blueprints/dashboard.py
@@ -36,6 +36,7 @@ def display_dashboard(user_dict=None):
log.info('display dashboard')
# Get the list of voting members.
+
can_vote = get_voting_members()
data = {}
diff --git a/conditional/blueprints/spring_evals.py b/conditional/blueprints/spring_evals.py
index 212ca88..8cc50c3 100644
--- a/conditional/blueprints/spring_evals.py
+++ b/conditional/blueprints/spring_evals.py
@@ -1,15 +1,14 @@
import structlog
from flask import Blueprint, request
+from sqlalchemy import func
from conditional import db, start_of_year, auth
-from conditional.models.models import HouseMeeting
-from conditional.models.models import MajorProject
-from conditional.models.models import MemberHouseMeetingAttendance
-from conditional.models.models import SpringEval
+from conditional.models.models import CommitteeMeeting, HouseMeeting, MemberCommitteeAttendance
+from conditional.models.models import MajorProject, MemberHouseMeetingAttendance, SpringEval
from conditional.util.auth import get_user
from conditional.util.flask import render_template
from conditional.util.ldap import ldap_get_active_members
-from conditional.util.member import get_cm, get_hm, req_cm
+from conditional.util.member import req_cm
spring_evals_bp = Blueprint('spring_evals_bp', __name__)
@@ -25,6 +24,52 @@ def display_spring_evals(internal=False, user_dict=None):
active_members = ldap_get_active_members()
+ cm_count = dict([tuple(row) for row in MemberCommitteeAttendance.query.join(
+ CommitteeMeeting,
+ MemberCommitteeAttendance.meeting_id == CommitteeMeeting.id
+ ).with_entities(
+ MemberCommitteeAttendance.uid,
+ CommitteeMeeting.timestamp,
+ CommitteeMeeting.approved,
+ ).filter(
+ CommitteeMeeting.approved,
+ CommitteeMeeting.timestamp >= start_of_year()
+ ).with_entities(
+ MemberCommitteeAttendance.uid,
+ func.count(MemberCommitteeAttendance.uid) #pylint: disable=not-callable
+ ).group_by(
+ MemberCommitteeAttendance.uid
+ ).all()])
+
+ hm_missed = dict([tuple(row) for row in MemberHouseMeetingAttendance.query.join(
+ HouseMeeting,
+ MemberHouseMeetingAttendance.meeting_id == HouseMeeting.id
+ ).filter(
+ HouseMeeting.date >= start_of_year(),
+ MemberHouseMeetingAttendance.attendance_status == 'Absent'
+ ).with_entities(
+ MemberHouseMeetingAttendance.uid,
+ func.count(MemberHouseMeetingAttendance.uid) #pylint: disable=not-callable
+ ).group_by(
+ MemberHouseMeetingAttendance.uid
+ ).all()])
+
+ major_project_query = MajorProject.query.filter(
+ MajorProject.date >= start_of_year()
+ ).all()
+
+ major_projects = {}
+
+ for project in major_project_query:
+ if not project.uid in major_projects:
+ major_projects[project.uid] = []
+
+ major_projects.get(project.uid).append({
+ 'name': project.name,
+ 'status': project.status,
+ 'description': project.description
+ })
+
sp_members = []
for account in active_members:
uid = account.uid
@@ -38,64 +83,45 @@ def display_spring_evals(internal=False, user_dict=None):
db.session.add(spring_entry)
db.session.flush()
db.session.commit()
- elif spring_entry.status != "Pending" and internal:
+ elif spring_entry.status != 'Pending' and internal:
continue
- eval_data = None
+ member_missed_hms = []
+
+ if hm_missed.get(uid, 0) != 0:
+ member_missed_hms = MemberHouseMeetingAttendance.query.join(
+ HouseMeeting,
+ MemberHouseMeetingAttendance.meeting_id == HouseMeeting.id
+ ).filter(
+ HouseMeeting.date >= start_of_year(),
+ MemberHouseMeetingAttendance.attendance_status == 'Absent',
+ MemberHouseMeetingAttendance.uid == uid,
+ ).with_entities(
+ func.array_agg(HouseMeeting.date)
+ ).scalar()
- h_meetings = [m.meeting_id for m in get_hm(account, only_absent=True)]
member = {
'name': account.cn,
'uid': uid,
'status': spring_entry.status,
- 'committee_meetings': len(get_cm(account)),
+ 'committee_meetings': cm_count.get(uid, 0),
'req_meetings': req_cm(account),
- 'house_meetings_missed':
- [
- {
- "date": m.date.strftime("%Y-%m-%d"),
- "reason":
- MemberHouseMeetingAttendance.query.filter(
- MemberHouseMeetingAttendance.uid == uid).filter(
- MemberHouseMeetingAttendance.meeting_id == m.id).first().excuse
- }
- for m in HouseMeeting.query.filter(
- HouseMeeting.id.in_(h_meetings)
- )
- ],
- 'major_projects': [
- {
- 'name': p.name,
- 'status': p.status,
- 'description': p.description
- } for p in MajorProject.query.filter(
- MajorProject.date > start_of_year(),
- MajorProject.uid == uid)]
+ 'house_meetings_missed': member_missed_hms,
+ 'major_projects': major_projects.get(uid, [])
}
+
+ passed_mps = [project for project in member['major_projects'] if project['status'] == 'Passed']
+
member['major_projects_len'] = len(member['major_projects'])
- member['major_projects_passed'] = [
- {
- 'name': p.name,
- 'status': p.status,
- 'description': p.description
- } for p in MajorProject.query.filter(
- MajorProject.date > start_of_year(),
- MajorProject.status == "Passed",
- MajorProject.uid == uid)]
+ member['major_projects_passed'] = passed_mps
member['major_projects_passed_len'] = len(member['major_projects_passed'])
- member['major_project_passed'] = False
- for mp in member['major_projects']:
- if mp['status'] == "Passed":
- member['major_project_passed'] = True
- break
-
- if internal:
- member['housing_evals'] = eval_data
+ member['major_project_passed'] = member['major_projects_passed_len'] > 0
+
sp_members.append(member)
sp_members.sort(key=lambda x: x['committee_meetings'], reverse=True)
sp_members.sort(key=lambda x: len(x['house_meetings_missed']))
- sp_members.sort(key=lambda x: len([p for p in x['major_projects'] if p['status'] == "Passed"]), reverse=True)
+ sp_members.sort(key=lambda x: x['major_projects_passed_len'], reverse=True)
# return names in 'first last (username)' format
if internal:
return sp_members
diff --git a/conditional/templates/spring_evals.html b/conditional/templates/spring_evals.html
index 8f4e4e8..6b9d265 100644
--- a/conditional/templates/spring_evals.html
+++ b/conditional/templates/spring_evals.html
@@ -114,8 +114,8 @@
Missed House Meetings
{% for hm_missed in m['house_meetings_missed'] %}
- | {{hm_missed['date']}} |
- {{hm_missed['reason']}} |
+ {{hm_missed}} |
+ |
{% endfor %}
diff --git a/conditional/util/housing.py b/conditional/util/housing.py
index 74a9200..b620340 100644
--- a/conditional/util/housing.py
+++ b/conditional/util/housing.py
@@ -2,8 +2,7 @@
from conditional.models.models import InHousingQueue
from conditional.models.models import OnFloorStatusAssigned
-from conditional.util.ldap import ldap_get_current_students
-from conditional.util.ldap import ldap_is_onfloor
+from conditional.util.ldap import ldap_get_member, ldap_is_current_student
def get_housing_queue(is_eval_director=False):
@@ -12,23 +11,34 @@ def get_housing_queue(is_eval_director=False):
# and {'time': } is the value. We are doing a left
# outer join on the two tables to get a single result that has
# both the member's UID and their on-floor datetime.
- in_queue = {entry.uid: {'time': entry.onfloor_granted} for entry
- in InHousingQueue.query.outerjoin(OnFloorStatusAssigned,
- OnFloorStatusAssigned.uid == InHousingQueue.uid)\
- .with_entities(InHousingQueue.uid, OnFloorStatusAssigned.onfloor_granted)\
- .all()}
+ in_queue = {
+ entry.uid: {
+ 'time': entry.onfloor_granted
+ } for entry in InHousingQueue.query.outerjoin(
+ OnFloorStatusAssigned,
+ OnFloorStatusAssigned.uid == InHousingQueue.uid
+ ).with_entities(
+ InHousingQueue.uid,
+ OnFloorStatusAssigned.onfloor_granted
+ ).all()
+ }
+
+ # CSHMember accounts that are in queue
+ potential_accounts = [ldap_get_member(username) for username in in_queue]
# Populate a list of dictionaries containing the name, username,
- # and on-floor datetime for each member who has on-floor status,
- # is not already assigned to a room and is in the above query.
- queue = [{"uid": account.uid,
- "name": account.cn,
- "points": account.housingPoints,
- "time": in_queue.get(account.uid, {}).get('time', datetime.now()) or datetime.now(),
- "in_queue": account.uid in in_queue}
- for account in ldap_get_current_students()
- if ldap_is_onfloor(account) and (is_eval_director or account.uid in in_queue)
- and account.roomNumber is None]
+ # and on-floor datetime for each current studetn who has on-floor status
+ # and is not already assigned to a room
+ queue = [
+ {
+ "uid": account.uid,
+ "name": account.cn,
+ "points": account.housingPoints,
+ "time": in_queue.get(account.uid, {}).get('time', datetime.now()) or datetime.now(),
+ "in_queue": account.uid in in_queue
+ } for account in potential_accounts
+ if ldap_is_current_student(account) and (is_eval_director or account.roomNumber is None)
+ ]
# Sort based on time (ascending) and then points (decending).
queue.sort(key=lambda m: m['time'])
@@ -40,8 +50,7 @@ def get_housing_queue(is_eval_director=False):
def get_queue_position(username):
queue = get_housing_queue()
try:
- index = next(index for (index, d) in enumerate(get_housing_queue())
- if d["uid"] == username) + 1
+ index = next(index for (index, d) in enumerate(queue) if d["uid"] == username) + 1
except (KeyError, StopIteration):
index = None
return index, len(queue)
diff --git a/conditional/util/ldap.py b/conditional/util/ldap.py
index 98cb853..01cb53d 100644
--- a/conditional/util/ldap.py
+++ b/conditional/util/ldap.py
@@ -1,65 +1,57 @@
+from csh_ldap import CSHMember
+
from conditional import ldap
from conditional.util.cache import service_cache
-
-def _ldap_get_group_members(group):
+def _ldap_get_group_members(group: str) -> list[CSHMember]:
return ldap.get_group(group).get_members()
-def _ldap_is_member_of_group(member, group):
- group_list = member.get("memberOf")
- for group_dn in group_list:
- if group == group_dn.split(",")[0][3:]:
- return True
- return False
+def _ldap_is_member_of_group(member: CSHMember, group: str) -> bool:
+ return ldap.get_group(group).check_member(member)
-def _ldap_add_member_to_group(account, group):
+def _ldap_add_member_to_group(account: CSHMember, group: str):
if not _ldap_is_member_of_group(account, group):
ldap.get_group(group).add_member(account, dn=False)
-def _ldap_remove_member_from_group(account, group):
+def _ldap_remove_member_from_group(account: CSHMember, group: str):
if _ldap_is_member_of_group(account, group):
ldap.get_group(group).del_member(account, dn=False)
@service_cache(maxsize=256)
-def _ldap_is_member_of_directorship(account, directorship):
- directors = ldap.get_directorship_heads(directorship)
- for director in directors:
- if director.uid == account.uid:
- return True
- return False
-
+def _ldap_is_member_of_directorship(account: CSHMember, directorship: str):
+ return account.in_group(f'eboard-{directorship}', dn=True)
+# TODO: try in_group(ldap.get_group(f'eboard-{directorship}')) and profile
@service_cache(maxsize=1024)
-def ldap_get_member(username):
+def ldap_get_member(username: str) -> CSHMember:
return ldap.get_member(username, uid=True)
-
@service_cache(maxsize=1024)
-def ldap_get_active_members():
+def ldap_get_active_members() -> list[CSHMember]:
return _ldap_get_group_members("active")
@service_cache(maxsize=1024)
-def ldap_get_intro_members():
+def ldap_get_intro_members() -> list[CSHMember]:
return _ldap_get_group_members("intromembers")
@service_cache(maxsize=1024)
-def ldap_get_onfloor_members():
+def ldap_get_onfloor_members() -> list[CSHMember]:
return _ldap_get_group_members("onfloor")
@service_cache(maxsize=1024)
-def ldap_get_current_students():
+def ldap_get_current_students() -> list[CSHMember]:
return _ldap_get_group_members("current_student")
@service_cache(maxsize=128)
-def ldap_get_roomnumber(account):
+def ldap_get_roomnumber(account) -> str:
try:
return account.roomNumber
except AttributeError:
@@ -67,57 +59,57 @@ def ldap_get_roomnumber(account):
@service_cache(maxsize=128)
-def ldap_is_active(account):
+def ldap_is_active(account) -> bool:
return _ldap_is_member_of_group(account, 'active')
@service_cache(maxsize=128)
-def ldap_is_bad_standing(account):
+def ldap_is_bad_standing(account) -> bool:
return _ldap_is_member_of_group(account, 'bad_standing')
@service_cache(maxsize=128)
-def ldap_is_alumni(account):
+def ldap_is_alumni(account) -> bool:
# If the user is not active, they are an alumni.
return not _ldap_is_member_of_group(account, 'active')
@service_cache(maxsize=128)
-def ldap_is_eboard(account):
+def ldap_is_eboard(account) -> bool:
return _ldap_is_member_of_group(account, 'eboard')
@service_cache(maxsize=128)
-def ldap_is_rtp(account):
+def ldap_is_rtp(account) -> bool:
return _ldap_is_member_of_group(account, 'rtp')
@service_cache(maxsize=128)
-def ldap_is_intromember(account):
+def ldap_is_intromember(account) -> bool:
return _ldap_is_member_of_group(account, 'intromembers')
@service_cache(maxsize=128)
-def ldap_is_onfloor(account):
+def ldap_is_onfloor(account) -> bool:
return _ldap_is_member_of_group(account, 'onfloor')
@service_cache(maxsize=128)
-def ldap_is_financial_director(account):
+def ldap_is_financial_director(account) -> bool:
return _ldap_is_member_of_directorship(account, 'Financial')
@service_cache(maxsize=128)
-def ldap_is_eval_director(account):
+def ldap_is_eval_director(account) -> bool:
return _ldap_is_member_of_directorship(account, 'Evaluations')
@service_cache(maxsize=256)
-def ldap_is_current_student(account):
+def ldap_is_current_student(account) -> bool:
return _ldap_is_member_of_group(account, 'current_student')
-def ldap_set_housingpoints(account, housing_points):
+def ldap_set_housingpoints(account, housing_points) -> bool:
account.housingPoints = housing_points
ldap_get_current_students.cache_clear()
ldap_get_member.cache_clear()
diff --git a/conditional/util/member.py b/conditional/util/member.py
index b1a26b8..f1f3bbc 100644
--- a/conditional/util/member.py
+++ b/conditional/util/member.py
@@ -1,4 +1,5 @@
from datetime import datetime
+from sqlalchemy import func, or_
from conditional import start_of_year
from conditional.models.models import CommitteeMeeting
@@ -20,34 +21,6 @@
from conditional.util.ldap import ldap_is_intromember
from conditional.util.ldap import ldap_get_member
-
-@service_cache(maxsize=1024)
-def get_voting_members():
-
- if datetime.today() < datetime(start_of_year().year, 12, 31):
- semester = 'Fall'
- else:
- semester = 'Spring'
-
- active_members = set(member.uid for member in ldap_get_active_members())
- intro_members = set(member.uid for member in ldap_get_intro_members())
- on_coop = set(member.uid for member in CurrentCoops.query.filter(
- CurrentCoops.date_created > start_of_year(),
- CurrentCoops.semester == semester).all())
- voting_set = active_members - intro_members - on_coop
-
- passed_fall = FreshmanEvalData.query.filter(
- FreshmanEvalData.freshman_eval_result == "Passed",
- FreshmanEvalData.eval_date > start_of_year()
- ).distinct()
-
- for intro_member in passed_fall:
- voting_set.add(intro_member.uid)
-
- voting_list = list(username for username in voting_set if gatekeep_status(username)["result"])
- return voting_list
-
-
@service_cache(maxsize=1024)
def get_members_info():
members = ldap_get_current_students()
@@ -117,22 +90,25 @@ def get_onfloor_members():
def get_cm(member):
+ query_result = CommitteeMeeting.query.join(
+ MemberCommitteeAttendance,
+ MemberCommitteeAttendance.meeting_id == CommitteeMeeting.id
+ ).with_entities(
+ MemberCommitteeAttendance.uid,
+ CommitteeMeeting.timestamp,
+ CommitteeMeeting.committee
+ ).filter(
+ CommitteeMeeting.timestamp > start_of_year(),
+ MemberCommitteeAttendance.uid == member.uid,
+ CommitteeMeeting.approved
+ ).all()
+
c_meetings = [{
"uid": cm.uid,
"timestamp": cm.timestamp,
"committee": cm.committee
- } for cm in CommitteeMeeting.query.join(
- MemberCommitteeAttendance,
- MemberCommitteeAttendance.meeting_id == CommitteeMeeting.id
- ).with_entities(
- MemberCommitteeAttendance.uid,
- CommitteeMeeting.timestamp,
- CommitteeMeeting.committee
- ).filter(
- CommitteeMeeting.timestamp > start_of_year(),
- MemberCommitteeAttendance.uid == member.uid,
- CommitteeMeeting.approved == True # pylint: disable=singleton-comparison
- ).all()]
+ } for cm in query_result]
+
return c_meetings
@@ -161,6 +137,102 @@ def req_cm(member):
return 15
return 30
+@service_cache(maxsize=256)
+def get_voting_members():
+ if datetime.today() < datetime(start_of_year().year, 12, 31):
+ semester = "Fall"
+ semester_start = datetime(start_of_year().year,6,1)
+ else:
+ semester = "Spring"
+ semester_start = datetime(start_of_year().year + 1,1,1)
+
+ active_members = set(ldap_get_active_members())
+ intro_members = set(ldap_get_intro_members())
+
+ coop_members = CurrentCoops.query.filter(
+ CurrentCoops.date_created > start_of_year(),
+ CurrentCoops.semester == semester,
+ ).with_entities(
+ func.array_agg(CurrentCoops.uid)
+ ).scalar()
+
+ # have to do this because if it's none then set constructor screams
+ if coop_members is None:
+ coop_members = set()
+ else:
+ coop_members = set(coop_members)
+
+ passed_fall_members = FreshmanEvalData.query.filter(
+ FreshmanEvalData.freshman_eval_result == "Passed",
+ FreshmanEvalData.eval_date > start_of_year(),
+ ).with_entities(
+ func.array_agg(FreshmanEvalData.uid)
+ ).scalar()
+
+ if passed_fall_members is None:
+ passed_fall_members = set()
+ else:
+ passed_fall_members = set(passed_fall_members)
+
+ active_not_intro = active_members - intro_members
+ active_not_intro = set(map(lambda member: member.uid, active_not_intro))
+
+ elligible_members = (active_not_intro - coop_members) | passed_fall_members
+
+ passing_dm = set(member.uid for member in MemberCommitteeAttendance.query.join(
+ CommitteeMeeting,
+ MemberCommitteeAttendance.meeting_id == CommitteeMeeting.id
+ ).with_entities(
+ MemberCommitteeAttendance.uid,
+ CommitteeMeeting.timestamp,
+ CommitteeMeeting.approved,
+ ).filter(
+ CommitteeMeeting.approved,
+ CommitteeMeeting.timestamp >= semester_start
+ ).with_entities(
+ MemberCommitteeAttendance.uid
+ ).group_by(
+ MemberCommitteeAttendance.uid
+ ).having(
+ func.count(MemberCommitteeAttendance.uid) >= 6 #pylint: disable=not-callable
+ ).with_entities(
+ MemberCommitteeAttendance.uid
+ ).all())
+
+ passing_ts = set(member.uid for member in MemberSeminarAttendance.query.join(
+ TechnicalSeminar,
+ MemberSeminarAttendance.seminar_id == TechnicalSeminar.id
+ ).filter(
+ TechnicalSeminar.approved,
+ TechnicalSeminar.timestamp >= semester_start
+ ).with_entities(
+ MemberSeminarAttendance.uid
+ ).group_by(
+ MemberSeminarAttendance.uid
+ ).having(
+ func.count(MemberSeminarAttendance.uid) >= 2 #pylint: disable=not-callable
+ ).all())
+
+ passing_hm = set(member.uid for member in MemberHouseMeetingAttendance.query.join(
+ HouseMeeting,
+ MemberHouseMeetingAttendance.meeting_id == HouseMeeting.id
+ ).filter(
+ HouseMeeting.date >= semester_start, or_(
+ MemberHouseMeetingAttendance.attendance_status == 'Attended',
+ # MemberHouseMeetingAttendance.attendance_status == 'Excused'
+ )
+ ).with_entities(
+ MemberHouseMeetingAttendance.uid
+ ).group_by(
+ MemberHouseMeetingAttendance.uid
+ ).having(
+ func.count(MemberHouseMeetingAttendance.uid) >= 6 #pylint: disable=not-callable
+ ).all())
+
+ passing_reqs = passing_dm & passing_ts & passing_hm
+
+ return elligible_members & passing_reqs
+
def gatekeep_status(username):
if datetime.today() < datetime(start_of_year().year, 12, 31):
semester = "Fall"
diff --git a/requirements.in b/requirements.in
index dcb502e..14fc826 100644
--- a/requirements.in
+++ b/requirements.in
@@ -1,8 +1,8 @@
alembic~=1.15.1
astroid~=3.3.9
blinker~=1.4
-csh_ldap>=2.3.1
-ddtrace~=3.2.1
+csh_ldap>=2.5.3
+ddtrace~=4.2.1
Flask~=3.1.0
Flask-Migrate~=2.1.1
Flask-Gzip~=0.2
diff --git a/requirements.txt b/requirements.txt
index 6508a12..dc93cb8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
-# uv pip compile requirements.in
+# uv pip compile requirements.in -o requirements.txt
alembic==1.15.2
# via
# -r requirements.in
@@ -15,11 +15,11 @@ blinker==1.9.0
# -r requirements.in
# flask
# sentry-sdk
-build==1.3.0
+build==1.4.0
# via pip-tools
bytecode==0.17.0
# via ddtrace
-certifi==2025.10.5
+certifi==2026.1.4
# via
# requests
# sentry-sdk
@@ -27,16 +27,16 @@ cffi==2.0.0
# via cryptography
charset-normalizer==3.4.4
# via requests
-click==8.3.0
+click==8.3.1
# via
# -r requirements.in
# flask
# pip-tools
cryptography==46.0.3
# via oic
-csh-ldap==2.4.0
+csh-ldap==2.5.3
# via -r requirements.in
-ddtrace==3.2.3
+ddtrace==4.2.1
# via -r requirements.in
defusedxml==0.7.1
# via oic
@@ -66,13 +66,13 @@ flask-sqlalchemy==3.1.1
# flask-migrate
future==1.0.0
# via pyjwkest
-greenlet==3.2.4
+greenlet==3.3.0
# via sqlalchemy
-gunicorn==20.1.0
+gunicorn==22.0.0
# via -r requirements.in
idna==3.11
# via requests
-importlib-metadata==8.7.0
+importlib-metadata==8.7.1
# via opentelemetry-api
importlib-resources==6.5.2
# via flask-pyoidc
@@ -111,21 +111,21 @@ oic==1.6.1
# via
# -r requirements.in
# flask-pyoidc
-opentelemetry-api==1.38.0
+opentelemetry-api==1.39.1
# via ddtrace
packaging==25.0
- # via build
-pip==25.2
+ # via
+ # build
+ # gunicorn
+pip==25.3
# via pip-tools
pip-tools==7.4.1
# via -r requirements.in
-platformdirs==4.5.0
+platformdirs==4.5.1
# via pylint
-protobuf==6.33.0
- # via ddtrace
psycopg2-binary==2.9.11
# via -r requirements.in
-pyasn1==0.6.1
+pyasn1==0.6.2
# via
# pyasn1-modules
# python-ldap
@@ -137,11 +137,11 @@ pycryptodomex==3.23.0
# via
# oic
# pyjwkest
-pydantic==2.12.3
+pydantic==2.12.5
# via pydantic-settings
-pydantic-core==2.41.4
+pydantic-core==2.41.5
# via pydantic
-pydantic-settings==2.11.0
+pydantic-settings==2.12.0
# via oic
pyjwkest==1.4.4
# via oic
@@ -153,11 +153,11 @@ pyproject-hooks==1.2.0
# pip-tools
python-dateutil==2.6.1
# via -r requirements.in
-python-dotenv==1.1.1
+python-dotenv==1.2.1
# via pydantic-settings
python-editor==1.0.4
# via -r requirements.in
-python-ldap==3.4.0
+python-ldap==3.4.5
# via csh-ldap
requests==2.32.5
# via
@@ -167,30 +167,27 @@ requests==2.32.5
sentry-sdk==2.24.1
# via -r requirements.in
setuptools==80.9.0
- # via
- # gunicorn
- # pip-tools
+ # via pip-tools
six==1.17.0
# via
# -r requirements.in
# pyjwkest
# python-dateutil
# structlog
-sqlalchemy==2.0.44
+sqlalchemy==2.0.45
# via
# -r requirements.in
# alembic
# flask-sqlalchemy
-srvlookup==2.0.0
+srvlookup==3.0.0
# via csh-ldap
structlog==18.1.0
# via -r requirements.in
-tomlkit==0.13.3
+tomlkit==0.14.0
# via pylint
typing-extensions==4.15.0
# via
# alembic
- # ddtrace
# opentelemetry-api
# pydantic
# pydantic-core
@@ -200,11 +197,11 @@ typing-inspection==0.4.2
# via
# pydantic
# pydantic-settings
-urllib3==2.5.0
+urllib3==2.6.3
# via
# requests
# sentry-sdk
-werkzeug==3.1.3
+werkzeug==3.1.5
# via
# -r requirements.in
# flask
@@ -214,7 +211,5 @@ wrapt==1.17.3
# via
# -r requirements.in
# ddtrace
-xmltodict==1.0.2
- # via ddtrace
zipp==3.23.0
# via importlib-metadata