add sumasen_lib

This commit is contained in:
hayano
2024-11-03 10:49:42 +00:00
parent e992e834da
commit 338643b0d7
14 changed files with 988 additions and 0 deletions

View File

@ -0,0 +1,276 @@
# sumaexcel/excel.py
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
import pandas as pd
from typing import Optional, Dict, List, Union, Any
import os
import shutil
from datetime import datetime
from typing import Optional, Dict, Any, Union, Tuple
from pathlib import Path
from .styles import StyleManager
from .merge import MergeManager
from .image import ImageManager
from .conditional import ConditionalFormatManager
from .page import PageManager, PaperSizes
class SumasenExcel:
"""Enhanced Excel handling class with extended functionality"""
def __init__(self, debug: bool = False):
self.debug = debug
self.workbook = None
self.template_filepath = None
self.output_filepath = None
self.current_sheet = None
self._style_manager = None
self._merge_manager = None
self._image_manager = None
self._conditional_manager = None
self._page_manager = None
def init(self, username: str, project_id: str, document: str,
lang: str = "jp", docbase: str = "./docbase") -> Dict[str, str]:
"""Initialize Excel document with basic settings
Args:
username: User name for file operations
project_id: Project identifier
document: Document name
lang: Language code (default: "jp")
docbase: Base directory for documents (default: "./docbase")
Returns:
Dict with status ("ACK"/"NCK") and optional error message
"""
try:
self.username = username
self.project_id = project_id
self.language = lang
# Setup directory structure
self.docpath = docbase
self.docpath2 = f"{docbase}/{project_id}"
# Create directories if they don't exist
for path in [docbase, self.docpath2]:
if not os.path.exists(path):
os.makedirs(path, mode=0o755)
# Load template
inifile = f"{document}.ini"
self.inifilepath = f"{self.docpath2}/{inifile}"
if not os.path.exists(self.inifilepath):
return {"status": "NCK", "message": f"INI file not found: {self.inifilepath}"}
# Load template workbook
template_file = self._get_ini_param("basic", f"templatefile_{lang}")
self.template_filepath = f"{self.docpath2}/{template_file}"
if not os.path.exists(self.template_filepath):
# Copy from default if not exists
default_template = f"{self.docpath}/{template_file}"
shutil.copy2(default_template, self.template_filepath)
self.workbook = openpyxl.load_workbook(self.template_filepath)
return {"status": "ACK"}
except Exception as e:
return {"status": "NCK", "message": str(e)}
def _get_ini_param(self, section: str, param: str) -> Optional[str]:
"""Get parameter from INI file
Args:
section: INI file section
param: Parameter name
Returns:
Parameter value or None if not found
"""
try:
# Use configparser to handle INI files
import configparser
config = configparser.ConfigParser()
config.read(self.inifilepath)
return config[section][param]
except:
return None
def make_report(self, db, data_rec: Dict[str, Any],
out_filename: Optional[str] = None,
screen_index: int = 0) -> None:
"""Generate Excel report from template
Args:
db: Database connection
data_rec: Data records to populate report
out_filename: Optional output filename
screen_index: Screen index for multi-screen reports
"""
# Get output filename
if out_filename:
outfile = f"{out_filename}_{self._get_ini_param('basic', 'doc_file')}"
else:
outfile = self._get_ini_param('basic', 'doc_file')
self.output_filepath = f"{self.docpath2}/{outfile}"
# Process sections
sections = self._get_ini_param('basic', 'sections')
if not sections:
return
for section in sections.split(','):
self._process_section(section, db, data_rec, screen_index)
# Save workbook
self.workbook.save(self.output_filepath)
def _process_section(self, section: str, db, data_rec: Dict[str, Any],
screen_index: int) -> None:
"""Process individual section of report
Args:
section: Section name
db: Database connection
data_rec: Data records
screen_index: Screen index
"""
# Get template sheet
sheet_orig = self._get_ini_param(section, 'sheet')
sheet_name = self._get_ini_param(section, f"sheetname_{self.language}")
if not sheet_orig or not sheet_name:
return
# Copy template sheet
template_sheet = self.workbook[sheet_orig]
new_sheet = self.workbook.copy_worksheet(template_sheet)
new_sheet.title = sheet_name
# Process groups
groups = self._get_ini_param(section, 'groups')
if groups:
for group in groups.split(','):
self._process_group(new_sheet, section, group, db, data_rec, screen_index)
def _process_group(self, sheet, section: str, group: str,
db, data_rec: Dict[str, Any], screen_index: int) -> None:
"""Process group within section
Args:
sheet: Worksheet to process
section: Section name
group: Group name
db: Database connection
data_rec: Data records
screen_index: Screen index
"""
pass # Implementation details will follow
def init_sheet(self, sheet_name: str) -> None:
"""Initialize worksheet and managers"""
self.current_sheet = self.workbook[sheet_name]
self._style_manager = StyleManager()
self._merge_manager = MergeManager(self.current_sheet)
self._image_manager = ImageManager(self.current_sheet)
self._conditional_manager = ConditionalFormatManager(self.current_sheet)
self._page_manager = PageManager(self.current_sheet)
# Style operations
def apply_style(
self,
cell_range: str,
font: Dict[str, Any] = None,
fill: Dict[str, Any] = None,
border: Dict[str, Any] = None,
alignment: Dict[str, Any] = None
) -> None:
"""Apply styles to cell range"""
for row in self.current_sheet[cell_range]:
for cell in row:
if font:
cell.font = self._style_manager.create_font(**font)
if fill:
cell.fill = self._style_manager.create_fill(**fill)
if border:
cell.border = self._style_manager.create_border(**border)
if alignment:
cell.alignment = self._style_manager.create_alignment(**alignment)
# Merge operations
def merge_range(
self,
start_row: int,
start_col: int,
end_row: int,
end_col: int
) -> None:
"""Merge cell range"""
self._merge_manager.merge_cells(start_row, start_col, end_row, end_col)
# Image operations
def add_image(
self,
image_path: Union[str, Path],
position: Tuple[int, int],
size: Optional[Tuple[int, int]] = None
) -> None:
"""Add image to worksheet"""
self._image_manager.add_image(image_path, position, size)
# Conditional formatting
def add_conditional_format(
self,
cell_range: str,
format_type: str,
**kwargs
) -> None:
"""Add conditional formatting"""
if format_type == 'color_scale':
self._conditional_manager.add_color_scale(cell_range, **kwargs)
elif format_type == 'data_bar':
self._conditional_manager.add_data_bar(cell_range, **kwargs)
elif format_type == 'icon_set':
self._conditional_manager.add_icon_set(cell_range, **kwargs)
elif format_type == 'custom':
self._conditional_manager.add_custom_rule(cell_range, **kwargs)
# Page setup
def setup_page(
self,
orientation: str = 'portrait',
paper_size: int = PaperSizes.A4,
margins: Dict[str, float] = None,
header_footer: Dict[str, Any] = None
) -> None:
"""Configure page setup"""
self._page_manager.set_page_setup(
orientation=orientation,
paper_size=paper_size
)
if margins:
self._page_manager.set_margins(**margins)
if header_footer:
self._page_manager.set_header_footer(**header_footer)
def cleanup(self) -> None:
"""Cleanup temporary files"""
if self._image_manager:
self._image_manager.cleanup()
def __del__(self):
"""Destructor"""
self.cleanup()