diff --git a/.gitignore b/.gitignore index a37cb82..42ccdd0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ -__pycache__/ -build/ -dist/ -other/ -*.egg-info/ -*.pdf -*.spec +__pycache__/ +build/ +dist/ +other/ +*.egg-info/ +*.pdf +*.spec diff --git a/.python-version b/.python-version index e4fba21..3d2704b 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.12 +3.12 diff --git a/README.md b/README.md index 63d59f8..4c8810d 100644 --- a/README.md +++ b/README.md @@ -1,165 +1,167 @@ -# EVA 值班排班工具 - -## 环境配置 - -本项目使用 `uv` 进行包管理。 - -安装项目中所需要的所有包的最新版本。其中 `pyside6` 是一个前端库,`pyinstaller` 用于打包项目,`ortools` 是一个高效的组合优化求解器,`pandas` 用于处理表格数据: -```bash -uv sync -``` - -## 项目结构 -``` -EVA_duty_arrange_tool/ - ├─ main.py // 主函数,定义了前端界面和组件的回调函数 - ├─ solve.py // 定义了值班排班问题的求解函数 - ├─ utils.py // 定义了读取、写入 excel 的函数 - ├─ pics // 储存了说明文档中用到的图片 - │ ├─ *.jpg/*.png - ├─ *.md // 说明文档 - ├─ *.xlsx // 测试用例 -``` - -## 项目运行 & 打包 -项目运行方式: -```bash -uv run main.py -``` - -本项目使用 `pyinstaller` 工具进行打包。如果要打包,请确保能够正常运行项目。打包命令如下: -```bash -uv run pyinstaller --onefile --windowed --name=EVA_duty_arrange_tool main.py -``` - -## 数学原理 -本项目将值班排班问题建模为了一个[组合优化](https://zh.wikipedia.org/wiki/%E7%BB%84%E5%90%88%E4%BC%98%E5%8C%96)问题,使用 Google 的 OR-Tools 求解器中的 SCIP 求解器进行求解。 - -### 问题描述 - -在本问题中,优化目标是一个涉及多个方面的量化指标: -- **目标1**:每一班同学数量的平均程度 -- **目标2**:每一班技术部老人数量接近期望值 -- **目标3**:每一班技术部小朋友数量接近期望值 -- **目标4**:每一班人资部小朋友数量接近期望值 -- **目标5**:每一班各部门人数的平均程度 - -在本问题中,主要约束是: -1. 让每位同学每周的班次数符合意愿(特别地,选择3次的同学可以安排2-3次) -2. 让每位同学只在自己有空的时间段值班 -3. 每班次的总人数在指定范围内 -4. 每班次技术部老人数量在指定范围内 -5. 每班次老人总数在指定范围内 -6. 每班次小朋友总数在指定范围内 - -### 数学建模 - -设一共有 $n$ 位同学,$m$ 个值班的班次,协会共有 $t=5$ 个部门(电脑部、电器部、人资部、财外部、文宣部),定义以下符号: - -**决策变量:** -- $x_{ij} \in \{0,1\}, \quad i=1,2,\dots,n,\quad j=1,2,\dots,m$ 表示第 $i$ 位同学是否值第 $j$ 班 - -**已知参数:** -- $N_i \in \mathbb{N}, \quad i=1,2,\dots,n$ 表示第 $i$ 个同学每周愿意值班次数 -- $v_{ij} \in \{0,1\}, \quad i=1,2,\dots,n,\quad j=1,2,\dots,m$ 表示第 $i$ 位同学是否有空值第 $j$ 班 -- $\text{old}_i \in \{0,1\}, \quad i=1,2,\dots,n$ 表示第 $i$ 位同学是否是老人(非小朋友) -- $d_{ik}\in\{0,1\}, \quad i=1,2,\dots,n,\quad k=1,2,\dots,t$ 表示第 $i$ 位同学是否属于第 $k$ 个部门 -- $\text{tech}_i = d_{i1} + d_{i2} \in \{0,1\}$ 表示第 $i$ 位同学是否属于技术部(电脑部或电器部) -- $\text{hr}_i = d_{i3} \in \{0,1\}$ 表示第 $i$ 位同学是否属于人资部 - -**派生量:** -- $M_j = \sum_{i=1}^{n} x_{ij}, \quad j=1,2,\dots,m$ 表示第 $j$ 班次实际安排的值班人数 -- $\bar{M} = \frac{\sum_{i=1}^{n}N_i}{m}$ 表示平均每班的人数 - -### 优化目标 - -为了处理多目标优化问题,我们采用**线性加权法**将多个目标组合成单一目标函数。引入归一化系数 $m_1=2.5, m_2=3, m_3=4, m_4=1, m_5=8$ 和权重 $w_1, w_2, w_3, w_4, w_5$,总目标函数为: - -$$\min \quad Z = w_1 \cdot \frac{X_1}{m_1 \cdot M} + w_2 \cdot \frac{X_2}{m_2 \cdot M} + w_3 \cdot \frac{X_3}{m_3 \cdot M} + w_4 \cdot \frac{X_4}{m_4 \cdot M} + w_5 \cdot \frac{X_5}{m_5 \cdot M}$$ - -各目标定义如下: - -**目标1:均衡班次人数** - -$$X_1 = \sum_{j=1}^{m} a_j^{(1)}$$ - -其中辅助变量 $a_j^{(1)} \in [0,+\infty)$ 满足: -$$a_j^{(1)} \ge M_j - \bar{M}, \quad a_j^{(1)} \ge \bar{M} - M_j, \quad \forall j$$ - -> 注:在组合优化问题中只能定义线性约束,不能直接使用绝对值。因此引入辅助变量 $a_j^{(1)}$ 并添加两个不等式约束,使得在优化过程中 $a_j^{(1)}$ 自动收敛到 $|M_j - \bar{M}|$。 - -**目标2:每班技术部老人数量接近期望值** - -期望每班有 $m_2 = 3$ 位技术部老人,定义缺口: - -$$X_2 = \sum_{j=1}^{m} a_j^{(2)}$$ - -其中辅助变量 $a_j^{(2)} \in [0,+\infty)$ 满足: -$$a_j^{(2)} \ge \sum_{i=1}^{n} x_{ij} \cdot \text{old}_i \cdot \text{tech}_i - m_2$$ -$$a_j^{(2)} \ge m_2 - \sum_{i=1}^{n} x_{ij} \cdot \text{old}_i \cdot \text{tech}_i, \quad \forall j$$ - -**目标3:每班技术部小朋友数量接近期望值** - -期望每班有 $m_3 = 4$ 位技术部小朋友,定义缺口: - -$$X_3 = \sum_{j=1}^{m} a_j^{(3)}$$ - -其中辅助变量 $a_j^{(3)} \in [0,+\infty)$ 满足: -$$a_j^{(3)} \ge \sum_{i=1}^{n} x_{ij} \cdot (1-\text{old}_i) \cdot \text{tech}_i - m_3$$ -$$a_j^{(3)} \ge m_3 - \sum_{i=1}^{n} x_{ij} \cdot (1-\text{old}_i) \cdot \text{tech}_i, \quad \forall j$$ - -**目标4:每班人资部小朋友数量接近期望值** - -期望每班有 $m_4 = 1$ 位人资部小朋友,定义缺口: - -$$X_4 = \sum_{j=1}^{m} a_j^{(4)}$$ - -其中辅助变量 $a_j^{(4)} \in [0,+\infty)$ 满足: -$$a_j^{(4)} \ge \sum_{i=1}^{n} x_{ij} \cdot (1-\text{old}_i) \cdot \text{hr}_i - m_4$$ -$$a_j^{(4)} \ge m_4 - \sum_{i=1}^{n} x_{ij} \cdot (1-\text{old}_i) \cdot \text{hr}_i, \quad \forall j$$ - -**目标5:均衡各部门人数分布** - -对每个班次 $j$ 和每个部门 $k$,定义该班次该部门的人数偏差: - -$$X_5 = \sum_{j=1}^{m} \sum_{k=1}^{t} a_{jk}^{(5)}$$ - -其中 $\bar{M}_j = \frac{M_j}{t}$ 表示第 $j$ 班次各部门的平均人数,$C_{jk} = \sum_{i=1}^{n} x_{ij} \cdot d_{ik}$ 表示第 $j$ 班次第 $k$ 部门的实际人数,辅助变量 $a_{jk}^{(5)} \in [0,+\infty)$ 满足: - -$$a_{jk}^{(5)} \ge C_{jk} - \bar{M}_j, \quad a_{jk}^{(5)} \ge \bar{M}_j - C_{jk}, \quad \forall j,k$$ - -### 约束条件 - -**基本约束:** - -1. **意愿班次约束**:每位同学的值班次数必须符合其意愿 - $$\sum_{j=1}^{m}x_{ij} = N_i, \quad \forall i \text{ where } N_i \neq 3$$ - $$2 \le \sum_{j=1}^{m}x_{ij} \le 3, \quad \forall i \text{ where } N_i = 3$$ - -2. **时间可行性约束**:只在有空的时间段排班 - $$x_{ij} \le v_{ij}, \quad \forall i,j$$ - -**班次人数约束:** - -3. **总人数约束**:每班次人数在 $[n_{\min}, n_{\max}]$ 范围内 - $$n_{\min} \le \sum_{i=1}^{n} x_{ij} \le n_{\max}, \quad \forall j$$ - -4. **技术部老人约束**:每班次技术部老人数在范围内 - $$n_{\text{tech\_old\_min}} \le \sum_{i=1}^{n} x_{ij} \cdot \text{old}_i \cdot \text{tech}_i \le n_{\text{tech\_old\_max}}, \quad \forall j$$ - -5. **老人总数约束**:每班次老人总数在范围内 - $$n_{\text{old\_min}} \le \sum_{i=1}^{n} x_{ij} \cdot \text{old}_i \le n_{\text{old\_max}}, \quad \forall j$$ - -6. **小朋友总数约束**:每班次小朋友总数在范围内 - $$n_{\text{new\_min}} \le \sum_{i=1}^{n} x_{ij} \cdot (1-\text{old}_i) \le n_{\text{new\_max}}, \quad \forall j$$ - -### 求解器 - -以上完成了整个排班问题的建模。本项目使用 Google OR-Tools 中的 **SCIP 求解器**(Solving Constraint Integer Programs)进行求解。SCIP 是一个强大的混合整数规划(MIP)求解器,特别适合处理这类组合优化问题。 - -OR-Tools 支持 `C++`、`Python`、`C#`、`Java` 等多种语言,并提供跨平台支持。在本项目中,我们使用其 Python 接口 `ortools.linear_solver.pywraplp` 进行建模和求解。当找到最优解时,求解器会输出详细的目标值和各个优化指标的统计信息。 - -## 维护指南 -- 如果你想更改 Excel 的读取、写入相关的功能,应该修改 `utils.py` 中的相关函数。 -- 如果你想更改软件的前端界面,应该修改 `main.py` 中 `MyWidget` 这个类相关的代码。 +# EVA 值班排班工具 + +## 环境配置 + +本项目使用 `uv` 进行包管理。 + +安装项目中所需要的所有包的最新版本。其中 `pyside6` 是一个前端库,`pyinstaller` 用于打包项目,`ortools` 是一个高效的组合优化求解器,`pandas` 用于处理表格数据: +```bash +uv sync +``` + +## 项目结构 +``` +EVA_duty_arrange_tool/ + ├─ main.py // 主函数,定义了前端界面和组件的回调函数 + ├─ solve.py // 定义了值班排班问题的求解函数 + ├─ utils.py // 定义了读取、写入 excel 的函数 + ├─ pics // 储存了说明文档中用到的图片 + │ ├─ *.jpg/*.png + ├─ *.md // 说明文档 + ├─ *.xlsx // 测试用例 +``` + +## 项目运行 & 打包 +项目运行方式: +```bash +uv run main.py +``` + +本项目使用 `pyinstaller` 工具进行打包。如果要打包,请确保能够正常运行项目。打包命令如下: +```bash +uv run pyinstaller --onefile --windowed --name=EVA_duty_arrange_tool --hidden-import=ortools --collect-all ortools --additional-hooks-dir=. main.py +``` + +> **注意**:由于 `ortools` 库包含大量 DLL 依赖,需要使用 `--collect-all ortools` 参数来收集所有必要的动态链接库。`--additional-hooks-dir=.` 参数指定使用项目根目录下的 `hook-ortools.py` 文件来确保正确打包。 + +## 数学原理 +本项目将值班排班问题建模为了一个[组合优化](https://zh.wikipedia.org/wiki/%E7%BB%84%E5%90%88%E4%BC%98%E5%8C%96)问题,使用 Google 的 OR-Tools 求解器中的 SCIP 求解器进行求解。 + +### 问题描述 + +在本问题中,优化目标是一个涉及多个方面的量化指标: +- **目标1**:每一班同学数量的平均程度 +- **目标2**:每一班技术部老人数量接近期望值 +- **目标3**:每一班技术部小朋友数量接近期望值 +- **目标4**:每一班人资部小朋友数量接近期望值 +- **目标5**:每一班各部门人数的平均程度 + +在本问题中,主要约束是: +1. 让每位同学每周的班次数符合意愿(特别地,选择3次的同学可以安排2-3次) +2. 让每位同学只在自己有空的时间段值班 +3. 每班次的总人数在指定范围内 +4. 每班次技术部老人数量在指定范围内 +5. 每班次老人总数在指定范围内 +6. 每班次小朋友总数在指定范围内 + +### 数学建模 + +设一共有 $n$ 位同学,$m$ 个值班的班次,协会共有 $t=5$ 个部门(电脑部、电器部、人资部、财外部、文宣部),定义以下符号: + +**决策变量:** +- $x_{ij} \in \{0,1\}, \quad i=1,2,\dots,n,\quad j=1,2,\dots,m$ 表示第 $i$ 位同学是否值第 $j$ 班 + +**已知参数:** +- $N_i \in \mathbb{N}, \quad i=1,2,\dots,n$ 表示第 $i$ 个同学每周愿意值班次数 +- $v_{ij} \in \{0,1\}, \quad i=1,2,\dots,n,\quad j=1,2,\dots,m$ 表示第 $i$ 位同学是否有空值第 $j$ 班 +- $\text{old}_i \in \{0,1\}, \quad i=1,2,\dots,n$ 表示第 $i$ 位同学是否是老人(非小朋友) +- $d_{ik}\in\{0,1\}, \quad i=1,2,\dots,n,\quad k=1,2,\dots,t$ 表示第 $i$ 位同学是否属于第 $k$ 个部门 +- $\text{tech}_i = d_{i1} + d_{i2} \in \{0,1\}$ 表示第 $i$ 位同学是否属于技术部(电脑部或电器部) +- $\text{hr}_i = d_{i3} \in \{0,1\}$ 表示第 $i$ 位同学是否属于人资部 + +**派生量:** +- $M_j = \sum_{i=1}^{n} x_{ij}, \quad j=1,2,\dots,m$ 表示第 $j$ 班次实际安排的值班人数 +- $\bar{M} = \frac{\sum_{i=1}^{n}N_i}{m}$ 表示平均每班的人数 + +### 优化目标 + +为了处理多目标优化问题,我们采用**线性加权法**将多个目标组合成单一目标函数。引入归一化系数 $m_1=2.5, m_2=3, m_3=4, m_4=1, m_5=8$ 和权重 $w_1, w_2, w_3, w_4, w_5$,总目标函数为: + +$$\min \quad Z = w_1 \cdot \frac{X_1}{m_1 \cdot M} + w_2 \cdot \frac{X_2}{m_2 \cdot M} + w_3 \cdot \frac{X_3}{m_3 \cdot M} + w_4 \cdot \frac{X_4}{m_4 \cdot M} + w_5 \cdot \frac{X_5}{m_5 \cdot M}$$ + +各目标定义如下: + +**目标1:均衡班次人数** + +$$X_1 = \sum_{j=1}^{m} a_j^{(1)}$$ + +其中辅助变量 $a_j^{(1)} \in [0,+\infty)$ 满足: +$$a_j^{(1)} \ge M_j - \bar{M}, \quad a_j^{(1)} \ge \bar{M} - M_j, \quad \forall j$$ + +> 注:在组合优化问题中只能定义线性约束,不能直接使用绝对值。因此引入辅助变量 $a_j^{(1)}$ 并添加两个不等式约束,使得在优化过程中 $a_j^{(1)}$ 自动收敛到 $|M_j - \bar{M}|$。 + +**目标2:每班技术部老人数量接近期望值** + +期望每班有 $m_2 = 3$ 位技术部老人,定义缺口: + +$$X_2 = \sum_{j=1}^{m} a_j^{(2)}$$ + +其中辅助变量 $a_j^{(2)} \in [0,+\infty)$ 满足: +$$a_j^{(2)} \ge \sum_{i=1}^{n} x_{ij} \cdot \text{old}_i \cdot \text{tech}_i - m_2$$ +$$a_j^{(2)} \ge m_2 - \sum_{i=1}^{n} x_{ij} \cdot \text{old}_i \cdot \text{tech}_i, \quad \forall j$$ + +**目标3:每班技术部小朋友数量接近期望值** + +期望每班有 $m_3 = 4$ 位技术部小朋友,定义缺口: + +$$X_3 = \sum_{j=1}^{m} a_j^{(3)}$$ + +其中辅助变量 $a_j^{(3)} \in [0,+\infty)$ 满足: +$$a_j^{(3)} \ge \sum_{i=1}^{n} x_{ij} \cdot (1-\text{old}_i) \cdot \text{tech}_i - m_3$$ +$$a_j^{(3)} \ge m_3 - \sum_{i=1}^{n} x_{ij} \cdot (1-\text{old}_i) \cdot \text{tech}_i, \quad \forall j$$ + +**目标4:每班人资部小朋友数量接近期望值** + +期望每班有 $m_4 = 1$ 位人资部小朋友,定义缺口: + +$$X_4 = \sum_{j=1}^{m} a_j^{(4)}$$ + +其中辅助变量 $a_j^{(4)} \in [0,+\infty)$ 满足: +$$a_j^{(4)} \ge \sum_{i=1}^{n} x_{ij} \cdot (1-\text{old}_i) \cdot \text{hr}_i - m_4$$ +$$a_j^{(4)} \ge m_4 - \sum_{i=1}^{n} x_{ij} \cdot (1-\text{old}_i) \cdot \text{hr}_i, \quad \forall j$$ + +**目标5:均衡各部门人数分布** + +对每个班次 $j$ 和每个部门 $k$,定义该班次该部门的人数偏差: + +$$X_5 = \sum_{j=1}^{m} \sum_{k=1}^{t} a_{jk}^{(5)}$$ + +其中 $\bar{M}_j = \frac{M_j}{t}$ 表示第 $j$ 班次各部门的平均人数,$C_{jk} = \sum_{i=1}^{n} x_{ij} \cdot d_{ik}$ 表示第 $j$ 班次第 $k$ 部门的实际人数,辅助变量 $a_{jk}^{(5)} \in [0,+\infty)$ 满足: + +$$a_{jk}^{(5)} \ge C_{jk} - \bar{M}_j, \quad a_{jk}^{(5)} \ge \bar{M}_j - C_{jk}, \quad \forall j,k$$ + +### 约束条件 + +**基本约束:** + +1. **意愿班次约束**:每位同学的值班次数必须符合其意愿 + $$\sum_{j=1}^{m}x_{ij} = N_i, \quad \forall i \text{ where } N_i \neq 3$$ + $$2 \le \sum_{j=1}^{m}x_{ij} \le 3, \quad \forall i \text{ where } N_i = 3$$ + +2. **时间可行性约束**:只在有空的时间段排班 + $$x_{ij} \le v_{ij}, \quad \forall i,j$$ + +**班次人数约束:** + +3. **总人数约束**:每班次人数在 $[n_{\min}, n_{\max}]$ 范围内 + $$n_{\min} \le \sum_{i=1}^{n} x_{ij} \le n_{\max}, \quad \forall j$$ + +4. **技术部老人约束**:每班次技术部老人数在范围内 + $$n_{\text{tech\_old\_min}} \le \sum_{i=1}^{n} x_{ij} \cdot \text{old}_i \cdot \text{tech}_i \le n_{\text{tech\_old\_max}}, \quad \forall j$$ + +5. **老人总数约束**:每班次老人总数在范围内 + $$n_{\text{old\_min}} \le \sum_{i=1}^{n} x_{ij} \cdot \text{old}_i \le n_{\text{old\_max}}, \quad \forall j$$ + +6. **小朋友总数约束**:每班次小朋友总数在范围内 + $$n_{\text{new\_min}} \le \sum_{i=1}^{n} x_{ij} \cdot (1-\text{old}_i) \le n_{\text{new\_max}}, \quad \forall j$$ + +### 求解器 + +以上完成了整个排班问题的建模。本项目使用 Google OR-Tools 中的 **SCIP 求解器**(Solving Constraint Integer Programs)进行求解。SCIP 是一个强大的混合整数规划(MIP)求解器,特别适合处理这类组合优化问题。 + +OR-Tools 支持 `C++`、`Python`、`C#`、`Java` 等多种语言,并提供跨平台支持。在本项目中,我们使用其 Python 接口 `ortools.linear_solver.pywraplp` 进行建模和求解。当找到最优解时,求解器会输出详细的目标值和各个优化指标的统计信息。 + +## 维护指南 +- 如果你想更改 Excel 的读取、写入相关的功能,应该修改 `utils.py` 中的相关函数。 +- 如果你想更改软件的前端界面,应该修改 `main.py` 中 `MyWidget` 这个类相关的代码。 - 如果你想更换排班问题的建模方式、更换求解器、增减限制条件,应该修改 `solve.py` 中的相关代码。 \ No newline at end of file diff --git a/hook-ortools.py b/hook-ortools.py new file mode 100644 index 0000000..4b9e5b8 --- /dev/null +++ b/hook-ortools.py @@ -0,0 +1,5 @@ +from PyInstaller.utils.hooks import collect_data_files, collect_dynamic_libs + +# 收集 ortools 的所有数据文件和动态链接库 +datas = collect_data_files('ortools') +binaries = collect_dynamic_libs('ortools') diff --git a/main.py b/main.py index dcebe28..57150ae 100644 --- a/main.py +++ b/main.py @@ -1,402 +1,402 @@ -from sys import exit -from PySide6 import QtCore, QtWidgets, QtGui -from PySide6.QtGui import QFont -from utils import read_excel, save_to_excel -from solve import solve_program -from datetime import datetime -from traceback import format_exc - -# 约束条件配置 -CONSTRAINTS_CONFIG = [ - ("每班次人数:", "5", "8"), - ("每班次电脑或电器的老人数:", "", "2"), - ("每班次老人数:", "1", ""), - ("每班次小朋友数:", "2", "") -] - -# 权重条件配置 -WEIGHTS_CONFIG = [ - ("目标 1 权重:每班人数平均程度", "1.0"), - ("目标 2 权重:每班技术部老人", "1.0"), - ("目标 3 权重:每班技术部小朋友", "1.0"), - ("目标 4 权重:每班人资部小朋友", "0.5"), - ("目标 5 权重:每班部门平均程度", "0.5") -] - -# 参数调优顺序和尝试值 -PARAM_CONFIGS = [ - ('num_min', [5, 4, 3, 2, 1]), - ('num_max', [8, 9, 10, 11, 12, 13, 14, 15]), - ('num_old_min', [2, 1, 0]), - ('num_old_max', [3, 4, 5, 6, 7, 8]), - ('num_new_min', [1, 0]), - ('num_tech_old_min', [1, 0]), - ('num_tech_old_max', [1, 2, 3, 4, 5, 6, 7, 8]), -] - -# 工作线程类,用于在后台执行求解任务 -class SolverThread(QtCore.QThread): - # 定义信号用于线程与主界面通信 - log_signal = QtCore.Signal(str) # 发送日志消息 - finished_signal = QtCore.Signal(object, dict) # 发送求解结果 (vars, params) - error_signal = QtCore.Signal(str) # 发送错误消息 - - def __init__(self, preference_mat, depart_mat, want_num_array, is_new_array, - auto_mode, manual_params=None, parent=None): - super().__init__(parent) - self.preference_mat = preference_mat - self.depart_mat = depart_mat - self.want_num_array = want_num_array - self.is_new_array = is_new_array - self.auto_mode = auto_mode - self.manual_params = manual_params or {} - - def run(self): - try: - vars = None - final_params = {} - - if self.auto_mode: - self.log_signal.emit("自动调参开始...") - - auto_params = { - 'num_min': None, 'num_max': None, - 'num_tech_old_min': None, 'num_tech_old_max': None, - 'num_old_min': None, 'num_old_max': None, - 'num_new_min': None, 'num_new_max': None, - 'weights': self.manual_params.get('weights', [1.0, 1.0, 1.0, 0.5, 0.5]) - } - - # 逐步优化参数 - for param_name, try_values in PARAM_CONFIGS: - value = None - vars = None - for try_value in try_values: - self.log_signal.emit(f"尝试 {param_name} = {try_value}...") - test_params = auto_params.copy() - test_params[param_name] = try_value - vars = solve_program( - preference_mat=self.preference_mat, - depart_mat=self.depart_mat, - want_num_array=self.want_num_array, - is_new_array=self.is_new_array, - **test_params - ) - if vars: - value = try_value - self.log_signal.emit(f"✓ {param_name} = {try_value} 成功") - break - else: - self.log_signal.emit(f"✗ {param_name} = {try_value} 失败") - if value: - auto_params[param_name] = value - else: - break - - final_params = auto_params - - else: - self.log_signal.emit("计算最优解中...") - vars = solve_program( - preference_mat=self.preference_mat, - want_num_array=self.want_num_array, - depart_mat=self.depart_mat, - is_new_array=self.is_new_array, - **self.manual_params - ) - final_params = self.manual_params - - # 发送完成信号 - self.finished_signal.emit(vars, final_params) - - except Exception as e: - self.error_signal.emit(format_exc()) - -class MyWidget(QtWidgets.QWidget): - def __init__(self): - super().__init__() - - self.solver_thread = None # 用于存储求解线程 - - self.main_layout = QtWidgets.QVBoxLayout(self) - - bold_font = QFont() - bold_font.setBold(True) - - thin_font = QFont() - thin_font.setBold(False) - - # 选择文件部分 - self._excel_dir = None - self.group_box_1 = QtWidgets.QGroupBox("Step 1. 选择问卷星导出的表格文件") - self.group_box_1.setFont(bold_font) - - self.openfile_layout = QtWidgets.QVBoxLayout() - self.button_openfile = QtWidgets.QPushButton("选择文件") - self.button_openfile.setFont(thin_font) - self.button_openfile.clicked.connect(self.open_file) - self.label_openfile = QtWidgets.QLabel("未选择文件") - self.label_openfile.setFont(thin_font) - - self.openfile_layout.addWidget(self.button_openfile) - self.openfile_layout.addWidget(self.label_openfile) - self.group_box_1.setLayout(self.openfile_layout) - self.main_layout.addWidget(self.group_box_1) - - # 限制条件部分 - self.group_box_2 = QtWidgets.QGroupBox("Step 2. 输入限制条件,权重参数均已归一化") - self.group_box_2.setFont(bold_font) - self.cond_layout_overall = QtWidgets.QVBoxLayout() - self.group_box_2.setLayout(self.cond_layout_overall) - - # 创建权重输入框 - self.weight_widgets = {} - - for i, (label_text, value) in enumerate(WEIGHTS_CONFIG, 1): - layout, edit = self._create_weight_row(label_text, value, thin_font) - self.weight_widgets[f'layout_{i}'] = layout - self.weight_widgets[f'edit_{i}'] = edit - self.cond_layout_overall.addLayout(layout) - - # 自动模式开关 - self._auto_mode = True - self.cond_layout_0 = QtWidgets.QVBoxLayout() - self.auto_switch = QtWidgets.QCheckBox("自动调参模式") - self.auto_switch.setTristate(False) # 禁用三态模式 - self.auto_switch.setChecked(True) - self.auto_switch.stateChanged.connect(self.on_switch_toggled) - self.auto_switch.setFont(thin_font) - - self.cond_layout_0.addWidget(self.auto_switch) - self.cond_layout_overall.addLayout(self.cond_layout_0) - - # 创建限制条件输入框 - self.constraint_widgets = {} - - for i, (label_text, min_value, max_value) in enumerate(CONSTRAINTS_CONFIG, 1): - layout, min_edit, max_edit = self._create_constraint_row(label_text, min_value, max_value, thin_font) - self.constraint_widgets[f'layout_{i}'] = layout - self.constraint_widgets[f'min_{i}'] = min_edit - self.constraint_widgets[f'max_{i}'] = max_edit - self.cond_layout_overall.addLayout(layout) - - self.main_layout.addWidget(self.group_box_2) - - # 处理文件部分 - self.group_box_3 = QtWidgets.QGroupBox("Step 3. 获取最优结果") - self.group_box_3.setFont(bold_font) - - self.button_solve = QtWidgets.QPushButton("开始排班!") - self.button_solve.setFont(thin_font) - self.text_solve = QtWidgets.QTextEdit(self) - self.text_solve.setFont(thin_font) - self.text_solve.setReadOnly(True) # 设置为只读模式 - - # 创建 QScrollArea 并将 QTextEdit 添加进去 - self.scroll_area = QtWidgets.QScrollArea(self) - self.scroll_area.setWidget(self.text_solve) # 将 QTextEdit 设置为 QScrollArea 的内容 - self.scroll_area.setWidgetResizable(True) # 允许 QTextEdit 根据 QScrollArea 的大小自动调整 - - self.button_solve.clicked.connect(self.magic) - - self.solve_layout = QtWidgets.QVBoxLayout() - self.solve_layout.addWidget(self.scroll_area) - self.solve_layout.addWidget(self.button_solve) - self.group_box_3.setLayout(self.solve_layout) - self.main_layout.addWidget(self.group_box_3) - - def _create_constraint_row(self, label_text, min_value, max_value, font): - """创建一行限制条件输入框""" - layout = QtWidgets.QHBoxLayout() - - label = QtWidgets.QLabel(label_text, self) - label.setFont(font) - - # 创建输入框的辅助函数 - def create_input(value): - edit = QtWidgets.QLineEdit(self) - edit.setFont(font) - edit.setValidator(QtGui.QIntValidator()) - edit.setPlaceholderText("无限制") - if value: - edit.setText(value) - edit.setEnabled(False) - return edit - - min_edit = create_input(min_value) - to_label = QtWidgets.QLabel("到", self) - to_label.setFont(font) - max_edit = create_input(max_value) - - layout.addWidget(label) - layout.addWidget(min_edit) - layout.addWidget(to_label) - layout.addWidget(max_edit) - - return layout, min_edit, max_edit - - def _create_weight_row(self, label_text, value, font): - """创建一行权重输入框""" - layout = QtWidgets.QHBoxLayout() - - label = QtWidgets.QLabel(label_text, self) - label.setFont(font) - - edit = QtWidgets.QLineEdit(self) - edit.setFont(font) - edit.setValidator(QtGui.QDoubleValidator(0.0, 1.0, 2, self)) - edit.setText(str(value)) - - layout.addWidget(label) - layout.addWidget(edit) - - return layout, edit - - def open_file(self): - file_path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打开文件", "", "所有文件 (*.*);;文本文件 (*.txt)") - if file_path: - self.label_openfile.setText(f"选中文件: {file_path}") - self._excel_dir = file_path - - def on_switch_toggled(self, state): - enabled = state == 0 - self._auto_mode = not enabled - - for i in range(1, len(CONSTRAINTS_CONFIG) + 1): - self.constraint_widgets[f'min_{i}'].setEnabled(enabled) - self.constraint_widgets[f'max_{i}'].setEnabled(enabled) - - @QtCore.Slot() - def magic(self): - try: - self.text_solve.append(f"******** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ********") - - # 读取文件 - if self._excel_dir is None: - self.text_solve.append("请先选择文件!") - return - - # 禁用按钮,防止重复点击 - self.button_solve.setEnabled(False) - self.button_solve.setText("计算中...") - - self.text_solve.append("开始排班...") - self.text_solve.append("读取文件中...") - self.all_data, self.index_to_name_dict, preference_mat, depart_mat, want_num_array, is_new_array = read_excel(self._excel_dir) - self.text_solve.append("读取文件成功!") - - # 计算并打印统计信息 - is_tech_array = [depart_mat[i][0] == 1 or depart_mat[i][1] == 1 for i in range(len(depart_mat))] - stu_num = len(self.index_to_name_dict) - class_num = len(preference_mat[0]) - tech_sum = sum(want_num_array[i] for i in range(len(preference_mat)) - if not is_new_array[i] and is_tech_array[i]) - all_sum = sum(want_num_array) - min_prefer = min(sum(preference_mat[i][j] for i in range(len(preference_mat))) - for j in range(class_num)) - - self.text_solve.append("信息统计:") - self.text_solve.append(f"\t学生总人数:{stu_num}") - self.text_solve.append(f"\t班次总数:{class_num}") - self.text_solve.append(f"\t电脑部或电器部的所有老人的意愿班次之和:{tech_sum}") - self.text_solve.append(f"\t所有人的意愿班次之和:{all_sum}") - self.text_solve.append(f"\t平均每班人数:{all_sum/20}") - self.text_solve.append(f"\t所有班次中最少拥有意愿数:{min_prefer}") - - def get_weight_value(key): - text = self.weight_widgets[key].text() - return float(text) if text else 0.0 - - def get_constraint_value(key): - text = self.constraint_widgets[key].text() - return int(text) if text else None - - # 准备参数 - params = { - 'num_min': get_constraint_value('min_1'), - 'num_max': get_constraint_value('max_1'), - 'num_tech_old_min': get_constraint_value('min_2'), - 'num_tech_old_max': get_constraint_value('max_2'), - 'num_old_min': get_constraint_value('min_3'), - 'num_old_max': get_constraint_value('max_3'), - 'num_new_min': get_constraint_value('min_4'), - 'num_new_max': get_constraint_value('max_4'), - 'weights': [get_weight_value(f'edit_{i}') for i in range(1, len(WEIGHTS_CONFIG) + 1)] - } - - # 创建并启动求解线程 - self.solver_thread = SolverThread( - preference_mat, depart_mat, want_num_array, is_new_array, - self._auto_mode, params, self - ) - - # 连接信号 - self.solver_thread.log_signal.connect(self.on_log_message) - self.solver_thread.finished_signal.connect(self.on_solve_finished) - self.solver_thread.error_signal.connect(self.on_solve_error) - - # 启动线程 - self.solver_thread.start() - - except Exception: - self.text_solve.append("程序出现严重错误,请联系开发者解决问题!!!") - self.text_solve.append(f"Error Details:\n{format_exc()}") - self.button_solve.setEnabled(True) - self.button_solve.setText("开始排班!") - - @QtCore.Slot(str) - def on_log_message(self, message): - """接收线程发送的日志消息并实时显示""" - self.text_solve.append(message) - - @QtCore.Slot(object, dict) - def on_solve_finished(self, vars, params): - """求解完成后的回调""" - try: - if vars is None: - if self._auto_mode: - self.text_solve.append("自动求解失败!请尝试手动调参!") - else: - self.text_solve.append("在目前限制条件下无解!请尝试更改限制条件!") - else: - if self._auto_mode: - self.text_solve.append("自动计算成功!") - self.text_solve.append("参数:") - self.text_solve.append(f"\t每班人数:[{params['num_min']}, {params['num_max']}]") - self.text_solve.append(f"\t每班电脑或电器的老人数:[{params['num_tech_old_min']}, {params['num_tech_old_max']}]") - self.text_solve.append(f"\t每班老人数:[{params['num_old_min']}, {params['num_old_max']}]") - self.text_solve.append(f"\t每班小朋友数:[{params['num_new_min']}, inf]") - else: - self.text_solve.append("计算最优解成功!") - - # 保存结果到 excel - self.text_solve.append("保存结果中...") - save_dir = f"result_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" - save_to_excel(vars, self.all_data, self.index_to_name_dict, save_dir) - self.text_solve.append(f"保存结果成功!保存路径:{save_dir}") - - except Exception: - self.text_solve.append("保存结果时出现错误!") - self.text_solve.append(f"Error Details:\n{format_exc()}") - finally: - # 恢复按钮状态 - self.button_solve.setEnabled(True) - self.button_solve.setText("开始排班!") - - @QtCore.Slot(str) - def on_solve_error(self, error_msg): - """处理求解过程中的错误""" - self.text_solve.append("程序出现严重错误,请联系开发者解决问题!!!") - self.text_solve.append(f"Error Details:\n{error_msg}") - self.button_solve.setEnabled(True) - self.button_solve.setText("开始排班!") - -if __name__ == "__main__": - app = QtWidgets.QApplication([]) - - widget = MyWidget() - widget.setWindowTitle("EVA 值班排班软件") - widget.resize(600, 600) - widget.show() - +from sys import exit +from PySide6 import QtCore, QtWidgets, QtGui +from PySide6.QtGui import QFont +from utils import read_excel, save_to_excel +from solve import solve_program +from datetime import datetime +from traceback import format_exc + +# 约束条件配置 +CONSTRAINTS_CONFIG = [ + ("每班次人数:", "5", "8"), + ("每班次电脑或电器的老人数:", "", "2"), + ("每班次老人数:", "1", ""), + ("每班次小朋友数:", "2", "") +] + +# 权重条件配置 +WEIGHTS_CONFIG = [ + ("目标 1 权重:每班人数平均程度", "1.0"), + ("目标 2 权重:每班技术部老人", "1.0"), + ("目标 3 权重:每班技术部小朋友", "1.0"), + ("目标 4 权重:每班人资部小朋友", "0.5"), + ("目标 5 权重:每班部门平均程度", "0.5") +] + +# 参数调优顺序和尝试值 +PARAM_CONFIGS = [ + ('num_min', [5, 4, 3, 2, 1]), + ('num_max', [8, 9, 10, 11, 12, 13, 14, 15]), + ('num_old_min', [2, 1, 0]), + ('num_old_max', [3, 4, 5, 6, 7, 8]), + ('num_new_min', [1, 0]), + ('num_tech_old_min', [1, 0]), + ('num_tech_old_max', [1, 2, 3, 4, 5, 6, 7, 8]), +] + +# 工作线程类,用于在后台执行求解任务 +class SolverThread(QtCore.QThread): + # 定义信号用于线程与主界面通信 + log_signal = QtCore.Signal(str) # 发送日志消息 + finished_signal = QtCore.Signal(object, dict) # 发送求解结果 (vars, params) + error_signal = QtCore.Signal(str) # 发送错误消息 + + def __init__(self, preference_mat, depart_mat, want_num_array, is_new_array, + auto_mode, manual_params=None, parent=None): + super().__init__(parent) + self.preference_mat = preference_mat + self.depart_mat = depart_mat + self.want_num_array = want_num_array + self.is_new_array = is_new_array + self.auto_mode = auto_mode + self.manual_params = manual_params or {} + + def run(self): + try: + vars = None + final_params = {} + + if self.auto_mode: + self.log_signal.emit("自动调参开始...") + + auto_params = { + 'num_min': None, 'num_max': None, + 'num_tech_old_min': None, 'num_tech_old_max': None, + 'num_old_min': None, 'num_old_max': None, + 'num_new_min': None, 'num_new_max': None, + 'weights': self.manual_params.get('weights', [1.0, 1.0, 1.0, 0.5, 0.5]) + } + + # 逐步优化参数 + for param_name, try_values in PARAM_CONFIGS: + value = None + vars = None + for try_value in try_values: + self.log_signal.emit(f"尝试 {param_name} = {try_value}...") + test_params = auto_params.copy() + test_params[param_name] = try_value + vars = solve_program( + preference_mat=self.preference_mat, + depart_mat=self.depart_mat, + want_num_array=self.want_num_array, + is_new_array=self.is_new_array, + **test_params + ) + if vars: + value = try_value + self.log_signal.emit(f"✓ {param_name} = {try_value} 成功") + break + else: + self.log_signal.emit(f"✗ {param_name} = {try_value} 失败") + if value: + auto_params[param_name] = value + else: + break + + final_params = auto_params + + else: + self.log_signal.emit("计算最优解中...") + vars = solve_program( + preference_mat=self.preference_mat, + want_num_array=self.want_num_array, + depart_mat=self.depart_mat, + is_new_array=self.is_new_array, + **self.manual_params + ) + final_params = self.manual_params + + # 发送完成信号 + self.finished_signal.emit(vars, final_params) + + except Exception as e: + self.error_signal.emit(format_exc()) + +class MyWidget(QtWidgets.QWidget): + def __init__(self): + super().__init__() + + self.solver_thread = None # 用于存储求解线程 + + self.main_layout = QtWidgets.QVBoxLayout(self) + + bold_font = QFont() + bold_font.setBold(True) + + thin_font = QFont() + thin_font.setBold(False) + + # 选择文件部分 + self._excel_dir = None + self.group_box_1 = QtWidgets.QGroupBox("Step 1. 选择问卷星导出的表格文件") + self.group_box_1.setFont(bold_font) + + self.openfile_layout = QtWidgets.QVBoxLayout() + self.button_openfile = QtWidgets.QPushButton("选择文件") + self.button_openfile.setFont(thin_font) + self.button_openfile.clicked.connect(self.open_file) + self.label_openfile = QtWidgets.QLabel("未选择文件") + self.label_openfile.setFont(thin_font) + + self.openfile_layout.addWidget(self.button_openfile) + self.openfile_layout.addWidget(self.label_openfile) + self.group_box_1.setLayout(self.openfile_layout) + self.main_layout.addWidget(self.group_box_1) + + # 限制条件部分 + self.group_box_2 = QtWidgets.QGroupBox("Step 2. 输入限制条件,权重参数均已归一化") + self.group_box_2.setFont(bold_font) + self.cond_layout_overall = QtWidgets.QVBoxLayout() + self.group_box_2.setLayout(self.cond_layout_overall) + + # 创建权重输入框 + self.weight_widgets = {} + + for i, (label_text, value) in enumerate(WEIGHTS_CONFIG, 1): + layout, edit = self._create_weight_row(label_text, value, thin_font) + self.weight_widgets[f'layout_{i}'] = layout + self.weight_widgets[f'edit_{i}'] = edit + self.cond_layout_overall.addLayout(layout) + + # 自动模式开关 + self._auto_mode = True + self.cond_layout_0 = QtWidgets.QVBoxLayout() + self.auto_switch = QtWidgets.QCheckBox("自动调参模式") + self.auto_switch.setTristate(False) # 禁用三态模式 + self.auto_switch.setChecked(True) + self.auto_switch.stateChanged.connect(self.on_switch_toggled) + self.auto_switch.setFont(thin_font) + + self.cond_layout_0.addWidget(self.auto_switch) + self.cond_layout_overall.addLayout(self.cond_layout_0) + + # 创建限制条件输入框 + self.constraint_widgets = {} + + for i, (label_text, min_value, max_value) in enumerate(CONSTRAINTS_CONFIG, 1): + layout, min_edit, max_edit = self._create_constraint_row(label_text, min_value, max_value, thin_font) + self.constraint_widgets[f'layout_{i}'] = layout + self.constraint_widgets[f'min_{i}'] = min_edit + self.constraint_widgets[f'max_{i}'] = max_edit + self.cond_layout_overall.addLayout(layout) + + self.main_layout.addWidget(self.group_box_2) + + # 处理文件部分 + self.group_box_3 = QtWidgets.QGroupBox("Step 3. 获取最优结果") + self.group_box_3.setFont(bold_font) + + self.button_solve = QtWidgets.QPushButton("开始排班!") + self.button_solve.setFont(thin_font) + self.text_solve = QtWidgets.QTextEdit(self) + self.text_solve.setFont(thin_font) + self.text_solve.setReadOnly(True) # 设置为只读模式 + + # 创建 QScrollArea 并将 QTextEdit 添加进去 + self.scroll_area = QtWidgets.QScrollArea(self) + self.scroll_area.setWidget(self.text_solve) # 将 QTextEdit 设置为 QScrollArea 的内容 + self.scroll_area.setWidgetResizable(True) # 允许 QTextEdit 根据 QScrollArea 的大小自动调整 + + self.button_solve.clicked.connect(self.magic) + + self.solve_layout = QtWidgets.QVBoxLayout() + self.solve_layout.addWidget(self.scroll_area) + self.solve_layout.addWidget(self.button_solve) + self.group_box_3.setLayout(self.solve_layout) + self.main_layout.addWidget(self.group_box_3) + + def _create_constraint_row(self, label_text, min_value, max_value, font): + """创建一行限制条件输入框""" + layout = QtWidgets.QHBoxLayout() + + label = QtWidgets.QLabel(label_text, self) + label.setFont(font) + + # 创建输入框的辅助函数 + def create_input(value): + edit = QtWidgets.QLineEdit(self) + edit.setFont(font) + edit.setValidator(QtGui.QIntValidator()) + edit.setPlaceholderText("无限制") + if value: + edit.setText(value) + edit.setEnabled(False) + return edit + + min_edit = create_input(min_value) + to_label = QtWidgets.QLabel("到", self) + to_label.setFont(font) + max_edit = create_input(max_value) + + layout.addWidget(label) + layout.addWidget(min_edit) + layout.addWidget(to_label) + layout.addWidget(max_edit) + + return layout, min_edit, max_edit + + def _create_weight_row(self, label_text, value, font): + """创建一行权重输入框""" + layout = QtWidgets.QHBoxLayout() + + label = QtWidgets.QLabel(label_text, self) + label.setFont(font) + + edit = QtWidgets.QLineEdit(self) + edit.setFont(font) + edit.setValidator(QtGui.QDoubleValidator(0.0, 1.0, 2, self)) + edit.setText(str(value)) + + layout.addWidget(label) + layout.addWidget(edit) + + return layout, edit + + def open_file(self): + file_path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打开文件", "", "所有文件 (*.*);;文本文件 (*.txt)") + if file_path: + self.label_openfile.setText(f"选中文件: {file_path}") + self._excel_dir = file_path + + def on_switch_toggled(self, state): + enabled = state == 0 + self._auto_mode = not enabled + + for i in range(1, len(CONSTRAINTS_CONFIG) + 1): + self.constraint_widgets[f'min_{i}'].setEnabled(enabled) + self.constraint_widgets[f'max_{i}'].setEnabled(enabled) + + @QtCore.Slot() + def magic(self): + try: + self.text_solve.append(f"******** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ********") + + # 读取文件 + if self._excel_dir is None: + self.text_solve.append("请先选择文件!") + return + + # 禁用按钮,防止重复点击 + self.button_solve.setEnabled(False) + self.button_solve.setText("计算中...") + + self.text_solve.append("开始排班...") + self.text_solve.append("读取文件中...") + self.all_data, self.index_to_name_dict, preference_mat, depart_mat, want_num_array, is_new_array = read_excel(self._excel_dir) + self.text_solve.append("读取文件成功!") + + # 计算并打印统计信息 + is_tech_array = [depart_mat[i][0] == 1 or depart_mat[i][1] == 1 for i in range(len(depart_mat))] + stu_num = len(self.index_to_name_dict) + class_num = len(preference_mat[0]) + tech_sum = sum(want_num_array[i] for i in range(len(preference_mat)) + if not is_new_array[i] and is_tech_array[i]) + all_sum = sum(want_num_array) + min_prefer = min(sum(preference_mat[i][j] for i in range(len(preference_mat))) + for j in range(class_num)) + + self.text_solve.append("信息统计:") + self.text_solve.append(f"\t学生总人数:{stu_num}") + self.text_solve.append(f"\t班次总数:{class_num}") + self.text_solve.append(f"\t电脑部或电器部的所有老人的意愿班次之和:{tech_sum}") + self.text_solve.append(f"\t所有人的意愿班次之和:{all_sum}") + self.text_solve.append(f"\t平均每班人数:{all_sum/20}") + self.text_solve.append(f"\t所有班次中最少拥有意愿数:{min_prefer}") + + def get_weight_value(key): + text = self.weight_widgets[key].text() + return float(text) if text else 0.0 + + def get_constraint_value(key): + text = self.constraint_widgets[key].text() + return int(text) if text else None + + # 准备参数 + params = { + 'num_min': get_constraint_value('min_1'), + 'num_max': get_constraint_value('max_1'), + 'num_tech_old_min': get_constraint_value('min_2'), + 'num_tech_old_max': get_constraint_value('max_2'), + 'num_old_min': get_constraint_value('min_3'), + 'num_old_max': get_constraint_value('max_3'), + 'num_new_min': get_constraint_value('min_4'), + 'num_new_max': get_constraint_value('max_4'), + 'weights': [get_weight_value(f'edit_{i}') for i in range(1, len(WEIGHTS_CONFIG) + 1)] + } + + # 创建并启动求解线程 + self.solver_thread = SolverThread( + preference_mat, depart_mat, want_num_array, is_new_array, + self._auto_mode, params, self + ) + + # 连接信号 + self.solver_thread.log_signal.connect(self.on_log_message) + self.solver_thread.finished_signal.connect(self.on_solve_finished) + self.solver_thread.error_signal.connect(self.on_solve_error) + + # 启动线程 + self.solver_thread.start() + + except Exception: + self.text_solve.append("程序出现严重错误,请联系开发者解决问题!!!") + self.text_solve.append(f"Error Details:\n{format_exc()}") + self.button_solve.setEnabled(True) + self.button_solve.setText("开始排班!") + + @QtCore.Slot(str) + def on_log_message(self, message): + """接收线程发送的日志消息并实时显示""" + self.text_solve.append(message) + + @QtCore.Slot(object, dict) + def on_solve_finished(self, vars, params): + """求解完成后的回调""" + try: + if vars is None: + if self._auto_mode: + self.text_solve.append("自动求解失败!请尝试手动调参!") + else: + self.text_solve.append("在目前限制条件下无解!请尝试更改限制条件!") + else: + if self._auto_mode: + self.text_solve.append("自动计算成功!") + self.text_solve.append("参数:") + self.text_solve.append(f"\t每班人数:[{params['num_min']}, {params['num_max']}]") + self.text_solve.append(f"\t每班电脑或电器的老人数:[{params['num_tech_old_min']}, {params['num_tech_old_max']}]") + self.text_solve.append(f"\t每班老人数:[{params['num_old_min']}, {params['num_old_max']}]") + self.text_solve.append(f"\t每班小朋友数:[{params['num_new_min']}, inf]") + else: + self.text_solve.append("计算最优解成功!") + + # 保存结果到 excel + self.text_solve.append("保存结果中...") + save_dir = f"result_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" + save_to_excel(vars, self.all_data, self.index_to_name_dict, save_dir) + self.text_solve.append(f"保存结果成功!保存路径:{save_dir}") + + except Exception: + self.text_solve.append("保存结果时出现错误!") + self.text_solve.append(f"Error Details:\n{format_exc()}") + finally: + # 恢复按钮状态 + self.button_solve.setEnabled(True) + self.button_solve.setText("开始排班!") + + @QtCore.Slot(str) + def on_solve_error(self, error_msg): + """处理求解过程中的错误""" + self.text_solve.append("程序出现严重错误,请联系开发者解决问题!!!") + self.text_solve.append(f"Error Details:\n{error_msg}") + self.button_solve.setEnabled(True) + self.button_solve.setText("开始排班!") + +if __name__ == "__main__": + app = QtWidgets.QApplication([]) + + widget = MyWidget() + widget.setWindowTitle("EVA 值班排班软件") + widget.resize(600, 600) + widget.show() + exit(app.exec()) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 5e0c2da..3164b68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,13 +1,13 @@ -[project] -name = "eva-duty-arrange-tool" -version = "2.0.0" -description = "EVA 值班排班工具" -readme = "README.md" -requires-python = ">=3.12" -dependencies = [ - "openpyxl>=3.1.5", - "ortools>=9.14.6206", - "pandas>=2.3.3", - "pyinstaller>=6.16.0", - "pyside6>=6.10.0", -] +[project] +name = "eva-duty-arrange-tool" +version = "2.0.0" +description = "EVA 值班排班工具" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "openpyxl>=3.1.5", + "ortools>=9.14.6206", + "pandas>=2.3.3", + "pyinstaller>=6.16.0", + "pyside6>=6.10.0", +] diff --git a/solve.py b/solve.py index 58625a2..8640083 100644 --- a/solve.py +++ b/solve.py @@ -1,210 +1,210 @@ -from ortools.linear_solver import pywraplp - -def solve_program(preference_mat:list, depart_mat:list, want_num_array:list, is_new_array:list, - num_min=None, num_max=None, - num_tech_old_min=None, num_tech_old_max=None, - num_old_min=None, num_old_max=None, - num_new_min=None, num_new_max=None, - weights=[1.0, 1.0, 1.0, 0.5, 0.5] - ): - - is_old_array = [not is_new for is_new in is_new_array] - is_tech_array = [depart_mat[i][0] == 1 or depart_mat[i][1] == 1 for i in range(len(depart_mat))] - is_hr_array = [depart_mat[i][2] == 1 for i in range(len(depart_mat))] - - # 使用 SCIP 求解器求解组合优化问题 - solver = pywraplp.Solver.CreateSolver("SCIP") - if not solver: - return - - N = len(preference_mat) # 学生人数 - M = len(preference_mat[0]) # 班次数 - avg_num = sum(want_num_array) / M - print(f"平均人数:{avg_num}") - - # 各个目标的期望值/归一化系数 - m = [2.5, 3, 4, 1, 8] # [班次人数偏差, 技术部老人, 技术部小朋友, 人资部小朋友, 部门均衡] - - # 创建决策变量:variables[i][j] 表示第 i 个学生是否选择第 j 个班次 - variables = [[solver.IntVar(0.0, 1.0, f"choice_{i}_{j}") - for j in range(M)] for i in range(N)] - - # 创建辅助变量用于优化目标1(均衡班次人数) - aux_vars_x1 = [solver.NumVar(0.0, solver.infinity(), f"aux_x1_{j}") - for j in range(M)] - - # 创建辅助变量用于优化目标2(每班次技术部老人数量缺口) - aux_vars_x2 = [solver.NumVar(0.0, solver.infinity(), f"aux_x2_{j}") - for j in range(M)] - - # 创建辅助变量用于优化目标3(每班次技术部小朋友数量缺口) - aux_vars_x3 = [solver.NumVar(0.0, solver.infinity(), f"aux_x3_{j}") - for j in range(M)] - - # 创建辅助变量用于优化目标4(每班次人资部小朋友数量缺口) - aux_vars_x4 = [solver.NumVar(0.0, solver.infinity(), f"aux_x4_{j}") - for j in range(M)] - - # 创建辅助变量用于优化目标5(均衡各部门人数) - # 如果提供了部门信息,则创建相应的辅助变量 - aux_vars_x5 = [] - num_departments = 5 # 电脑部、电器部、人资部、财外部、文宣部 - # 为每个班次的每个部门创建辅助变量 - aux_vars_x5 = [[solver.NumVar(0.0, solver.infinity(), f"aux_x5_{j}_{k}") - for k in range(num_departments)] for j in range(M)] - - print("Number of variables =", solver.NumVariables()) - - # 约束:每个同学意愿一定要满足 - for i in range(N): - for j in range(M): - solver.Add(variables[i][j] <= preference_mat[i][j]) - - # 约束:辅助变量 X1 用于计算与平均人数的偏差(绝对值) - for j in range(M): - actual_num = sum(variables[i][j] for i in range(N)) - solver.Add(aux_vars_x1[j] >= actual_num - avg_num) - solver.Add(aux_vars_x1[j] >= avg_num - actual_num) - - # 约束:辅助变量 X2 用于计算每班次技术部老人数量超出期望值的部分 - # 辅助变量表示超出期望值(期望每班有m2个技术部老人,超过即计入) - # 目标是让尽可能多的班次达到但不超过这个期望值 - tech_old_target = m[1] # 使用归一化系数作为期望值 - for j in range(M): - tech_old_count = sum(variables[i][j] * is_old_array[i] * is_tech_array[i] for i in range(N)) - # 辅助变量捕获超出部分和不足部分 - solver.Add(aux_vars_x2[j] >= tech_old_count - tech_old_target) - solver.Add(aux_vars_x2[j] >= tech_old_target - tech_old_count) - - # 约束:辅助变量 X3 用于计算每班次技术部小朋友数量超出期望值的部分 - tech_new_target = m[2] # 使用归一化系数作为期望值 - for j in range(M): - tech_new_count = sum(variables[i][j] * is_new_array[i] * is_tech_array[i] for i in range(N)) - solver.Add(aux_vars_x3[j] >= tech_new_count - tech_new_target) - solver.Add(aux_vars_x3[j] >= tech_new_target - tech_new_count) - - # 约束:辅助变量 X4 用于计算每班次人资部小朋友数量超出期望值的部分 - hr_new_target = m[3] # 使用归一化系数作为期望值 - for j in range(M): - hr_new_count = sum(variables[i][j] * is_new_array[i] * is_hr_array[i] for i in range(N)) - solver.Add(aux_vars_x4[j] >= hr_new_count - hr_new_target) - solver.Add(aux_vars_x4[j] >= hr_new_target - hr_new_count) - - # 约束:辅助变量 X5 用于计算各部门人数与平均值的偏差(绝对值) - if aux_vars_x5 is not None: - for j in range(M): - # 计算第 j 班次的总人数 - shift_total = sum(variables[i][j] for i in range(N)) - # 计算平均每个部门应有的人数 - avg_dept = shift_total / num_departments - - # 对每个部门 k - for k in range(num_departments): - # 计算第 j 班次中第 k 个部门的实际人数 - dept_count = sum(variables[i][j] * depart_mat[i][k] for i in range(N)) - # 添加绝对值约束 - solver.Add(aux_vars_x5[j][k] >= dept_count - avg_dept) - solver.Add(aux_vars_x5[j][k] >= avg_dept - dept_count) - - # 约束:满足每位同学的意愿班次数 - for i in range(N): - total_shifts = sum(variables[i]) - if want_num_array[i] == 3: - solver.Add(total_shifts >= 2) - solver.Add(total_shifts <= 3) - else: - solver.Add(total_shifts == want_num_array[i]) - - # 添加约束的辅助函数 - def add_shift_constraint(array, min_val, max_val): - for j in range(M): - shift_count = sum(variables[i][j] * array[i] for i in range(N)) - if min_val is not None: - solver.Add(shift_count >= min_val) - if max_val is not None: - solver.Add(shift_count <= max_val) - - # 约束:每个班次的总人数 - add_shift_constraint([1] * N, num_min, num_max) - - # 约束:每班次电脑或电器的老人数量 - tech_old_array = [is_old_array[i] * is_tech_array[i] for i in range(N)] - add_shift_constraint(tech_old_array, num_tech_old_min, num_tech_old_max) - - # 约束:每班次老人数量 - add_shift_constraint(is_old_array, num_old_min, num_old_max) - - # 约束:每班次小朋友数量 - add_shift_constraint(is_new_array, num_new_min, num_new_max) - - # 构建多目标优化函数 - m1, m2, m3, m4, m5 = m[0], m[1], m[2], m[3], m[4] - - # X1: 每班次人数与平均人数的偏差 - x1 = sum(aux_vars_x1) - obj_x1 = x1 / (m1 * M) # 归一化处理,防止数值过大 - - # X2: 每班次技术部老人数量的缺口总和(优化目标是让每班都达到目标值) - x2 = sum(aux_vars_x2) - obj_x2 = x2 / (m2 * M) # 归一化处理,缺口越小越好 - - # X3: 每班次技术部小朋友数量的缺口总和 - x3 = sum(aux_vars_x3) - obj_x3 = x3 / (m3 * M) # 归一化处理,缺口越小越好 - - # X4: 每班次人资部小朋友数量的缺口总和 - x4 = sum(aux_vars_x4) - obj_x4 = x4 / (m4 * M) # 归一化处理,缺口越小越好 - - # X5: 每班次各部门人数的平均程度 - obj_x5 = 0 - if aux_vars_x5: - x5 = sum(sum(aux_vars_x5[j]) for j in range(M)) - obj_x5 = x5 / (m5 * M) # 归一化处理 - - # 线性加权组合所有目标 - total_objective = ( - weights[0] * obj_x1 + weights[1] * obj_x2 + weights[2] * obj_x3 + weights[3] * obj_x4 + weights[4] * obj_x5 - ) - - print(f"优化目标权重: X1={weights[0]}, X2={weights[1]}, X3={weights[2]}, X4={weights[3]}, X5={weights[4]}") - solver.Minimize(total_objective) - - # 求解 - status = solver.Solve() - - if status == pywraplp.Solver.OPTIMAL: - print("Optimal solution found:") - variables_return = [[variables[i][j].solution_value() for j in range(M)] - for i in range(N)] - aux_vars_x1_return = sum([aux_vars_x1[j].solution_value() for j in range(M)]) / M - - print(f"Optimized objective value: {solver.Objective().Value()}") - print(f"X1 (每班次人数平均偏差): {aux_vars_x1_return}") - - # 输出各个优化目标的详细信息 - tech_old_array = [is_old_array[i] * is_tech_array[i] for i in range(N)] - tech_new_array = [is_new_array[i] * is_tech_array[i] for i in range(N)] - hr_new_array = [is_new_array[i] * is_hr_array[i] for i in range(N)] - - x2_value = sum(sum(variables[i][j].solution_value() * tech_old_array[i] for i in range(N)) for j in range(M)) / M - x3_value = sum(sum(variables[i][j].solution_value() * tech_new_array[i] for i in range(N)) for j in range(M)) / M - x4_value = sum(sum(variables[i][j].solution_value() * hr_new_array[i] for i in range(N)) for j in range(M)) / M - - aux_vars_x2_return = sum([aux_vars_x2[j].solution_value() for j in range(M)]) / M - aux_vars_x3_return = sum([aux_vars_x3[j].solution_value() for j in range(M)]) / M - aux_vars_x4_return = sum([aux_vars_x4[j].solution_value() for j in range(M)]) / M - - print(f"X2 (每班平均技术部老人): {x2_value}, 缺口平均: {aux_vars_x2_return}") - print(f"X3 (每班平均技术部小朋友): {x3_value}, 缺口平均: {aux_vars_x3_return}") - print(f"X4 (每班平均人资部小朋友): {x4_value}, 缺口平均: {aux_vars_x4_return}") - - if aux_vars_x5: - x5_value = sum(sum(aux_vars_x5[j][k].solution_value() for k in range(num_departments)) for j in range(M)) - print(f"X5 (部门人数偏差): {x5_value}") - - return variables_return - else: - print("No solution found.") - return None +from ortools.linear_solver import pywraplp + +def solve_program(preference_mat:list, depart_mat:list, want_num_array:list, is_new_array:list, + num_min=None, num_max=None, + num_tech_old_min=None, num_tech_old_max=None, + num_old_min=None, num_old_max=None, + num_new_min=None, num_new_max=None, + weights=[1.0, 1.0, 1.0, 0.5, 0.5] + ): + + is_old_array = [not is_new for is_new in is_new_array] + is_tech_array = [depart_mat[i][0] == 1 or depart_mat[i][1] == 1 for i in range(len(depart_mat))] + is_hr_array = [depart_mat[i][2] == 1 for i in range(len(depart_mat))] + + # 使用 SCIP 求解器求解组合优化问题 + solver = pywraplp.Solver.CreateSolver("SCIP") + if not solver: + return + + N = len(preference_mat) # 学生人数 + M = len(preference_mat[0]) # 班次数 + avg_num = sum(want_num_array) / M + print(f"平均人数:{avg_num}") + + # 各个目标的期望值/归一化系数 + m = [2.5, 3, 4, 1, 8] # [班次人数偏差, 技术部老人, 技术部小朋友, 人资部小朋友, 部门均衡] + + # 创建决策变量:variables[i][j] 表示第 i 个学生是否选择第 j 个班次 + variables = [[solver.IntVar(0.0, 1.0, f"choice_{i}_{j}") + for j in range(M)] for i in range(N)] + + # 创建辅助变量用于优化目标1(均衡班次人数) + aux_vars_x1 = [solver.NumVar(0.0, solver.infinity(), f"aux_x1_{j}") + for j in range(M)] + + # 创建辅助变量用于优化目标2(每班次技术部老人数量缺口) + aux_vars_x2 = [solver.NumVar(0.0, solver.infinity(), f"aux_x2_{j}") + for j in range(M)] + + # 创建辅助变量用于优化目标3(每班次技术部小朋友数量缺口) + aux_vars_x3 = [solver.NumVar(0.0, solver.infinity(), f"aux_x3_{j}") + for j in range(M)] + + # 创建辅助变量用于优化目标4(每班次人资部小朋友数量缺口) + aux_vars_x4 = [solver.NumVar(0.0, solver.infinity(), f"aux_x4_{j}") + for j in range(M)] + + # 创建辅助变量用于优化目标5(均衡各部门人数) + # 如果提供了部门信息,则创建相应的辅助变量 + aux_vars_x5 = [] + num_departments = 5 # 电脑部、电器部、人资部、财外部、文宣部 + # 为每个班次的每个部门创建辅助变量 + aux_vars_x5 = [[solver.NumVar(0.0, solver.infinity(), f"aux_x5_{j}_{k}") + for k in range(num_departments)] for j in range(M)] + + print("Number of variables =", solver.NumVariables()) + + # 约束:每个同学意愿一定要满足 + for i in range(N): + for j in range(M): + solver.Add(variables[i][j] <= preference_mat[i][j]) + + # 约束:辅助变量 X1 用于计算与平均人数的偏差(绝对值) + for j in range(M): + actual_num = sum(variables[i][j] for i in range(N)) + solver.Add(aux_vars_x1[j] >= actual_num - avg_num) + solver.Add(aux_vars_x1[j] >= avg_num - actual_num) + + # 约束:辅助变量 X2 用于计算每班次技术部老人数量超出期望值的部分 + # 辅助变量表示超出期望值(期望每班有m2个技术部老人,超过即计入) + # 目标是让尽可能多的班次达到但不超过这个期望值 + tech_old_target = m[1] # 使用归一化系数作为期望值 + for j in range(M): + tech_old_count = sum(variables[i][j] * is_old_array[i] * is_tech_array[i] for i in range(N)) + # 辅助变量捕获超出部分和不足部分 + solver.Add(aux_vars_x2[j] >= tech_old_count - tech_old_target) + solver.Add(aux_vars_x2[j] >= tech_old_target - tech_old_count) + + # 约束:辅助变量 X3 用于计算每班次技术部小朋友数量超出期望值的部分 + tech_new_target = m[2] # 使用归一化系数作为期望值 + for j in range(M): + tech_new_count = sum(variables[i][j] * is_new_array[i] * is_tech_array[i] for i in range(N)) + solver.Add(aux_vars_x3[j] >= tech_new_count - tech_new_target) + solver.Add(aux_vars_x3[j] >= tech_new_target - tech_new_count) + + # 约束:辅助变量 X4 用于计算每班次人资部小朋友数量超出期望值的部分 + hr_new_target = m[3] # 使用归一化系数作为期望值 + for j in range(M): + hr_new_count = sum(variables[i][j] * is_new_array[i] * is_hr_array[i] for i in range(N)) + solver.Add(aux_vars_x4[j] >= hr_new_count - hr_new_target) + solver.Add(aux_vars_x4[j] >= hr_new_target - hr_new_count) + + # 约束:辅助变量 X5 用于计算各部门人数与平均值的偏差(绝对值) + if aux_vars_x5 is not None: + for j in range(M): + # 计算第 j 班次的总人数 + shift_total = sum(variables[i][j] for i in range(N)) + # 计算平均每个部门应有的人数 + avg_dept = shift_total / num_departments + + # 对每个部门 k + for k in range(num_departments): + # 计算第 j 班次中第 k 个部门的实际人数 + dept_count = sum(variables[i][j] * depart_mat[i][k] for i in range(N)) + # 添加绝对值约束 + solver.Add(aux_vars_x5[j][k] >= dept_count - avg_dept) + solver.Add(aux_vars_x5[j][k] >= avg_dept - dept_count) + + # 约束:满足每位同学的意愿班次数 + for i in range(N): + total_shifts = sum(variables[i]) + if want_num_array[i] == 3: + solver.Add(total_shifts >= 2) + solver.Add(total_shifts <= 3) + else: + solver.Add(total_shifts == want_num_array[i]) + + # 添加约束的辅助函数 + def add_shift_constraint(array, min_val, max_val): + for j in range(M): + shift_count = sum(variables[i][j] * array[i] for i in range(N)) + if min_val is not None: + solver.Add(shift_count >= min_val) + if max_val is not None: + solver.Add(shift_count <= max_val) + + # 约束:每个班次的总人数 + add_shift_constraint([1] * N, num_min, num_max) + + # 约束:每班次电脑或电器的老人数量 + tech_old_array = [is_old_array[i] * is_tech_array[i] for i in range(N)] + add_shift_constraint(tech_old_array, num_tech_old_min, num_tech_old_max) + + # 约束:每班次老人数量 + add_shift_constraint(is_old_array, num_old_min, num_old_max) + + # 约束:每班次小朋友数量 + add_shift_constraint(is_new_array, num_new_min, num_new_max) + + # 构建多目标优化函数 + m1, m2, m3, m4, m5 = m[0], m[1], m[2], m[3], m[4] + + # X1: 每班次人数与平均人数的偏差 + x1 = sum(aux_vars_x1) + obj_x1 = x1 / (m1 * M) # 归一化处理,防止数值过大 + + # X2: 每班次技术部老人数量的缺口总和(优化目标是让每班都达到目标值) + x2 = sum(aux_vars_x2) + obj_x2 = x2 / (m2 * M) # 归一化处理,缺口越小越好 + + # X3: 每班次技术部小朋友数量的缺口总和 + x3 = sum(aux_vars_x3) + obj_x3 = x3 / (m3 * M) # 归一化处理,缺口越小越好 + + # X4: 每班次人资部小朋友数量的缺口总和 + x4 = sum(aux_vars_x4) + obj_x4 = x4 / (m4 * M) # 归一化处理,缺口越小越好 + + # X5: 每班次各部门人数的平均程度 + obj_x5 = 0 + if aux_vars_x5: + x5 = sum(sum(aux_vars_x5[j]) for j in range(M)) + obj_x5 = x5 / (m5 * M) # 归一化处理 + + # 线性加权组合所有目标 + total_objective = ( + weights[0] * obj_x1 + weights[1] * obj_x2 + weights[2] * obj_x3 + weights[3] * obj_x4 + weights[4] * obj_x5 + ) + + print(f"优化目标权重: X1={weights[0]}, X2={weights[1]}, X3={weights[2]}, X4={weights[3]}, X5={weights[4]}") + solver.Minimize(total_objective) + + # 求解 + status = solver.Solve() + + if status == pywraplp.Solver.OPTIMAL: + print("Optimal solution found:") + variables_return = [[variables[i][j].solution_value() for j in range(M)] + for i in range(N)] + aux_vars_x1_return = sum([aux_vars_x1[j].solution_value() for j in range(M)]) / M + + print(f"Optimized objective value: {solver.Objective().Value()}") + print(f"X1 (每班次人数平均偏差): {aux_vars_x1_return}") + + # 输出各个优化目标的详细信息 + tech_old_array = [is_old_array[i] * is_tech_array[i] for i in range(N)] + tech_new_array = [is_new_array[i] * is_tech_array[i] for i in range(N)] + hr_new_array = [is_new_array[i] * is_hr_array[i] for i in range(N)] + + x2_value = sum(sum(variables[i][j].solution_value() * tech_old_array[i] for i in range(N)) for j in range(M)) / M + x3_value = sum(sum(variables[i][j].solution_value() * tech_new_array[i] for i in range(N)) for j in range(M)) / M + x4_value = sum(sum(variables[i][j].solution_value() * hr_new_array[i] for i in range(N)) for j in range(M)) / M + + aux_vars_x2_return = sum([aux_vars_x2[j].solution_value() for j in range(M)]) / M + aux_vars_x3_return = sum([aux_vars_x3[j].solution_value() for j in range(M)]) / M + aux_vars_x4_return = sum([aux_vars_x4[j].solution_value() for j in range(M)]) / M + + print(f"X2 (每班平均技术部老人): {x2_value}, 缺口平均: {aux_vars_x2_return}") + print(f"X3 (每班平均技术部小朋友): {x3_value}, 缺口平均: {aux_vars_x3_return}") + print(f"X4 (每班平均人资部小朋友): {x4_value}, 缺口平均: {aux_vars_x4_return}") + + if aux_vars_x5: + x5_value = sum(sum(aux_vars_x5[j][k].solution_value() for k in range(num_departments)) for j in range(M)) + print(f"X5 (部门人数偏差): {x5_value}") + + return variables_return + else: + print("No solution found.") + return None \ No newline at end of file diff --git a/utils.py b/utils.py index fc4f0d0..9f1278f 100644 --- a/utils.py +++ b/utils.py @@ -1,143 +1,143 @@ -import pandas as pd -from datetime import datetime -from random import choice - -# 对于表格每一列: -col_timestamp = 1 # i = 1 -> 时间戳 -col_name = 6 # i = 6 -> 姓名 -col_department = 7 # i = 7 -> 部门编号 -col_member = 8 # i = 8 -> 老人 / 小朋友 -col_dutystart = 9 # i = 9 -> 值班开始时间 -col_dutyend = 28 # i = 28 -> 值班结束时间 -col_dutyfreq = 29 # i = 29 -> 想要值班的次数 - -index_to_departments = { # 部门编号和部门名称的对应关系 - 1: "电脑部", - 2: "电器部", - 3: "人资部", - 4: "财外部", - 5: "文宣部" -} - -index_to_type = { # 老人 / 小朋友和对应的编号 - 1: "老人", - 2: "小朋友" -} - -def read_excel(file_path): - df = pd.read_excel(file_path) - - # 待返回的所有信息。N 为学生人数,M 为班次数 - index_to_name_dict = {} # 长度为 N 的字典,包含数组下标和学生姓名的对应关系 - preference_mat = [] # N x M 的二维数组,表示每位学生对每个班次的满意度 - depart_mat = [] # N x D 的二维数组,表示每位学生所属的部门(D 为部门数量) - want_num_array = [] # 长度为 N 的数组,表示每位学生想要值班的次数 - is_new_array = [] # 长度为 N 的数组,表示每位学生是否是小朋友 - - ''' - 考虑到原始填表信息中可能有某位同学多次提交的记录,先过滤一下冗余信息 - ''' - all_data = {} # 储存过滤后的数据 - time_format = "%Y/%m/%d %H:%M:%S" - for index, row in df.iterrows(): - name = row.iloc[col_name] - time_this = datetime.strptime(row.iloc[col_timestamp], time_format) - if name not in all_data or time_this > datetime.strptime(all_data[name][col_timestamp], time_format): - all_data[name] = row.tolist() - - for index, (name, info_list) in enumerate(all_data.items()): - index_to_name_dict[index] = name - preference_mat.append(info_list[col_dutystart:col_dutyend + 1]) - depart_mat.append([1 if info_list[col_department] == i else 0 for i in range(1, 6)]) - want_num_array.append(info_list[col_dutyfreq]) - is_new_array.append(index_to_type[info_list[col_member]] == "小朋友") - - return all_data, index_to_name_dict, preference_mat, depart_mat, want_num_array, is_new_array - -def save_to_excel(variables, all_data, index_to_name_dict, file_path): - - # 收集每个班次的值班人员信息 - all_result = [] - max_single_class_num = 0 - - for j in range(len(variables[0])): - on_duty_list = [] - hr_new_index = [] - none_tech_new_index = [] - new_index = [] - all_index = [] - - for i in range(len(variables)): - if variables[i][j] == 1: - name = index_to_name_dict[i] - department = index_to_departments[all_data[name][col_department]] - member_type = index_to_type[all_data[name][col_member]] - - single_stu_info = { - "name": name, - "department": department, - "type": member_type, - "duty_monitor": False - } - on_duty_list.append(single_stu_info) - - idx = len(all_index) - all_index.append(idx) - if member_type == "小朋友": - new_index.append(idx) - if department in ["人资部", "财外部", "文宣部"]: - none_tech_new_index.append(idx) - if department == "人资部": - hr_new_index.append(idx) - - # 选择值班组长(优先级:人资部小朋友 > 非技术部小朋友 > 小朋友 > 所有人) - if hr_new_index: - duty_monitor_index = choice(hr_new_index) - elif none_tech_new_index: - duty_monitor_index = choice(none_tech_new_index) - elif new_index: - duty_monitor_index = choice(new_index) - else: - duty_monitor_index = choice(all_index) - on_duty_list[duty_monitor_index]["duty_monitor"] = True - - on_duty_list.sort(key=lambda x: x["department"]) - all_result.append(on_duty_list) - max_single_class_num = max(max_single_class_num, len(on_duty_list)) - - - from openpyxl import Workbook - from openpyxl.styles import PatternFill - - wb = Workbook() - ws = wb.active if wb.active else wb.create_sheet(title="Sheet1") - - # 设置表头 - weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"] - for idx, day in enumerate(weekdays, start=2): - col = chr(ord('A') + idx - 1) - ws[f'{col}1'] = day - ws.column_dimensions[col].width = 25 - - # 设置班次标题和起始行 - shift_names = ["第一班", "第二班", "第三班"] - start_index = [] - for shift_idx, shift_name in enumerate(shift_names): - start_row = 2 + shift_idx * (max_single_class_num + 1) - end_row = start_row + max_single_class_num - ws.merge_cells(f'A{start_row}:A{end_row}') - ws[f'A{start_row}'] = shift_name - start_index.append(start_row) - - # 填写值班人员信息 - yellow_fill = PatternFill(fill_type='solid', start_color='FFFF00', end_color='FFFF00') - for duty_index, duty in enumerate(all_result): - col = chr(ord('B') + duty_index // 3) - for stu_index, stu in enumerate(duty): - row = start_index[duty_index % 3] + stu_index - cell = ws[f'{col}{row}'] - cell.value = f"{stu['name']} {stu['department']} {stu['type']}" - if stu['duty_monitor']: - cell.fill = yellow_fill - +import pandas as pd +from datetime import datetime +from random import choice + +# 对于表格每一列: +col_timestamp = 1 # i = 1 -> 时间戳 +col_name = 6 # i = 6 -> 姓名 +col_department = 7 # i = 7 -> 部门编号 +col_member = 8 # i = 8 -> 老人 / 小朋友 +col_dutystart = 9 # i = 9 -> 值班开始时间 +col_dutyend = 28 # i = 28 -> 值班结束时间 +col_dutyfreq = 29 # i = 29 -> 想要值班的次数 + +index_to_departments = { # 部门编号和部门名称的对应关系 + 1: "电脑部", + 2: "电器部", + 3: "人资部", + 4: "财外部", + 5: "文宣部" +} + +index_to_type = { # 老人 / 小朋友和对应的编号 + 1: "老人", + 2: "小朋友" +} + +def read_excel(file_path): + df = pd.read_excel(file_path) + + # 待返回的所有信息。N 为学生人数,M 为班次数 + index_to_name_dict = {} # 长度为 N 的字典,包含数组下标和学生姓名的对应关系 + preference_mat = [] # N x M 的二维数组,表示每位学生对每个班次的满意度 + depart_mat = [] # N x D 的二维数组,表示每位学生所属的部门(D 为部门数量) + want_num_array = [] # 长度为 N 的数组,表示每位学生想要值班的次数 + is_new_array = [] # 长度为 N 的数组,表示每位学生是否是小朋友 + + ''' + 考虑到原始填表信息中可能有某位同学多次提交的记录,先过滤一下冗余信息 + ''' + all_data = {} # 储存过滤后的数据 + time_format = "%Y/%m/%d %H:%M:%S" + for index, row in df.iterrows(): + name = row.iloc[col_name] + time_this = datetime.strptime(row.iloc[col_timestamp], time_format) + if name not in all_data or time_this > datetime.strptime(all_data[name][col_timestamp], time_format): + all_data[name] = row.tolist() + + for index, (name, info_list) in enumerate(all_data.items()): + index_to_name_dict[index] = name + preference_mat.append(info_list[col_dutystart:col_dutyend + 1]) + depart_mat.append([1 if info_list[col_department] == i else 0 for i in range(1, 6)]) + want_num_array.append(info_list[col_dutyfreq]) + is_new_array.append(index_to_type[info_list[col_member]] == "小朋友") + + return all_data, index_to_name_dict, preference_mat, depart_mat, want_num_array, is_new_array + +def save_to_excel(variables, all_data, index_to_name_dict, file_path): + + # 收集每个班次的值班人员信息 + all_result = [] + max_single_class_num = 0 + + for j in range(len(variables[0])): + on_duty_list = [] + hr_new_index = [] + none_tech_new_index = [] + new_index = [] + all_index = [] + + for i in range(len(variables)): + if variables[i][j] == 1: + name = index_to_name_dict[i] + department = index_to_departments[all_data[name][col_department]] + member_type = index_to_type[all_data[name][col_member]] + + single_stu_info = { + "name": name, + "department": department, + "type": member_type, + "duty_monitor": False + } + on_duty_list.append(single_stu_info) + + idx = len(all_index) + all_index.append(idx) + if member_type == "小朋友": + new_index.append(idx) + if department in ["人资部", "财外部", "文宣部"]: + none_tech_new_index.append(idx) + if department == "人资部": + hr_new_index.append(idx) + + # 选择值班组长(优先级:人资部小朋友 > 非技术部小朋友 > 小朋友 > 所有人) + if hr_new_index: + duty_monitor_index = choice(hr_new_index) + elif none_tech_new_index: + duty_monitor_index = choice(none_tech_new_index) + elif new_index: + duty_monitor_index = choice(new_index) + else: + duty_monitor_index = choice(all_index) + on_duty_list[duty_monitor_index]["duty_monitor"] = True + + on_duty_list.sort(key=lambda x: x["department"]) + all_result.append(on_duty_list) + max_single_class_num = max(max_single_class_num, len(on_duty_list)) + + + from openpyxl import Workbook + from openpyxl.styles import PatternFill + + wb = Workbook() + ws = wb.active if wb.active else wb.create_sheet(title="Sheet1") + + # 设置表头 + weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"] + for idx, day in enumerate(weekdays, start=2): + col = chr(ord('A') + idx - 1) + ws[f'{col}1'] = day + ws.column_dimensions[col].width = 25 + + # 设置班次标题和起始行 + shift_names = ["第一班", "第二班", "第三班"] + start_index = [] + for shift_idx, shift_name in enumerate(shift_names): + start_row = 2 + shift_idx * (max_single_class_num + 1) + end_row = start_row + max_single_class_num + ws.merge_cells(f'A{start_row}:A{end_row}') + ws[f'A{start_row}'] = shift_name + start_index.append(start_row) + + # 填写值班人员信息 + yellow_fill = PatternFill(fill_type='solid', start_color='FFFF00', end_color='FFFF00') + for duty_index, duty in enumerate(all_result): + col = chr(ord('B') + duty_index // 3) + for stu_index, stu in enumerate(duty): + row = start_index[duty_index % 3] + stu_index + cell = ws[f'{col}{row}'] + cell.value = f"{stu['name']} {stu['department']} {stu['type']}" + if stu['duty_monitor']: + cell.fill = yellow_fill + wb.save(file_path) \ No newline at end of file diff --git a/uv.lock b/uv.lock index e7cd98d..b286e75 100644 --- a/uv.lock +++ b/uv.lock @@ -1,426 +1,426 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" - -[[package]] -name = "absl-py" -version = "2.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588, upload-time = "2025-07-03T09:31:44.05Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811, upload-time = "2025-07-03T09:31:42.253Z" }, -] - -[[package]] -name = "altgraph" -version = "0.17.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/a8/7145824cf0b9e3c28046520480f207df47e927df83aa9555fb47f8505922/altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406", size = 48418, upload-time = "2023-09-25T09:04:52.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/3f/3bc3f1d83f6e4a7fcb834d3720544ca597590425be5ba9db032b2bf322a2/altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff", size = 21212, upload-time = "2023-09-25T09:04:50.691Z" }, -] - -[[package]] -name = "et-xmlfile" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, -] - -[[package]] -name = "eva-duty-arrange-tool" -version = "2.0.0" -source = { virtual = "." } -dependencies = [ - { name = "openpyxl" }, - { name = "ortools" }, - { name = "pandas" }, - { name = "pyinstaller" }, - { name = "pyside6" }, -] - -[package.metadata] -requires-dist = [ - { name = "openpyxl", specifier = ">=3.1.5" }, - { name = "ortools", specifier = ">=9.14.6206" }, - { name = "pandas", specifier = ">=2.3.3" }, - { name = "pyinstaller", specifier = ">=6.16.0" }, - { name = "pyside6", specifier = ">=6.10.0" }, -] - -[[package]] -name = "immutabledict" -version = "4.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ce/12/1da8e1a9050d0603ba65fb1796ed8860a705b906701c96e77f85cc7490be/immutabledict-4.2.2.tar.gz", hash = "sha256:cb6ed3090df593148f94cb407d218ca526fd2639694afdb553dc4f50ce6feeca", size = 6099, upload-time = "2025-10-12T13:32:59.755Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/7b/04ab6afa1ff7eb9ccb09049918c0407b205f5009092c0416147d163e4e2b/immutabledict-4.2.2-py3-none-any.whl", hash = "sha256:97c31d098a2c850e93a958badeef765e4736ed7942ec73e439facd764a3a7217", size = 4736, upload-time = "2025-10-12T13:32:58.326Z" }, -] - -[[package]] -name = "macholib" -version = "1.16.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "altgraph" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/ee/af1a3842bdd5902ce133bd246eb7ffd4375c38642aeb5dc0ae3a0329dfa2/macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30", size = 59309, upload-time = "2023-09-25T09:10:16.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/5d/c059c180c84f7962db0aeae7c3b9303ed1d73d76f2bfbc32bc231c8be314/macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c", size = 38094, upload-time = "2023-09-25T09:10:14.188Z" }, -] - -[[package]] -name = "numpy" -version = "2.3.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf", size = 20957014, upload-time = "2025-09-09T15:56:29.966Z" }, - { url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25", size = 14185220, upload-time = "2025-09-09T15:56:32.175Z" }, - { url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe", size = 5113918, upload-time = "2025-09-09T15:56:34.175Z" }, - { url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b", size = 6647922, upload-time = "2025-09-09T15:56:36.149Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8", size = 14281991, upload-time = "2025-09-09T15:56:40.548Z" }, - { url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20", size = 16641643, upload-time = "2025-09-09T15:56:43.343Z" }, - { url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea", size = 16056787, upload-time = "2025-09-09T15:56:46.141Z" }, - { url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7", size = 18579598, upload-time = "2025-09-09T15:56:49.844Z" }, - { url = "https://files.pythonhosted.org/packages/17/b6/fc8f82cb3520768718834f310c37d96380d9dc61bfdaf05fe5c0b7653e01/numpy-2.3.3-cp312-cp312-win32.whl", hash = "sha256:5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf", size = 6320800, upload-time = "2025-09-09T15:56:52.499Z" }, - { url = "https://files.pythonhosted.org/packages/32/ee/de999f2625b80d043d6d2d628c07d0d5555a677a3cf78fdf868d409b8766/numpy-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb", size = 12786615, upload-time = "2025-09-09T15:56:54.422Z" }, - { url = "https://files.pythonhosted.org/packages/49/6e/b479032f8a43559c383acb20816644f5f91c88f633d9271ee84f3b3a996c/numpy-2.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5", size = 10195936, upload-time = "2025-09-09T15:56:56.541Z" }, - { url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf", size = 20949588, upload-time = "2025-09-09T15:56:59.087Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7", size = 14177802, upload-time = "2025-09-09T15:57:01.73Z" }, - { url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6", size = 5106537, upload-time = "2025-09-09T15:57:03.765Z" }, - { url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7", size = 6640743, upload-time = "2025-09-09T15:57:07.921Z" }, - { url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c", size = 14278881, upload-time = "2025-09-09T15:57:11.349Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93", size = 16636301, upload-time = "2025-09-09T15:57:14.245Z" }, - { url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae", size = 16053645, upload-time = "2025-09-09T15:57:16.534Z" }, - { url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86", size = 18578179, upload-time = "2025-09-09T15:57:18.883Z" }, - { url = "https://files.pythonhosted.org/packages/20/d9/487a2bccbf7cc9d4bfc5f0f197761a5ef27ba870f1e3bbb9afc4bbe3fcc2/numpy-2.3.3-cp313-cp313-win32.whl", hash = "sha256:9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8", size = 6312250, upload-time = "2025-09-09T15:57:21.296Z" }, - { url = "https://files.pythonhosted.org/packages/1b/b5/263ebbbbcede85028f30047eab3d58028d7ebe389d6493fc95ae66c636ab/numpy-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf", size = 12783269, upload-time = "2025-09-09T15:57:23.034Z" }, - { url = "https://files.pythonhosted.org/packages/fa/75/67b8ca554bbeaaeb3fac2e8bce46967a5a06544c9108ec0cf5cece559b6c/numpy-2.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5", size = 10195314, upload-time = "2025-09-09T15:57:25.045Z" }, - { url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc", size = 21048025, upload-time = "2025-09-09T15:57:27.257Z" }, - { url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc", size = 14301053, upload-time = "2025-09-09T15:57:30.077Z" }, - { url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b", size = 5229444, upload-time = "2025-09-09T15:57:32.733Z" }, - { url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19", size = 6738039, upload-time = "2025-09-09T15:57:34.328Z" }, - { url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30", size = 14352314, upload-time = "2025-09-09T15:57:36.255Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e", size = 16701722, upload-time = "2025-09-09T15:57:38.622Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3", size = 16132755, upload-time = "2025-09-09T15:57:41.16Z" }, - { url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea", size = 18651560, upload-time = "2025-09-09T15:57:43.459Z" }, - { url = "https://files.pythonhosted.org/packages/5b/8e/3ab61a730bdbbc201bb245a71102aa609f0008b9ed15255500a99cd7f780/numpy-2.3.3-cp313-cp313t-win32.whl", hash = "sha256:a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd", size = 6442776, upload-time = "2025-09-09T15:57:45.793Z" }, - { url = "https://files.pythonhosted.org/packages/1c/3a/e22b766b11f6030dc2decdeff5c2fb1610768055603f9f3be88b6d192fb2/numpy-2.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d", size = 12927281, upload-time = "2025-09-09T15:57:47.492Z" }, - { url = "https://files.pythonhosted.org/packages/7b/42/c2e2bc48c5e9b2a83423f99733950fbefd86f165b468a3d85d52b30bf782/numpy-2.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1", size = 10265275, upload-time = "2025-09-09T15:57:49.647Z" }, - { url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593", size = 20951527, upload-time = "2025-09-09T15:57:52.006Z" }, - { url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652", size = 14186159, upload-time = "2025-09-09T15:57:54.407Z" }, - { url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7", size = 5114624, upload-time = "2025-09-09T15:57:56.5Z" }, - { url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a", size = 6642627, upload-time = "2025-09-09T15:57:58.206Z" }, - { url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe", size = 14296926, upload-time = "2025-09-09T15:58:00.035Z" }, - { url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421", size = 16638958, upload-time = "2025-09-09T15:58:02.738Z" }, - { url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021", size = 16071920, upload-time = "2025-09-09T15:58:05.029Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf", size = 18577076, upload-time = "2025-09-09T15:58:07.745Z" }, - { url = "https://files.pythonhosted.org/packages/f0/a1/b87a284fb15a42e9274e7fcea0dad259d12ddbf07c1595b26883151ca3b4/numpy-2.3.3-cp314-cp314-win32.whl", hash = "sha256:cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0", size = 6366952, upload-time = "2025-09-09T15:58:10.096Z" }, - { url = "https://files.pythonhosted.org/packages/70/5f/1816f4d08f3b8f66576d8433a66f8fa35a5acfb3bbd0bf6c31183b003f3d/numpy-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8", size = 12919322, upload-time = "2025-09-09T15:58:12.138Z" }, - { url = "https://files.pythonhosted.org/packages/8c/de/072420342e46a8ea41c324a555fa90fcc11637583fb8df722936aed1736d/numpy-2.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe", size = 10478630, upload-time = "2025-09-09T15:58:14.64Z" }, - { url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00", size = 21047987, upload-time = "2025-09-09T15:58:16.889Z" }, - { url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a", size = 14301076, upload-time = "2025-09-09T15:58:20.343Z" }, - { url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d", size = 5229491, upload-time = "2025-09-09T15:58:22.481Z" }, - { url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a", size = 6737913, upload-time = "2025-09-09T15:58:24.569Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54", size = 14352811, upload-time = "2025-09-09T15:58:26.416Z" }, - { url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e", size = 16702689, upload-time = "2025-09-09T15:58:28.831Z" }, - { url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097", size = 16133855, upload-time = "2025-09-09T15:58:31.349Z" }, - { url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970", size = 18652520, upload-time = "2025-09-09T15:58:33.762Z" }, - { url = "https://files.pythonhosted.org/packages/fd/c2/e5ed830e08cd0196351db55db82f65bc0ab05da6ef2b72a836dcf1936d2f/numpy-2.3.3-cp314-cp314t-win32.whl", hash = "sha256:1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5", size = 6515371, upload-time = "2025-09-09T15:58:36.04Z" }, - { url = "https://files.pythonhosted.org/packages/47/c7/b0f6b5b67f6788a0725f744496badbb604d226bf233ba716683ebb47b570/numpy-2.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f", size = 13112576, upload-time = "2025-09-09T15:58:37.927Z" }, - { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" }, -] - -[[package]] -name = "openpyxl" -version = "3.1.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "et-xmlfile" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" }, -] - -[[package]] -name = "ortools" -version = "9.14.6206" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "absl-py" }, - { name = "immutabledict" }, - { name = "numpy" }, - { name = "pandas" }, - { name = "protobuf" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/2e/b0d7e7ee9d1a45001a05ca2f65ccc1ab28cee510e725e01e248496510ac1/ortools-9.14.6206-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:e38c8c4a184820cbfdb812a8d484f6506cf16993ce2a95c88bc1c9d23b17c63e", size = 22239884, upload-time = "2025-06-19T15:45:51.774Z" }, - { url = "https://files.pythonhosted.org/packages/31/ad/964ee5341767dd9e2f13e76f0a36d45aa8d81ad776c80bdd6dedc8f2f462/ortools-9.14.6206-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db685073cbed9f8bfaa744f5e883f3dea57c93179b0abe1788276fd3b074fa61", size = 20211128, upload-time = "2025-06-19T15:45:54.829Z" }, - { url = "https://files.pythonhosted.org/packages/7c/93/d94a66cdfadeb2747d96f1c8d3f590d81c4ad47fd357dfc57de8d7a75bbe/ortools-9.14.6206-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4bfb8bffb29991834cf4bde7048ca8ee8caed73e8dd21e5ec7de99a33bbfea0", size = 25663650, upload-time = "2025-06-19T15:47:20.207Z" }, - { url = "https://files.pythonhosted.org/packages/eb/74/1d374bf510e9fb36bba82ecd3e09461cd8394afef3e418fa5b060f129401/ortools-9.14.6206-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb464a698837e7f90ca5f9b3d748b6ddf553198a70032bc77824d1cd88695d2b", size = 27670004, upload-time = "2025-06-19T15:47:23.111Z" }, - { url = "https://files.pythonhosted.org/packages/e9/e3/5943467ae41efa06cff12b79bfd146c4a54903345f0cc5c896884829d14a/ortools-9.14.6206-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8f33deaeb7c3dda8ca1d29c5b9aa9c3a4f2ca9ecf34f12a1f809bb2995f41274", size = 27653302, upload-time = "2025-06-19T15:53:47.615Z" }, - { url = "https://files.pythonhosted.org/packages/6c/28/a896080fe6e13b4bdae74601f19c28b7ba05ec45f3adca3e992d6174ac57/ortools-9.14.6206-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:086e7c2dc4f23efffb20a5e20f618c7d6adb99b2d94f684cab482387da3bc434", size = 29503497, upload-time = "2025-06-19T15:53:51.174Z" }, - { url = "https://files.pythonhosted.org/packages/38/ad/8d02013116cc5d51084fe24c5b73f5b349dd10925d2927d19d21a2068a4c/ortools-9.14.6206-cp312-cp312-win_amd64.whl", hash = "sha256:17c13b0bfde17ac57789ad35243edf1318ecd5db23cf949b75ab62480599f188", size = 20512174, upload-time = "2025-06-19T15:46:33.339Z" }, - { url = "https://files.pythonhosted.org/packages/d8/9c/fe6d5ca549c2fb5ed3e8e1d928a11f4509c024c974e1f6e2d75d206bc226/ortools-9.14.6206-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:8d0df7eef8ba53ad235e29018389259bad2e667d9594b9c2a412ed6a5756bd4e", size = 22259719, upload-time = "2025-06-19T15:45:57.504Z" }, - { url = "https://files.pythonhosted.org/packages/19/0f/0b9858e7d5b31dfb23a1fe945d3e9fd5d2feba893190e53f70374ac7e084/ortools-9.14.6206-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:57dfe10844ce8331634d4723040fe249263fd490407346efc314c0bc656849b5", size = 20231092, upload-time = "2025-06-19T15:46:00.003Z" }, - { url = "https://files.pythonhosted.org/packages/6b/48/de4eff68cb4ea124c9d6edf68049d329a332aab36d011c2798ea1afb4c98/ortools-9.14.6206-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5c0c2c00a6e5d5c462e76fdda7dbd40d0f9139f1df4211d34b36906696248020", size = 25662903, upload-time = "2025-06-19T15:47:26.368Z" }, - { url = "https://files.pythonhosted.org/packages/c9/44/970f72cd5e537fc10e20582dadbdd2f8b2b3c802ac812105c40119572ea7/ortools-9.14.6206-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:38044cf39952d93cbcc02f6acdbe0a9bd3628fbf17f0d7eb0374060fa028c22e", size = 27669250, upload-time = "2025-06-19T15:47:29.738Z" }, - { url = "https://files.pythonhosted.org/packages/a4/31/37e2eacc37e4448090f693d482316a67bf2b47f2aefae6d6236b4ed41924/ortools-9.14.6206-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:98564de773d709e1e49cb3c32f6917589c314f047786d88bd5f324c0eb7be96e", size = 27652453, upload-time = "2025-06-19T15:53:54.742Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8f/0b1bb2a3268114b95381d5a1146c293259d6e45c69e4e3f2d2cf52617796/ortools-9.14.6206-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:80528b0ac72dc3de00cbeef2ce028517a476450b5877b1cda1b8ecb9fa98505e", size = 29503368, upload-time = "2025-06-19T15:53:57.989Z" }, - { url = "https://files.pythonhosted.org/packages/d9/ad/93c05084eec710c38bc952a19002aac31d4c26bb6f8fcbf1057ccc55254b/ortools-9.14.6206-cp313-cp313-win_amd64.whl", hash = "sha256:47b1b15dcb085d32c61621b790259193aefa9e4577abadf233d47fbe7d0b81ef", size = 20530387, upload-time = "2025-06-19T15:46:35.964Z" }, - { url = "https://files.pythonhosted.org/packages/51/85/aeba0d90a3cee4a23ba74a4f2d4c27088923c8b77fa1b73a52ad34593be5/ortools-9.14.6206-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d26a0f9ed97ef9d3384a9069923585f5f974c3fde555a41f4d6381fbe7840bc4", size = 25704462, upload-time = "2025-06-19T15:47:33.064Z" }, - { url = "https://files.pythonhosted.org/packages/98/83/2954b6aa032b77f49a478e0d9682ae8176abfb4bd33d935f0260ecb1741b/ortools-9.14.6206-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d40d8141667d47405f296a9f687058c566d7816586e9a672b59e9fcec8493133", size = 27687594, upload-time = "2025-06-19T15:47:36.241Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9a/9e9866952841e9f4aadccb110c0012d6a76f01b8798432fac0971d60aac1/ortools-9.14.6206-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:aefea81ed81aa937873efc520381785ed65380e52917f492ab566f46bbb5660d", size = 27698487, upload-time = "2025-06-19T15:54:01.03Z" }, - { url = "https://files.pythonhosted.org/packages/91/a5/97a86cdd52c961273981e9ab905a301b7f086593b597d8974c411decf61a/ortools-9.14.6206-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f044bb277db3ab6a1b958728fe1cf14ca87c3800d67d7b321d876b48269340f6", size = 29519050, upload-time = "2025-06-19T15:54:04.147Z" }, -] - -[[package]] -name = "packaging" -version = "25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, -] - -[[package]] -name = "pandas" -version = "2.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, - { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, - { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, - { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, - { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, - { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, - { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, - { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, - { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, - { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, - { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, - { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, - { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, - { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, - { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, - { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, - { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, - { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, - { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, - { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, - { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, - { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, - { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, - { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, - { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, - { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, - { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, - { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, - { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, - { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, - { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, - { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, -] - -[[package]] -name = "pefile" -version = "2023.2.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/78/c5/3b3c62223f72e2360737fd2a57c30e5b2adecd85e70276879609a7403334/pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc", size = 74854, upload-time = "2023-02-07T12:23:55.958Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/55/26/d0ad8b448476d0a1e8d3ea5622dc77b916db84c6aa3cb1e1c0965af948fc/pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6", size = 71791, upload-time = "2023-02-07T12:28:36.678Z" }, -] - -[[package]] -name = "protobuf" -version = "6.31.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797, upload-time = "2025-05-28T19:25:54.947Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603, upload-time = "2025-05-28T19:25:41.198Z" }, - { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283, upload-time = "2025-05-28T19:25:44.275Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604, upload-time = "2025-05-28T19:25:45.702Z" }, - { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115, upload-time = "2025-05-28T19:25:47.128Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070, upload-time = "2025-05-28T19:25:50.036Z" }, - { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" }, -] - -[[package]] -name = "pyinstaller" -version = "6.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "altgraph" }, - { name = "macholib", marker = "sys_platform == 'darwin'" }, - { name = "packaging" }, - { name = "pefile", marker = "sys_platform == 'win32'" }, - { name = "pyinstaller-hooks-contrib" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/94/94/1f62e95e4a28b64cfbb5b922ef3046f968b47170d37a1e1a029f56ac9cb4/pyinstaller-6.16.0.tar.gz", hash = "sha256:53559fe1e041a234f2b4dcc3288ea8bdd57f7cad8a6644e422c27bb407f3edef", size = 4008473, upload-time = "2025-09-13T20:07:01.733Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/0a/c42ce6e5d3de287f2e9432a074fb209f1fb72a86a72f3903849fdb5e4829/pyinstaller-6.16.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:7fd1c785219a87ca747c21fa92f561b0d2926a7edc06d0a0fe37f3736e00bd7a", size = 1027899, upload-time = "2025-09-13T20:05:59.2Z" }, - { url = "https://files.pythonhosted.org/packages/4e/d0/f18fedde32835d5a758f464c75924e2154065625f09d5456c3c303527654/pyinstaller-6.16.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:b756ddb9007b8141c5476b553351f9d97559b8af5d07f9460869bfae02be26b0", size = 727990, upload-time = "2025-09-13T20:06:03.583Z" }, - { url = "https://files.pythonhosted.org/packages/7a/db/c8bb47514ce857b24bf9294cf1ff74844b6a489fa0ab4ef6f923288c4e38/pyinstaller-6.16.0-py3-none-manylinux2014_i686.whl", hash = "sha256:0a48f55b85ff60f83169e10050f2759019cf1d06773ad1c4da3a411cd8751058", size = 739238, upload-time = "2025-09-13T20:06:07.69Z" }, - { url = "https://files.pythonhosted.org/packages/c6/3e/451dc784a8fcca0fe9f9b6b802d58555364a95b60f253613a2c83fc6b023/pyinstaller-6.16.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:73ba72e04fcece92e32518bbb1e1fb5ac2892677943dfdff38e01a06e8742851", size = 737142, upload-time = "2025-09-13T20:06:11.732Z" }, - { url = "https://files.pythonhosted.org/packages/71/37/2f457479ef8fa2821cdb448acee2421dfb19fbe908bf5499d1930c164084/pyinstaller-6.16.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:b1752488248f7899281b17ca3238eefb5410521291371a686a4f5830f29f52b3", size = 734133, upload-time = "2025-09-13T20:06:15.477Z" }, - { url = "https://files.pythonhosted.org/packages/63/c4/0f7daac4d062a4d1ac2571d8a8b9b5d6812094fcd914d139af591ca5e1ba/pyinstaller-6.16.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ba618a61627ee674d6d68e5de084ba17c707b59a4f2a856084b3999bdffbd3f0", size = 733817, upload-time = "2025-09-13T20:06:19.683Z" }, - { url = "https://files.pythonhosted.org/packages/11/e4/b6127265b42bef883e8873d850becadf748bc5652e5a7029b059328f3c31/pyinstaller-6.16.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:c8b7ef536711617e12fef4673806198872033fa06fa92326ad7fd1d84a9fa454", size = 732912, upload-time = "2025-09-13T20:06:23.46Z" }, - { url = "https://files.pythonhosted.org/packages/2b/00/c6663107bdf814b2916e71563beabd09f693c47712213bc228994cb2cc65/pyinstaller-6.16.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d1ebf84d02c51fed19b82a8abb4df536923abd55bb684d694e1356e4ae2a0ce5", size = 732773, upload-time = "2025-09-13T20:06:27.352Z" }, - { url = "https://files.pythonhosted.org/packages/a3/14/cabe9bc5f60b95d2e70e7d045ab94b0015ff8f6c8b16e2142d3597e30749/pyinstaller-6.16.0-py3-none-win32.whl", hash = "sha256:6d5f8617f3650ff9ef893e2ab4ddbf3c0d23d0c602ef74b5df8fbef4607840c8", size = 1313878, upload-time = "2025-09-13T20:06:33.234Z" }, - { url = "https://files.pythonhosted.org/packages/aa/99/2005efbc297e7813c1d6f18484aa94a1a81ce87b6a5b497c563681f4c4ea/pyinstaller-6.16.0-py3-none-win_amd64.whl", hash = "sha256:bc10eb1a787f99fea613509f55b902fbd2d8b73ff5f51ff245ea29a481d97d41", size = 1374706, upload-time = "2025-09-13T20:06:39.95Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f4/4dfcf69b86d60fcaae05a42bbff1616d48a91e71726e5ed795d773dae9b3/pyinstaller-6.16.0-py3-none-win_arm64.whl", hash = "sha256:d0af8a401de792c233c32c44b16d065ca9ab8262ee0c906835c12bdebc992a64", size = 1315923, upload-time = "2025-09-13T20:06:45.846Z" }, -] - -[[package]] -name = "pyinstaller-hooks-contrib" -version = "2025.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/83/be0f57c0b77b66c33c2283ebd4ea341022b5a743e97c5fb3bebab82b38b9/pyinstaller_hooks_contrib-2025.9.tar.gz", hash = "sha256:56e972bdaad4e9af767ed47d132362d162112260cbe488c9da7fee01f228a5a6", size = 165189, upload-time = "2025-09-24T11:21:35.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/26/23b4cfc77d7f808c69f59070e1e8293a579ec281a547c61562357160b346/pyinstaller_hooks_contrib-2025.9-py3-none-any.whl", hash = "sha256:ccbfaa49399ef6b18486a165810155e5a8d4c59b41f20dc5da81af7482aaf038", size = 444283, upload-time = "2025-09-24T11:21:33.67Z" }, -] - -[[package]] -name = "pyside6" -version = "6.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyside6-addons" }, - { name = "pyside6-essentials" }, - { name = "shiboken6" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/98/84b16f78b5d92dd234fb1eb9890a350a5b0c83d985bb8c44a92f813a2d02/pyside6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:c2cbc5dc2a164e3c7c51b3435e24203e90e5edd518c865466afccbd2e5872bb0", size = 558115, upload-time = "2025-10-08T09:47:09.246Z" }, - { url = "https://files.pythonhosted.org/packages/4e/76/0961c8c5653ecb60a6881b649dcb6b71a6be5bd1c8d441ecc48ac7f50b1a/pyside6-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ae8c3c8339cd7c3c9faa7cc5c52670dcc8662ccf4b63a6fed61c6345b90c4c01", size = 557762, upload-time = "2025-10-08T09:47:11.819Z" }, - { url = "https://files.pythonhosted.org/packages/c8/73/6187502fff8b6599443d15c46dd900b2ded24be5aacb2becce33f6faf566/pyside6-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:9f402f883e640048fab246d36e298a5e16df9b18ba2e8c519877e472d3602820", size = 558299, upload-time = "2025-10-08T09:47:14.255Z" }, - { url = "https://files.pythonhosted.org/packages/43/67/94794ebaf198bbdb35cb77f19f38370f9b323b036ab149874bc33c38faab/pyside6-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:70a8bcc73ea8d6baab70bba311eac77b9a1d31f658d0b418e15eb6ea36c97e6f", size = 564367, upload-time = "2025-10-08T09:47:16.287Z" }, - { url = "https://files.pythonhosted.org/packages/1d/cc/552331d413c1b933d54ed45e33cc7ff29d0b239677975fe2528e7ac8bfbc/pyside6-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:4b709bdeeb89d386059343a5a706fc185cee37b517bda44c7d6b64d5fdaf3339", size = 548826, upload-time = "2025-10-08T09:47:18.399Z" }, -] - -[[package]] -name = "pyside6-addons" -version = "6.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyside6-essentials" }, - { name = "shiboken6" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/23/9fbdec2ce16244ac3fe28e6d44c39c70465c93a03325939a792fd00fde7f/pyside6_addons-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:88e61e21ee4643cdd9efb39ec52f4dc1ac74c0b45c5b7fa453d03c094f0a8a5c", size = 322248256, upload-time = "2025-10-08T09:47:37.844Z" }, - { url = "https://files.pythonhosted.org/packages/b7/b8/d129210f2c7366b4e1bf5bb6230be42052b29e8ba1b1d7db6ef333cf5a39/pyside6_addons-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:08d4ed46c4c9a353a9eb84134678f8fdd4ce17fb8cce2b3686172a7575025464", size = 170238987, upload-time = "2025-10-08T09:47:51.446Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ae/ede1edd009395092219f3437d2ee59f9ba93739c28c040542ed47c6cc831/pyside6_addons-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:15d32229d681be0bba1b936c4a300da43d01e1917ada5b57f9e03a387c245ab0", size = 165939425, upload-time = "2025-10-08T09:48:02.073Z" }, - { url = "https://files.pythonhosted.org/packages/7d/5d/a3c32f85ac7f905c95679967c0ddda0ba043c273b75623cc90d8185064e4/pyside6_addons-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:99d93a32c17c5f6d797c3b90dd58f2a8bae13abde81e85802c34ceafaee11859", size = 164814172, upload-time = "2025-10-08T09:48:12.891Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2a/4ff71b09571202c8e1320c45276fc1d0cd81ee53107dfc17bb22d4243f88/pyside6_addons-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:92536427413f3b6557cf53f1a515cd766725ee46a170aff57ad2ff1dfce0ffb1", size = 34104251, upload-time = "2025-10-08T09:48:18.287Z" }, -] - -[[package]] -name = "pyside6-essentials" -version = "6.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "shiboken6" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/55/bad02ab890c8b8101abef0db4a2e5304be78a69e23a438e4d8555b664467/pyside6_essentials-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:003e871effe1f3e5b876bde715c15a780d876682005a6e989d89f48b8b93e93a", size = 105034090, upload-time = "2025-10-08T09:48:24.944Z" }, - { url = "https://files.pythonhosted.org/packages/5c/75/e17efc7eb900993e0e3925885635c6cf373c817196f09bcbcc102b00ac94/pyside6_essentials-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1d5e013a8698e37ab8ef360e6960794eb5ef20832a8d562e649b8c5a0574b2d8", size = 76362150, upload-time = "2025-10-08T09:48:31.849Z" }, - { url = "https://files.pythonhosted.org/packages/06/62/fbd1e81caafcda97b147c03f5b06cfaadd8da5fa8298f527d2ec648fa5b7/pyside6_essentials-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:b1dd0864f0577a448fb44426b91cafff7ee7cccd1782ba66491e1c668033f998", size = 75454169, upload-time = "2025-10-08T09:48:38.21Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3a/d8211d17e6ca70f641c6ebd309f08ef18930acda60e74082c75875a274da/pyside6_essentials-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:fc167eb211dd1580e20ba90d299e74898e7a5a1306d832421e879641fc03b6fe", size = 74361794, upload-time = "2025-10-08T09:48:44.335Z" }, - { url = "https://files.pythonhosted.org/packages/61/e9/0e22e3c10325c4ff09447fadb43f7962afb82cef0b65358f5704251c6b32/pyside6_essentials-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:6dd0936394cb14da2fd8e869899f5e0925a738b1c8d74c2f22503720ea363fb1", size = 55099467, upload-time = "2025-10-08T09:48:50.902Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "pytz" -version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, -] - -[[package]] -name = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "setuptools" -version = "80.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, -] - -[[package]] -name = "shiboken6" -version = "6.10.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/78/3e730aea82089dd82b1e092bc265778bda329459e6ad9b7134eec5fff3f2/shiboken6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:7a5f5f400ebfb3a13616030815708289c2154e701a60b9db7833b843e0bee543", size = 476535, upload-time = "2025-10-08T09:49:08Z" }, - { url = "https://files.pythonhosted.org/packages/ea/09/4ffa3284a17b6b765d45b41c9a7f1b2cde6c617c853ac6f170fb62bbbece/shiboken6-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:e612734da515d683696980107cdc0396a3ae0f07b059f0f422ec8a2333810234", size = 271098, upload-time = "2025-10-08T09:49:09.47Z" }, - { url = "https://files.pythonhosted.org/packages/31/29/00e26f33a0fb259c2edce9c761a7a438d7531ca514bdb1a4c072673bd437/shiboken6-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:b01377e68d14132360efb0f4b7233006d26aa8ae9bb50edf00960c2a5f52d148", size = 267698, upload-time = "2025-10-08T09:49:10.694Z" }, - { url = "https://files.pythonhosted.org/packages/11/30/e4624a7e3f0dc9796b701079b77defcce0d32d1afc86bb1d0df04bc3d9e2/shiboken6-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:0bc5631c1bf150cbef768a17f5f289aae1cb4db6c6b0c19b2421394e27783717", size = 1234227, upload-time = "2025-10-08T09:49:12.774Z" }, - { url = "https://files.pythonhosted.org/packages/dd/e5/0ab862005ea87dc8647ba958a3099b3b0115fd6491c65da5c5a0f6364db1/shiboken6-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:dfc4beab5fec7dbbebbb418f3bf99af865d6953aa0795435563d4cbb82093b61", size = 1794775, upload-time = "2025-10-08T09:49:14.641Z" }, -] - -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "tzdata" -version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, -] +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "absl-py" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588, upload-time = "2025-07-03T09:31:44.05Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811, upload-time = "2025-07-03T09:31:42.253Z" }, +] + +[[package]] +name = "altgraph" +version = "0.17.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/a8/7145824cf0b9e3c28046520480f207df47e927df83aa9555fb47f8505922/altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406", size = 48418, upload-time = "2023-09-25T09:04:52.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/3f/3bc3f1d83f6e4a7fcb834d3720544ca597590425be5ba9db032b2bf322a2/altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff", size = 21212, upload-time = "2023-09-25T09:04:50.691Z" }, +] + +[[package]] +name = "et-xmlfile" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, +] + +[[package]] +name = "eva-duty-arrange-tool" +version = "2.0.0" +source = { virtual = "." } +dependencies = [ + { name = "openpyxl" }, + { name = "ortools" }, + { name = "pandas" }, + { name = "pyinstaller" }, + { name = "pyside6" }, +] + +[package.metadata] +requires-dist = [ + { name = "openpyxl", specifier = ">=3.1.5" }, + { name = "ortools", specifier = ">=9.14.6206" }, + { name = "pandas", specifier = ">=2.3.3" }, + { name = "pyinstaller", specifier = ">=6.16.0" }, + { name = "pyside6", specifier = ">=6.10.0" }, +] + +[[package]] +name = "immutabledict" +version = "4.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/12/1da8e1a9050d0603ba65fb1796ed8860a705b906701c96e77f85cc7490be/immutabledict-4.2.2.tar.gz", hash = "sha256:cb6ed3090df593148f94cb407d218ca526fd2639694afdb553dc4f50ce6feeca", size = 6099, upload-time = "2025-10-12T13:32:59.755Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/7b/04ab6afa1ff7eb9ccb09049918c0407b205f5009092c0416147d163e4e2b/immutabledict-4.2.2-py3-none-any.whl", hash = "sha256:97c31d098a2c850e93a958badeef765e4736ed7942ec73e439facd764a3a7217", size = 4736, upload-time = "2025-10-12T13:32:58.326Z" }, +] + +[[package]] +name = "macholib" +version = "1.16.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altgraph" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/ee/af1a3842bdd5902ce133bd246eb7ffd4375c38642aeb5dc0ae3a0329dfa2/macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30", size = 59309, upload-time = "2023-09-25T09:10:16.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/5d/c059c180c84f7962db0aeae7c3b9303ed1d73d76f2bfbc32bc231c8be314/macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c", size = 38094, upload-time = "2023-09-25T09:10:14.188Z" }, +] + +[[package]] +name = "numpy" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf", size = 20957014, upload-time = "2025-09-09T15:56:29.966Z" }, + { url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25", size = 14185220, upload-time = "2025-09-09T15:56:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe", size = 5113918, upload-time = "2025-09-09T15:56:34.175Z" }, + { url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b", size = 6647922, upload-time = "2025-09-09T15:56:36.149Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8", size = 14281991, upload-time = "2025-09-09T15:56:40.548Z" }, + { url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20", size = 16641643, upload-time = "2025-09-09T15:56:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea", size = 16056787, upload-time = "2025-09-09T15:56:46.141Z" }, + { url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7", size = 18579598, upload-time = "2025-09-09T15:56:49.844Z" }, + { url = "https://files.pythonhosted.org/packages/17/b6/fc8f82cb3520768718834f310c37d96380d9dc61bfdaf05fe5c0b7653e01/numpy-2.3.3-cp312-cp312-win32.whl", hash = "sha256:5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf", size = 6320800, upload-time = "2025-09-09T15:56:52.499Z" }, + { url = "https://files.pythonhosted.org/packages/32/ee/de999f2625b80d043d6d2d628c07d0d5555a677a3cf78fdf868d409b8766/numpy-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb", size = 12786615, upload-time = "2025-09-09T15:56:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/49/6e/b479032f8a43559c383acb20816644f5f91c88f633d9271ee84f3b3a996c/numpy-2.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5", size = 10195936, upload-time = "2025-09-09T15:56:56.541Z" }, + { url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf", size = 20949588, upload-time = "2025-09-09T15:56:59.087Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7", size = 14177802, upload-time = "2025-09-09T15:57:01.73Z" }, + { url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6", size = 5106537, upload-time = "2025-09-09T15:57:03.765Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7", size = 6640743, upload-time = "2025-09-09T15:57:07.921Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c", size = 14278881, upload-time = "2025-09-09T15:57:11.349Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93", size = 16636301, upload-time = "2025-09-09T15:57:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae", size = 16053645, upload-time = "2025-09-09T15:57:16.534Z" }, + { url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86", size = 18578179, upload-time = "2025-09-09T15:57:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/487a2bccbf7cc9d4bfc5f0f197761a5ef27ba870f1e3bbb9afc4bbe3fcc2/numpy-2.3.3-cp313-cp313-win32.whl", hash = "sha256:9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8", size = 6312250, upload-time = "2025-09-09T15:57:21.296Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b5/263ebbbbcede85028f30047eab3d58028d7ebe389d6493fc95ae66c636ab/numpy-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf", size = 12783269, upload-time = "2025-09-09T15:57:23.034Z" }, + { url = "https://files.pythonhosted.org/packages/fa/75/67b8ca554bbeaaeb3fac2e8bce46967a5a06544c9108ec0cf5cece559b6c/numpy-2.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5", size = 10195314, upload-time = "2025-09-09T15:57:25.045Z" }, + { url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc", size = 21048025, upload-time = "2025-09-09T15:57:27.257Z" }, + { url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc", size = 14301053, upload-time = "2025-09-09T15:57:30.077Z" }, + { url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b", size = 5229444, upload-time = "2025-09-09T15:57:32.733Z" }, + { url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19", size = 6738039, upload-time = "2025-09-09T15:57:34.328Z" }, + { url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30", size = 14352314, upload-time = "2025-09-09T15:57:36.255Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e", size = 16701722, upload-time = "2025-09-09T15:57:38.622Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3", size = 16132755, upload-time = "2025-09-09T15:57:41.16Z" }, + { url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea", size = 18651560, upload-time = "2025-09-09T15:57:43.459Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8e/3ab61a730bdbbc201bb245a71102aa609f0008b9ed15255500a99cd7f780/numpy-2.3.3-cp313-cp313t-win32.whl", hash = "sha256:a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd", size = 6442776, upload-time = "2025-09-09T15:57:45.793Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3a/e22b766b11f6030dc2decdeff5c2fb1610768055603f9f3be88b6d192fb2/numpy-2.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d", size = 12927281, upload-time = "2025-09-09T15:57:47.492Z" }, + { url = "https://files.pythonhosted.org/packages/7b/42/c2e2bc48c5e9b2a83423f99733950fbefd86f165b468a3d85d52b30bf782/numpy-2.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1", size = 10265275, upload-time = "2025-09-09T15:57:49.647Z" }, + { url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593", size = 20951527, upload-time = "2025-09-09T15:57:52.006Z" }, + { url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652", size = 14186159, upload-time = "2025-09-09T15:57:54.407Z" }, + { url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7", size = 5114624, upload-time = "2025-09-09T15:57:56.5Z" }, + { url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a", size = 6642627, upload-time = "2025-09-09T15:57:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe", size = 14296926, upload-time = "2025-09-09T15:58:00.035Z" }, + { url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421", size = 16638958, upload-time = "2025-09-09T15:58:02.738Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021", size = 16071920, upload-time = "2025-09-09T15:58:05.029Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf", size = 18577076, upload-time = "2025-09-09T15:58:07.745Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a1/b87a284fb15a42e9274e7fcea0dad259d12ddbf07c1595b26883151ca3b4/numpy-2.3.3-cp314-cp314-win32.whl", hash = "sha256:cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0", size = 6366952, upload-time = "2025-09-09T15:58:10.096Z" }, + { url = "https://files.pythonhosted.org/packages/70/5f/1816f4d08f3b8f66576d8433a66f8fa35a5acfb3bbd0bf6c31183b003f3d/numpy-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8", size = 12919322, upload-time = "2025-09-09T15:58:12.138Z" }, + { url = "https://files.pythonhosted.org/packages/8c/de/072420342e46a8ea41c324a555fa90fcc11637583fb8df722936aed1736d/numpy-2.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe", size = 10478630, upload-time = "2025-09-09T15:58:14.64Z" }, + { url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00", size = 21047987, upload-time = "2025-09-09T15:58:16.889Z" }, + { url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a", size = 14301076, upload-time = "2025-09-09T15:58:20.343Z" }, + { url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d", size = 5229491, upload-time = "2025-09-09T15:58:22.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a", size = 6737913, upload-time = "2025-09-09T15:58:24.569Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54", size = 14352811, upload-time = "2025-09-09T15:58:26.416Z" }, + { url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e", size = 16702689, upload-time = "2025-09-09T15:58:28.831Z" }, + { url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097", size = 16133855, upload-time = "2025-09-09T15:58:31.349Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970", size = 18652520, upload-time = "2025-09-09T15:58:33.762Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c2/e5ed830e08cd0196351db55db82f65bc0ab05da6ef2b72a836dcf1936d2f/numpy-2.3.3-cp314-cp314t-win32.whl", hash = "sha256:1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5", size = 6515371, upload-time = "2025-09-09T15:58:36.04Z" }, + { url = "https://files.pythonhosted.org/packages/47/c7/b0f6b5b67f6788a0725f744496badbb604d226bf233ba716683ebb47b570/numpy-2.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f", size = 13112576, upload-time = "2025-09-09T15:58:37.927Z" }, + { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" }, +] + +[[package]] +name = "openpyxl" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "et-xmlfile" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" }, +] + +[[package]] +name = "ortools" +version = "9.14.6206" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py" }, + { name = "immutabledict" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "protobuf" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/2e/b0d7e7ee9d1a45001a05ca2f65ccc1ab28cee510e725e01e248496510ac1/ortools-9.14.6206-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:e38c8c4a184820cbfdb812a8d484f6506cf16993ce2a95c88bc1c9d23b17c63e", size = 22239884, upload-time = "2025-06-19T15:45:51.774Z" }, + { url = "https://files.pythonhosted.org/packages/31/ad/964ee5341767dd9e2f13e76f0a36d45aa8d81ad776c80bdd6dedc8f2f462/ortools-9.14.6206-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db685073cbed9f8bfaa744f5e883f3dea57c93179b0abe1788276fd3b074fa61", size = 20211128, upload-time = "2025-06-19T15:45:54.829Z" }, + { url = "https://files.pythonhosted.org/packages/7c/93/d94a66cdfadeb2747d96f1c8d3f590d81c4ad47fd357dfc57de8d7a75bbe/ortools-9.14.6206-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4bfb8bffb29991834cf4bde7048ca8ee8caed73e8dd21e5ec7de99a33bbfea0", size = 25663650, upload-time = "2025-06-19T15:47:20.207Z" }, + { url = "https://files.pythonhosted.org/packages/eb/74/1d374bf510e9fb36bba82ecd3e09461cd8394afef3e418fa5b060f129401/ortools-9.14.6206-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb464a698837e7f90ca5f9b3d748b6ddf553198a70032bc77824d1cd88695d2b", size = 27670004, upload-time = "2025-06-19T15:47:23.111Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e3/5943467ae41efa06cff12b79bfd146c4a54903345f0cc5c896884829d14a/ortools-9.14.6206-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8f33deaeb7c3dda8ca1d29c5b9aa9c3a4f2ca9ecf34f12a1f809bb2995f41274", size = 27653302, upload-time = "2025-06-19T15:53:47.615Z" }, + { url = "https://files.pythonhosted.org/packages/6c/28/a896080fe6e13b4bdae74601f19c28b7ba05ec45f3adca3e992d6174ac57/ortools-9.14.6206-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:086e7c2dc4f23efffb20a5e20f618c7d6adb99b2d94f684cab482387da3bc434", size = 29503497, upload-time = "2025-06-19T15:53:51.174Z" }, + { url = "https://files.pythonhosted.org/packages/38/ad/8d02013116cc5d51084fe24c5b73f5b349dd10925d2927d19d21a2068a4c/ortools-9.14.6206-cp312-cp312-win_amd64.whl", hash = "sha256:17c13b0bfde17ac57789ad35243edf1318ecd5db23cf949b75ab62480599f188", size = 20512174, upload-time = "2025-06-19T15:46:33.339Z" }, + { url = "https://files.pythonhosted.org/packages/d8/9c/fe6d5ca549c2fb5ed3e8e1d928a11f4509c024c974e1f6e2d75d206bc226/ortools-9.14.6206-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:8d0df7eef8ba53ad235e29018389259bad2e667d9594b9c2a412ed6a5756bd4e", size = 22259719, upload-time = "2025-06-19T15:45:57.504Z" }, + { url = "https://files.pythonhosted.org/packages/19/0f/0b9858e7d5b31dfb23a1fe945d3e9fd5d2feba893190e53f70374ac7e084/ortools-9.14.6206-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:57dfe10844ce8331634d4723040fe249263fd490407346efc314c0bc656849b5", size = 20231092, upload-time = "2025-06-19T15:46:00.003Z" }, + { url = "https://files.pythonhosted.org/packages/6b/48/de4eff68cb4ea124c9d6edf68049d329a332aab36d011c2798ea1afb4c98/ortools-9.14.6206-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5c0c2c00a6e5d5c462e76fdda7dbd40d0f9139f1df4211d34b36906696248020", size = 25662903, upload-time = "2025-06-19T15:47:26.368Z" }, + { url = "https://files.pythonhosted.org/packages/c9/44/970f72cd5e537fc10e20582dadbdd2f8b2b3c802ac812105c40119572ea7/ortools-9.14.6206-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:38044cf39952d93cbcc02f6acdbe0a9bd3628fbf17f0d7eb0374060fa028c22e", size = 27669250, upload-time = "2025-06-19T15:47:29.738Z" }, + { url = "https://files.pythonhosted.org/packages/a4/31/37e2eacc37e4448090f693d482316a67bf2b47f2aefae6d6236b4ed41924/ortools-9.14.6206-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:98564de773d709e1e49cb3c32f6917589c314f047786d88bd5f324c0eb7be96e", size = 27652453, upload-time = "2025-06-19T15:53:54.742Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8f/0b1bb2a3268114b95381d5a1146c293259d6e45c69e4e3f2d2cf52617796/ortools-9.14.6206-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:80528b0ac72dc3de00cbeef2ce028517a476450b5877b1cda1b8ecb9fa98505e", size = 29503368, upload-time = "2025-06-19T15:53:57.989Z" }, + { url = "https://files.pythonhosted.org/packages/d9/ad/93c05084eec710c38bc952a19002aac31d4c26bb6f8fcbf1057ccc55254b/ortools-9.14.6206-cp313-cp313-win_amd64.whl", hash = "sha256:47b1b15dcb085d32c61621b790259193aefa9e4577abadf233d47fbe7d0b81ef", size = 20530387, upload-time = "2025-06-19T15:46:35.964Z" }, + { url = "https://files.pythonhosted.org/packages/51/85/aeba0d90a3cee4a23ba74a4f2d4c27088923c8b77fa1b73a52ad34593be5/ortools-9.14.6206-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d26a0f9ed97ef9d3384a9069923585f5f974c3fde555a41f4d6381fbe7840bc4", size = 25704462, upload-time = "2025-06-19T15:47:33.064Z" }, + { url = "https://files.pythonhosted.org/packages/98/83/2954b6aa032b77f49a478e0d9682ae8176abfb4bd33d935f0260ecb1741b/ortools-9.14.6206-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d40d8141667d47405f296a9f687058c566d7816586e9a672b59e9fcec8493133", size = 27687594, upload-time = "2025-06-19T15:47:36.241Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9a/9e9866952841e9f4aadccb110c0012d6a76f01b8798432fac0971d60aac1/ortools-9.14.6206-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:aefea81ed81aa937873efc520381785ed65380e52917f492ab566f46bbb5660d", size = 27698487, upload-time = "2025-06-19T15:54:01.03Z" }, + { url = "https://files.pythonhosted.org/packages/91/a5/97a86cdd52c961273981e9ab905a301b7f086593b597d8974c411decf61a/ortools-9.14.6206-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f044bb277db3ab6a1b958728fe1cf14ca87c3800d67d7b321d876b48269340f6", size = 29519050, upload-time = "2025-06-19T15:54:04.147Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, +] + +[[package]] +name = "pefile" +version = "2023.2.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/78/c5/3b3c62223f72e2360737fd2a57c30e5b2adecd85e70276879609a7403334/pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc", size = 74854, upload-time = "2023-02-07T12:23:55.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/26/d0ad8b448476d0a1e8d3ea5622dc77b916db84c6aa3cb1e1c0965af948fc/pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6", size = 71791, upload-time = "2023-02-07T12:28:36.678Z" }, +] + +[[package]] +name = "protobuf" +version = "6.31.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797, upload-time = "2025-05-28T19:25:54.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603, upload-time = "2025-05-28T19:25:41.198Z" }, + { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283, upload-time = "2025-05-28T19:25:44.275Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604, upload-time = "2025-05-28T19:25:45.702Z" }, + { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115, upload-time = "2025-05-28T19:25:47.128Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070, upload-time = "2025-05-28T19:25:50.036Z" }, + { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" }, +] + +[[package]] +name = "pyinstaller" +version = "6.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altgraph" }, + { name = "macholib", marker = "sys_platform == 'darwin'" }, + { name = "packaging" }, + { name = "pefile", marker = "sys_platform == 'win32'" }, + { name = "pyinstaller-hooks-contrib" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/94/1f62e95e4a28b64cfbb5b922ef3046f968b47170d37a1e1a029f56ac9cb4/pyinstaller-6.16.0.tar.gz", hash = "sha256:53559fe1e041a234f2b4dcc3288ea8bdd57f7cad8a6644e422c27bb407f3edef", size = 4008473, upload-time = "2025-09-13T20:07:01.733Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/0a/c42ce6e5d3de287f2e9432a074fb209f1fb72a86a72f3903849fdb5e4829/pyinstaller-6.16.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:7fd1c785219a87ca747c21fa92f561b0d2926a7edc06d0a0fe37f3736e00bd7a", size = 1027899, upload-time = "2025-09-13T20:05:59.2Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d0/f18fedde32835d5a758f464c75924e2154065625f09d5456c3c303527654/pyinstaller-6.16.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:b756ddb9007b8141c5476b553351f9d97559b8af5d07f9460869bfae02be26b0", size = 727990, upload-time = "2025-09-13T20:06:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/7a/db/c8bb47514ce857b24bf9294cf1ff74844b6a489fa0ab4ef6f923288c4e38/pyinstaller-6.16.0-py3-none-manylinux2014_i686.whl", hash = "sha256:0a48f55b85ff60f83169e10050f2759019cf1d06773ad1c4da3a411cd8751058", size = 739238, upload-time = "2025-09-13T20:06:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3e/451dc784a8fcca0fe9f9b6b802d58555364a95b60f253613a2c83fc6b023/pyinstaller-6.16.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:73ba72e04fcece92e32518bbb1e1fb5ac2892677943dfdff38e01a06e8742851", size = 737142, upload-time = "2025-09-13T20:06:11.732Z" }, + { url = "https://files.pythonhosted.org/packages/71/37/2f457479ef8fa2821cdb448acee2421dfb19fbe908bf5499d1930c164084/pyinstaller-6.16.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:b1752488248f7899281b17ca3238eefb5410521291371a686a4f5830f29f52b3", size = 734133, upload-time = "2025-09-13T20:06:15.477Z" }, + { url = "https://files.pythonhosted.org/packages/63/c4/0f7daac4d062a4d1ac2571d8a8b9b5d6812094fcd914d139af591ca5e1ba/pyinstaller-6.16.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ba618a61627ee674d6d68e5de084ba17c707b59a4f2a856084b3999bdffbd3f0", size = 733817, upload-time = "2025-09-13T20:06:19.683Z" }, + { url = "https://files.pythonhosted.org/packages/11/e4/b6127265b42bef883e8873d850becadf748bc5652e5a7029b059328f3c31/pyinstaller-6.16.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:c8b7ef536711617e12fef4673806198872033fa06fa92326ad7fd1d84a9fa454", size = 732912, upload-time = "2025-09-13T20:06:23.46Z" }, + { url = "https://files.pythonhosted.org/packages/2b/00/c6663107bdf814b2916e71563beabd09f693c47712213bc228994cb2cc65/pyinstaller-6.16.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d1ebf84d02c51fed19b82a8abb4df536923abd55bb684d694e1356e4ae2a0ce5", size = 732773, upload-time = "2025-09-13T20:06:27.352Z" }, + { url = "https://files.pythonhosted.org/packages/a3/14/cabe9bc5f60b95d2e70e7d045ab94b0015ff8f6c8b16e2142d3597e30749/pyinstaller-6.16.0-py3-none-win32.whl", hash = "sha256:6d5f8617f3650ff9ef893e2ab4ddbf3c0d23d0c602ef74b5df8fbef4607840c8", size = 1313878, upload-time = "2025-09-13T20:06:33.234Z" }, + { url = "https://files.pythonhosted.org/packages/aa/99/2005efbc297e7813c1d6f18484aa94a1a81ce87b6a5b497c563681f4c4ea/pyinstaller-6.16.0-py3-none-win_amd64.whl", hash = "sha256:bc10eb1a787f99fea613509f55b902fbd2d8b73ff5f51ff245ea29a481d97d41", size = 1374706, upload-time = "2025-09-13T20:06:39.95Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f4/4dfcf69b86d60fcaae05a42bbff1616d48a91e71726e5ed795d773dae9b3/pyinstaller-6.16.0-py3-none-win_arm64.whl", hash = "sha256:d0af8a401de792c233c32c44b16d065ca9ab8262ee0c906835c12bdebc992a64", size = 1315923, upload-time = "2025-09-13T20:06:45.846Z" }, +] + +[[package]] +name = "pyinstaller-hooks-contrib" +version = "2025.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/83/be0f57c0b77b66c33c2283ebd4ea341022b5a743e97c5fb3bebab82b38b9/pyinstaller_hooks_contrib-2025.9.tar.gz", hash = "sha256:56e972bdaad4e9af767ed47d132362d162112260cbe488c9da7fee01f228a5a6", size = 165189, upload-time = "2025-09-24T11:21:35.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/26/23b4cfc77d7f808c69f59070e1e8293a579ec281a547c61562357160b346/pyinstaller_hooks_contrib-2025.9-py3-none-any.whl", hash = "sha256:ccbfaa49399ef6b18486a165810155e5a8d4c59b41f20dc5da81af7482aaf038", size = 444283, upload-time = "2025-09-24T11:21:33.67Z" }, +] + +[[package]] +name = "pyside6" +version = "6.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyside6-addons" }, + { name = "pyside6-essentials" }, + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/98/84b16f78b5d92dd234fb1eb9890a350a5b0c83d985bb8c44a92f813a2d02/pyside6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:c2cbc5dc2a164e3c7c51b3435e24203e90e5edd518c865466afccbd2e5872bb0", size = 558115, upload-time = "2025-10-08T09:47:09.246Z" }, + { url = "https://files.pythonhosted.org/packages/4e/76/0961c8c5653ecb60a6881b649dcb6b71a6be5bd1c8d441ecc48ac7f50b1a/pyside6-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ae8c3c8339cd7c3c9faa7cc5c52670dcc8662ccf4b63a6fed61c6345b90c4c01", size = 557762, upload-time = "2025-10-08T09:47:11.819Z" }, + { url = "https://files.pythonhosted.org/packages/c8/73/6187502fff8b6599443d15c46dd900b2ded24be5aacb2becce33f6faf566/pyside6-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:9f402f883e640048fab246d36e298a5e16df9b18ba2e8c519877e472d3602820", size = 558299, upload-time = "2025-10-08T09:47:14.255Z" }, + { url = "https://files.pythonhosted.org/packages/43/67/94794ebaf198bbdb35cb77f19f38370f9b323b036ab149874bc33c38faab/pyside6-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:70a8bcc73ea8d6baab70bba311eac77b9a1d31f658d0b418e15eb6ea36c97e6f", size = 564367, upload-time = "2025-10-08T09:47:16.287Z" }, + { url = "https://files.pythonhosted.org/packages/1d/cc/552331d413c1b933d54ed45e33cc7ff29d0b239677975fe2528e7ac8bfbc/pyside6-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:4b709bdeeb89d386059343a5a706fc185cee37b517bda44c7d6b64d5fdaf3339", size = 548826, upload-time = "2025-10-08T09:47:18.399Z" }, +] + +[[package]] +name = "pyside6-addons" +version = "6.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyside6-essentials" }, + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/23/9fbdec2ce16244ac3fe28e6d44c39c70465c93a03325939a792fd00fde7f/pyside6_addons-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:88e61e21ee4643cdd9efb39ec52f4dc1ac74c0b45c5b7fa453d03c094f0a8a5c", size = 322248256, upload-time = "2025-10-08T09:47:37.844Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b8/d129210f2c7366b4e1bf5bb6230be42052b29e8ba1b1d7db6ef333cf5a39/pyside6_addons-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:08d4ed46c4c9a353a9eb84134678f8fdd4ce17fb8cce2b3686172a7575025464", size = 170238987, upload-time = "2025-10-08T09:47:51.446Z" }, + { url = "https://files.pythonhosted.org/packages/cf/ae/ede1edd009395092219f3437d2ee59f9ba93739c28c040542ed47c6cc831/pyside6_addons-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:15d32229d681be0bba1b936c4a300da43d01e1917ada5b57f9e03a387c245ab0", size = 165939425, upload-time = "2025-10-08T09:48:02.073Z" }, + { url = "https://files.pythonhosted.org/packages/7d/5d/a3c32f85ac7f905c95679967c0ddda0ba043c273b75623cc90d8185064e4/pyside6_addons-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:99d93a32c17c5f6d797c3b90dd58f2a8bae13abde81e85802c34ceafaee11859", size = 164814172, upload-time = "2025-10-08T09:48:12.891Z" }, + { url = "https://files.pythonhosted.org/packages/a3/2a/4ff71b09571202c8e1320c45276fc1d0cd81ee53107dfc17bb22d4243f88/pyside6_addons-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:92536427413f3b6557cf53f1a515cd766725ee46a170aff57ad2ff1dfce0ffb1", size = 34104251, upload-time = "2025-10-08T09:48:18.287Z" }, +] + +[[package]] +name = "pyside6-essentials" +version = "6.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/55/bad02ab890c8b8101abef0db4a2e5304be78a69e23a438e4d8555b664467/pyside6_essentials-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:003e871effe1f3e5b876bde715c15a780d876682005a6e989d89f48b8b93e93a", size = 105034090, upload-time = "2025-10-08T09:48:24.944Z" }, + { url = "https://files.pythonhosted.org/packages/5c/75/e17efc7eb900993e0e3925885635c6cf373c817196f09bcbcc102b00ac94/pyside6_essentials-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1d5e013a8698e37ab8ef360e6960794eb5ef20832a8d562e649b8c5a0574b2d8", size = 76362150, upload-time = "2025-10-08T09:48:31.849Z" }, + { url = "https://files.pythonhosted.org/packages/06/62/fbd1e81caafcda97b147c03f5b06cfaadd8da5fa8298f527d2ec648fa5b7/pyside6_essentials-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:b1dd0864f0577a448fb44426b91cafff7ee7cccd1782ba66491e1c668033f998", size = 75454169, upload-time = "2025-10-08T09:48:38.21Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3a/d8211d17e6ca70f641c6ebd309f08ef18930acda60e74082c75875a274da/pyside6_essentials-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:fc167eb211dd1580e20ba90d299e74898e7a5a1306d832421e879641fc03b6fe", size = 74361794, upload-time = "2025-10-08T09:48:44.335Z" }, + { url = "https://files.pythonhosted.org/packages/61/e9/0e22e3c10325c4ff09447fadb43f7962afb82cef0b65358f5704251c6b32/pyside6_essentials-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:6dd0936394cb14da2fd8e869899f5e0925a738b1c8d74c2f22503720ea363fb1", size = 55099467, upload-time = "2025-10-08T09:48:50.902Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + +[[package]] +name = "shiboken6" +version = "6.10.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/78/3e730aea82089dd82b1e092bc265778bda329459e6ad9b7134eec5fff3f2/shiboken6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:7a5f5f400ebfb3a13616030815708289c2154e701a60b9db7833b843e0bee543", size = 476535, upload-time = "2025-10-08T09:49:08Z" }, + { url = "https://files.pythonhosted.org/packages/ea/09/4ffa3284a17b6b765d45b41c9a7f1b2cde6c617c853ac6f170fb62bbbece/shiboken6-6.10.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:e612734da515d683696980107cdc0396a3ae0f07b059f0f422ec8a2333810234", size = 271098, upload-time = "2025-10-08T09:49:09.47Z" }, + { url = "https://files.pythonhosted.org/packages/31/29/00e26f33a0fb259c2edce9c761a7a438d7531ca514bdb1a4c072673bd437/shiboken6-6.10.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:b01377e68d14132360efb0f4b7233006d26aa8ae9bb50edf00960c2a5f52d148", size = 267698, upload-time = "2025-10-08T09:49:10.694Z" }, + { url = "https://files.pythonhosted.org/packages/11/30/e4624a7e3f0dc9796b701079b77defcce0d32d1afc86bb1d0df04bc3d9e2/shiboken6-6.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:0bc5631c1bf150cbef768a17f5f289aae1cb4db6c6b0c19b2421394e27783717", size = 1234227, upload-time = "2025-10-08T09:49:12.774Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e5/0ab862005ea87dc8647ba958a3099b3b0115fd6491c65da5c5a0f6364db1/shiboken6-6.10.0-cp39-abi3-win_arm64.whl", hash = "sha256:dfc4beab5fec7dbbebbb418f3bf99af865d6953aa0795435563d4cbb82093b61", size = 1794775, upload-time = "2025-10-08T09:49:14.641Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, +] diff --git a/使用手册.md b/使用手册.md index b81b46a..c17b5f5 100644 --- a/使用手册.md +++ b/使用手册.md @@ -1,100 +1,100 @@ -# EVA 值班排班工具使用手册 - v2.0.0 - -## 前言 -这是浙江大学学生E志者协会“排班工具软件”的使用手册,将简要地介绍该软件的使用方法和注意事项。请注意,这是面向使用者而非开发者的手册,如果想了解该工具的开发流程和所使用的技术,请移步至[此仓库](https://git.zjueva.net/happywind/EVA_duty_arrange_tool)的说明手册。 - -## 软件运行环境 & 基本情况 -- 本软件只能在 Windows 系统上运行。 -- 本软件在部分版本的 Win10 和 Win11 系统上均测试过,能够正常运行。但是不排除可能出现缺少相关 dll 文件而无法运行的情况,如果出现请联系开发者解决。 -- 本软件将排班问题建模为**多目标组合优化问题**,使用 Google OR-Tools 的 SCIP 求解器求解。 -- 优化目标包括: - 1. 各班次人数尽可能平均 - 2. 每班技术部老人数量接近期望值(3人) - 3. 每班技术部小朋友数量接近期望值(4人) - 4. 每班人资部小朋友数量接近期望值(1人) - 5. 每班各部门人数尽可能均衡 -- 在指定的限制条件下,若有解,则得到的一定是**全局最优解**。 - -## 软件使用方法 - -双击运行可执行文件,会看到如下软件界面: - -
- -
- -进行以下三个步骤: - -1. 选择待输入的问卷星结果 Excel 表格。对于选择的 Excel 表格,有以下**格式要求**: - - 第 1 行,即表头的名称不会影响软件运行,但需要保证每一列的数据的格式和含义正确 - - 第 1 列为序号,不会用到,但需要有 - - 第 2 列为提交答卷时间,格式为 "year/month/day hour:min:sec" - - 第 3 列为填表所用时间,不会用到,但需要有 - - 第 4 列为来源,不会用到,但需要有 - - 第 5 列为来源详情,不会用到,但需要有 - - 第 6 列为 IP,不会用到,但需要有 - - 第 7 列为姓名,这是每一位同学的唯一标识 - - 第 8 列为部门序号,用一个数字表示,映射关系如下: - ```python - index_to_departments = { # 部门编号和部门名称的对应关系 - 1: "电脑部", - 2: "电器部", - 3: "人资部", - 4: "财外部", - 5: "文宣部" - } - ``` - - 第 9 列标识老人还是小朋友,用一个数字表示,映射关系如下: - ```python - index_to_type = { # 老人/小朋友和对应的编号 - 1: "老人", - 2: "小朋友" - } - ``` - - 第 10 ~ 29 列,表示对于每个班次的意愿(1 代表有时间,0 代表没时间)。因为从周一第一班到周日第二班一共有 20 班,所以总共有 20 列。 - - 第 30 列,表示愿意排几班,用一个数字表示。 - - **我们提供了一个名为“问卷星结果_样例输入.xlsx”的文件,以作为正确输入格式的参考,以及一个名为“问卷星结果_较极端案例.xlsx”的文件,以作为程序运行的测试。** - - **本软件可以自动处理同一名同学多次填写问卷的情况**,只要保证多次填写中“姓名”保持一致即可,软件将会自动选择最后一次填写的结果作为最终意愿。由于“姓名”是每位同学的唯一标识符,如果出现了某位同学“姓名”填错的情况,需要手动删除该姓名的记录。 - - 如果出现了**两位同学撞名的情况**,需要在填表时“姓名”后加上后缀以作区分,比如“王五1”和“王五2”,否则就会当作一个人来看待。 - -2. 设置优化参数 - - 软件界面的 Step 2 部分包含两类参数: - - ### 2.1 权重参数(始终可设置) - - 软件提供了 5 个优化目标的权重设置,用于调整各个目标的重要程度: - - **目标 1 权重**:每班人数平均程度(默认 1.0) - - **目标 2 权重**:每班技术部老人数量(默认 1.0) - - **目标 3 权重**:每班技术部小朋友数量(默认 1.0) - - **目标 4 权重**:每班人资部小朋友数量(默认 0.5) - - **目标 5 权重**:每班部门平均程度(默认 0.5) - - 权重值范围建议为 0.0 - 5.0,权重越大表示该目标越重要。所有权重参数均已归一化处理。 - - ### 2.2 约束条件(自动/手动模式) - - **自动调参模式(推荐):** - - 勾选"自动调参模式"复选框 - - 软件会自动尝试不同的约束参数组合,实时显示尝试过程 - - 从严格到宽松逐步调整参数,找到第一个可行解 - - 无需手动设置约束条件,约束输入框会被禁用 - - **手动模式:** - - 取消勾选"自动调参模式" - - 可手动设置以下约束条件: - - **每班次人数**:最少和最多人数(建议 5-8 人) - - **每班次电脑或电器的老人数**:技术部老人数量范围(建议最多 2 人) - - **每班次老人数**:所有老人数量范围(建议最少 1 人) - - **每班次小朋友数**:所有小朋友数量范围(建议最少 2 人) - - 留空表示该项无限制 - - **注意**:约束条件设置过严可能导致无解,需要适当放宽限制 - -3. 开始排班并查看进度 - - 点击"开始排班!"按钮开始计算: - - 输出结果以 Excel 表格形式保存,文件名为 `result_<当前时间戳>.xlsx` - - **在输出结果中,组长会被标黄** +# EVA 值班排班工具使用手册 - v2.0.0 + +## 前言 +这是浙江大学学生E志者协会“排班工具软件”的使用手册,将简要地介绍该软件的使用方法和注意事项。请注意,这是面向使用者而非开发者的手册,如果想了解该工具的开发流程和所使用的技术,请移步至[此仓库](https://git.zjueva.net/happywind/EVA_duty_arrange_tool)的说明手册。 + +## 软件运行环境 & 基本情况 +- 本软件只能在 Windows 系统上运行。 +- 本软件在部分版本的 Win10 和 Win11 系统上均测试过,能够正常运行。但是不排除可能出现缺少相关 dll 文件而无法运行的情况,如果出现请联系开发者解决。 +- 本软件将排班问题建模为**多目标组合优化问题**,使用 Google OR-Tools 的 SCIP 求解器求解。 +- 优化目标包括: + 1. 各班次人数尽可能平均 + 2. 每班技术部老人数量接近期望值(3人) + 3. 每班技术部小朋友数量接近期望值(4人) + 4. 每班人资部小朋友数量接近期望值(1人) + 5. 每班各部门人数尽可能均衡 +- 在指定的限制条件下,若有解,则得到的一定是**全局最优解**。 + +## 软件使用方法 + +双击运行可执行文件,会看到如下软件界面: + +
+ +
+ +进行以下三个步骤: + +1. 选择待输入的问卷星结果 Excel 表格。对于选择的 Excel 表格,有以下**格式要求**: + - 第 1 行,即表头的名称不会影响软件运行,但需要保证每一列的数据的格式和含义正确 + - 第 1 列为序号,不会用到,但需要有 + - 第 2 列为提交答卷时间,格式为 "year/month/day hour:min:sec" + - 第 3 列为填表所用时间,不会用到,但需要有 + - 第 4 列为来源,不会用到,但需要有 + - 第 5 列为来源详情,不会用到,但需要有 + - 第 6 列为 IP,不会用到,但需要有 + - 第 7 列为姓名,这是每一位同学的唯一标识 + - 第 8 列为部门序号,用一个数字表示,映射关系如下: + ```python + index_to_departments = { # 部门编号和部门名称的对应关系 + 1: "电脑部", + 2: "电器部", + 3: "人资部", + 4: "财外部", + 5: "文宣部" + } + ``` + - 第 9 列标识老人还是小朋友,用一个数字表示,映射关系如下: + ```python + index_to_type = { # 老人/小朋友和对应的编号 + 1: "老人", + 2: "小朋友" + } + ``` + - 第 10 ~ 29 列,表示对于每个班次的意愿(1 代表有时间,0 代表没时间)。因为从周一第一班到周日第二班一共有 20 班,所以总共有 20 列。 + - 第 30 列,表示愿意排几班,用一个数字表示。 + + **我们提供了一个名为“问卷星结果_样例输入.xlsx”的文件,以作为正确输入格式的参考,以及一个名为“问卷星结果_较极端案例.xlsx”的文件,以作为程序运行的测试。** + + **本软件可以自动处理同一名同学多次填写问卷的情况**,只要保证多次填写中“姓名”保持一致即可,软件将会自动选择最后一次填写的结果作为最终意愿。由于“姓名”是每位同学的唯一标识符,如果出现了某位同学“姓名”填错的情况,需要手动删除该姓名的记录。 + + 如果出现了**两位同学撞名的情况**,需要在填表时“姓名”后加上后缀以作区分,比如“王五1”和“王五2”,否则就会当作一个人来看待。 + +2. 设置优化参数 + + 软件界面的 Step 2 部分包含两类参数: + + ### 2.1 权重参数(始终可设置) + + 软件提供了 5 个优化目标的权重设置,用于调整各个目标的重要程度: + - **目标 1 权重**:每班人数平均程度(默认 1.0) + - **目标 2 权重**:每班技术部老人数量(默认 1.0) + - **目标 3 权重**:每班技术部小朋友数量(默认 1.0) + - **目标 4 权重**:每班人资部小朋友数量(默认 0.5) + - **目标 5 权重**:每班部门平均程度(默认 0.5) + + 权重值范围建议为 0.0 - 5.0,权重越大表示该目标越重要。所有权重参数均已归一化处理。 + + ### 2.2 约束条件(自动/手动模式) + + **自动调参模式(推荐):** + - 勾选"自动调参模式"复选框 + - 软件会自动尝试不同的约束参数组合,实时显示尝试过程 + - 从严格到宽松逐步调整参数,找到第一个可行解 + - 无需手动设置约束条件,约束输入框会被禁用 + + **手动模式:** + - 取消勾选"自动调参模式" + - 可手动设置以下约束条件: + - **每班次人数**:最少和最多人数(建议 5-8 人) + - **每班次电脑或电器的老人数**:技术部老人数量范围(建议最多 2 人) + - **每班次老人数**:所有老人数量范围(建议最少 1 人) + - **每班次小朋友数**:所有小朋友数量范围(建议最少 2 人) + - 留空表示该项无限制 + - **注意**:约束条件设置过严可能导致无解,需要适当放宽限制 + +3. 开始排班并查看进度 + + 点击"开始排班!"按钮开始计算: + - 输出结果以 Excel 表格形式保存,文件名为 `result_<当前时间戳>.xlsx` + - **在输出结果中,组长会被标黄**