增加按照手机号排序
增加自动选择文以及识别文件 修改座位信息表位置
This commit is contained in:
parent
f52df5e42d
commit
02341150a9
52
README.md
52
README.md
@ -5,22 +5,35 @@
|
|||||||
## 🎯 功能特点
|
## 🎯 功能特点
|
||||||
|
|
||||||
- ✅ **智能分配**: 支持1-10人的各种连坐需求
|
- ✅ **智能分配**: 支持1-10人的各种连坐需求
|
||||||
- ✅ **数据校验**: 完整的文件格式和逻辑校验
|
- ✅ **智能识别**: 自动识别Excel文件类型,支持默认文件名和智能识别两种模式
|
||||||
|
- ✅ **双重分组**: 支持备注分组和手机号分组两种模式
|
||||||
|
- ✅ **数据校验**: 完整的文件格式和逻辑校验,支持重名+身份证号校验
|
||||||
- ✅ **不连续座位**: 自动处理座位号间隙
|
- ✅ **不连续座位**: 自动处理座位号间隙
|
||||||
- ✅ **详细日志**: 完整的操作记录和结果验证
|
- ✅ **详细日志**: 完整的操作记录和结果验证
|
||||||
- ✅ **跨平台**: 支持Windows、macOS、Linux
|
- ✅ **跨平台**: 支持Windows、macOS、Linux
|
||||||
|
- ✅ **智能覆盖**: 人员信息覆盖座位表对应列,分配备注单独存储在第13列
|
||||||
|
|
||||||
## 📋 使用方法
|
## 📋 使用方法
|
||||||
|
|
||||||
### 准备Excel文件
|
### 准备Excel文件
|
||||||
|
|
||||||
1. **人员信息.xlsx** - 包含以下列:
|
1. **人员信息文件** - 包含以下列:
|
||||||
- 姓名、证件类型、证件号、手机号、备注
|
- 姓名、证件类型、证件号、手机号、备注
|
||||||
- 备注数字表示连坐人数(如:备注4表示当前行+后3行共4人连坐)
|
- **备注模式**: 备注数字表示连坐人数(如:备注4表示当前行+后3行共4人连坐)
|
||||||
|
- **手机号模式**: 相同手机号的人员自动识别为连坐组
|
||||||
|
|
||||||
2. **座位信息.xlsx** - 包含以下列:
|
2. **座位信息文件** - 包含以下列:
|
||||||
- 区域、楼层、排号、座位号
|
- 区域、楼层、排号、座位号
|
||||||
|
|
||||||
|
### 文件识别方式
|
||||||
|
|
||||||
|
程序启动时会提供两种文件识别方式:
|
||||||
|
1. **默认文件名** - 读取固定文件名 `人员信息.xlsx` 和 `座位信息.xlsx`
|
||||||
|
2. **智能识别** - 自动分析目录中的Excel文件并识别类型
|
||||||
|
- 根据列名特征自动判断文件类型
|
||||||
|
- 支持任意文件名(如 `1.xlsx`、`2.xlsx`)
|
||||||
|
- 提供手动选择功能作为备选
|
||||||
|
|
||||||
### 运行程序
|
### 运行程序
|
||||||
|
|
||||||
#### Python环境运行
|
#### Python环境运行
|
||||||
@ -41,12 +54,28 @@ python simple_build.py
|
|||||||
python cross_platform_build.py
|
python cross_platform_build.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 选择文件识别模式
|
||||||
|
|
||||||
|
程序运行时首先选择文件识别模式:
|
||||||
|
1. **默认文件名** - 读取 `人员信息.xlsx` 和 `座位信息.xlsx`
|
||||||
|
2. **智能识别** - 自动识别目录中的Excel文件
|
||||||
|
|
||||||
|
### 选择分组方式
|
||||||
|
|
||||||
|
然后选择连坐分组方式:
|
||||||
|
1. **备注分组** - 根据备注数字识别连坐组(默认)
|
||||||
|
2. **手机号分组** - 根据相同手机号识别连坐组
|
||||||
|
|
||||||
### 查看结果
|
### 查看结果
|
||||||
|
|
||||||
程序运行后会生成:
|
程序运行后会生成:
|
||||||
- `座位信息_最终分配.xlsx` - 最终分配结果
|
- `log/座位信息_最终分配.xlsx` - 最终分配结果
|
||||||
- `最终座位分配日志.xlsx` - 详细分配记录
|
- 第1-5列:座位基本信息(保持不变)
|
||||||
- `seat_allocation_log.txt` - 完整运行日志
|
- 第6-10列:人员信息(覆盖原座位表数据)
|
||||||
|
- 第11-12列:原座位表其他列(保持不变)
|
||||||
|
- 第13列:分配备注(新增列)
|
||||||
|
- `log/最终座位分配日志.xlsx` - 详细分配记录
|
||||||
|
- `log/seat_allocation_log.txt` - 完整运行日志
|
||||||
|
|
||||||
## 🔧 构建选项
|
## 🔧 构建选项
|
||||||
|
|
||||||
@ -117,9 +146,12 @@ A区通道 | 一层 | 1排 | 3号
|
|||||||
## ⚠️ 注意事项
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
1. **备注逻辑**: 备注数字表示连坐人数,只有组长填写备注,成员留空
|
1. **备注逻辑**: 备注数字表示连坐人数,只有组长填写备注,成员留空
|
||||||
2. **证件号格式**: 支持包含X的身份证号,自动处理为字符串格式
|
2. **手机号逻辑**: 相同手机号的人员会被识别为连坐组
|
||||||
3. **文字清理**: 自动清除姓名等字段的多余空格
|
3. **重名处理**: 支持重名人员,通过姓名+身份证号组合进行唯一性校验
|
||||||
4. **座位连续性**: 支持不连续座位号,算法会自动寻找合适的连续段
|
4. **证件号格式**: 支持包含X的身份证号,自动处理为字符串格式
|
||||||
|
5. **文字清理**: 自动清除姓名等字段的多余空格
|
||||||
|
6. **座位连续性**: 支持不连续座位号,算法会自动寻找合适的连续段
|
||||||
|
7. **智能覆盖**: 人员信息覆盖座位表对应列,分配备注单独存储
|
||||||
|
|
||||||
## 🔍 故障排除
|
## 🔍 故障排除
|
||||||
|
|
||||||
|
284
Windows部署说明.md
284
Windows部署说明.md
@ -1,284 +0,0 @@
|
|||||||
# 座位分配系统 - Windows部署说明
|
|
||||||
|
|
||||||
## 目录
|
|
||||||
- [概述](#概述)
|
|
||||||
- [系统要求](#系统要求)
|
|
||||||
- [开发者构建指南](#开发者构建指南)
|
|
||||||
- [用户使用指南](#用户使用指南)
|
|
||||||
- [故障排除](#故障排除)
|
|
||||||
- [技术支持](#技术支持)
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
本文档说明如何在Windows系统上构建和部署座位分配系统。该系统支持1-10人连坐需求,能够处理不连续座位分配。
|
|
||||||
|
|
||||||
**主要功能:**
|
|
||||||
- 智能座位分配算法
|
|
||||||
- 支持连坐组合(1-10人)
|
|
||||||
- 完整的文件校验
|
|
||||||
- 详细的分配日志
|
|
||||||
- 用户友好的命令行界面
|
|
||||||
|
|
||||||
## 系统要求
|
|
||||||
|
|
||||||
### 开发环境要求
|
|
||||||
- **操作系统**: Windows 7/10/11 (64位)
|
|
||||||
- **Python版本**: Python 3.8 或更高版本
|
|
||||||
- **内存**: 至少 2GB RAM
|
|
||||||
- **磁盘空间**: 至少 500MB 可用空间
|
|
||||||
- **网络**: 需要网络连接以下载依赖包
|
|
||||||
|
|
||||||
### 运行环境要求(最终用户)
|
|
||||||
- **操作系统**: Windows 7/10/11 (64位)
|
|
||||||
- **内存**: 至少 1GB RAM
|
|
||||||
- **磁盘空间**: 至少 100MB 可用空间
|
|
||||||
- **Excel**: Microsoft Excel 2010 或更高版本(查看结果文件)
|
|
||||||
|
|
||||||
## 开发者构建指南
|
|
||||||
|
|
||||||
### 1. 环境准备
|
|
||||||
|
|
||||||
#### 1.1 安装Python
|
|
||||||
从 [Python官网](https://www.python.org/downloads/) 下载并安装Python 3.8+
|
|
||||||
|
|
||||||
确保安装时勾选:
|
|
||||||
- ✅ Add Python to PATH
|
|
||||||
- ✅ Install pip
|
|
||||||
|
|
||||||
#### 1.2 验证安装
|
|
||||||
```cmd
|
|
||||||
python --version
|
|
||||||
pip --version
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 下载源代码
|
|
||||||
|
|
||||||
```cmd
|
|
||||||
git clone <repository-url>
|
|
||||||
cd TableSynthesis
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 自动构建(推荐)
|
|
||||||
|
|
||||||
#### 3.1 运行构建脚本
|
|
||||||
```cmd
|
|
||||||
python windows_build.py
|
|
||||||
```
|
|
||||||
|
|
||||||
构建脚本将自动完成:
|
|
||||||
- ✅ 检查系统环境
|
|
||||||
- ✅ 安装必要依赖
|
|
||||||
- ✅ 构建独立exe文件
|
|
||||||
- ✅ 创建分发包
|
|
||||||
|
|
||||||
#### 3.2 构建输出
|
|
||||||
构建成功后,会生成:
|
|
||||||
```
|
|
||||||
座位分配系统_Windows_分发包/
|
|
||||||
├── 座位分配系统.exe # 主程序
|
|
||||||
├── 运行座位分配系统.bat # 启动脚本
|
|
||||||
├── 使用说明.txt # 使用说明
|
|
||||||
├── 人员信息_示例.xlsx # 示例文件
|
|
||||||
└── 座位信息_示例.xlsx # 示例文件
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 手动构建(可选)
|
|
||||||
|
|
||||||
如果自动构建失败,可以手动构建:
|
|
||||||
|
|
||||||
#### 4.1 安装依赖
|
|
||||||
```cmd
|
|
||||||
pip install pandas>=1.3.0 openpyxl>=3.0.0 numpy>=1.20.0 pyinstaller>=4.0
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 4.2 使用PyInstaller构建
|
|
||||||
```cmd
|
|
||||||
pyinstaller --clean --noconfirm seat_allocation.spec
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 4.3 手动创建分发包
|
|
||||||
```cmd
|
|
||||||
mkdir 座位分配系统_Windows_分发包
|
|
||||||
copy dist\座位分配系统.exe 座位分配系统_Windows_分发包\
|
|
||||||
copy 人员信息.xlsx 座位分配系统_Windows_分发包\人员信息_示例.xlsx
|
|
||||||
copy 座位信息.xlsx 座位分配系统_Windows_分发包\座位信息_示例.xlsx
|
|
||||||
copy 运行座位分配系统.bat 座位分配系统_Windows_分发包\
|
|
||||||
```
|
|
||||||
|
|
||||||
## 用户使用指南
|
|
||||||
|
|
||||||
### 1. 获取分发包
|
|
||||||
|
|
||||||
从开发者处获取 `座位分配系统_Windows_分发包` 文件夹,或下载已构建的发布版本。
|
|
||||||
|
|
||||||
### 2. 准备数据文件
|
|
||||||
|
|
||||||
#### 2.1 复制示例文件
|
|
||||||
```
|
|
||||||
人员信息_示例.xlsx → 人员信息.xlsx
|
|
||||||
座位信息_示例.xlsx → 座位信息.xlsx
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2.2 填写人员信息.xlsx
|
|
||||||
|
|
||||||
**必需列:**
|
|
||||||
- **姓名**: 人员姓名
|
|
||||||
- **证件类型**: 身份证、护照等
|
|
||||||
- **证件号**: 证件号码
|
|
||||||
- **手机号**: 联系电话
|
|
||||||
- **备注**: 连坐人数(留空表示单独坐)
|
|
||||||
|
|
||||||
**连坐规则:**
|
|
||||||
- 单人坐位:备注列留空
|
|
||||||
- 连坐组合:第一人填写总人数,其他人留空
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
| 姓名 | 证件类型 | 证件号 | 手机号 | 备注 |
|
|
||||||
|------|----------|--------|--------|------|
|
|
||||||
| 张三 | 身份证 | 123456789012345678 | 13800138000 | 3 |
|
|
||||||
| 李四 | 身份证 | 123456789012345679 | 13800138001 | |
|
|
||||||
| 王五 | 身份证 | 123456789012345680 | 13800138002 | |
|
|
||||||
| 赵六 | 身份证 | 123456789012345681 | 13800138003 | |
|
|
||||||
|
|
||||||
上述示例表示:张三、李四、王五需要3人连坐,赵六单独坐。
|
|
||||||
|
|
||||||
#### 2.3 填写座位信息.xlsx
|
|
||||||
|
|
||||||
**必需列:**
|
|
||||||
- **区域**: 座位区域(如"A区"、"B区")
|
|
||||||
- **楼层**: 楼层信息(如"1层"、"2层")
|
|
||||||
- **排号**: 排号(如"第1排"、"第2排")
|
|
||||||
- **座位号**: 具体座位号(如"1号"、"2号")
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
| 区域 | 楼层 | 排号 | 座位号 |
|
|
||||||
|------|------|------|--------|
|
|
||||||
| A区 | 1层 | 第1排 | 1号 |
|
|
||||||
| A区 | 1层 | 第1排 | 2号 |
|
|
||||||
| A区 | 1层 | 第1排 | 3号 |
|
|
||||||
|
|
||||||
### 3. 运行程序
|
|
||||||
|
|
||||||
#### 3.1 推荐方式(使用bat脚本)
|
|
||||||
双击 `运行座位分配系统.bat`
|
|
||||||
|
|
||||||
脚本会自动:
|
|
||||||
- ✅ 检查环境和文件
|
|
||||||
- ✅ 提供友好的错误提示
|
|
||||||
- ✅ 运行主程序
|
|
||||||
- ✅ 显示运行结果
|
|
||||||
|
|
||||||
#### 3.2 直接运行
|
|
||||||
双击 `座位分配系统.exe`
|
|
||||||
|
|
||||||
### 4. 查看结果
|
|
||||||
|
|
||||||
程序运行成功后,会生成以下文件:
|
|
||||||
|
|
||||||
- **座位信息_最终分配.xlsx**: 最终座位分配结果
|
|
||||||
- **最终座位分配日志.xlsx**: 详细分配记录
|
|
||||||
- **seat_allocation_log.txt**: 运行日志
|
|
||||||
|
|
||||||
使用Excel打开xlsx文件查看分配结果。
|
|
||||||
|
|
||||||
## 故障排除
|
|
||||||
|
|
||||||
### 常见问题
|
|
||||||
|
|
||||||
#### Q1: 程序无法启动
|
|
||||||
**可能原因:**
|
|
||||||
- 系统不兼容(需要Windows 7+)
|
|
||||||
- 缺少Visual C++ Redistributable
|
|
||||||
|
|
||||||
**解决方案:**
|
|
||||||
1. 确认系统版本:Windows 7/10/11 64位
|
|
||||||
2. 下载安装 [Microsoft Visual C++ Redistributable](https://aka.ms/vs/17/release/vc_redist.x64.exe)
|
|
||||||
|
|
||||||
#### Q2: 提示缺少依赖包
|
|
||||||
**可能原因:**
|
|
||||||
- 网络连接问题
|
|
||||||
- 权限不足
|
|
||||||
|
|
||||||
**解决方案:**
|
|
||||||
1. 确保网络连接正常
|
|
||||||
2. 以管理员身份运行
|
|
||||||
3. 手动安装:`pip install pandas openpyxl numpy`
|
|
||||||
|
|
||||||
#### Q3: 文件权限错误
|
|
||||||
**可能原因:**
|
|
||||||
- Excel文件被占用
|
|
||||||
- 程序目录只读
|
|
||||||
|
|
||||||
**解决方案:**
|
|
||||||
1. 关闭所有Excel窗口
|
|
||||||
2. 确保程序目录有写入权限
|
|
||||||
3. 避免在系统盘根目录运行
|
|
||||||
|
|
||||||
#### Q4: 数据文件格式错误
|
|
||||||
**可能原因:**
|
|
||||||
- 列名不匹配
|
|
||||||
- 数据类型错误
|
|
||||||
- 编码问题
|
|
||||||
|
|
||||||
**解决方案:**
|
|
||||||
1. 对照示例文件检查列名
|
|
||||||
2. 确保数据格式正确
|
|
||||||
3. 使用UTF-8编码保存
|
|
||||||
|
|
||||||
#### Q5: 无法找到连续座位
|
|
||||||
**可能原因:**
|
|
||||||
- 座位信息不连续
|
|
||||||
- 连坐组过大
|
|
||||||
|
|
||||||
**解决方案:**
|
|
||||||
1. 检查座位信息的连续性
|
|
||||||
2. 适当调整连坐组大小
|
|
||||||
3. 增加座位数量
|
|
||||||
|
|
||||||
### 日志分析
|
|
||||||
|
|
||||||
查看 `seat_allocation_log.txt` 文件可获取详细错误信息:
|
|
||||||
|
|
||||||
- **文件加载错误**: 检查文件路径和格式
|
|
||||||
- **数据校验错误**: 检查数据完整性
|
|
||||||
- **分配算法错误**: 检查座位和人员匹配
|
|
||||||
|
|
||||||
### 性能优化
|
|
||||||
|
|
||||||
#### 大数据处理
|
|
||||||
- **人员数量**: 建议不超过1000人
|
|
||||||
- **座位数量**: 建议不超过2000个
|
|
||||||
- **连坐组大小**: 建议不超过8人
|
|
||||||
|
|
||||||
#### 内存使用
|
|
||||||
- 小型场景(<100人): 约50MB内存
|
|
||||||
- 中型场景(100-500人): 约100MB内存
|
|
||||||
- 大型场景(500-1000人): 约200MB内存
|
|
||||||
|
|
||||||
## 技术支持
|
|
||||||
|
|
||||||
### 获取帮助
|
|
||||||
|
|
||||||
1. **查看日志文件**: `seat_allocation_log.txt`
|
|
||||||
2. **检查使用说明**: `使用说明.txt`
|
|
||||||
3. **参考示例文件**: 确保数据格式正确
|
|
||||||
|
|
||||||
### 报告问题
|
|
||||||
|
|
||||||
报告问题时,请提供:
|
|
||||||
- 系统信息(操作系统版本)
|
|
||||||
- 错误截图或错误信息
|
|
||||||
- 数据文件(脱敏后)
|
|
||||||
- seat_allocation_log.txt 内容
|
|
||||||
|
|
||||||
### 更新与维护
|
|
||||||
|
|
||||||
- 定期检查更新版本
|
|
||||||
- 备份重要数据文件
|
|
||||||
- 保持系统和依赖包更新
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**版本**: v1.0
|
|
||||||
**最后更新**: 2024年12月
|
|
||||||
**兼容性**: Windows 7/10/11 (64位)
|
|
@ -1,89 +0,0 @@
|
|||||||
# GitHub Actions工作流 - 多平台构建
|
|
||||||
# 将此文件放在 .github/workflows/ 目录下
|
|
||||||
|
|
||||||
name: Build Multi-Platform Executables
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-windows:
|
|
||||||
runs-on: windows-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
arch: [x64, x86]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: '3.10'
|
|
||||||
architecture: ${{ matrix.arch }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install pandas openpyxl pyinstaller
|
|
||||||
|
|
||||||
- name: Build executable
|
|
||||||
run: |
|
|
||||||
pyinstaller --onefile --console --name "座位分配系统_${{ matrix.arch }}" seat_allocation_system.py
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: windows-${{ matrix.arch }}
|
|
||||||
path: dist/座位分配系统_${{ matrix.arch }}.exe
|
|
||||||
|
|
||||||
build-macos:
|
|
||||||
runs-on: macos-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: '3.10'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install pandas openpyxl pyinstaller
|
|
||||||
|
|
||||||
- name: Build executable
|
|
||||||
run: |
|
|
||||||
pyinstaller --onefile --console --name "座位分配系统_macos" seat_allocation_system.py
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: macos
|
|
||||||
path: dist/座位分配系统_macos
|
|
||||||
|
|
||||||
create-release:
|
|
||||||
needs: [build-windows, build-macos]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Download all artifacts
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
|
|
||||||
- name: Create Release
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
with:
|
|
||||||
tag_name: v${{ github.run_number }}
|
|
||||||
name: Release v${{ github.run_number }}
|
|
||||||
files: |
|
|
||||||
windows-x64/座位分配系统_x64.exe
|
|
||||||
windows-x86/座位分配系统_x86.exe
|
|
||||||
macos/座位分配系统_macos
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -1,326 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
跨平台构建脚本
|
|
||||||
在macOS上构建Windows x86/x64版本的exe文件
|
|
||||||
使用Wine和PyInstaller实现跨平台编译
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
import platform
|
|
||||||
import shutil
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def check_dependencies():
|
|
||||||
"""检查必要的依赖"""
|
|
||||||
print("检查构建依赖...")
|
|
||||||
|
|
||||||
# 检查是否在macOS上
|
|
||||||
if platform.system() != 'Darwin':
|
|
||||||
print(f"当前系统: {platform.system()}")
|
|
||||||
print("此脚本专为macOS设计,用于构建Windows版本")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 检查Python包
|
|
||||||
required_packages = ['pandas', 'openpyxl', 'pyinstaller']
|
|
||||||
missing_packages = []
|
|
||||||
|
|
||||||
for package in required_packages:
|
|
||||||
try:
|
|
||||||
__import__(package)
|
|
||||||
print(f"✅ {package} 已安装")
|
|
||||||
except ImportError:
|
|
||||||
missing_packages.append(package)
|
|
||||||
print(f"❌ {package} 未安装")
|
|
||||||
|
|
||||||
if missing_packages:
|
|
||||||
print(f"\n安装缺失的包...")
|
|
||||||
for package in missing_packages:
|
|
||||||
try:
|
|
||||||
subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
|
|
||||||
print(f"✅ {package} 安装成功")
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
print(f"❌ {package} 安装失败")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def install_wine():
|
|
||||||
"""安装Wine(如果需要)"""
|
|
||||||
print("\n检查Wine环境...")
|
|
||||||
|
|
||||||
# 检查是否已安装Wine
|
|
||||||
try:
|
|
||||||
result = subprocess.run(['wine', '--version'], capture_output=True, text=True)
|
|
||||||
if result.returncode == 0:
|
|
||||||
print(f"✅ Wine已安装: {result.stdout.strip()}")
|
|
||||||
return True
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
print("Wine未安装,开始安装...")
|
|
||||||
print("请按照以下步骤手动安装Wine:")
|
|
||||||
print("1. 安装Homebrew (如果未安装): /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"")
|
|
||||||
print("2. 安装Wine: brew install --cask wine-stable")
|
|
||||||
print("3. 配置Wine: winecfg")
|
|
||||||
|
|
||||||
response = input("是否已完成Wine安装? (y/N): ")
|
|
||||||
return response.lower() == 'y'
|
|
||||||
|
|
||||||
def setup_wine_python():
|
|
||||||
"""在Wine中设置Python环境"""
|
|
||||||
print("\n设置Wine Python环境...")
|
|
||||||
|
|
||||||
# 检查Wine中是否已安装Python
|
|
||||||
try:
|
|
||||||
result = subprocess.run(['wine', 'python', '--version'], capture_output=True, text=True)
|
|
||||||
if result.returncode == 0:
|
|
||||||
print(f"✅ Wine Python已安装: {result.stdout.strip()}")
|
|
||||||
return True
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
print("需要在Wine中安装Python...")
|
|
||||||
print("请按照以下步骤:")
|
|
||||||
print("1. 下载Windows版Python: https://www.python.org/downloads/windows/")
|
|
||||||
print("2. 使用Wine安装: wine python-3.10.x-amd64.exe")
|
|
||||||
print("3. 安装pip包: wine python -m pip install pandas openpyxl pyinstaller")
|
|
||||||
|
|
||||||
response = input("是否已完成Wine Python安装? (y/N): ")
|
|
||||||
return response.lower() == 'y'
|
|
||||||
|
|
||||||
def create_build_spec(target_arch='x64'):
|
|
||||||
"""创建构建规格文件"""
|
|
||||||
exe_name = f"座位分配系统_{target_arch}"
|
|
||||||
|
|
||||||
spec_content = f'''# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
|
|
||||||
block_cipher = None
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['seat_allocation_system.py'],
|
|
||||||
pathex=[],
|
|
||||||
binaries=[],
|
|
||||||
datas=[],
|
|
||||||
hiddenimports=[
|
|
||||||
'pandas',
|
|
||||||
'openpyxl',
|
|
||||||
'numpy',
|
|
||||||
'xlsxwriter',
|
|
||||||
'xlrd',
|
|
||||||
'datetime',
|
|
||||||
'pathlib'
|
|
||||||
],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={{}},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[
|
|
||||||
'matplotlib',
|
|
||||||
'scipy',
|
|
||||||
'IPython',
|
|
||||||
'jupyter',
|
|
||||||
'notebook',
|
|
||||||
'tkinter'
|
|
||||||
],
|
|
||||||
win_no_prefer_redirects=False,
|
|
||||||
win_private_assemblies=False,
|
|
||||||
cipher=block_cipher,
|
|
||||||
noarchive=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.zipfiles,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='{exe_name}',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=True,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
icon=None,
|
|
||||||
)
|
|
||||||
'''
|
|
||||||
|
|
||||||
spec_file = f'{exe_name}.spec'
|
|
||||||
with open(spec_file, 'w', encoding='utf-8') as f:
|
|
||||||
f.write(spec_content)
|
|
||||||
|
|
||||||
return spec_file, exe_name
|
|
||||||
|
|
||||||
def build_with_docker():
|
|
||||||
"""使用Docker构建Windows版本"""
|
|
||||||
print("\n使用Docker构建Windows版本...")
|
|
||||||
|
|
||||||
# 创建Dockerfile
|
|
||||||
dockerfile_content = '''FROM python:3.10-windowsservercore
|
|
||||||
|
|
||||||
# 设置工作目录
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# 复制文件
|
|
||||||
COPY requirements.txt .
|
|
||||||
COPY seat_allocation_system.py .
|
|
||||||
|
|
||||||
# 安装依赖
|
|
||||||
RUN pip install -r requirements.txt
|
|
||||||
|
|
||||||
# 构建exe
|
|
||||||
RUN pyinstaller --onefile --console --name "座位分配系统_x64" seat_allocation_system.py
|
|
||||||
|
|
||||||
# 输出目录
|
|
||||||
VOLUME ["/app/dist"]
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open('Dockerfile.windows', 'w') as f:
|
|
||||||
f.write(dockerfile_content)
|
|
||||||
|
|
||||||
print("Docker方法需要Docker Desktop和Windows容器支持")
|
|
||||||
print("这是一个高级选项,建议使用其他方法")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def build_native_macos():
|
|
||||||
"""在macOS上直接构建(生成macOS版本)"""
|
|
||||||
print("\n在macOS上构建本地版本...")
|
|
||||||
|
|
||||||
# 清理之前的构建
|
|
||||||
if os.path.exists('dist'):
|
|
||||||
shutil.rmtree('dist')
|
|
||||||
if os.path.exists('build'):
|
|
||||||
shutil.rmtree('build')
|
|
||||||
|
|
||||||
# 构建命令
|
|
||||||
cmd = [
|
|
||||||
sys.executable, '-m', 'PyInstaller',
|
|
||||||
'--onefile',
|
|
||||||
'--console',
|
|
||||||
'--clean',
|
|
||||||
'--name', '座位分配系统_macos',
|
|
||||||
'seat_allocation_system.py'
|
|
||||||
]
|
|
||||||
|
|
||||||
try:
|
|
||||||
print(f"执行命令: {' '.join(cmd)}")
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("✅ macOS版本构建成功!")
|
|
||||||
|
|
||||||
# 检查生成的文件
|
|
||||||
app_path = Path('dist') / '座位分配系统_macos'
|
|
||||||
if app_path.exists():
|
|
||||||
file_size = app_path.stat().st_size / (1024 * 1024) # MB
|
|
||||||
print(f"生成文件: {app_path}")
|
|
||||||
print(f"文件大小: {file_size:.1f} MB")
|
|
||||||
return True, app_path
|
|
||||||
else:
|
|
||||||
print("❌ 构建失败!")
|
|
||||||
print("错误输出:")
|
|
||||||
print(result.stderr)
|
|
||||||
return False, None
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 构建过程中出现错误: {e}")
|
|
||||||
return False, None
|
|
||||||
|
|
||||||
def create_cross_compile_script():
|
|
||||||
"""创建跨编译脚本"""
|
|
||||||
script_content = '''#!/bin/bash
|
|
||||||
# Windows交叉编译脚本
|
|
||||||
|
|
||||||
echo "座位分配系统 - Windows交叉编译"
|
|
||||||
echo "================================"
|
|
||||||
|
|
||||||
# 检查是否安装了mingw-w64
|
|
||||||
if ! command -v x86_64-w64-mingw32-gcc &> /dev/null; then
|
|
||||||
echo "安装mingw-w64..."
|
|
||||||
brew install mingw-w64
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 设置交叉编译环境
|
|
||||||
export CC=x86_64-w64-mingw32-gcc
|
|
||||||
export CXX=x86_64-w64-mingw32-g++
|
|
||||||
|
|
||||||
# 构建Windows版本
|
|
||||||
echo "开始构建Windows x64版本..."
|
|
||||||
python -m PyInstaller \\
|
|
||||||
--onefile \\
|
|
||||||
--console \\
|
|
||||||
--clean \\
|
|
||||||
--name "座位分配系统_x64" \\
|
|
||||||
--target-arch x86_64 \\
|
|
||||||
seat_allocation_system.py
|
|
||||||
|
|
||||||
echo "构建完成!"
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open('cross_compile.sh', 'w') as f:
|
|
||||||
f.write(script_content)
|
|
||||||
|
|
||||||
# 设置执行权限
|
|
||||||
os.chmod('cross_compile.sh', 0o755)
|
|
||||||
print("✅ 已创建交叉编译脚本: cross_compile.sh")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主函数"""
|
|
||||||
print("座位分配系统 - 跨平台构建工具")
|
|
||||||
print("=" * 50)
|
|
||||||
print("在macOS上构建Windows版本")
|
|
||||||
|
|
||||||
# 检查依赖
|
|
||||||
if not check_dependencies():
|
|
||||||
return
|
|
||||||
|
|
||||||
print("\n选择构建方法:")
|
|
||||||
print("1. 构建macOS版本(推荐)")
|
|
||||||
print("2. 使用Wine构建Windows版本(复杂)")
|
|
||||||
print("3. 创建交叉编译脚本(实验性)")
|
|
||||||
print("4. 生成Docker构建文件(高级)")
|
|
||||||
|
|
||||||
choice = input("\n请选择 (1-4): ").strip()
|
|
||||||
|
|
||||||
if choice == '1':
|
|
||||||
print("\n构建macOS版本...")
|
|
||||||
success, app_path = build_native_macos()
|
|
||||||
if success:
|
|
||||||
print(f"\n🎉 构建完成!")
|
|
||||||
print(f"生成文件: {app_path}")
|
|
||||||
print("\n注意: 这是macOS版本,不能在Windows上运行")
|
|
||||||
print("如需Windows版本,请在Windows系统上运行构建脚本")
|
|
||||||
|
|
||||||
elif choice == '2':
|
|
||||||
print("\n使用Wine构建Windows版本...")
|
|
||||||
if install_wine() and setup_wine_python():
|
|
||||||
print("Wine环境已准备就绪")
|
|
||||||
print("请手动在Wine中运行: wine python windows_build.py")
|
|
||||||
else:
|
|
||||||
print("Wine环境设置失败")
|
|
||||||
|
|
||||||
elif choice == '3':
|
|
||||||
print("\n创建交叉编译脚本...")
|
|
||||||
create_cross_compile_script()
|
|
||||||
print("请运行: ./cross_compile.sh")
|
|
||||||
|
|
||||||
elif choice == '4':
|
|
||||||
print("\n生成Docker构建文件...")
|
|
||||||
build_with_docker()
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("无效选择")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
@ -1,127 +0,0 @@
|
|||||||
[2025-07-01 15:41:09] ============================================================
|
|
||||||
[2025-07-01 15:41:09] 座位分配系统 - 文件校验
|
|
||||||
[2025-07-01 15:41:09] ============================================================
|
|
||||||
[2025-07-01 15:41:09] === 开始加载数据 ===
|
|
||||||
[2025-07-01 15:41:09] 正在读取人员信息文件: 人员信息.xlsx
|
|
||||||
[2025-07-01 15:41:09] 正在读取座位信息文件: 座位信息.xlsx
|
|
||||||
[2025-07-01 15:41:09] 开始过滤座位数据...
|
|
||||||
[2025-07-01 15:41:09] 原始数据行数: 17
|
|
||||||
[2025-07-01 15:41:09] 过滤后数据行数: 6
|
|
||||||
[2025-07-01 15:41:09] 移除无效行数: 11
|
|
||||||
[2025-07-01 15:41:09] ✅ 已过滤掉 11 行无效数据(空行、示例数据等)
|
|
||||||
[2025-07-01 15:41:09] 有效座位区域: 西北门入口场地A5区
|
|
||||||
[2025-07-01 15:41:09] 有效座位楼层: 场地
|
|
||||||
[2025-07-01 15:41:09] 清理文字数据中的空格...
|
|
||||||
[2025-07-01 15:41:09] ✅ 文字数据清理完成
|
|
||||||
[2025-07-01 15:41:09] ✅ 文件加载成功
|
|
||||||
[2025-07-01 15:41:09] 人员信息: 6 行 × 6 列
|
|
||||||
[2025-07-01 15:41:09] 座位信息: 6 行 × 12 列
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
=== 人员信息结构校验 ===
|
|
||||||
[2025-07-01 15:41:09] ✅ 所有必需列都存在
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
数据完整性检查:
|
|
||||||
[2025-07-01 15:41:09] ✅ 姓名 列数据完整
|
|
||||||
[2025-07-01 15:41:09] ✅ 证件类型 列数据完整
|
|
||||||
[2025-07-01 15:41:09] ✅ 证件号 列数据完整
|
|
||||||
[2025-07-01 15:41:09] ✅ 手机号 列数据完整
|
|
||||||
[2025-07-01 15:41:09] ✅ 姓名无重复
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
=== 连坐组校验 ===
|
|
||||||
[2025-07-01 15:41:09] ✅ 第 1 组: 叶一帆 (单独)
|
|
||||||
[2025-07-01 15:41:09] ✅ 第 2 组: 刘泽 (单独)
|
|
||||||
[2025-07-01 15:41:09] ✅ 第 3 组: 黄锡恩 (单独)
|
|
||||||
[2025-07-01 15:41:09] ✅ 第 4 组: 胡中, 丁逸夫 (2人连坐)
|
|
||||||
[2025-07-01 15:41:09] ✅ 第 5 组: 沈佳琰 (单独)
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
=== 座位信息结构校验 ===
|
|
||||||
[2025-07-01 15:41:09] ✅ 所有必需列都存在
|
|
||||||
[2025-07-01 15:41:09] ✅ 区域 列数据完整
|
|
||||||
[2025-07-01 15:41:09] ✅ 楼层 列数据完整
|
|
||||||
[2025-07-01 15:41:09] ✅ 排号 列数据完整
|
|
||||||
[2025-07-01 15:41:09] ✅ 座位号 列数据完整
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
座位结构分析:
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
座位结构分析:
|
|
||||||
[2025-07-01 15:41:09] ✅ 西北门入口场地A5区-场地-19排: 6 个座位完全连续 (1-6)
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
=== 容量和可行性校验 ===
|
|
||||||
[2025-07-01 15:41:09] 总人数: 6
|
|
||||||
[2025-07-01 15:41:09] 总座位数: 6
|
|
||||||
[2025-07-01 15:41:09] ✅ 座位充足: 剩余 0 个座位
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
连坐组需求分析:
|
|
||||||
[2025-07-01 15:41:09] 最大连坐组: 2 人
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
连续座位可行性分析:
|
|
||||||
[2025-07-01 15:41:09] 西北门入口场地A5区-场地-19排: 最大连续 6 个座位
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
全场最大连续座位: 6 个
|
|
||||||
[2025-07-01 15:41:09] ✅ 可以容纳最大连坐组
|
|
||||||
[2025-07-01 15:41:09] 可容纳最大连坐组的排数: 1 个
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
连坐组分布:
|
|
||||||
[2025-07-01 15:41:09] 单人组: 4 个
|
|
||||||
[2025-07-01 15:41:09] 2人连坐组: 1 个
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
============================================================
|
|
||||||
[2025-07-01 15:41:09] 校验结果总结
|
|
||||||
[2025-07-01 15:41:09] ============================================================
|
|
||||||
[2025-07-01 15:41:09] 人员信息结构: ✅ 通过
|
|
||||||
[2025-07-01 15:41:09] 连坐组完整性: ✅ 通过
|
|
||||||
[2025-07-01 15:41:09] 座位信息结构: ✅ 通过
|
|
||||||
[2025-07-01 15:41:09] 容量可行性: ✅ 通过
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
总体校验结果: ✅ 全部通过
|
|
||||||
[2025-07-01 15:41:09]
|
|
||||||
🎉 文件校验通过,可以进行座位分配!
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
============================================================
|
|
||||||
[2025-07-01 15:41:19] 开始座位分配
|
|
||||||
[2025-07-01 15:41:19] ============================================================
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
=== 人员连坐需求分析 ===
|
|
||||||
[2025-07-01 15:41:19] 总共识别出 5 个座位组:
|
|
||||||
[2025-07-01 15:41:19] 单人组: 4 个
|
|
||||||
[2025-07-01 15:41:19] 2人连坐组: 1 个
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
=== 高级座位结构分析 ===
|
|
||||||
[2025-07-01 15:41:19] ✅ 西北门入口场地A5区-场地-19排: 6 个座位完全连续
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
=== 开始智能座位分配 ===
|
|
||||||
[2025-07-01 15:41:19] 需要分配 5 个组
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
处理第 1 组: 胡中 (group, 2 人)
|
|
||||||
[2025-07-01 15:41:19] 分配到 西北门入口场地A5区-场地-19排 (连续座位: 1-2)
|
|
||||||
[2025-07-01 15:41:19] 胡中 -> 1号
|
|
||||||
[2025-07-01 15:41:19] 丁逸夫 -> 2号
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
处理第 2 组: 叶一帆 (single, 1 人)
|
|
||||||
[2025-07-01 15:41:19] 分配到 西北门入口场地A5区-场地-19排: 叶一帆 -> 3号
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
处理第 3 组: 刘泽 (single, 1 人)
|
|
||||||
[2025-07-01 15:41:19] 分配到 西北门入口场地A5区-场地-19排: 刘泽 -> 4号
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
处理第 4 组: 黄锡恩 (single, 1 人)
|
|
||||||
[2025-07-01 15:41:19] 分配到 西北门入口场地A5区-场地-19排: 黄锡恩 -> 5号
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
处理第 5 组: 沈佳琰 (single, 1 人)
|
|
||||||
[2025-07-01 15:41:19] 分配到 西北门入口场地A5区-场地-19排: 沈佳琰 -> 6号
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
座位分配结果已保存到: log/座位信息_最终分配.xlsx
|
|
||||||
[2025-07-01 15:41:19] 分配日志已保存到: log/最终座位分配日志.xlsx
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
=== 分配统计 ===
|
|
||||||
[2025-07-01 15:41:19] 总共分配了 6 个座位
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
按组大小统计:
|
|
||||||
[2025-07-01 15:41:19] 单人组: 4 个组, 4 人
|
|
||||||
[2025-07-01 15:41:19] 2人连坐组: 1 个组, 2 人
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
=== 连续性验证 ===
|
|
||||||
[2025-07-01 15:41:19] ✅ 组 1 (胡中): 2人连坐,座位连续 [1, 2]
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
连续性检查结果: 1/1 个多人组座位连续 (100.0%)
|
|
||||||
[2025-07-01 15:41:19]
|
|
||||||
🎉 座位分配完成!
|
|
Binary file not shown.
Binary file not shown.
@ -1,128 +0,0 @@
|
|||||||
# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
# 完全独立的PyInstaller配置
|
|
||||||
# 包含Python解释器和所有依赖,无需目标机器安装Python
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
block_cipher = None
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['seat_allocation_system.py'],
|
|
||||||
pathex=[str(Path.cwd())],
|
|
||||||
binaries=[],
|
|
||||||
datas=[
|
|
||||||
# 包含必要的数据文件(如果有的话)
|
|
||||||
],
|
|
||||||
hiddenimports=[
|
|
||||||
# 核心依赖
|
|
||||||
'pandas',
|
|
||||||
'openpyxl',
|
|
||||||
'numpy',
|
|
||||||
'xlsxwriter',
|
|
||||||
'xlrd',
|
|
||||||
'datetime',
|
|
||||||
'pathlib',
|
|
||||||
'subprocess',
|
|
||||||
'platform',
|
|
||||||
'sys',
|
|
||||||
'os',
|
|
||||||
|
|
||||||
# openpyxl相关
|
|
||||||
'openpyxl.workbook',
|
|
||||||
'openpyxl.worksheet',
|
|
||||||
'openpyxl.styles',
|
|
||||||
'openpyxl.utils',
|
|
||||||
'openpyxl.writer.excel',
|
|
||||||
'openpyxl.reader.excel',
|
|
||||||
'openpyxl.cell',
|
|
||||||
'openpyxl.formatting',
|
|
||||||
'openpyxl.formatting.rule',
|
|
||||||
|
|
||||||
# pandas相关
|
|
||||||
'pandas.io.excel',
|
|
||||||
'pandas.io.common',
|
|
||||||
'pandas.io.parsers',
|
|
||||||
'pandas.io.formats',
|
|
||||||
'pandas.io.formats.excel',
|
|
||||||
'pandas._libs.tslibs.timedeltas',
|
|
||||||
'pandas._libs.tslibs.np_datetime',
|
|
||||||
'pandas._libs.tslibs.nattype',
|
|
||||||
'pandas._libs.skiplist',
|
|
||||||
|
|
||||||
# numpy相关
|
|
||||||
'numpy.core.multiarray',
|
|
||||||
'numpy.core.umath',
|
|
||||||
'numpy.core._methods',
|
|
||||||
'numpy.lib.format',
|
|
||||||
'numpy.random._pickle',
|
|
||||||
|
|
||||||
# 编码相关
|
|
||||||
'encodings',
|
|
||||||
'encodings.utf_8',
|
|
||||||
'encodings.gbk',
|
|
||||||
'encodings.cp1252',
|
|
||||||
|
|
||||||
# 其他必要模块
|
|
||||||
'pkg_resources.py2_warn',
|
|
||||||
'_ctypes',
|
|
||||||
'ctypes.util'
|
|
||||||
],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[
|
|
||||||
# 排除不必要的大型库
|
|
||||||
'matplotlib',
|
|
||||||
'scipy',
|
|
||||||
'IPython',
|
|
||||||
'jupyter',
|
|
||||||
'notebook',
|
|
||||||
'tkinter',
|
|
||||||
'PyQt5',
|
|
||||||
'PyQt6',
|
|
||||||
'PySide2',
|
|
||||||
'PySide6',
|
|
||||||
'test',
|
|
||||||
'tests',
|
|
||||||
'unittest',
|
|
||||||
'doctest',
|
|
||||||
'pdb',
|
|
||||||
'pydoc',
|
|
||||||
'setuptools',
|
|
||||||
'pip',
|
|
||||||
'wheel',
|
|
||||||
'distutils'
|
|
||||||
],
|
|
||||||
win_no_prefer_redirects=False,
|
|
||||||
win_private_assemblies=False,
|
|
||||||
cipher=block_cipher,
|
|
||||||
noarchive=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 过滤和优化
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.zipfiles,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='座位分配系统',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False, # 保留符号信息以便调试
|
|
||||||
upx=False, # 不使用UPX压缩以提高兼容性
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=True,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
# 确保包含Python运行时
|
|
||||||
exclude_binaries=False,
|
|
||||||
)
|
|
@ -1,85 +0,0 @@
|
|||||||
[2025-07-01 15:33:27] ============================================================
|
|
||||||
[2025-07-01 15:33:27] 座位分配系统 - 文件校验
|
|
||||||
[2025-07-01 15:33:27] ============================================================
|
|
||||||
[2025-07-01 15:33:27] === 开始加载数据 ===
|
|
||||||
[2025-07-01 15:33:27] 正在读取人员信息文件: 人员信息.xlsx
|
|
||||||
[2025-07-01 15:33:27] 正在读取座位信息文件: 座位信息.xlsx
|
|
||||||
[2025-07-01 15:33:27] 清理文字数据中的空格...
|
|
||||||
[2025-07-01 15:33:27] ✅ 文字数据清理完成
|
|
||||||
[2025-07-01 15:33:27] ✅ 文件加载成功
|
|
||||||
[2025-07-01 15:33:27] 人员信息: 6 行 × 6 列
|
|
||||||
[2025-07-01 15:33:27] 座位信息: 17 行 × 12 列
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
=== 人员信息结构校验 ===
|
|
||||||
[2025-07-01 15:33:27] ✅ 所有必需列都存在
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
数据完整性检查:
|
|
||||||
[2025-07-01 15:33:27] ✅ 姓名 列数据完整
|
|
||||||
[2025-07-01 15:33:27] ✅ 证件类型 列数据完整
|
|
||||||
[2025-07-01 15:33:27] ✅ 证件号 列数据完整
|
|
||||||
[2025-07-01 15:33:27] ✅ 手机号 列数据完整
|
|
||||||
[2025-07-01 15:33:27] ✅ 姓名无重复
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
=== 连坐组校验 ===
|
|
||||||
[2025-07-01 15:33:27] ✅ 第 1 组: 叶一帆 (单独)
|
|
||||||
[2025-07-01 15:33:27] ✅ 第 2 组: 刘泽 (单独)
|
|
||||||
[2025-07-01 15:33:27] ✅ 第 3 组: 黄锡恩 (单独)
|
|
||||||
[2025-07-01 15:33:27] ✅ 第 4 组: 胡中, 丁逸夫 (2人连坐)
|
|
||||||
[2025-07-01 15:33:27] ✅ 第 5 组: 沈佳琰 (单独)
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
=== 座位信息结构校验 ===
|
|
||||||
[2025-07-01 15:33:27] ✅ 所有必需列都存在
|
|
||||||
[2025-07-01 15:33:27] ❌ 区域 列有 11 个空值
|
|
||||||
[2025-07-01 15:33:27] ❌ 楼层 列有 11 个空值
|
|
||||||
[2025-07-01 15:33:27] ❌ 排号 列有 11 个空值
|
|
||||||
[2025-07-01 15:33:27] ❌ 座位号 列有 11 个空值
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
座位结构分析:
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
座位结构分析:
|
|
||||||
[2025-07-01 15:33:27] ✅ 西北门入口场地A5区-场地-19排: 6 个座位完全连续 (1-6)
|
|
||||||
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
|
|
||||||
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
|
|
||||||
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
|
|
||||||
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
|
|
||||||
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
|
|
||||||
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
|
|
||||||
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
|
|
||||||
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
|
|
||||||
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
|
|
||||||
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
|
|
||||||
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
|
|
||||||
[2025-07-01 15:33:27] 📊 nan-nan-nan: 11 个座位,0 个连续段:
|
|
||||||
[2025-07-01 15:33:27] 最大连续段: 0 个座位
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
=== 容量和可行性校验 ===
|
|
||||||
[2025-07-01 15:33:27] 总人数: 6
|
|
||||||
[2025-07-01 15:33:27] 总座位数: 17
|
|
||||||
[2025-07-01 15:33:27] ✅ 座位充足: 剩余 11 个座位
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
连坐组需求分析:
|
|
||||||
[2025-07-01 15:33:27] 最大连坐组: 2 人
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
连续座位可行性分析:
|
|
||||||
[2025-07-01 15:33:27] 西北门入口场地A5区-场地-19排: 最大连续 6 个座位
|
|
||||||
[2025-07-01 15:33:27] nan-nan-nan: 最大连续 0 个座位
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
全场最大连续座位: 6 个
|
|
||||||
[2025-07-01 15:33:27] ✅ 可以容纳最大连坐组
|
|
||||||
[2025-07-01 15:33:27] 可容纳最大连坐组的排数: 1 个
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
连坐组分布:
|
|
||||||
[2025-07-01 15:33:27] 单人组: 4 个
|
|
||||||
[2025-07-01 15:33:27] 2人连坐组: 1 个
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
============================================================
|
|
||||||
[2025-07-01 15:33:27] 校验结果总结
|
|
||||||
[2025-07-01 15:33:27] ============================================================
|
|
||||||
[2025-07-01 15:33:27] 人员信息结构: ✅ 通过
|
|
||||||
[2025-07-01 15:33:27] 连坐组完整性: ✅ 通过
|
|
||||||
[2025-07-01 15:33:27] 座位信息结构: ❌ 失败
|
|
||||||
[2025-07-01 15:33:27] 容量可行性: ✅ 通过
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
总体校验结果: ❌ 存在问题
|
|
||||||
[2025-07-01 15:33:27]
|
|
||||||
⚠️ 请修复上述问题后再进行座位分配。
|
|
@ -10,6 +10,8 @@ import sys
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import datetime
|
import datetime
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
def check_dependencies():
|
def check_dependencies():
|
||||||
"""检查并安装必要的依赖包"""
|
"""检查并安装必要的依赖包"""
|
||||||
@ -192,16 +194,8 @@ def check_data_files():
|
|||||||
|
|
||||||
return True, str(personnel_file), str(seat_file)
|
return True, str(personnel_file), str(seat_file)
|
||||||
|
|
||||||
# 只有在依赖检查通过后才导入这些包
|
# 检查依赖
|
||||||
if check_dependencies():
|
if not check_dependencies():
|
||||||
try:
|
|
||||||
import pandas as pd
|
|
||||||
import numpy as np
|
|
||||||
except ImportError as e:
|
|
||||||
print(f"❌ 导入依赖包失败: {e}")
|
|
||||||
input("按Enter键退出...")
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
print("❌ 依赖检查失败")
|
print("❌ 依赖检查失败")
|
||||||
input("按Enter键退出...")
|
input("按Enter键退出...")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@ -253,11 +247,212 @@ class SeatAllocationSystem:
|
|||||||
self.personnel_file = None
|
self.personnel_file = None
|
||||||
self.seat_file = None
|
self.seat_file = None
|
||||||
|
|
||||||
|
def choose_file_mode(self):
|
||||||
|
"""选择文件识别模式"""
|
||||||
|
self.logger.log("\n=== 选择文件识别模式 ===")
|
||||||
|
self.logger.log("1. 默认文件名 - 读取 '人员信息.xlsx' 和 '座位信息.xlsx'")
|
||||||
|
self.logger.log("2. 智能识别 - 自动识别目录中的Excel文件")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
choice = input("\n请选择文件识别模式 (1/2,直接回车选择默认): ").strip()
|
||||||
|
|
||||||
|
if choice == '' or choice == '1':
|
||||||
|
self.logger.log("✅ 选择默认文件名模式")
|
||||||
|
return 'default'
|
||||||
|
elif choice == '2':
|
||||||
|
self.logger.log("✅ 选择智能识别模式")
|
||||||
|
return 'smart'
|
||||||
|
else:
|
||||||
|
print("请输入 1 或 2")
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
self.logger.log("\n用户取消操作")
|
||||||
|
return 'default'
|
||||||
|
|
||||||
|
def smart_identify_files(self):
|
||||||
|
"""智能识别Excel文件"""
|
||||||
|
self.logger.log("\n=== 智能识别Excel文件 ===")
|
||||||
|
|
||||||
|
# 获取当前目录下的所有Excel文件
|
||||||
|
excel_files = []
|
||||||
|
for ext in ['*.xlsx', '*.xls']:
|
||||||
|
excel_files.extend(Path('.').glob(ext))
|
||||||
|
|
||||||
|
if len(excel_files) < 2:
|
||||||
|
self.logger.log(f"❌ 当前目录只找到 {len(excel_files)} 个Excel文件,需要至少2个")
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
self.logger.log(f"📁 找到 {len(excel_files)} 个Excel文件:")
|
||||||
|
|
||||||
|
# 分析每个文件的内容
|
||||||
|
file_analysis = []
|
||||||
|
for file_path in excel_files:
|
||||||
|
try:
|
||||||
|
# 先读取完整文件获取真实行数
|
||||||
|
df_full = pd.read_excel(file_path)
|
||||||
|
total_rows = len(df_full)
|
||||||
|
|
||||||
|
# 再读取前5行进行列分析
|
||||||
|
df_sample = pd.read_excel(file_path, nrows=5)
|
||||||
|
analysis = {
|
||||||
|
'path': file_path,
|
||||||
|
'name': file_path.name,
|
||||||
|
'rows': total_rows, # 使用真实行数
|
||||||
|
'cols': len(df_sample.columns),
|
||||||
|
'columns': list(df_sample.columns),
|
||||||
|
'score': 0,
|
||||||
|
'type': 'unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 判断文件类型 - 改进的识别算法
|
||||||
|
columns_str = ' '.join(str(col).lower() for col in df_sample.columns)
|
||||||
|
columns_list = [str(col).lower() for col in df_sample.columns]
|
||||||
|
|
||||||
|
# 座位信息文件的强特征(优先判断)
|
||||||
|
seat_strong_keywords = ['座位id', '区域', '楼层', '排号', '座位号']
|
||||||
|
seat_strong_score = sum(1 for keyword in seat_strong_keywords if keyword in columns_str)
|
||||||
|
|
||||||
|
# 人员信息文件的强特征
|
||||||
|
personnel_strong_keywords = ['备注'] # 备注列是人员信息文件的强特征
|
||||||
|
personnel_strong_score = sum(1 for keyword in personnel_strong_keywords if keyword in columns_str)
|
||||||
|
|
||||||
|
# 如果有座位强特征,优先识别为座位文件
|
||||||
|
if seat_strong_score >= 3: # 至少包含3个座位强特征
|
||||||
|
analysis['type'] = 'seat'
|
||||||
|
analysis['score'] = seat_strong_score * 10 # 给予高权重
|
||||||
|
# 如果有人员强特征且列数较少,识别为人员文件
|
||||||
|
elif personnel_strong_score > 0 and len(df_sample.columns) <= 8:
|
||||||
|
analysis['type'] = 'personnel'
|
||||||
|
analysis['score'] = personnel_strong_score * 10
|
||||||
|
else:
|
||||||
|
# 使用原有的弱特征判断
|
||||||
|
personnel_keywords = ['姓名', '证件', '手机', 'name', 'id', 'phone']
|
||||||
|
personnel_score = sum(1 for keyword in personnel_keywords if keyword in columns_str)
|
||||||
|
|
||||||
|
seat_keywords = ['区域', '楼层', '排号', '座位', 'area', 'floor', 'row', 'seat']
|
||||||
|
seat_score = sum(1 for keyword in seat_keywords if keyword in columns_str)
|
||||||
|
|
||||||
|
# 结合列数进行判断
|
||||||
|
if len(df_sample.columns) <= 8 and personnel_score > 0:
|
||||||
|
analysis['type'] = 'personnel'
|
||||||
|
analysis['score'] = personnel_score
|
||||||
|
elif len(df_sample.columns) > 8 and seat_score > 0:
|
||||||
|
analysis['type'] = 'seat'
|
||||||
|
analysis['score'] = seat_score
|
||||||
|
elif personnel_score > seat_score:
|
||||||
|
analysis['type'] = 'personnel'
|
||||||
|
analysis['score'] = personnel_score
|
||||||
|
elif seat_score > personnel_score:
|
||||||
|
analysis['type'] = 'seat'
|
||||||
|
analysis['score'] = seat_score
|
||||||
|
|
||||||
|
file_analysis.append(analysis)
|
||||||
|
|
||||||
|
self.logger.log(f" 📄 {file_path.name}: {total_rows}行 × {len(df_sample.columns)}列")
|
||||||
|
self.logger.log(f" 列名: {', '.join(str(col) for col in df_sample.columns[:5])}{'...' if len(df_sample.columns) > 5 else ''}")
|
||||||
|
self.logger.log(f" 推测类型: {analysis['type']} (得分: {analysis['score']})")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.log(f" ❌ {file_path.name}: 读取失败 - {e}")
|
||||||
|
|
||||||
|
# 自动匹配最佳文件
|
||||||
|
personnel_files = [f for f in file_analysis if f['type'] == 'personnel']
|
||||||
|
seat_files = [f for f in file_analysis if f['type'] == 'seat']
|
||||||
|
|
||||||
|
personnel_file = None
|
||||||
|
seat_file = None
|
||||||
|
|
||||||
|
if personnel_files:
|
||||||
|
personnel_file = max(personnel_files, key=lambda x: x['score'])['path']
|
||||||
|
self.logger.log(f"\n🎯 自动识别人员信息文件: {personnel_file.name}")
|
||||||
|
|
||||||
|
if seat_files:
|
||||||
|
seat_file = max(seat_files, key=lambda x: x['score'])['path']
|
||||||
|
self.logger.log(f"🎯 自动识别座位信息文件: {seat_file.name}")
|
||||||
|
|
||||||
|
# 如果自动识别不完整,提供手动选择
|
||||||
|
if not personnel_file or not seat_file:
|
||||||
|
self.logger.log("\n⚠️ 自动识别不完整,请手动选择:")
|
||||||
|
personnel_file, seat_file = self.manual_select_files(file_analysis)
|
||||||
|
|
||||||
|
return personnel_file, seat_file
|
||||||
|
|
||||||
|
def manual_select_files(self, file_analysis):
|
||||||
|
"""手动选择文件"""
|
||||||
|
self.logger.log("\n=== 手动选择文件 ===")
|
||||||
|
|
||||||
|
print("\n可用的Excel文件:")
|
||||||
|
for i, analysis in enumerate(file_analysis):
|
||||||
|
print(f"{i+1}. {analysis['name']} ({analysis['rows']}行 × {analysis['cols']}列)")
|
||||||
|
print(f" 列名: {', '.join(str(col) for col in analysis['columns'][:3])}...")
|
||||||
|
|
||||||
|
personnel_file = None
|
||||||
|
seat_file = None
|
||||||
|
|
||||||
|
# 选择人员信息文件
|
||||||
|
while not personnel_file:
|
||||||
|
try:
|
||||||
|
choice = input(f"\n请选择人员信息文件 (1-{len(file_analysis)}): ").strip()
|
||||||
|
idx = int(choice) - 1
|
||||||
|
if 0 <= idx < len(file_analysis):
|
||||||
|
personnel_file = file_analysis[idx]['path']
|
||||||
|
self.logger.log(f"✅ 选择人员信息文件: {personnel_file.name}")
|
||||||
|
else:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
except (ValueError, KeyboardInterrupt):
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
|
# 选择座位信息文件
|
||||||
|
while not seat_file:
|
||||||
|
try:
|
||||||
|
choice = input(f"\n请选择座位信息文件 (1-{len(file_analysis)}): ").strip()
|
||||||
|
idx = int(choice) - 1
|
||||||
|
if 0 <= idx < len(file_analysis):
|
||||||
|
if file_analysis[idx]['path'] == personnel_file:
|
||||||
|
print("不能选择相同的文件,请重新选择")
|
||||||
|
continue
|
||||||
|
seat_file = file_analysis[idx]['path']
|
||||||
|
self.logger.log(f"✅ 选择座位信息文件: {seat_file.name}")
|
||||||
|
else:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
except (ValueError, KeyboardInterrupt):
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
|
return personnel_file, seat_file
|
||||||
|
|
||||||
def set_data_files(self, personnel_file, seat_file):
|
def set_data_files(self, personnel_file, seat_file):
|
||||||
"""设置数据文件路径"""
|
"""设置数据文件路径"""
|
||||||
self.personnel_file = personnel_file
|
self.personnel_file = personnel_file
|
||||||
self.seat_file = seat_file
|
self.seat_file = seat_file
|
||||||
|
|
||||||
|
def identify_and_set_files(self):
|
||||||
|
"""识别并设置数据文件"""
|
||||||
|
file_mode = self.choose_file_mode()
|
||||||
|
|
||||||
|
if file_mode == 'default':
|
||||||
|
# 默认文件名模式
|
||||||
|
personnel_file = Path('人员信息.xlsx')
|
||||||
|
seat_file = Path('座位信息.xlsx')
|
||||||
|
|
||||||
|
if not personnel_file.exists():
|
||||||
|
self.logger.log(f"❌ 错误: {personnel_file} 文件不存在")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not seat_file.exists():
|
||||||
|
self.logger.log(f"❌ 错误: {seat_file} 文件不存在")
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
# 智能识别模式
|
||||||
|
personnel_file, seat_file = self.smart_identify_files()
|
||||||
|
if not personnel_file or not seat_file:
|
||||||
|
self.logger.log("❌ 文件识别失败")
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.set_data_files(personnel_file, seat_file)
|
||||||
|
return True
|
||||||
|
|
||||||
def load_data(self):
|
def load_data(self):
|
||||||
"""加载人员信息和座位信息数据"""
|
"""加载人员信息和座位信息数据"""
|
||||||
self.logger.log("=== 开始加载数据 ===")
|
self.logger.log("=== 开始加载数据 ===")
|
||||||
@ -411,15 +606,35 @@ class SeatAllocationSystem:
|
|||||||
self.logger.log(f"✅ {col} 列数据完整")
|
self.logger.log(f"✅ {col} 列数据完整")
|
||||||
validation_results.append(True)
|
validation_results.append(True)
|
||||||
|
|
||||||
# 检查重复姓名
|
# 检查重复人员(姓名+身份证号组合)
|
||||||
duplicate_names = self.personnel_df[self.personnel_df['姓名'].duplicated()]
|
self.logger.log("\n重复人员检查:")
|
||||||
if not duplicate_names.empty:
|
if '姓名' in self.personnel_df.columns and '证件号' in self.personnel_df.columns:
|
||||||
self.logger.log(f"⚠️ 发现重复姓名: {duplicate_names['姓名'].tolist()}")
|
# 创建姓名+证件号的组合标识
|
||||||
|
self.personnel_df['人员标识'] = self.personnel_df['姓名'].astype(str) + '_' + self.personnel_df['证件号'].astype(str)
|
||||||
|
duplicate_persons = self.personnel_df[self.personnel_df['人员标识'].duplicated()]
|
||||||
|
|
||||||
|
if not duplicate_persons.empty:
|
||||||
|
self.logger.log(f"⚠️ 发现重复人员:")
|
||||||
|
for _, person in duplicate_persons.iterrows():
|
||||||
|
self.logger.log(f" {person['姓名']} (证件号: {person['证件号']})")
|
||||||
validation_results.append(False)
|
validation_results.append(False)
|
||||||
else:
|
else:
|
||||||
self.logger.log("✅ 姓名无重复")
|
self.logger.log("✅ 无重复人员(姓名+证件号组合检查)")
|
||||||
validation_results.append(True)
|
validation_results.append(True)
|
||||||
|
|
||||||
|
# 单独检查重名情况(仅提示,不影响校验结果)
|
||||||
|
duplicate_names = self.personnel_df[self.personnel_df['姓名'].duplicated(keep=False)]
|
||||||
|
if not duplicate_names.empty:
|
||||||
|
self.logger.log(f"📝 发现重名人员(但证件号不同,允许通过):")
|
||||||
|
name_groups = duplicate_names.groupby('姓名')
|
||||||
|
for name, group in name_groups:
|
||||||
|
self.logger.log(f" 姓名: {name}")
|
||||||
|
for _, person in group.iterrows():
|
||||||
|
self.logger.log(f" 证件号: {person['证件号']}")
|
||||||
|
else:
|
||||||
|
self.logger.log("❌ 缺少姓名或证件号列,无法进行重复检查")
|
||||||
|
validation_results.append(False)
|
||||||
|
|
||||||
return all(validation_results)
|
return all(validation_results)
|
||||||
|
|
||||||
def validate_seating_groups(self):
|
def validate_seating_groups(self):
|
||||||
@ -693,10 +908,73 @@ class SeatAllocationSystem:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def analyze_seating_requirements(self):
|
def analyze_seating_requirements(self, use_phone_grouping=False):
|
||||||
"""分析人员连坐需求"""
|
"""分析人员连坐需求"""
|
||||||
self.logger.log("\n=== 人员连坐需求分析 ===")
|
self.logger.log("\n=== 人员连坐需求分析 ===")
|
||||||
|
|
||||||
|
if use_phone_grouping:
|
||||||
|
self.logger.log("🔍 使用手机号分组模式")
|
||||||
|
return self.analyze_seating_by_phone()
|
||||||
|
else:
|
||||||
|
self.logger.log("🔍 使用备注分组模式")
|
||||||
|
return self.analyze_seating_by_remark()
|
||||||
|
|
||||||
|
def analyze_seating_by_phone(self):
|
||||||
|
"""基于手机号分析连坐需求"""
|
||||||
|
self.logger.log("根据相同手机号识别连坐组...")
|
||||||
|
|
||||||
|
# 按手机号分组
|
||||||
|
phone_groups = self.personnel_df.groupby('手机号')
|
||||||
|
self.seating_groups = []
|
||||||
|
|
||||||
|
for phone, group in phone_groups:
|
||||||
|
group_members = [row for _, row in group.iterrows()]
|
||||||
|
group_size = len(group_members)
|
||||||
|
|
||||||
|
if group_size == 1:
|
||||||
|
# 单人组
|
||||||
|
self.seating_groups.append({
|
||||||
|
'type': 'single',
|
||||||
|
'size': 1,
|
||||||
|
'members': group_members,
|
||||||
|
'leader': group_members[0]['姓名'],
|
||||||
|
'grouping_method': 'phone'
|
||||||
|
})
|
||||||
|
self.logger.log(f"📱 单人: {group_members[0]['姓名']} (手机号: {phone})")
|
||||||
|
else:
|
||||||
|
# 连坐组
|
||||||
|
leader_name = group_members[0]['姓名']
|
||||||
|
member_names = [member['姓名'] for member in group_members]
|
||||||
|
|
||||||
|
self.seating_groups.append({
|
||||||
|
'type': 'group',
|
||||||
|
'size': group_size,
|
||||||
|
'members': group_members,
|
||||||
|
'leader': leader_name,
|
||||||
|
'grouping_method': 'phone'
|
||||||
|
})
|
||||||
|
self.logger.log(f"📱 连坐组: {', '.join(member_names)} (手机号: {phone}, {group_size}人)")
|
||||||
|
|
||||||
|
# 统计
|
||||||
|
size_stats = {}
|
||||||
|
for group in self.seating_groups:
|
||||||
|
size = group['size']
|
||||||
|
size_stats[size] = size_stats.get(size, 0) + 1
|
||||||
|
|
||||||
|
self.logger.log(f"\n基于手机号识别出 {len(self.seating_groups)} 个座位组:")
|
||||||
|
for size in sorted(size_stats.keys()):
|
||||||
|
count = size_stats[size]
|
||||||
|
if size == 1:
|
||||||
|
self.logger.log(f" 单人组: {count} 个")
|
||||||
|
else:
|
||||||
|
self.logger.log(f" {size}人连坐组: {count} 个")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def analyze_seating_by_remark(self):
|
||||||
|
"""基于备注分析连坐需求"""
|
||||||
|
self.logger.log("根据备注数字识别连坐组...")
|
||||||
|
|
||||||
self.seating_groups = []
|
self.seating_groups = []
|
||||||
i = 0
|
i = 0
|
||||||
|
|
||||||
@ -710,7 +988,8 @@ class SeatAllocationSystem:
|
|||||||
'type': 'single',
|
'type': 'single',
|
||||||
'size': 1,
|
'size': 1,
|
||||||
'members': [person],
|
'members': [person],
|
||||||
'leader': person['姓名']
|
'leader': person['姓名'],
|
||||||
|
'grouping_method': 'remark'
|
||||||
})
|
})
|
||||||
i += 1
|
i += 1
|
||||||
else:
|
else:
|
||||||
@ -727,7 +1006,8 @@ class SeatAllocationSystem:
|
|||||||
'type': 'group' if group_size > 1 else 'single',
|
'type': 'group' if group_size > 1 else 'single',
|
||||||
'size': len(group_members),
|
'size': len(group_members),
|
||||||
'members': group_members,
|
'members': group_members,
|
||||||
'leader': person['姓名']
|
'leader': person['姓名'],
|
||||||
|
'grouping_method': 'remark'
|
||||||
})
|
})
|
||||||
|
|
||||||
i += group_size
|
i += group_size
|
||||||
@ -738,7 +1018,7 @@ class SeatAllocationSystem:
|
|||||||
size = group['size']
|
size = group['size']
|
||||||
size_stats[size] = size_stats.get(size, 0) + 1
|
size_stats[size] = size_stats.get(size, 0) + 1
|
||||||
|
|
||||||
self.logger.log(f"总共识别出 {len(self.seating_groups)} 个座位组:")
|
self.logger.log(f"\n基于备注识别出 {len(self.seating_groups)} 个座位组:")
|
||||||
for size in sorted(size_stats.keys()):
|
for size in sorted(size_stats.keys()):
|
||||||
count = size_stats[size]
|
count = size_stats[size]
|
||||||
if size == 1:
|
if size == 1:
|
||||||
@ -861,13 +1141,14 @@ class SeatAllocationSystem:
|
|||||||
person_info = seating_group['members'][0]
|
person_info = seating_group['members'][0]
|
||||||
seat_index = seat_info['index']
|
seat_index = seat_info['index']
|
||||||
|
|
||||||
# 更新座位信息,确保数据类型正确
|
# 覆盖座位表中的人员信息列
|
||||||
seat_df_copy.loc[seat_index, '姓名'] = str(person_info['姓名']).strip()
|
seat_df_copy.loc[seat_index, '姓名'] = str(person_info['姓名']).strip()
|
||||||
seat_df_copy.loc[seat_index, '证件类型'] = str(person_info['证件类型']).strip()
|
seat_df_copy.loc[seat_index, '证件类型'] = str(person_info['证件类型']).strip()
|
||||||
seat_df_copy.loc[seat_index, '证件号'] = str(person_info['证件号']).strip()
|
seat_df_copy.loc[seat_index, '证件号'] = str(person_info['证件号']).strip()
|
||||||
seat_df_copy.loc[seat_index, '手机国家号'] = person_info.get('Unnamed: 3', 86)
|
seat_df_copy.loc[seat_index, '手机国家号'] = person_info.get('Unnamed: 3', 86)
|
||||||
seat_df_copy.loc[seat_index, '手机号'] = str(person_info['手机号']).strip()
|
seat_df_copy.loc[seat_index, '手机号'] = str(person_info['手机号']).strip()
|
||||||
seat_df_copy.loc[seat_index, '签发地/国籍'] = str(person_info.get('备注', '')).strip()
|
# 只有分配备注放在第13列
|
||||||
|
seat_df_copy.loc[seat_index, '分配备注'] = str(person_info.get('备注', '')).strip()
|
||||||
|
|
||||||
assignment_log.append({
|
assignment_log.append({
|
||||||
'组号': group_idx + 1,
|
'组号': group_idx + 1,
|
||||||
@ -945,7 +1226,8 @@ class SeatAllocationSystem:
|
|||||||
seat_df_copy.loc[seat_index, '证件号'] = str(person_info['证件号']).strip()
|
seat_df_copy.loc[seat_index, '证件号'] = str(person_info['证件号']).strip()
|
||||||
seat_df_copy.loc[seat_index, '手机国家号'] = person_info.get('Unnamed: 3', 86)
|
seat_df_copy.loc[seat_index, '手机国家号'] = person_info.get('Unnamed: 3', 86)
|
||||||
seat_df_copy.loc[seat_index, '手机号'] = str(person_info['手机号']).strip()
|
seat_df_copy.loc[seat_index, '手机号'] = str(person_info['手机号']).strip()
|
||||||
seat_df_copy.loc[seat_index, '签发地/国籍'] = str(person_info.get('备注', '')).strip()
|
# 只有分配备注放在第13列
|
||||||
|
seat_df_copy.loc[seat_index, '分配备注'] = str(person_info.get('备注', '')).strip()
|
||||||
|
|
||||||
assignment_log.append({
|
assignment_log.append({
|
||||||
'组号': group_idx + 1,
|
'组号': group_idx + 1,
|
||||||
@ -990,6 +1272,15 @@ class SeatAllocationSystem:
|
|||||||
output_file = self.logger.get_log_path('座位信息_最终分配.xlsx')
|
output_file = self.logger.get_log_path('座位信息_最终分配.xlsx')
|
||||||
seat_df_result.to_excel(output_file, index=False)
|
seat_df_result.to_excel(output_file, index=False)
|
||||||
self.logger.log(f"\n座位分配结果已保存到: {output_file}")
|
self.logger.log(f"\n座位分配结果已保存到: {output_file}")
|
||||||
|
self.logger.log(f"📋 人员信息已覆盖到座位表对应列:")
|
||||||
|
self.logger.log(f" 第6列: 姓名")
|
||||||
|
self.logger.log(f" 第7列: 证件类型")
|
||||||
|
self.logger.log(f" 第8列: 证件号")
|
||||||
|
self.logger.log(f" 第9列: 手机国家号")
|
||||||
|
self.logger.log(f" 第10列: 手机号")
|
||||||
|
self.logger.log(f"📝 分配备注信息:")
|
||||||
|
self.logger.log(f" 第13列: 分配备注(新增列,不覆盖原数据)")
|
||||||
|
self.logger.log(f"💡 座位基本信息(第1-5列)保持不变")
|
||||||
|
|
||||||
# 保存分配日志
|
# 保存分配日志
|
||||||
if assignment_log:
|
if assignment_log:
|
||||||
@ -1067,6 +1358,10 @@ class SeatAllocationSystem:
|
|||||||
self.logger.log("座位分配系统 - 文件校验")
|
self.logger.log("座位分配系统 - 文件校验")
|
||||||
self.logger.log("=" * 60)
|
self.logger.log("=" * 60)
|
||||||
|
|
||||||
|
# 识别并设置数据文件
|
||||||
|
if not self.identify_and_set_files():
|
||||||
|
return False
|
||||||
|
|
||||||
# 加载数据
|
# 加载数据
|
||||||
if not self.load_data():
|
if not self.load_data():
|
||||||
return False
|
return False
|
||||||
@ -1120,8 +1415,11 @@ class SeatAllocationSystem:
|
|||||||
self.logger.log("开始座位分配")
|
self.logger.log("开始座位分配")
|
||||||
self.logger.log("=" * 60)
|
self.logger.log("=" * 60)
|
||||||
|
|
||||||
|
# 选择分组方式
|
||||||
|
grouping_method = self.choose_grouping_method()
|
||||||
|
|
||||||
# 分析人员连坐需求
|
# 分析人员连坐需求
|
||||||
if not self.analyze_seating_requirements():
|
if not self.analyze_seating_requirements(use_phone_grouping=(grouping_method == 'phone')):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 高级座位结构分析
|
# 高级座位结构分析
|
||||||
@ -1139,6 +1437,29 @@ class SeatAllocationSystem:
|
|||||||
self.logger.log("\n❌ 座位分配失败!")
|
self.logger.log("\n❌ 座位分配失败!")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def choose_grouping_method(self):
|
||||||
|
"""选择分组方式"""
|
||||||
|
self.logger.log("\n=== 选择连坐分组方式 ===")
|
||||||
|
self.logger.log("1. 备注分组 - 根据备注数字识别连坐组(默认)")
|
||||||
|
self.logger.log("2. 手机号分组 - 根据相同手机号识别连坐组")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
choice = input("\n请选择分组方式 (1/2,直接回车选择默认): ").strip()
|
||||||
|
|
||||||
|
if choice == '' or choice == '1':
|
||||||
|
self.logger.log("✅ 选择备注分组模式")
|
||||||
|
return 'remark'
|
||||||
|
elif choice == '2':
|
||||||
|
self.logger.log("✅ 选择手机号分组模式")
|
||||||
|
return 'phone'
|
||||||
|
else:
|
||||||
|
print("请输入 1 或 2")
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
self.logger.log("\n用户取消操作")
|
||||||
|
return 'remark'
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""主函数"""
|
"""主函数"""
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
|
232
simple_build.py
232
simple_build.py
@ -1,232 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
简化构建脚本
|
|
||||||
在当前平台构建可执行文件
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
import platform
|
|
||||||
import shutil
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def get_platform_info():
|
|
||||||
"""获取平台信息"""
|
|
||||||
system = platform.system()
|
|
||||||
machine = platform.machine().lower()
|
|
||||||
|
|
||||||
if system == 'Windows':
|
|
||||||
if machine in ['amd64', 'x86_64']:
|
|
||||||
return 'windows_x64'
|
|
||||||
elif machine in ['i386', 'i686', 'x86']:
|
|
||||||
return 'windows_x86'
|
|
||||||
else:
|
|
||||||
return f'windows_{machine}'
|
|
||||||
elif system == 'Darwin':
|
|
||||||
if machine in ['arm64', 'aarch64']:
|
|
||||||
return 'macos_arm64'
|
|
||||||
else:
|
|
||||||
return 'macos_x64'
|
|
||||||
elif system == 'Linux':
|
|
||||||
return f'linux_{machine}'
|
|
||||||
else:
|
|
||||||
return f'{system.lower()}_{machine}'
|
|
||||||
|
|
||||||
def install_dependencies():
|
|
||||||
"""安装依赖"""
|
|
||||||
print("安装依赖包...")
|
|
||||||
packages = ['pandas', 'openpyxl', 'pyinstaller']
|
|
||||||
|
|
||||||
for package in packages:
|
|
||||||
try:
|
|
||||||
__import__(package)
|
|
||||||
print(f"✅ {package} 已安装")
|
|
||||||
except ImportError:
|
|
||||||
print(f"📦 安装 {package}...")
|
|
||||||
try:
|
|
||||||
subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
|
|
||||||
print(f"✅ {package} 安装成功")
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
print(f"❌ {package} 安装失败")
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def build_executable():
|
|
||||||
"""构建可执行文件"""
|
|
||||||
print("\n开始构建可执行文件...")
|
|
||||||
|
|
||||||
# 获取平台信息
|
|
||||||
platform_name = get_platform_info()
|
|
||||||
exe_name = f"座位分配系统_{platform_name}"
|
|
||||||
|
|
||||||
print(f"目标平台: {platform_name}")
|
|
||||||
print(f"输出文件: {exe_name}")
|
|
||||||
|
|
||||||
# 清理之前的构建
|
|
||||||
if os.path.exists('dist'):
|
|
||||||
shutil.rmtree('dist')
|
|
||||||
if os.path.exists('build'):
|
|
||||||
shutil.rmtree('build')
|
|
||||||
|
|
||||||
# 构建命令
|
|
||||||
cmd = [
|
|
||||||
sys.executable, '-m', 'PyInstaller',
|
|
||||||
'--onefile',
|
|
||||||
'--console',
|
|
||||||
'--clean',
|
|
||||||
'--name', exe_name,
|
|
||||||
'seat_allocation_system.py'
|
|
||||||
]
|
|
||||||
|
|
||||||
try:
|
|
||||||
print(f"执行命令: {' '.join(cmd)}")
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("✅ 构建成功!")
|
|
||||||
|
|
||||||
# 检查生成的文件
|
|
||||||
if platform.system() == 'Windows':
|
|
||||||
exe_path = Path('dist') / f'{exe_name}.exe'
|
|
||||||
else:
|
|
||||||
exe_path = Path('dist') / exe_name
|
|
||||||
|
|
||||||
if exe_path.exists():
|
|
||||||
file_size = exe_path.stat().st_size / (1024 * 1024) # MB
|
|
||||||
print(f"生成文件: {exe_path}")
|
|
||||||
print(f"文件大小: {file_size:.1f} MB")
|
|
||||||
return True, exe_path
|
|
||||||
else:
|
|
||||||
print("❌ 未找到生成的文件")
|
|
||||||
return False, None
|
|
||||||
else:
|
|
||||||
print("❌ 构建失败!")
|
|
||||||
print("错误输出:")
|
|
||||||
print(result.stderr)
|
|
||||||
return False, None
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 构建过程中出现错误: {e}")
|
|
||||||
return False, None
|
|
||||||
|
|
||||||
def create_distribution():
|
|
||||||
"""创建分发包"""
|
|
||||||
print("\n创建分发包...")
|
|
||||||
|
|
||||||
platform_name = get_platform_info()
|
|
||||||
package_name = f"座位分配系统_{platform_name}_分发包"
|
|
||||||
package_dir = Path(package_name)
|
|
||||||
|
|
||||||
# 创建分发目录
|
|
||||||
if package_dir.exists():
|
|
||||||
shutil.rmtree(package_dir)
|
|
||||||
package_dir.mkdir()
|
|
||||||
|
|
||||||
# 复制可执行文件
|
|
||||||
dist_dir = Path('dist')
|
|
||||||
if dist_dir.exists():
|
|
||||||
for file in dist_dir.iterdir():
|
|
||||||
if file.is_file():
|
|
||||||
shutil.copy2(file, package_dir)
|
|
||||||
print(f"复制文件: {file.name}")
|
|
||||||
|
|
||||||
# 复制示例文件
|
|
||||||
if Path('人员信息.xlsx').exists():
|
|
||||||
shutil.copy2('人员信息.xlsx', package_dir / '人员信息_示例.xlsx')
|
|
||||||
print("复制示例文件: 人员信息_示例.xlsx")
|
|
||||||
|
|
||||||
if Path('座位信息.xlsx').exists():
|
|
||||||
shutil.copy2('座位信息.xlsx', package_dir / '座位信息_示例.xlsx')
|
|
||||||
print("复制示例文件: 座位信息_示例.xlsx")
|
|
||||||
|
|
||||||
# 创建使用说明
|
|
||||||
readme_content = f"""座位分配系统 使用说明
|
|
||||||
|
|
||||||
平台: {platform_name}
|
|
||||||
构建时间: {platform.platform()}
|
|
||||||
|
|
||||||
使用方法:
|
|
||||||
1. 准备Excel文件
|
|
||||||
- 人员信息.xlsx: 包含姓名、证件信息、备注等
|
|
||||||
- 座位信息.xlsx: 包含区域、楼层、排号、座位号等
|
|
||||||
|
|
||||||
2. 运行程序
|
|
||||||
- 将可执行文件放在Excel文件同一目录
|
|
||||||
- 双击运行可执行文件
|
|
||||||
- 等待处理完成
|
|
||||||
|
|
||||||
3. 查看结果
|
|
||||||
- 座位信息_最终分配.xlsx: 分配结果
|
|
||||||
- 最终座位分配日志.xlsx: 详细记录
|
|
||||||
- seat_allocation_log.txt: 运行日志
|
|
||||||
|
|
||||||
功能特点:
|
|
||||||
- 支持1-10人连坐需求
|
|
||||||
- 自动处理不连续座位
|
|
||||||
- 完整的数据校验
|
|
||||||
- 详细的分配日志
|
|
||||||
|
|
||||||
注意事项:
|
|
||||||
- 确保Excel文件格式正确
|
|
||||||
- 备注数字表示连坐人数
|
|
||||||
- 运行时会在同目录生成结果文件
|
|
||||||
"""
|
|
||||||
|
|
||||||
with open(package_dir / '使用说明.txt', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(readme_content)
|
|
||||||
|
|
||||||
print(f"✅ 分发包已创建: {package_dir}")
|
|
||||||
return package_dir
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主函数"""
|
|
||||||
print("座位分配系统 - 简化构建工具")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# 显示系统信息
|
|
||||||
print(f"系统: {platform.system()} {platform.release()}")
|
|
||||||
print(f"架构: {platform.machine()}")
|
|
||||||
print(f"Python: {sys.version}")
|
|
||||||
|
|
||||||
# 检查主程序文件
|
|
||||||
if not os.path.exists('seat_allocation_system.py'):
|
|
||||||
print("❌ 未找到 seat_allocation_system.py 文件")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 安装依赖
|
|
||||||
if not install_dependencies():
|
|
||||||
print("❌ 依赖安装失败")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 构建可执行文件
|
|
||||||
success, exe_path = build_executable()
|
|
||||||
if not success:
|
|
||||||
print("❌ 构建失败")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 创建分发包
|
|
||||||
package_dir = create_distribution()
|
|
||||||
|
|
||||||
print("\n🎉 构建完成!")
|
|
||||||
print(f"✅ 可执行文件: {exe_path}")
|
|
||||||
print(f"✅ 分发包: {package_dir}")
|
|
||||||
|
|
||||||
# 平台特定说明
|
|
||||||
if platform.system() == 'Windows':
|
|
||||||
print("\n📝 Windows使用说明:")
|
|
||||||
print("- 可以直接在Windows系统上运行")
|
|
||||||
print("- 确保安装了Visual C++ Redistributable")
|
|
||||||
elif platform.system() == 'Darwin':
|
|
||||||
print("\n📝 macOS使用说明:")
|
|
||||||
print("- 只能在macOS系统上运行")
|
|
||||||
print("- 如需Windows版本,请在Windows系统上构建")
|
|
||||||
print("- 或使用GitHub Actions自动构建")
|
|
||||||
else:
|
|
||||||
print(f"\n📝 {platform.system()}使用说明:")
|
|
||||||
print("- 只能在相同系统上运行")
|
|
||||||
print("- 如需其他平台版本,请在对应系统上构建")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
116
运行座位分配系统.bat
116
运行座位分配系统.bat
@ -1,116 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001 >nul
|
|
||||||
title 座位分配系统 v2.0
|
|
||||||
|
|
||||||
:: 设置颜色
|
|
||||||
color 0F
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ==========================================
|
|
||||||
echo 座位分配系统 v2.0
|
|
||||||
echo ==========================================
|
|
||||||
echo.
|
|
||||||
echo 正在检查运行环境...
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 检查可执行文件是否存在
|
|
||||||
if not exist "座位分配系统.exe" (
|
|
||||||
echo [错误] 未找到 座位分配系统.exe 文件
|
|
||||||
echo.
|
|
||||||
echo 请确保以下文件在同一目录下:
|
|
||||||
echo - 座位分配系统.exe
|
|
||||||
echo - 人员信息.xlsx
|
|
||||||
echo - 座位信息.xlsx
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [成功] 程序文件检查通过
|
|
||||||
|
|
||||||
:: 检查Excel文件
|
|
||||||
echo 正在扫描Excel文件...
|
|
||||||
|
|
||||||
:: 计算xlsx文件数量
|
|
||||||
set count=0
|
|
||||||
for %%f in (*.xlsx) do (
|
|
||||||
:: 排除输出和示例文件
|
|
||||||
echo "%%f" | findstr /v /i "最终分配\|分配日志\|示例\|temp\|backup" >nul
|
|
||||||
if not errorlevel 1 (
|
|
||||||
set /a count+=1
|
|
||||||
echo 发现文件: %%f
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if %count% equ 0 (
|
|
||||||
echo.
|
|
||||||
echo [错误] 未找到Excel数据文件
|
|
||||||
echo.
|
|
||||||
echo 请确保当前目录下有Excel数据文件:
|
|
||||||
echo 1. 人员信息文件 (5-6列): 姓名、证件类型、证件号、手机号、备注等
|
|
||||||
echo 2. 座位信息文件 (10+列): 区域、楼层、排号、座位号等
|
|
||||||
echo.
|
|
||||||
echo 提示: 程序会自动识别文件类型,无需固定文件名
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
if %count% gtr 2 (
|
|
||||||
echo.
|
|
||||||
echo [警告] 发现超过2个Excel文件
|
|
||||||
echo 为避免识别混淆,请确保目录下只有2个数据文件
|
|
||||||
echo 程序会自动排除输出文件和示例文件
|
|
||||||
echo.
|
|
||||||
echo 当前Excel文件:
|
|
||||||
for %%f in (*.xlsx) do echo %%f
|
|
||||||
echo.
|
|
||||||
echo 请移除多余文件后重试
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [成功] 找到 %count% 个Excel文件,程序将自动识别文件类型
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ==========================================
|
|
||||||
echo 开始运行程序
|
|
||||||
echo ==========================================
|
|
||||||
echo.
|
|
||||||
echo 所有检查通过,正在启动座位分配系统...
|
|
||||||
echo 请等待程序运行完成...
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 运行程序并捕获错误
|
|
||||||
"座位分配系统.exe"
|
|
||||||
set "exit_code=%ERRORLEVEL%"
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ==========================================
|
|
||||||
echo 运行结果
|
|
||||||
echo ==========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
if %exit_code% equ 0 (
|
|
||||||
echo [成功] 程序运行成功!
|
|
||||||
echo.
|
|
||||||
echo 输出文件说明:
|
|
||||||
echo - 座位信息_最终分配.xlsx: 最终座位分配结果
|
|
||||||
echo - 最终座位分配日志.xlsx: 详细分配记录
|
|
||||||
echo - seat_allocation_log.txt: 运行日志文件
|
|
||||||
echo.
|
|
||||||
echo 您可以用Excel打开xlsx文件查看结果
|
|
||||||
) else (
|
|
||||||
echo [错误] 程序运行出现错误 (错误代码: %exit_code%)
|
|
||||||
echo.
|
|
||||||
echo 可能的原因:
|
|
||||||
echo 1. 数据文件格式不正确
|
|
||||||
echo 2. 文件权限不足
|
|
||||||
echo 3. 磁盘空间不足
|
|
||||||
echo 4. 缺少必要的依赖
|
|
||||||
echo.
|
|
||||||
echo 请查看 seat_allocation_log.txt 获取详细错误信息
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo 按任意键退出...
|
|
||||||
pause >nul
|
|
Loading…
Reference in New Issue
Block a user