Skip to content

Dataset Scheming Form Pages [OPEN-4034]#1582

Open
wardi wants to merge 27 commits intomasterfrom
OPEN-4034-form-pages
Open

Dataset Scheming Form Pages [OPEN-4034]#1582
wardi wants to merge 27 commits intomasterfrom
OPEN-4034-form-pages

Conversation

@wardi
Copy link
Member

@wardi wardi commented Jun 13, 2025

split dataset type into separate pages using scheming form pages and dataset completeness feature

@wardi wardi force-pushed the OPEN-4034-form-pages branch from 1432f66 to 7b9b271 Compare August 19, 2025 01:18
@wardi
Copy link
Member Author

wardi commented Jan 30, 2026

@JVickery-TBS this is working for me now since open-data/ckan#214 was merged. See also @RabiaSajjad 's CSS changes in #1633 that makes more room for form page tabs by moving the side bar above the content.

@JVickery-TBS
Copy link
Contributor

Had to do some more css and template stuff, mainly for the Add Resource in Draft part. and then some small screen stuff.

Have to do a couple more to polish this up. But this is in staging and working fairly well now.

@codecov
Copy link

codecov bot commented Feb 21, 2026

❌ 5 Tests Failed:

Tests completed Failed Passed Skipped
229 5 224 0
View the top 3 failed test(s) by shortest run time
ckanext/canada/tests/test_validators.py::TestNAVLSchema::test_raw_required
Stack Traces | 0.395s run time
self = <ckanext.canada.tests.test_validators.TestNAVLSchema object at 0x7ff43b285580>

    def test_raw_required(self):
        raw_pkg = dict(self.complete_pkg)
        del raw_pkg['title_translated']
    
        with pytest.raises(ValidationError) as ve:
>           self.normal_action.package_create(**raw_pkg)
E           Failed: DID NOT RAISE <class 'ckan.logic.ValidationError'>

.../canada/tests/test_validators.py:215: Failed
ckanext/canada/tests/test_logic.py::TestResourcePositionLogic::test_delete_resource_assigns_positions
Stack Traces | 0.686s run time
self = <ckanext.canada.tests.test_logic.TestResourcePositionLogic object at 0x7f48aa65b580>

    def test_delete_resource_assigns_positions(self):
        """
        Deleting a Resource from a package should reorder the other
        Resources properly, and set the deleted position to 0.
        """
        pkg = self.sysadmin_action.package_create(
            name='44cefde8-7cce-defc-1234-56789abcd34c',
            resources=[self.res_dict, self.res_dict, self.res_dict,
                       self.res_dict, self.res_dict, self.res_dict],
            **self.pkg_dict)
    
        assert len(pkg['resources']) == 6
        assert pkg['resources'][0]['position'] == 0
        assert pkg['resources'][1]['position'] == 1
        assert pkg['resources'][2]['position'] == 2
        assert pkg['resources'][3]['position'] == 3
        assert pkg['resources'][4]['position'] == 4
        assert pkg['resources'][5]['position'] == 5
    
        deleted_res_id = pkg['resources'][2]['id']
    
        self.sysadmin_action.resource_delete(
            id=deleted_res_id)
    
        pkg = self.sysadmin_action.package_show(id=pkg['id'])
    
        assert len(pkg['resources']) == 5
        assert pkg['resources'][0]['position'] == 0
        assert pkg['resources'][1]['position'] == 1
        assert pkg['resources'][2]['position'] == 2
        assert pkg['resources'][3]['position'] == 3
        assert pkg['resources'][4]['position'] == 4
        for r in pkg['resources']:
            assert r['id'] != deleted_res_id
    
        res = model.Resource.get(deleted_res_id)
    
        assert res.id == deleted_res_id
>       assert res.position == 0
E       assert None == 0
E        +  where None = <Resource id=78673fee-bd6d-40c1-8339-04d6916a230f package_id=f022e8e1-b39d-455c-a00e-05e6e1eea31c url=http://www.annakarenina.com/download/ format=TXT description=None hash= position=None name=None resource_type=dataset mimetype=None mimetype_inner=None size=42 created=1994-01-01 00:00:01 last_modified=1994-01-01 00:00:01 metadata_modified=1994-01-01 00:00:01 cache_url=None cache_last_updated=None url_type=None extras={'data_quality': '[]', 'language': '["zxx"]', 'name_translated': '{"en": "Full text.", "fr": "Full text."}'} state=deleted>.position

.../canada/tests/test_logic.py:472: AssertionError
ckanext/canada/tests/test_webforms.py::TestPackageWebForms::test_new_dataset_missing_fields
Stack Traces | 10.1s run time
self = <ckanext.canada.tests.test_webforms.TestPackageWebForms object at 0x7f3ff3415400>
app = <ckan.tests.helpers.CKANTestApp object at 0x7f3ff2d19c70>

    @mock.patch.object(h, 'is_registry_domain', mock_is_registry_domain)
    def test_new_dataset_missing_fields(self, app):
        dataset_id = 'f3e4adb9-6e32-4cb4-bf68-1eab9d1288f4'
    
        offset = h.url_for('dataset.new')
        response = app.get(offset, extra_environ=self.extra_environ_tester,
                           environ_overrides=self.environ_overrides_tester)
    
        assert 'Create Dataset' in response.body
        assert 'Before you can create a dataset you need to create an organization' not in response.body
    
        incomplete_dataset_form = {
            'id': dataset_id,
            'save': '',
            '_ckan_phase': '1',
        }
        response = app.post(offset,
                            data=incomplete_dataset_form,
                            extra_environ=self.extra_environ_tester,
                            environ_overrides=self.environ_overrides_tester,
                            follow_redirects=True)
    
>       assert 'Errors in form' in response.body
E       assert 'Errors in form' in '<!DOCTYPE html><!--[if lt IE 9]>\n  <html class="no-js lt-ie9" lang="en" dir="ltr"><![endif]--> <!--[if gt IE 8]><!-->\n  <html class="no-js" lang="en" dir="ltr"> <!--<![endif]-->\n  <head>\n    \n  <meta charset="utf-8" />\n      <meta name="csrf_field_name" content="_csrf_token" />\n      <meta name="_csrf_token" content="IjM0YzRiYzExNWQ3MDc3YjQyNDNkNzBmNjYzYTEzMDI2YTliNTZkZjUi.abq1eA.qZDyAbNAnBcbE44q4v8yPbU8-IU" />\n\n      <meta name="generator" content="ckan 2.10.8" />\n      <meta name="viewport" content="width=device-width, initial-scale=1.0">\n  \n    <meta property="og:title" content="Error 404 - CKAN">\n  \n  \n    \n  \n\n    <title>Error 404 - CKAN</title>\n\n    \n    \n  <link rel="shortcut icon" href="https://www.canada..../wet-boew/assets/favicon.ico" />\n    \n  \n    \n    \n    \n  \n  \n  \n\n  \n  \n      \n      \n      \n    \n  \n\n  \n\n\n    \n      \n      <link rel="stylesheet" href="TEST_TEMPLATE_HEAD_END.css" type="text/css">\n    \n\n    \n    \n  \n    \n    \n  \n  <script src="https://www.canada..../cdts/compiled/soyutils.js" integrity="sha384-hfwnpowMIP7hDqCMoNULlqSq7k2nu8R...D6AxjVdhrxo2JmzqWI\');\n    newWetFooterString += _style.outerHTML;\n  });\n  const wetFooterObjLinks = wetFooterDOC.querySelectorAll(\'link\');\n  wetFooterObjLinks.forEach(function(_link){\n    _link.setAttribute(\'nonce\', \'QETkD6AxjVdhrxo2JmzqWI\');\n    newWetFooterString += _link.outerHTML;\n  });\n  document.write(newWetFooterString);\n  \n</script>\n  \n  \n    \n      \n    \n  \n  \n  \n  \n    \n  \n  \n    \n\n  \n\n  \n      <script nonce="QETkD6AxjVdhrxo2JmzqWI">\n        window.addEventListener(\'load\', function(){\n          $(document).ready(function(){\n            if (navigator.appName == \'Microsoft Internet Explorer\' ||\n            !!(navigator.userAgent.match(/Trident/) || navigator.userAgent.match(/rv:11/)) ||\n            (typeof $.browser !== "undefined" && $.browser.msie == 1))\n            {\n              let ieWarning = document.getElementById("ie_warning");\n              if( ieWarning.length > 0 ){\n                $(ieWarning).show();\n              }\n            }\n          });\n        });\n      </script>\n    \n  \n\n      <div role="region"><strong>TEST TEMPLATE_FOOTER_END TEST</strong></div>\n\n    \n    \n    \n    \n  </body>\n</html>'
E        +  where '<!DOCTYPE html><!--[if lt IE 9]>\n  <html class="no-js lt-ie9" lang="en" dir="ltr"><![endif]--> <!--[if gt IE 8]><!-->\n  <html class="no-js" lang="en" dir="ltr"> <!--<![endif]-->\n  <head>\n    \n  <meta charset="utf-8" />\n      <meta name="csrf_field_name" content="_csrf_token" />\n      <meta name="_csrf_token" content="IjM0YzRiYzExNWQ3MDc3YjQyNDNkNzBmNjYzYTEzMDI2YTliNTZkZjUi.abq1eA.qZDyAbNAnBcbE44q4v8yPbU8-IU" />\n\n      <meta name="generator" content="ckan 2.10.8" />\n      <meta name="viewport" content="width=device-width, initial-scale=1.0">\n  \n    <meta property="og:title" content="Error 404 - CKAN">\n  \n  \n    \n  \n\n    <title>Error 404 - CKAN</title>\n\n    \n    \n  <link rel="shortcut icon" href="https://www.canada..../wet-boew/assets/favicon.ico" />\n    \n  \n    \n    \n    \n  \n  \n  \n\n  \n  \n      \n      \n      \n    \n  \n\n  \n\n\n    \n      \n      <link rel="stylesheet" href="TEST_TEMPLATE_HEAD_END.css" type="text/css">\n    \n\n    \n    \n  \n    \n    \n  \n  <script src="https://www.canada..../cdts/compiled/soyutils.js" integrity="sha384-hfwnpowMIP7hDqCMoNULlqSq7k2nu8R...D6AxjVdhrxo2JmzqWI\');\n    newWetFooterString += _style.outerHTML;\n  });\n  const wetFooterObjLinks = wetFooterDOC.querySelectorAll(\'link\');\n  wetFooterObjLinks.forEach(function(_link){\n    _link.setAttribute(\'nonce\', \'QETkD6AxjVdhrxo2JmzqWI\');\n    newWetFooterString += _link.outerHTML;\n  });\n  document.write(newWetFooterString);\n  \n</script>\n  \n  \n    \n      \n    \n  \n  \n  \n  \n    \n  \n  \n    \n\n  \n\n  \n      <script nonce="QETkD6AxjVdhrxo2JmzqWI">\n        window.addEventListener(\'load\', function(){\n          $(document).ready(function(){\n            if (navigator.appName == \'Microsoft Internet Explorer\' ||\n            !!(navigator.userAgent.match(/Trident/) || navigator.userAgent.match(/rv:11/)) ||\n            (typeof $.browser !== "undefined" && $.browser.msie == 1))\n            {\n              let ieWarning = document.getElementById("ie_warning");\n              if( ieWarning.length > 0 ){\n                $(ieWarning).show();\n              }\n            }\n          });\n        });\n      </script>\n    \n  \n\n      <div role="region"><strong>TEST TEMPLATE_FOOTER_END TEST</strong></div>\n\n    \n    \n    \n    \n  </body>\n</html>' = <WrapperTestResponse 8977 bytes [404 NOT FOUND]>.body

.../canada/tests/test_webforms.py:116: AssertionError
ckanext/canada/tests/test_webforms.py::TestPackageWebForms::test_new_dataset_required_fields
Stack Traces | 10.4s run time
self = <ckanext.canada.tests.test_webforms.TestPackageWebForms object at 0x7fc48aa48a90>
app = <ckan.tests.helpers.CKANTestApp object at 0x7fc48b6bb400>

    @mock.patch.object(h, 'flash_success', flashes.mock_flash)
    @mock.patch.object(h, 'get_flashed_messages', flashes.mock_get_flashed_messages)
    @mock.patch.object(h, 'is_registry_domain', mock_is_registry_domain)
    def test_new_dataset_required_fields(self, app):
        dataset_id = 'f3e4adb9-6e32-4cb4-bf68-1eab9d1288f5'
    
        offset = h.url_for('dataset.new')
        response = app.get(offset, extra_environ=self.extra_environ_tester,
                           environ_overrides=self.environ_overrides_tester)
    
        assert 'Create Dataset' in response.body
        assert 'Before you can create a dataset you need to create an organization' not in response.body
    
        response = app.post(offset,
                            data=self._filled_dataset_form(dataset_id),
                            extra_environ=self.extra_environ_tester,
                            environ_overrides=self.environ_overrides_tester,
                            follow_redirects=False)
    
        offset = _get_relative_offset_from_response(response)
        response = app.get(offset, extra_environ=self.extra_environ_tester,
                           environ_overrides=self.environ_overrides_tester)
    
>       assert 'Add data to the dataset' in response.body
E       assert 'Add data to the dataset' in '\n  <!DOCTYPE html><!--[if lt IE 9]>\n  <html class="no-js lt-ie9" lang="en" dir="ltr"><![endif]--> <!--[if gt IE 8]><!-->\n  <html class="no-js" lang="en" dir="ltr"> <!--<![endif]-->\n  <head>\n    \n  <meta charset="utf-8" />\n      <meta name="csrf_field_name" content="_csrf_token" />\n      <meta name="_csrf_token" content="IjcxNjQxYjc3NDhhYWI3YWVmN2QzMzkzZDgxOGZkNWEzM2YzZjdjNmIi.abq1aQ.SHy1i5BQY0XTNPhhH6vZO9enfc8" />\n\n      <meta name="generator" content="ckan 2.10.8" />\n      <meta name="viewport" content="width=device-width, initial-scale=1.0">\n  \n    <meta property="og:title" content="\n    Edit Draft Open Data Record:&nbsp;english title\n  \n - CKAN">\n  \n  \n    \n  \n\n    <title>\n    Edit Draft Open Data Record:&nbsp;english title\n  \n - CKAN</title>\n\n    \n    \n  <link rel="shortcut icon" href="https://www.canada..../wet-boew/assets/favicon.ico" />\n    \n  \n    \n    \n    \n  \n  \n  \n\n  \n  \n      \n      \n      \n    \n  \n\n  \n\n\n    \n      \n      <link rel="stylesheet" href="TEST_TEMPLATE_HEAD_END.css" type="text/css">\n    \n\n    \n    \n  \n    \n    \n  \n  <script src="https://www.canada.ca/etc/desig...  $(document).ready(function(){\n            if (navigator.appName == \'Microsoft Internet Explorer\' ||\n            !!(navigator.userAgent.match(/Trident/) || navigator.userAgent.match(/rv:11/)) ||\n            (typeof $.browser !== "undefined" && $.browser.msie == 1))\n            {\n              let ieWarning = document.getElementById("ie_warning");\n              if( ieWarning.length > 0 ){\n                $(ieWarning).show();\n              }\n            }\n          });\n        });\n      </script>\n    \n  \n\n  <script nonce="8eN5s8eUYnhJBgrvVZWIsN">\n    window.addEventListener(\'load\', function(){\n      $(document).ready(function(){\n        let select2Fields = $(\'[data-field-select2="True"]\');\n        if( select2Fields.length > 0 ){\n          $(select2Fields).each(function(_index, _select2Field){\n            $(_select2Field).select2();\n          });\n        }\n      });\n    });\n  </script>\n\n  \n\n      <div role="region"><strong>TEST TEMPLATE_FOOTER_END TEST</strong></div>\n\n    \n    \n    \n    <script src="....../webassets/ckanext-scheming/079217e5_scheming_subfields.js" type="text/javascript" nonce="8eN5s8eUYnhJBgrvVZWIsN"></script>\n  </body>\n</html>'
E        +  where '\n  <!DOCTYPE html><!--[if lt IE 9]>\n  <html class="no-js lt-ie9" lang="en" dir="ltr"><![endif]--> <!--[if gt IE 8]><!-->\n  <html class="no-js" lang="en" dir="ltr"> <!--<![endif]-->\n  <head>\n    \n  <meta charset="utf-8" />\n      <meta name="csrf_field_name" content="_csrf_token" />\n      <meta name="_csrf_token" content="IjcxNjQxYjc3NDhhYWI3YWVmN2QzMzkzZDgxOGZkNWEzM2YzZjdjNmIi.abq1aQ.SHy1i5BQY0XTNPhhH6vZO9enfc8" />\n\n      <meta name="generator" content="ckan 2.10.8" />\n      <meta name="viewport" content="width=device-width, initial-scale=1.0">\n  \n    <meta property="og:title" content="\n    Edit Draft Open Data Record:&nbsp;english title\n  \n - CKAN">\n  \n  \n    \n  \n\n    <title>\n    Edit Draft Open Data Record:&nbsp;english title\n  \n - CKAN</title>\n\n    \n    \n  <link rel="shortcut icon" href="https://www.canada..../wet-boew/assets/favicon.ico" />\n    \n  \n    \n    \n    \n  \n  \n  \n\n  \n  \n      \n      \n      \n    \n  \n\n  \n\n\n    \n      \n      <link rel="stylesheet" href="TEST_TEMPLATE_HEAD_END.css" type="text/css">\n    \n\n    \n    \n  \n    \n    \n  \n  <script src="https://www.canada.ca/etc/desig...  $(document).ready(function(){\n            if (navigator.appName == \'Microsoft Internet Explorer\' ||\n            !!(navigator.userAgent.match(/Trident/) || navigator.userAgent.match(/rv:11/)) ||\n            (typeof $.browser !== "undefined" && $.browser.msie == 1))\n            {\n              let ieWarning = document.getElementById("ie_warning");\n              if( ieWarning.length > 0 ){\n                $(ieWarning).show();\n              }\n            }\n          });\n        });\n      </script>\n    \n  \n\n  <script nonce="8eN5s8eUYnhJBgrvVZWIsN">\n    window.addEventListener(\'load\', function(){\n      $(document).ready(function(){\n        let select2Fields = $(\'[data-field-select2="True"]\');\n        if( select2Fields.length > 0 ){\n          $(select2Fields).each(function(_index, _select2Field){\n            $(_select2Field).select2();\n          });\n        }\n      });\n    });\n  </script>\n\n  \n\n      <div role="region"><strong>TEST TEMPLATE_FOOTER_END TEST</strong></div>\n\n    \n    \n    \n    <script src="....../webassets/ckanext-scheming/079217e5_scheming_subfields.js" type="text/javascript" nonce="8eN5s8eUYnhJBgrvVZWIsN"></script>\n  </body>\n</html>' = <WrapperTestResponse 32770 bytes [200 OK]>.body

.../canada/tests/test_webforms.py:80: AssertionError
View the full list of 1 ❄️ flaky test(s)
ckanext/canada/tests/test_validators.py::TestNAVLSchema::test_basic_package

Flake rate in main: 8.82% (Passed 62 times, Failed 6 times)

Stack Traces | 0.181s run time
self = <sqlalchemy.engine.base.Connection object at 0x7feae5879370>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7feae5cc87c0>
constructor = <bound method DefaultExecutionContext._init_compiled of <class 'sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2'>>
statement = 'UPDATE package SET id=%(id)s, creator_user_id=%(creator_user_id)s WHERE package.id = %(package_id)s'
parameters = {'creator_user_id': '310effb0-95fc-4654-af65-91c0b5cdec65', 'id': '3cdb9b39-639b-4763-8e4a-011999f80607', 'package_id': '03ec1800-10f3-4304-9b92-abd083088183'}
execution_options = immutabledict({'autocommit': True, 'compiled_cache': {(<sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 obj...age_id'), False, False), <sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7feae6054160>, 35]}})
args = (<sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7feae6054160>, [{'creator_user_id': '310effb...183'}], <sqlalchemy.sql.dml.Update object at 0x7feae671be20>, [BindParameter('package_id', None, type_=UnicodeText())])
kw = {'cache_hit': symbol('CACHE_MISS')}
branched = <sqlalchemy.engine.base.Connection object at 0x7feae5879370>
yp = None
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7feae58791f0>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7feae6054670>
cursor = <cursor object at 0x7feae62e97c0; closed: -1>, evt_handled = False

    def _execute_context(
        self,
        dialect,
        constructor,
        statement,
        parameters,
        execution_options,
        *args,
        **kw
    ):
        """Create an :class:`.ExecutionContext` and execute, returning
        a :class:`_engine.CursorResult`."""
    
        branched = self
        if self.__branch_from:
            # if this is a "branched" connection, do everything in terms
            # of the "root" connection, *except* for .close(), which is
            # the only feature that branching provides
            self = self.__branch_from
    
        if execution_options:
            yp = execution_options.get("yield_per", None)
            if yp:
                execution_options = execution_options.union(
                    {"stream_results": True, "max_row_buffer": yp}
                )
    
        try:
            conn = self._dbapi_connection
            if conn is None:
                conn = self._revalidate_connection()
    
            context = constructor(
                dialect, self, conn, execution_options, *args, **kw
            )
        except (exc.PendingRollbackError, exc.ResourceClosedError):
            raise
        except BaseException as e:
            self._handle_dbapi_exception(
                e, util.text_type(statement), parameters, None, None
            )
    
        if (
            self._transaction
            and not self._transaction.is_active
            or (
                self._nested_transaction
                and not self._nested_transaction.is_active
            )
        ):
            self._invalid_transaction()
    
        elif self._trans_context_manager:
            TransactionalContext._trans_ctx_check(self)
    
        if self._is_future and self._transaction is None:
            self._autobegin()
    
        context.pre_exec()
    
        if dialect.use_setinputsizes:
            context._set_input_sizes()
    
        cursor, statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        if not context.executemany:
            parameters = parameters[0]
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                statement, parameters = fn(
                    self,
                    cursor,
                    statement,
                    parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
    
            self._log_info(statement)
    
            stats = context._get_cache_stats()
    
            if not self.engine.hide_parameters:
                self._log_info(
                    "[%s] %r",
                    stats,
                    sql_util._repr_params(
                        parameters, batches=10, ismulti=context.executemany
                    ),
                )
            else:
                self._log_info(
                    "[%s] [SQL parameters hidden due to hide_parameters=True]"
                    % (stats,)
                )
    
        evt_handled = False
        try:
            if context.executemany:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor, statement, parameters, context
                    )
            elif not parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, statement, context
                    )
            else:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, statement, parameters, context
                    )

.../venv/lib/python3.9.../sqlalchemy/engine/base.py:1900: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7feae5cc87c0>
cursor = <cursor object at 0x7feae62e97c0; closed: -1>
statement = 'UPDATE package SET id=%(id)s, creator_user_id=%(creator_user_id)s WHERE package.id = %(package_id)s'
parameters = {'creator_user_id': '310effb0-95fc-4654-af65-91c0b5cdec65', 'id': '3cdb9b39-639b-4763-8e4a-011999f80607', 'package_id': '03ec1800-10f3-4304-9b92-abd083088183'}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7feae6054670>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       psycopg2.errors.ForeignKeyViolation: update or delete on table "package" violates foreign key constraint "package_extra_package_id_fkey" on table "package_extra"
E       DETAIL:  Key (id)=(03ec1800-10f3-4304-9b92-abd083088183) is still referenced from table "package_extra".

.../venv/lib/python3.9.../sqlalchemy/engine/default.py:736: ForeignKeyViolation

The above exception was the direct cause of the following exception:

self = <ckanext.canada.tests.test_validators.TestNAVLSchema object at 0x7feae7beadf0>

    def test_basic_package(self):
        with pytest.raises(ValidationError) as ve:
>           self.normal_action.package_create(
                name='12345678-9abc-def0-1234-56789abcdef0',
                **self.incomplete_pkg)

.../canada/tests/test_validators.py:123: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../ckanapi/ckanapi/common.py:60: in action
    return self._ckan.call_action(name, data_dict=kwargs)
.../ckanapi/ckanapi/localckan.py:74: in call_action
    return self._get_action(action)(context, data_dict)
.../ckan/logic/__init__.py:581: in wrapped
    result = _action(context, data_dict, **kw)
.../logic/action/create.py:217: in package_create
    model.Session.flush()
<string>:2: in flush
    ???
.../venv/lib/python3.9.../sqlalchemy/orm/session.py:3386: in flush
    self._flush(objects)
.../venv/lib/python3.9.../sqlalchemy/orm/session.py:3526: in _flush
    transaction.rollback(_capture_exception=True)
.../venv/lib/python3.9.../sqlalchemy/util/langhelpers.py:70: in __exit__
    compat.raise_(
.../venv/lib/python3.9.../sqlalchemy/util/compat.py:208: in raise_
    raise exception
.../venv/lib/python3.9.../sqlalchemy/orm/session.py:3486: in _flush
    flush_context.execute()
.../venv/lib/python3.9.../sqlalchemy/orm/unitofwork.py:456: in execute
    rec.execute(self)
.../venv/lib/python3.9.../sqlalchemy/orm/unitofwork.py:630: in execute
    util.preloaded.orm_persistence.save_obj(
.../venv/lib/python3.9.../sqlalchemy/orm/persistence.py:237: in save_obj
    _emit_update_statements(
.../venv/lib/python3.9.../sqlalchemy/orm/persistence.py:1001: in _emit_update_statements
    c = connection._execute_20(
.../venv/lib/python3.9.../sqlalchemy/engine/base.py:1705: in _execute_20
    return meth(self, args_10style, kwargs_10style, execution_options)
.../venv/lib/python3.9.../sqlalchemy/sql/elements.py:333: in _execute_on_connection
    return connection._execute_clauseelement(
.../venv/lib/python3.9.../sqlalchemy/engine/base.py:1572: in _execute_clauseelement
    ret = self._execute_context(
.../venv/lib/python3.9.../sqlalchemy/engine/base.py:1943: in _execute_context
    self._handle_dbapi_exception(
.../venv/lib/python3.9.../sqlalchemy/engine/base.py:2124: in _handle_dbapi_exception
    util.raise_(
.../venv/lib/python3.9.../sqlalchemy/util/compat.py:208: in raise_
    raise exception
.../venv/lib/python3.9.../sqlalchemy/engine/base.py:1900: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7feae5cc87c0>
cursor = <cursor object at 0x7feae62e97c0; closed: -1>
statement = 'UPDATE package SET id=%(id)s, creator_user_id=%(creator_user_id)s WHERE package.id = %(package_id)s'
parameters = {'creator_user_id': '310effb0-95fc-4654-af65-91c0b5cdec65', 'id': '3cdb9b39-639b-4763-8e4a-011999f80607', 'package_id': '03ec1800-10f3-4304-9b92-abd083088183'}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7feae6054670>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.IntegrityError: (psycopg2.errors.ForeignKeyViolation) update or delete on table "package" violates foreign key constraint "package_extra_package_id_fkey" on table "package_extra"
E       DETAIL:  Key (id)=(03ec1800-10f3-4304-9b92-abd083088183) is still referenced from table "package_extra".
E       
E       [SQL: UPDATE package SET id=%(id)s, creator_user_id=%(creator_user_id)s WHERE package.id = %(package_id)s]
E       [parameters: {'id': '3cdb9b39-639b-4763-8e4a-011999f80607', 'creator_user_id': '310effb0-95fc-4654-af65-91c0b5cdec65', 'package_id': '03ec1800-10f3-4304-9b92-abd083088183'}]
E       (Background on this error at: https://sqlalche..../e/14/gkpj)

.../venv/lib/python3.9.../sqlalchemy/engine/default.py:736: IntegrityError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

- Additional layout changes for the sidebar stages/pages.
@JVickery-TBS
Copy link
Contributor

@wardi sooo I think the HTML and CSS stuff is done, the issue (idk if its an issue or not) is that if you Add a Resource when making a dataset with missing fields, it will still created the Resource, assuming you have put all the fields in correctly. But the dataset will still be draft. I think thats fine? The specific issue is that in our schemas we have a required metadata field with the same id as a resource metadata field. So if you miss the dataset one, the error may popup on your Resource form.

- Fix stages sidebar template call for pd types.
- Fix a11y issues in the webform pages.
@JVickery-TBS JVickery-TBS changed the title OPEN-4034: form pages for dataset type Dataset Scheming Form Pages [OPEN-4034] Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants