Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions ooui/graph/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from ooui.graph.axis import get_y_axis_fieldname
from ooui.graph.timerange import process_timerange_data
from ooui.graph.processor import (
get_values_grouped_by_field, get_values_for_y_field, get_min_max
get_values_grouped_by_field, get_values_grouped_by_fields,
get_values_for_y_field, get_min_max
)


Expand Down Expand Up @@ -41,6 +42,9 @@ def fields(self):
if y.label and y.label not in fields:
fields.append(y.label)

if y.stacked and y.stacked not in fields:
fields.append(y.stacked)

return fields

def process(self, values, fields, options=None):
Expand Down Expand Up @@ -88,14 +92,26 @@ def process(self, values, fields, options=None):
'stacked': y_field.stacked
})
else:
Comment thread
ecarreras marked this conversation as resolved.
values_grouped_by_y_label = get_values_grouped_by_field(
y_field.label, fields, objects_for_x_value
)
if y_field.stacked in fields:
Comment thread
ecarreras marked this conversation as resolved.
values_grouped_by_y_label = get_values_grouped_by_fields(
[y_field.label, y_field.stacked],
fields,
objects_for_x_value
)
else:
values_grouped_by_y_label = get_values_grouped_by_field(
y_field.label, fields, objects_for_x_value
)

for y_unique_value, grouped_entries in values_grouped_by_y_label.items():
entries = grouped_entries['entries']
label = grouped_entries['label']

if isinstance(y_unique_value, tuple):
y_label, stacked = y_unique_value
else:
stacked = y_field.stacked

values_for_y_field = get_values_for_y_field(
entries, y_field.name, fields
)
Expand All @@ -108,7 +124,7 @@ def process(self, values, fields, options=None):
'value': final_value,
'type': label,
'operator': y_field.operator,
'stacked': y_field.stacked
'stacked': stacked
})

# Check if data should be flagged as grouped or stacked
Expand Down
33 changes: 33 additions & 0 deletions ooui/graph/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,39 @@ def get_values_for_y_field(entries, field_name, fields):
]


def get_values_grouped_by_fields(fields_names, fields, values):
"""
Group values by multiple fields.

:param list fields_names: A list of field names by which to group values.
:param dict fields: A dictionary containing field definitions.
:param list values: A list of dictionaries representing the values to be
grouped.

:rtype: dict
:returns: A dictionary where keys are tuples of field values and values are
dictionaries containing a label and an "entries" list.
"""
grouped_values = {}

for entry in values:
field_values = [
get_value_and_label_for_field(fields, entry, field_name)
for field_name in fields_names
]
key = tuple(field_value['value'] for field_value in field_values)
label = ' - '.join(
field_value['label'] or '' for field_value in field_values
)

if key not in grouped_values:
grouped_values[key] = {'label': label, 'entries': []}

grouped_values[key]['entries'].append(entry)

return grouped_values


def get_values_grouped_by_field(field_name, fields, values):
"""
Group values by a specific field.
Expand Down
147 changes: 147 additions & 0 deletions spec/graph/graph_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@
expect(graph.progressbar).to(be_false)
expect(graph.suffix).to(equal('kW'))

with it('stacked attribute should be considered a field'):
xml = """<?xml version="1.0"?>
<graph type="bar" timerange="month" string="Ficheros B70">
<field name="create_date" string="Date" axis="x"/>
<field name="reclamacio" operator="count" axis="y" label="reclamacio" stacked="import_phase"/>
</graph>"""
graph = parse_graph(xml)
expect(graph.fields).to(contain('create_date', 'reclamacio', 'import_phase'))

with it('should support progressbar attribute'):
xml = """<?xml version="1.0"?>
<graph string="My indicator" progressbar="1" type="indicator" />
Expand Down Expand Up @@ -196,3 +205,141 @@
expect(result['yAxisProps']).to(equal({
'mode': 'full',
}))
with description("A bar graph with stacked field"):
with it("should read the stacked field"):
xml = """<?xml version="1.0"?>
<graph type="bar" timerange="month" string="Ficheros B70">
<field name="create_date" string="Date" axis="x"/>
<field name="reclamacio" operator="count" axis="y" label="reclamacio" stacked="import_phase"/>
</graph>"""
graph = parse_graph(xml)
fields = {
'create_date': {'type': 'datetime', 'string': 'Create date'},
'reclamacio': {'type': 'char', 'string': 'Reclamacio', 'size': 256},
'import_phase': {'type': 'selection', 'selection': [('10', 'Phase 1'), ('20', 'Phase 2')], 'string': 'Import Phase'}
}
values = [
{
"create_date": "2025-03-03 21:00:57",
"id": 504776,
"reclamacio": "",
"import_phase": "10",
},
{
"create_date": "2025-02-28 21:01:07",
"id": 504257,
"reclamacio": "48-a2",
"import_phase": "20",
},
{
"create_date": "2025-02-28 21:00:41",
"id": 503955,
"reclamacio": "48-a2",
"import_phase": "10",
},
{
"create_date": "2025-02-27 21:01:59",
"id": 503688,
"reclamacio": "48-a3",
"import_phase": "10",
},
{
"create_date": "2025-02-07 21:00:39",
"id": 488390,
"reclamacio": "",
"import_phase": "10",
},
{
"create_date": "2025-01-27 21:01:02",
"id": 478886,
"reclamacio": "",
"import_phase": "10",
},
{
"create_date": "2025-01-24 21:01:20",
"id": 478167,
"reclamacio": "48-a3",
"import_phase": "10",
},
{
"create_date": "2025-01-23 21:00:45",
"id": 477151,
"reclamacio": "",
"import_phase": "10",
},
{
"create_date": "2025-01-23 21:00:43",
"id": 477130,
"reclamacio": "",
"import_phase": "20",
},
{
"create_date": "2025-01-08 21:00:58",
"id": 467715,
"reclamacio": "",
"import_phase": "20",
}
]
result = graph.process(values, fields)
expect(result).to(equal({
"num_items": 10,
"type": "bar",
"isGroup": True,
"isStack": True,
"data": [{
"stacked": "10",
"operator": "count",
"x": "2025-01",
"type": " - Phase 1",
"value": 2.0
},
{
"stacked": "20",
"operator": "count",
"x": "2025-01",
"type": " - Phase 2",
"value": 2.0
},
{
"stacked": "10",
"operator": "count",
"x": "2025-01",
"type": "48-a3 - Phase 1",
"value": 1.0
},
{
"stacked": "10",
"operator": "count",
"x": "2025-02",
"type": " - Phase 1",
"value": 1.0
},
{
"stacked": "10",
"operator": "count",
"x": "2025-02",
"type": "48-a2 - Phase 1",
"value": 1.0
},
{
"stacked": "20",
"operator": "count",
"x": "2025-02",
"type": "48-a2 - Phase 2",
"value": 1.0
},
{
"stacked": "10",
"operator": "count",
"x": "2025-02",
"type": "48-a3 - Phase 1",
"value": 1.0
},
{
"stacked": "10",
"operator": "count",
"x": "2025-03",
"type": " - Phase 1",
"value": 1.0
}]
}))
28 changes: 27 additions & 1 deletion spec/graph/processor_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from ooui.graph import parse_graph
from ooui.graph.processor import (
get_values_grouped_by_field, get_all_objects_in_grouped_values,
get_values_for_y_field, process_graph_data, get_min_max
get_values_for_y_field, process_graph_data, get_min_max,
get_values_grouped_by_fields
)

current_dir = os.path.dirname(os.path.abspath(__file__))
Expand Down Expand Up @@ -471,6 +472,31 @@ def get_graph_data(xml, model):
}))


with description('Testing get_values_grouped_by_fields') as self:
with context('when a selection field has no value'):
with it('should preserve the group key and use an empty label'):
fields_data = {
'category': {'type': 'string'},
'phase': {'type': 'selection', 'selection': [
('10', 'Phase 1')
]}
}
values_data = [
{'category': 'Fruit', 'phase': False}
]

grouped_values = get_values_grouped_by_fields(
['category', 'phase'], fields_data, values_data
)

expect(grouped_values).to(equal({
('Fruit', False): {
'label': 'Fruit - ',
'entries': values_data
}
}))


with description('Testing get_all_objects_in_grouped_values') as self:
with context('when retrieving all objects from grouped values'):
with it('should return a single list containing all objects'):
Expand Down
Loading