StrictDoc Documentation
strictdoc/backend/excel/import_/excel_sheet_proxy.py
Source file coverage
Path:
strictdoc/backend/excel/import_/excel_sheet_proxy.py
Lines:
94
Non-empty lines:
81
Non-empty lines covered with requirements:
81 / 81 (100.0%)
Functions:
8
Functions covered by requirements:
8 / 8 (100.0%)
1
"""
2
@relation(SDOC-SRS-152, scope=file)
3
"""
4
 
5
from enum import Enum
6
from typing import List
7
 
8
import openpyxl
9
import xlrd
10
 
11
from strictdoc.helpers.auto_described import auto_described
12
from strictdoc.helpers.cast import assert_cast
13
 
14
 
15
class ExcelLibType(Enum):
16
    XLRD = 1
17
    OPENPYXL = 2
18
 
19
 
20
@auto_described
21
class ExcelSheetProxy:
22
    """
23
    An adapter class to open xls(xlrd) or xlsx(openpyxl) with the same interface.
24
    """
25
 
26
    def __init__(self, file: str):
27
        self.file = file
28
        if file.endswith(".xlsx"):
29
            self.workbook = openpyxl.load_workbook(file)
30
            self.sheet = self.workbook.active
31
            if self.sheet is None:  # pragma: no cover
32
                raise RuntimeError(
33
                    "Couldn't open the first sheet of the workbook"
34
                )
35
            self.lib = ExcelLibType.OPENPYXL
36
        elif file.endswith(".xls"):
37
            self.workbook = xlrd.open_workbook(file, on_demand=True)
38
            self.sheet = self.workbook.sheet_by_index(0)
39
            self.lib = ExcelLibType.XLRD
40
        else:  # pragma: no cover
41
            raise AssertionError("Unsupported file format")
42
 
43
    @property
44
    def name(self) -> str:
45
        """
46
        Returns the name of the first sheet.
47
        """
48
        if self.lib == ExcelLibType.OPENPYXL:
49
            return str(self.sheet.title)
50
        elif self.lib == ExcelLibType.XLRD:
51
            return str(self.sheet.name)
52
        raise AssertionError
53
 
54
    @property
55
    def ncols(self) -> int:
56
        """
57
        The number of columns.
58
        """
59
        if self.lib == ExcelLibType.OPENPYXL:
60
            return assert_cast(self.sheet.max_column, int)
61
        elif self.lib == ExcelLibType.XLRD:
62
            return assert_cast(self.sheet.ncols, int)
63
        raise AssertionError
64
 
65
    @property
66
    def nrows(self) -> int:
67
        """
68
        The number of rows.
69
        """
70
        if self.lib == ExcelLibType.OPENPYXL:
71
            return assert_cast(self.sheet.max_row, int)
72
        elif self.lib == ExcelLibType.XLRD:
73
            return assert_cast(self.sheet.nrows, int)
74
        raise AssertionError
75
 
76
    def get_cell_value(self, row: int, col: int) -> str:
77
        """
78
        Returns the value at row/col as string.
79
        """
80
        if self.lib == ExcelLibType.OPENPYXL:
81
            return self.sheet.cell(row=row + 1, column=col + 1).value or ""
82
        elif self.lib == ExcelLibType.XLRD:
83
            return assert_cast(self.sheet.cell_value(row, col), str)
84
        raise AssertionError
85
 
86
    def row_values(self, row: int) -> List[str]:
87
        """
88
        Returns a full row as list.
89
        """
90
        if self.lib == ExcelLibType.OPENPYXL:
91
            return [(cell.value or "") for cell in self.sheet[row + 1]]
92
        elif self.lib == ExcelLibType.XLRD:
93
            return assert_cast(self.sheet.row_values(row), list)
94
        raise AssertionError