ssb-dash-framework 0.2.2__tar.gz → 0.2.2a0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/PKG-INFO +1 -1
  2. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/pyproject.toml +1 -1
  3. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/__init__.py +0 -4
  4. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/assets/dashAgGridFunctions.js +10 -37
  5. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/assets/stylesheet.css +0 -13
  6. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/control/control_framework_base.py +14 -41
  7. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/__init__.py +0 -2
  8. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_control_view.py +11 -19
  9. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_editor/altinn_editor_history.py +5 -14
  10. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_editor/altinn_editor_primary_table.py +37 -58
  11. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_editor/altinn_editor_supporting_table.py +3 -21
  12. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/bofregistry.py +31 -27
  13. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/macro_module.py +73 -260
  14. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/parquet_editor.py +26 -176
  15. ssb_dash_framework-0.2.2a0/src/ssb_dash_framework/utils/core_query_functions.py +83 -0
  16. ssb_dash_framework-0.2.2/src/ssb_dash_framework/utils/core_query_functions.py +0 -170
  17. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/LICENSE +0 -0
  18. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/README.md +0 -0
  19. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/__main__.py +0 -0
  20. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/control/__init__.py +0 -0
  21. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/aarsregnskap.py +0 -0
  22. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/agg_dist_plotter.py +0 -0
  23. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_data_capture.py +0 -0
  24. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_editor/__init__.py +0 -0
  25. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_editor/altinn_editor_comment.py +0 -0
  26. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_editor/altinn_editor_contact.py +0 -0
  27. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_editor/altinn_editor_control.py +0 -0
  28. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_editor/altinn_editor_main_view.py +0 -0
  29. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_editor/altinn_editor_submitted_forms.py +0 -0
  30. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_editor/altinn_editor_unit_details.py +0 -0
  31. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/altinn_editor/altinn_editor_utility.py +0 -0
  32. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/bedriftstabell.py +0 -0
  33. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/building_blocks/__init__.py +0 -0
  34. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/building_blocks/canvas.py +0 -0
  35. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/building_blocks/figuredisplay.py +0 -0
  36. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/building_blocks/map_display.py +0 -0
  37. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/building_blocks/multimodule.py +0 -0
  38. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/building_blocks/tables.py +0 -0
  39. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/freesearch.py +0 -0
  40. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/hb_method.py +0 -0
  41. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/nspek.py +0 -0
  42. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/pi_memorizer.py +0 -0
  43. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/skjemapdfviewer.py +0 -0
  44. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/modules/visualizationbuilder.py +0 -0
  45. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/py.typed +0 -0
  46. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/setup/__init__.py +0 -0
  47. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/setup/app_setup.py +0 -0
  48. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/setup/main_layout.py +0 -0
  49. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/setup/variableselector.py +0 -0
  50. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/utils/__init__.py +0 -0
  51. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/utils/alert_handler.py +0 -0
  52. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/utils/app_logger.py +0 -0
  53. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/utils/datahelper.py +0 -0
  54. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/utils/debugger_modal.py +0 -0
  55. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/utils/eimerdb_helpers.py +0 -0
  56. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/utils/functions.py +0 -0
  57. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/utils/implementations.py +0 -0
  58. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/utils/module_validation.py +0 -0
  59. {ssb_dash_framework-0.2.2 → ssb_dash_framework-0.2.2a0}/src/ssb_dash_framework/utils/r_helpers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssb-dash-framework
3
- Version: 0.2.2
3
+ Version: 0.2.2a0
4
4
  Summary: SSB Dash Framework
5
5
  License-Expression: GPL-3.0
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ssb-dash-framework"
3
- version = "0.2.2"
3
+ version = "0.2.2a0"
4
4
  description = "SSB Dash Framework"
5
5
  authors = [{ name = "Magnus Theodor Engh", email = "dor@ssb.no" }]
6
6
  license = "GPL-3.0"
@@ -60,7 +60,6 @@ from .modules import VisualizationBuilder
60
60
  from .modules import VisualizationBuilderWindow
61
61
  from .modules import apply_edits
62
62
  from .modules import export_from_parqueteditor
63
- from .modules import get_export_log_path
64
63
  from .modules import get_log_path
65
64
  from .setup import VariableSelector
66
65
  from .setup import VariableSelectorOption
@@ -81,7 +80,6 @@ from .utils import create_database
81
80
  from .utils import create_database_engine
82
81
  from .utils import enable_app_logging
83
82
  from .utils import hb_method
84
- from .utils import ibis_filter_with_dict
85
83
  from .utils import module_validator
86
84
  from .utils import sidebar_button
87
85
 
@@ -166,9 +164,7 @@ __all__ = [
166
164
  "create_database_engine",
167
165
  "enable_app_logging",
168
166
  "export_from_parqueteditor",
169
- "get_export_log_path",
170
167
  "get_log_path",
171
- "ibis_filter_with_dict",
172
168
  "main_layout",
173
169
  "module_validator",
174
170
  "register_control",
@@ -155,12 +155,11 @@ window.dashAgGridFunctions.MacroModule = {
155
155
 
156
156
  // === Function displayDiffHighlight (Compare current cell's value to previous year, highlight if different) ===
157
157
  displayDiffHighlight(props) {
158
-
159
- if (!props.data) return {};
160
-
161
158
  const field = props.colDef.field;
162
159
  const value = props.value;
163
160
 
161
+ if (!props.data) return {};
162
+
164
163
  const normalize = (v) =>
165
164
  v !== null && v !== undefined ? String(v).trim() : "";
166
165
 
@@ -191,6 +190,14 @@ window.dashAgGridFunctions.MacroModule = {
191
190
  return {}; // no change
192
191
  }
193
192
 
193
+ // all other columns
194
+ if (field.endsWith("_x")) {
195
+ return {
196
+ backgroundColor: '#e8e9eb',
197
+ color: 'black'
198
+ };
199
+ }
200
+
194
201
  const prevField = field + "_x";
195
202
  const prevValue = normalize(props.data[prevField]);
196
203
  const currentValue = normalize(value);
@@ -221,40 +228,6 @@ window.dashAgGridFunctions.MacroModule = {
221
228
  const prefixF = String(naringF).substring(0, 2);
222
229
 
223
230
  return prefixB !== prefixF;
224
- },
225
-
226
- // === Function displayDiffColumnHighlight (Mark tilgang & avgang on the diff-column) ===
227
- displayDiffColumnHighlight(props) {
228
- if (!props.data) return {};
229
-
230
- const field = props.colDef.field;
231
-
232
- const isDiffColumn = field.endsWith("_diff");
233
- const isExitHighlightField =
234
- (field === "orgnr_b" || field === "navn") &&
235
- props.data.is_avgang &&
236
- props.data.is_exiter;
237
-
238
- // Guard: only apply to allowed columns
239
- if (!isDiffColumn && !isExitHighlightField) return {};
240
-
241
- // tilgang (still only for diff columns)
242
- if (isDiffColumn && props.data.is_tilgang) {
243
- return {
244
- backgroundColor: "#c7f5c7",
245
- color: "black",
246
- };
247
- }
248
-
249
- // avgang (diff columns OR exiter name/orgnr)
250
- if (props.data.is_avgang) {
251
- return {
252
- backgroundColor: "#ffc7c7",
253
- color: "black",
254
- };
255
- }
256
-
257
- return {};
258
231
  }
259
232
 
260
233
  };
@@ -230,19 +230,6 @@ Altinn Editor History
230
230
  }
231
231
 
232
232
 
233
- /*
234
- Altinn Editor Support Tables
235
- */
236
- .hjelpetabeller-modal .modal-dialog {
237
- max-width: 90vw !important;
238
- width: 90vw !important;
239
- }
240
-
241
- .hjelpetabeller-modal .modal-content {
242
- height: 70vh !important;
243
- overflow: hidden;
244
- }
245
-
246
233
  /*
247
234
  MacroModule
248
235
  */
@@ -227,7 +227,8 @@ class ControlFrameworkBase: # TODO: Add some common control methods here for ea
227
227
  self.conn.insert("kontroller", rows_to_register)
228
228
  elif conn_is_ibis(self.conn):
229
229
  conn = self.conn
230
- conn.insert("kontroller", rows_to_register)
230
+ k = conn.table("kontroller") # type: ignore[attr-defined]
231
+ k.insert(rows_to_register)
231
232
  else:
232
233
  raise NotImplementedError(
233
234
  f"Connection type '{type(self.conn)}' is currently not implemented."
@@ -323,6 +324,7 @@ class ControlFrameworkBase: # TODO: Add some common control methods here for ea
323
324
  """
324
325
  logger.info(f"Running control: {control}")
325
326
  results = getattr(self, control)()
327
+
326
328
  logger.debug("Starting validation of results.")
327
329
  if not isinstance(results, pd.DataFrame):
328
330
  raise TypeError(
@@ -346,14 +348,10 @@ class ControlFrameworkBase: # TODO: Add some common control methods here for ea
346
348
  raise ValueError(
347
349
  f"Error when running {control}. Could not find {results['kontrollid'].unique()} among registered controls. Valid options retrieved from the 'kontrollutslag' table: {self.controls}"
348
350
  )
349
-
350
- # IMPORTANT! This code must not fix the error for the user.
351
- # This check exists because it is expected that users mistakenly forget to filter on periods / form type
352
- # and that forgetting that filtering WILL cause false positives/negatives. Do not change this validation check.
353
351
  for key, value in self.applies_to_subset.items():
354
352
  if len(results[key].unique()) != 1:
355
353
  raise ValueError(
356
- f"Results from control '{control}' has too many unique values for '{key}'. Expected '{value[0]}'. Received: '{results[key].unique()}'\nYou "
354
+ f"Results from control {control} has too many unique values for '{key}'. Expected '{value[0]}'. Received: '{results[key].unique()}'"
357
355
  )
358
356
  if not value[0] == results[key].unique()[0]:
359
357
  raise ValueError(
@@ -440,10 +438,7 @@ class ControlFrameworkBase: # TODO: Add some common control methods here for ea
440
438
  else:
441
439
  specific_control = None
442
440
  existing_kontrollutslag = self.get_current_kontrollutslag(specific_control)
443
- if existing_kontrollutslag is not None and not existing_kontrollutslag.empty:
444
- logger.debug(
445
- f"Merging new results with existing results.\nNew:{control_results.shape}\n{control_results.head(10)}\n\nExisting:{existing_kontrollutslag.shape}\n{existing_kontrollutslag.head(10)}"
446
- )
441
+ if existing_kontrollutslag is not None:
447
442
  control_results = control_results.merge(
448
443
  existing_kontrollutslag,
449
444
  on=[*self.applies_to_subset.keys(), "kontrollid", "ident", "refnr"],
@@ -457,7 +452,7 @@ class ControlFrameworkBase: # TODO: Add some common control methods here for ea
457
452
  "kontrollid",
458
453
  "ident",
459
454
  "refnr",
460
- "verdi_x" if "verdi_x" in control_results.columns else "verdi",
455
+ "verdi_x",
461
456
  "utslag_x",
462
457
  ]
463
458
  ]
@@ -475,7 +470,8 @@ class ControlFrameworkBase: # TODO: Add some common control methods here for ea
475
470
  self.conn.insert("kontrollutslag", control_results)
476
471
  elif conn_is_ibis(self.conn):
477
472
  conn = self.conn
478
- conn.insert("kontrollutslag", control_results)
473
+ k = conn.table("kontrollutslag") # type: ignore[attr-defined]
474
+ k.insert(control_results)
479
475
  else:
480
476
  raise NotImplementedError(
481
477
  f"Connection type '{type(self.conn)}' is currently not implemented."
@@ -498,48 +494,25 @@ class ControlFrameworkBase: # TODO: Add some common control methods here for ea
498
494
  logger.info("No existing rows found, ending here.")
499
495
  return None
500
496
  else:
501
- logger.debug(
502
- f"Merging new results with existing results.\nNew:{control_results.shape}\n{control_results.head(10)}\n\nExisting:{existing_kontrollutslag.shape}\n{existing_kontrollutslag.head(10)}"
503
- )
504
497
  control_results = control_results.merge(
505
498
  existing_kontrollutslag,
506
- on=[*self.applies_to_subset.keys(), "kontrollid", "ident", "refnr"],
499
+ on=["kontrollid", "ident", "refnr"],
507
500
  how="outer",
508
501
  indicator=True,
509
- )
510
- logger.debug(f"{control_results}")
511
- control_results = control_results.dropna(
512
- subset=[col for col in control_results if col != "verdi"]
513
- ) # TODO: Check if this causes unexpected behavior.
502
+ ).dropna()
514
503
  if control_results.empty:
515
- logger.debug(f"Empty control_results:\n{control_results}")
516
504
  raise ValueError(
517
- "Combined results from 'control_results' and 'existing_kontrollutslag' is empty when it shouldn't be."
505
+ "Combined results from 'control_results' and 'existing_kontrollutslag' is empty."
518
506
  )
519
507
  logger.debug(control_results)
520
508
  logger.debug(
521
509
  f"Utslag left:\n{control_results['utslag_x'].value_counts()}\nUtslag right:\n{control_results['utslag_y'].value_counts()}"
522
510
  )
523
- changed = control_results[ # This might have issues.
511
+ changed = control_results[
524
512
  control_results["utslag_x"] != control_results["utslag_y"]
525
- ][
526
- [
527
- *self.time_units,
528
- "kontrollid",
529
- "ident",
530
- "refnr",
531
- "verdi_x" if "verdi_x" in control_results.columns else "verdi",
532
- "utslag_x",
533
- ]
534
- ].rename(
535
- columns={"utslag_x": "utslag"}
513
+ ][["kontrollid", "ident", "refnr", "verdi_x", "utslag_x"]].rename(
514
+ columns={"utslag_x": "utslag", "verdi_x": "verdi"}
536
515
  )
537
- if "verdi_x" in changed.columns:
538
- changed = changed.rename(columns={"verdi_x": "verdi"})
539
-
540
- missing = set(self._required_kontrollutslag_columns) - set(changed.columns)
541
- if missing:
542
- raise ValueError(f"Missing required columns: {missing}")
543
516
  if changed.empty:
544
517
  logger.info("No changed rows, ending here.")
545
518
  return None
@@ -52,7 +52,6 @@ from .parquet_editor import ParquetEditor
52
52
  from .parquet_editor import ParquetEditorChangelog
53
53
  from .parquet_editor import apply_edits
54
54
  from .parquet_editor import export_from_parqueteditor
55
- from .parquet_editor import get_export_log_path
56
55
  from .parquet_editor import get_log_path
57
56
  from .pi_memorizer import PimemorizerTab
58
57
  from .skjemapdfviewer import SkjemapdfViewer
@@ -120,6 +119,5 @@ __all__ = [
120
119
  "VisualizationBuilderWindow",
121
120
  "apply_edits",
122
121
  "export_from_parqueteditor",
123
- "get_export_log_path",
124
122
  "get_log_path",
125
123
  ]
@@ -54,19 +54,15 @@ class ControlView(ABC):
54
54
  control_dict: A dictionary with one control class per skjema.
55
55
  conn: The eimerdb connection.
56
56
  outputs: Variable selector fields to output to. Defaults to ['ident']
57
-
58
- Raises:
59
- TypeError: if conn type is not 'EimerDBInstance' or ibis connection.
60
57
  """
61
58
  logger.warning(
62
59
  f"{self.__class__.__name__} is under development and may change in future releases."
63
60
  )
64
61
  if outputs is None:
65
62
  outputs = ["ident"]
66
- if not conn_is_ibis(conn) and not isinstance(conn, EimerDBInstance):
67
- raise TypeError(
68
- f"Argument 'conn' must be an 'EimerDBInstance' or Ibis backend. Received: {type(conn)}"
69
- )
63
+ assert hasattr(
64
+ conn, "query"
65
+ ), "The database connection object must have a 'query' method."
70
66
  self.module_number = ControlView._id_number
71
67
  self.module_name = self.__class__.__name__
72
68
  ControlView._id_number += 1
@@ -176,11 +172,13 @@ class ControlView(ABC):
176
172
  Input("var-altinnskjema", "value"),
177
173
  self.variableselector.get_all_inputs(),
178
174
  )
179
- def kontroller_show_selected_controls(skjema: str | None, *args: Any):
175
+ def kontroller_show_selected_controls(skjema: str, *args: Any):
180
176
  logger.debug(f"Args:\nskjema: {skjema}\nargs: {args}")
181
177
  partition_args = dict(zip(self.time_units, args, strict=False))
182
178
  if partition_args is not None and skjema is not None:
183
- valgte_vars = f"valgt partisjon: {partition_args}\nvalgt skjema: {skjema if skjema else 'Velg skjema for å starte'}"
179
+ valgte_vars = (
180
+ f"valgt partisjon: {partition_args}\nvalgt skjema: {skjema}"
181
+ )
184
182
  return valgte_vars
185
183
 
186
184
  @callback(
@@ -189,9 +187,7 @@ class ControlView(ABC):
189
187
  State("alert_store", "data"),
190
188
  prevent_initial_call=True,
191
189
  )
192
- def alert_user_of_controls(
193
- click: int | None, alert_store: list[dict[str, Any]]
194
- ) -> list[dict[str, Any]]:
190
+ def alert_user_of_controls(click, alert_store):
195
191
  return [
196
192
  create_alert(
197
193
  "Kjører kontroller, dette kan ta litt tid, du får beskjed når den er ferdig. Ikke klikk på knappen igjen.",
@@ -213,11 +209,7 @@ class ControlView(ABC):
213
209
  prevent_initial_call=True,
214
210
  )
215
211
  def get_kontroller_overview(
216
- skjema: str,
217
- refresh: int | None,
218
- rerun: int | None,
219
- alert_store: list[dict[str, Any]],
220
- *args: Any,
212
+ skjema: str, refresh: int | None, rerun: int | None, alert_store, *args: Any
221
213
  ):
222
214
  logger.debug(
223
215
  f"Args:\n"
@@ -310,7 +302,7 @@ class ControlView(ABC):
310
302
  utslag = (
311
303
  kontrollutslag.filter(kontrollutslag.utslag == True)
312
304
  .group_by(kontrollutslag.kontrollid)
313
- .aggregate(ant_utslag=ibis._.count())
305
+ .aggregate(ant_utslag=kontrollutslag.row_id.count())
314
306
  )
315
307
 
316
308
  subq = (
@@ -327,7 +319,7 @@ class ControlView(ABC):
327
319
  )
328
320
  .filter(kontrollutslag.utslag == True)
329
321
  .group_by(kontrollutslag.kontrollid)
330
- .aggregate(uediterte=ibis._.count())
322
+ .aggregate(uediterte=kontrollutslag.row_id.count())
331
323
  .select("kontrollid", "uediterte")
332
324
  )
333
325
 
@@ -12,7 +12,6 @@ from dash.dependencies import State
12
12
  from dash.exceptions import PreventUpdate
13
13
  from eimerdb import EimerDBInstance
14
14
  from ibis import _
15
- import tzlocal
16
15
 
17
16
  from ssb_dash_framework.utils import conn_is_ibis
18
17
 
@@ -21,8 +20,6 @@ from ...utils.eimerdb_helpers import create_partition_select
21
20
 
22
21
  logger = logging.getLogger(__name__)
23
22
 
24
- local_tz = tzlocal.get_localzone()
25
-
26
23
 
27
24
  class AltinnEditorHistory:
28
25
  """Module for viewing the editing history for the selected observation."""
@@ -67,12 +64,10 @@ class AltinnEditorHistory:
67
64
  dbc.ModalBody(
68
65
  dag.AgGrid(
69
66
  defaultColDef={
67
+ "editable": True,
70
68
  "filter": True,
71
69
  "resizable": True,
72
70
  },
73
- dashGridOptions={
74
- "enableCellTextSelection": True,
75
- },
76
71
  id="skjemadata-historikkmodal-table1",
77
72
  className="ag-theme-alpine header-style-on-filter",
78
73
  columnSize="responsiveSizeToFit",
@@ -158,15 +153,11 @@ class AltinnEditorHistory:
158
153
  if conn_is_ibis(self.conn):
159
154
  conn = self.conn
160
155
  t = conn.table("skjemadataendringshistorikk")
161
- df = t.filter(_.refnr == refnr).to_pandas()
162
- df["endret_tid"] = (
163
- pd.to_datetime(df["endret_tid"], utc=True)
164
- .dt.tz_convert(local_tz)
165
- .dt.floor("s")
166
- .dt.tz_localize(None)
167
- .dt.strftime("%Y-%m-%d %H:%M:%S")
156
+ df = (
157
+ t.filter(_.refnr == refnr)
158
+ .order_by(_.endret_tid.cast("timestamp").desc())
159
+ .to_pandas()
168
160
  )
169
- df = df.sort_values("endret_tid", ascending=False)
170
161
  columns = [
171
162
  {
172
163
  "headerName": col,
@@ -119,41 +119,18 @@ class AltinnEditorPrimaryTable:
119
119
  def module_callbacks(self) -> None:
120
120
  """Defines the callbacks for the module."""
121
121
 
122
- # check if var-bedrift exists
123
- try:
124
- self.variableselector.get_option("var-bedrift", search_target="id")
125
- has_bedrift = True
126
- except ValueError:
127
- has_bedrift = False
128
- logger.debug("var-bedrift not available, skipping bedrift sorting")
129
-
130
122
  @callback( # type: ignore[misc]
131
123
  Output("altinnedit-table-skjemadata", "rowData", allow_duplicate=True),
132
124
  Output("altinnedit-table-skjemadata", "columnDefs", allow_duplicate=True),
133
125
  Input("altinnedit-refnr", "value"),
134
126
  Input("altinnedit-option1", "value"),
135
- Input("var-ident", "value"),
136
127
  State("altinnedit-skjemaer", "value"),
137
- (
138
- State("var-bedrift", "value")
139
- if has_bedrift
140
- else State("altinnedit-refnr", "value")
141
- ), # Dummy state if no bedrift
142
128
  self.variableselector.get_all_states(),
143
129
  prevent_initial_call=True,
144
130
  )
145
131
  def hovedside_update_altinnskjema(
146
- refnr: str,
147
- tabell: str,
148
- ident: str,
149
- skjema: str,
150
- bedrift_or_dummy: str,
151
- *args: Any,
132
+ refnr: str, tabell: str, skjema: str, *args: Any
152
133
  ) -> tuple[list[dict[str, Any]] | None, list[dict[str, Any]] | None]:
153
-
154
- # extract bedrift if it exists
155
- bedrift = bedrift_or_dummy if has_bedrift else None
156
-
157
134
  logger.debug(
158
135
  f"Args:\n"
159
136
  f"refnr: {refnr}\n"
@@ -202,20 +179,12 @@ class AltinnEditorPrimaryTable:
202
179
  f"partition_select:\n{create_partition_select(desired_partitions=self.time_units,skjema=skjema,**partition_args,)}"
203
180
  )
204
181
 
205
- t = t.filter(_.refnr == refnr).join(d, "variabel", how="left")
182
+ t = (
183
+ t.filter(_.refnr == refnr)
184
+ .join(d, "variabel", how="left")
185
+ .order_by(_.radnr)
186
+ )
206
187
  t = t.filter(ibis_filter_with_dict(filter_dict))
207
-
208
- # sort by bedrift if available
209
- if bedrift and "ident" in t.columns:
210
- t = t.mutate(
211
- sort_priority=ibis.case()
212
- .when(_.ident == bedrift, 0)
213
- .else_(1)
214
- .end()
215
- ).order_by(["sort_priority", _.radnr])
216
- else:
217
- t = t.order_by(_.radnr)
218
-
219
188
  df = t.drop(
220
189
  [col for col in t.columns if col.endswith("_right")]
221
190
  + ["datatype", "radnr", "tabell"]
@@ -256,13 +225,6 @@ class AltinnEditorPrimaryTable:
256
225
  .to_pandas()
257
226
  )
258
227
 
259
- # sort by bedrift if it exists
260
- if bedrift and "ident" in df.columns:
261
- df = df.sort_values(
262
- by="ident",
263
- key=lambda x: x.map(lambda v: 0 if v == bedrift else 1),
264
- )
265
-
266
228
  columndefs = [
267
229
  {
268
230
  "headerName": col,
@@ -287,6 +249,35 @@ class AltinnEditorPrimaryTable:
287
249
  )
288
250
  return None, None
289
251
 
252
+ try: # TODO Find better solution to sort - config file?
253
+ self.variableselector.get_option("var-bedrift", search_target="id")
254
+
255
+ @callback(
256
+ Output("altinnedit-table-skjemadata", "rowData"),
257
+ Output("altinnedit-table-skjemadata", "columnDefs"),
258
+ Input("altinnedit-table-skjemadata", "rowData"),
259
+ Input("altinnedit-table-skjemadata", "columnDefs"),
260
+ State("var-bedrift", "value"),
261
+ prevent_initial_call=True,
262
+ )
263
+ def sort_by_bedrift(
264
+ row_data: list[dict[str, Any]],
265
+ column_defs: list[dict[str, Any]],
266
+ bedrift: str,
267
+ ) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
268
+ """Sort table to prioritize selected bedrift."""
269
+ if not bedrift or not row_data:
270
+ return row_data, column_defs
271
+
272
+ sorted_data = sorted(
273
+ row_data, key=lambda row: 0 if row.get("ident") == bedrift else 1
274
+ )
275
+
276
+ return sorted_data, column_defs
277
+
278
+ except ValueError:
279
+ logger.debug("var-bedrift not available, skipping bedrift sorting callback")
280
+
290
281
  @callback( # type: ignore[misc]
291
282
  Output("var-statistikkvariabel", "value"),
292
283
  Input("altinnedit-table-skjemadata", "cellClicked"),
@@ -296,21 +287,9 @@ class AltinnEditorPrimaryTable:
296
287
  click: dict[str, Any], row_data: list[dict[str, Any]]
297
288
  ) -> str:
298
289
  logger.debug(f"Args:\nclick: {click}\nrow_data: {row_data}")
299
-
300
- if not click or not row_data:
290
+ if not click:
301
291
  raise PreventUpdate
302
-
303
- columns = list(row_data[0].keys())
304
-
305
- long_format = "variabel" in columns and "verdi" in columns
306
- if long_format:
307
- return str(row_data[click["rowIndex"]]["variabel"])
308
-
309
- column = click.get("colId") # wide format
310
- if column in ("aar", "ident", "skjema", "refnr", "tabell"):
311
- raise PreventUpdate
312
-
313
- return str(column)
292
+ return str(row_data[click["rowIndex"]]["variabel"])
314
293
 
315
294
  @callback( # type: ignore[misc]
316
295
  Output("alert_store", "data", allow_duplicate=True),
@@ -39,8 +39,6 @@ class AltinnSupportTable:
39
39
  get_data_func: Callable[..., pd.DataFrame],
40
40
  editor_inputs: list[str] | None = None,
41
41
  variableselector: VariableSelector | None = None,
42
- pin_leftmost_column: bool = True,
43
- suffix_to_colour_grey: list[str] | None = None,
44
42
  ) -> None:
45
43
  """Initializes the support table.
46
44
 
@@ -49,8 +47,6 @@ class AltinnSupportTable:
49
47
  get_data_func: Function that returns data to show in the supporting table.
50
48
  editor_inputs: List of inputs from the AltinnSkjemadataEditor to use as arguments in the get_data_func.
51
49
  variableselector: VariableSelector instance for adding arguments from the overall variableselector.
52
- pin_leftmost_column: Optional. Boolean to pin the leftmost column in the AgGrid (usually the ident-col). Defaults to "True".
53
- suffix_to_colour_grey: Optional. List of column-name suffixes to colour light grey in the AgGrid. Defaults to "[_x]".
54
50
 
55
51
  Note:
56
52
  The component is automatically added to the panel inside the modal.
@@ -58,8 +54,6 @@ class AltinnSupportTable:
58
54
  self.label = label
59
55
  self.inputs = editor_inputs
60
56
  self.get_data_func = get_data_func
61
- self.pin_leftmost_column = pin_leftmost_column
62
- self.suffix_to_colour_grey = suffix_to_colour_grey or ["_x"]
63
57
  if variableselector:
64
58
  self.variableselector = variableselector
65
59
  else:
@@ -82,10 +76,8 @@ class AltinnSupportTable:
82
76
  """The content to show in the support table."""
83
77
  return html.Div(
84
78
  dag.AgGrid(
85
- defaultColDef={"editable": False, "filter": True},
86
- dashGridOptions={"enableCellTextSelection": True},
79
+ defaultColDef={"editable": False},
87
80
  id=f"support-table-{self.suptable_id}",
88
- style={"height": "700px"},
89
81
  )
90
82
  )
91
83
 
@@ -103,17 +95,7 @@ class AltinnSupportTable:
103
95
  f"Running get_data_func for table '{self.label}' using args: {args}"
104
96
  )
105
97
  data = self.get_data_func(*args)
106
- column_defs = []
107
- for col in data.columns:
108
- col_def: dict[str, Any] = {"field": col, "headerName": col.lower()}
109
- if self.suffix_to_colour_grey and col.endswith(
110
- tuple(self.suffix_to_colour_grey)
111
- ):
112
- col_def["cellStyle"] = {"backgroundColor": "#e8e9eb"}
113
- column_defs.append(col_def)
114
- if self.pin_leftmost_column and column_defs:
115
- column_defs[0]["pinned"] = "left"
116
- return data.to_dict("records"), column_defs
98
+ return data.to_dict("records"), [{"field": col} for col in data.columns]
117
99
 
118
100
  def support_table_layout(self) -> dbc.Tab:
119
101
  """Creates the layout."""
@@ -197,7 +179,7 @@ class AltinnEditorSupportTables:
197
179
  ],
198
180
  id="skjemadata-hjelpetabellmodal",
199
181
  is_open=False,
200
- className="hjelpetabeller-modal",
182
+ size="xl",
201
183
  )
202
184
  logger.debug("Created support tables modal")
203
185
  return hjelpetabellmodal