[release_table] Support specifying a column by its index

Column could only be referenced by their name. Now they can also be referenced by their 1-based index.
This commit is contained in:
Marc Wrobel
2024-03-16 09:45:11 +01:00
parent fb6c3ec1f9
commit f5f743c647

View File

@@ -23,7 +23,7 @@ necessary information. Available configuration options are:
- ignore_empty_releases (optional, default = false): A boolean value indicating whether to ignore releases with no
fields except the name.
- fields: A dictionary that maps release fields to the table's columns. Field definition include:
- column (mandatory): The name of the column in the table. This is case-insensitive.
- column (mandatory): The name or index (starts at 1) of the column in the table.
- type (mandatory, default = string): The type of the field. Supported types are:
- string: The raw string value.
- date : A full or year-month date (supported patterns available in common.dates).
@@ -38,6 +38,20 @@ necessary information. Available configuration options are:
- template (mandatory, default = DEFAULT_TEMPLATE): A liquid template used to clean up the value using the matched
groups from a 'regex'.
Note that defining the column attribute directly instead of its full definition is allowed when
there the column name or index is the only attribute. For example, this:
```
fields:
releaseCycle:
column: "End of life"
```
can be replaced with this:
```
fields:
releaseCycle: "End of life"
```
Supported CSS selectors are defined by BeautifulSoup and documented on its website. For more information, see
https://beautiful-soup-4.readthedocs.io/en/latest/index.html?highlight=selector#css-selectors."""
@@ -54,7 +68,9 @@ TODAY = dates.today()
class Field:
def __init__(self, name: str, definition: str | dict) -> None:
if isinstance(definition, str):
# Directly specifying the column name or index instead of its full definition is allowed.
# In this case we must convert it to a full definition.
if isinstance(definition, (str, int)):
definition = {"column": definition}
self.name = name
@@ -63,7 +79,12 @@ class Field:
definition["regex"] = definition.get("regex", [DEFAULT_RELEASE_REGEX])
definition["template"] = definition.get("template", DEFAULT_TEMPLATE)
self.column = definition["column"].lower()
self.is_index = isinstance(definition["column"], int)
if self.is_index:
self.column = definition["column"] - 1 # convert to 0-based index
else:
self.column = definition["column"].lower()
self.type = definition.get("type", "string")
if self.name in DATE_FIELDS and self.type not in DATE_TYPES:
self.type = "date" # override type for known date fields
@@ -150,7 +171,7 @@ for config in endoflife.list_configs(p_filter, METHOD, m_filter):
try:
fields_index = {"releaseCycle": headers.index(release_cycle_field.column)}
for field in fields:
fields_index[field.name] = headers.index(field.column)
fields_index[field.name] = field.column if field.is_index else headers.index(field.column)
min_column_count = max(fields_index.values()) + 1
for row in table.select(rows_selector):
@@ -183,6 +204,5 @@ for config in endoflife.list_configs(p_filter, METHOD, m_filter):
logging.info(f"removing future release '{release}'")
product_data.remove_release(release_name)
except ValueError as e:
logging.info(f"skipping table with headers {headers}: {e}")