Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cannot import name 'url_fix' from 'werkzeug' #35

Open
yashodhank opened this issue Mar 13, 2024 · 1 comment
Open

cannot import name 'url_fix' from 'werkzeug' #35

yashodhank opened this issue Mar 13, 2024 · 1 comment

Comments

@yashodhank
Copy link

yashodhank commented Mar 13, 2024

Installation in Frappe/ERPNext version 15 environment is not possible due to the following errors:

bench get-app https://github.com/aakvatech/CSF_TZ.git
bench get-app https://github.com/aakvatech/PropMS.git
bench --site "erpnext15-dev1.xxxx.xxx" install-app csf_tz
bench --site "erpnext15-dev1.xxxx.xxx" install-app propms
bench --site "erpnext15-dev1.xxxx.xxx" migrate
Updating DocTypes for frappe        : [========================================] 100%
Updating DocTypes for erpnext       : [========================================] 100%
Updating DocTypes for non_profit    : [========================================] 100%
Updating DocTypes for lms           : [========================================] 100%
Updating DocTypes for lending       : [========================================] 100%
Updating DocTypes for helpdesk      : [========================================] 100%
Updating DocTypes for agriculture   : [========================================] 100%
Updating DocTypes for chat          : [========================================] 100%
Updating DocTypes for frappe_whatsapp: [========================================] 100%
Updating DocTypes for csf_tz        : [=====                                   ] 12%Queued rebuilding of search index for erpnext15-dev1.xxxx.xxx

Traceback with variables (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
      mod_name = 'frappe.utils.bench_helper'
      alter_argv = True
      mod_spec = ModuleSpec(name='frappe.utils.bench_helper', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7f01a5ff5ad0>, origin='/opt/bench/frappe-bench/apps/frappe/frappe/utils/bench_helper.py')
      code = <code object <module> at 0x2eb8bc0, file "/opt/bench/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", line 1>
      main_globals = {'__name__': '__main__', '__doc__': None, '__package__': 'frappe.utils', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f01a5ff5ad0>, '__spec__': ModuleSpec(name='frappe.utils.bench_helper', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7f01a5ff5ad0>, origin='/opt/bench/frappe-bench/apps/frappe/frappe/utils/bench_helper.py'), '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/opt/bench/frappe-bench/apps/frappe/frappe/utils/bench_helper.py', '__cached__': '/opt/bench/frappe-bench/apps/frappe/frappe/utils/__pycache__/bench_helper.cpython-311.pyc', 'importlib': <module 'importlib' from '/usr/lib/python3.11/importlib/__init__.py'>, 'json': <module 'json' from '/usr/lib/python3.11/json/__init__.py'>, 'os': <module 'os' (frozen)>, 'traceback': <module 'traceback' from '/usr/lib/python3.11/traceback.py'>, 'warnings': <module 'warnings' from '/usr/lib/python3.11/warnings.py'>, 'dedent': <function dedent at 0x7f01a5fa7...
  File "<frozen runpy>", line 88, in _run_code
      code = <code object <module> at 0x2eb8bc0, file "/opt/bench/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", line 1>
      run_globals = {'__name__': '__main__', '__doc__': None, '__package__': 'frappe.utils', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f01a5ff5ad0>, '__spec__': ModuleSpec(name='frappe.utils.bench_helper', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7f01a5ff5ad0>, origin='/opt/bench/frappe-bench/apps/frappe/frappe/utils/bench_helper.py'), '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/opt/bench/frappe-bench/apps/frappe/frappe/utils/bench_helper.py', '__cached__': '/opt/bench/frappe-bench/apps/frappe/frappe/utils/__pycache__/bench_helper.cpython-311.pyc', 'importlib': <module 'importlib' from '/usr/lib/python3.11/importlib/__init__.py'>, 'json': <module 'json' from '/usr/lib/python3.11/json/__init__.py'>, 'os': <module 'os' (frozen)>, 'traceback': <module 'traceback' from '/usr/lib/python3.11/traceback.py'>, 'warnings': <module 'warnings' from '/usr/lib/python3.11/warnings.py'>, 'dedent': <function dedent at 0x7f01a5fa7...
      init_globals = None
      mod_name = '__main__'
      mod_spec = ModuleSpec(name='frappe.utils.bench_helper', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7f01a5ff5ad0>, origin='/opt/bench/frappe-bench/apps/frappe/frappe/utils/bench_helper.py')
      pkg_name = 'frappe.utils'
      script_name = None
      loader = <_frozen_importlib_external.SourceFileLoader object at 0x7f01a5ff5ad0>
      fname = '/opt/bench/frappe-bench/apps/frappe/frappe/utils/bench_helper.py'
      cached = '/opt/bench/frappe-bench/apps/frappe/frappe/utils/__pycache__/bench_helper.cpython-311.pyc'
  File "/opt/bench/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", line 128, in <module>
    main()
      ...skipped... 27 vars
  File "/opt/bench/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", line 34, in main
    FrappeCommandGroup(commands=commands)(prog_name="bench")
      commands = {'frappe': <FrappeCommandGroup frappe>, 'get-frappe-commands': <Command get-frappe-commands>, 'get-frappe-help': <Command get-frappe-help>}
  File "/opt/bench/frappe-bench/env/lib/python3.11/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
      self = <FrappeCommandGroup None>
      args = ()
      kwargs = {'prog_name': 'bench'}
  File "/opt/bench/frappe-bench/env/lib/python3.11/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
      self = <FrappeCommandGroup None>
      args = ['frappe', '--site', 'erpnext15-dev1.xxxx.xxx', 'migrate']
      prog_name = 'bench'
      complete_var = None
      standalone_mode = True
      windows_expand_args = True
      extra = {}
      ctx = <click.core.Context object at 0x7f01a4782250>
  File "/opt/bench/frappe-bench/env/lib/python3.11/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
      self = <FrappeCommandGroup None>
      ctx = <click.core.Context object at 0x7f01a4782250>
      _process_result = <function MultiCommand.invoke.<locals>._process_result at 0x7f01a48cd940>
      args = ['migrate']
      cmd_name = 'frappe'
      cmd = <FrappeCommandGroup frappe>
      sub_ctx = <click.core.Context object at 0x7f01a5146310>
      __class__ = <class 'click.core.MultiCommand'>
  File "/opt/bench/frappe-bench/env/lib/python3.11/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
      self = <FrappeCommandGroup frappe>
      ctx = <click.core.Context object at 0x7f01a5146310>
      _process_result = <function MultiCommand.invoke.<locals>._process_result at 0x7f01a48cd9e0>
      args = []
      cmd_name = 'migrate'
      cmd = <Command migrate>
      sub_ctx = <click.core.Context object at 0x7f01a478f890>
      __class__ = <class 'click.core.MultiCommand'>
  File "/opt/bench/frappe-bench/env/lib/python3.11/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
      self = <Command migrate>
      ctx = <click.core.Context object at 0x7f01a478f890>
  File "/opt/bench/frappe-bench/env/lib/python3.11/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
      _Context__self = <click.core.Context object at 0x7f01a478f890>
      _Context__callback = <function migrate at 0x7f01a4740860>
      args = ()
      kwargs = {'skip_failing': False, 'skip_search_index': False}
      ctx = <click.core.Context object at 0x7f01a478f890>
  File "/opt/bench/frappe-bench/env/lib/python3.11/site-packages/click/decorators.py", line 33, in new_func
    return f(get_current_context(), *args, **kwargs)
      args = ()
      kwargs = {'skip_failing': False, 'skip_search_index': False}
      f = <function migrate at 0x7f01a47405e0>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/commands/__init__.py", line 29, in _func
    ret = f(frappe._dict(ctx.obj), *args, **kwargs)
      ctx = <click.core.Context object at 0x7f01a478f890>
      args = ()
      kwargs = {'skip_failing': False, 'skip_search_index': False}
      profile = False
      f = <function migrate at 0x7f01a4740540>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/commands/site.py", line 724, in migrate
    ).run(site=site)
      context = {'sites': ['erpnext15-dev1.xxxx.xxx'], 'force': False, 'verbose': False, 'profile': False}
      skip_failing = False
      skip_search_index = False
      activate_by_import = <module 'traceback_with_variables.activate_by_import' from '/opt/bench/frappe-bench/env/lib/python3.11/site-packages/traceback_with_variables/activate_by_import.py'>
      SiteMigration = <class 'frappe.migrate.SiteMigration'>
      site = 'erpnext15-dev1.xxxx.xxx'
  File "/opt/bench/frappe-bench/apps/frappe/frappe/migrate.py", line 186, in run
    self.run_schema_updates()
      self = <frappe.migrate.SiteMigration object at 0x7f0197cecc10>
      site = 'erpnext15-dev1.xxxx.xxx'
      filelock = <function filelock at 0x7f0197876340>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/migrate.py", line 52, in wrapper
    raise e
      args = (<frappe.migrate.SiteMigration object at 0x7f0197cecc10>,)
      kwargs = {}
      method = <function SiteMigration.run_schema_updates at 0x7f0197875da0>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/migrate.py", line 44, in wrapper
    ret = method(*args, **kwargs)
      args = (<frappe.migrate.SiteMigration object at 0x7f0197cecc10>,)
      kwargs = {}
      method = <function SiteMigration.run_schema_updates at 0x7f0197875da0>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/migrate.py", line 120, in run_schema_updates
    frappe.model.sync.sync_all()
      self = <frappe.migrate.SiteMigration object at 0x7f0197cecc10>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/model/sync.py", line 43, in sync_all
    sync_for(app, force, reset_permissions=reset_permissions)
      force = 0
      reset_permissions = False
      app = 'csf_tz'
  File "/opt/bench/frappe-bench/apps/frappe/frappe/model/sync.py", line 111, in sync_for
    import_file_by_path(
      app_name = 'csf_tz'
      force = 0
      reset_permissions = False
      files = ['/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/theme/theme.json', '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/piecework_payment_allocation/piecework_payment_allocation.json', '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/tz_village/tz_village.json', '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/csf_api_response_log/csf_api_response_log.json', '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/expense_type/expense_type.json', '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/expense_record/expense_record.json', '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/email_salary_slips/email_salary_slips.json', '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/repack_template/repack_template.json', '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/repack_template_detail/repack_template_detail.json', '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/parking_bill_details/parking_bill_detail...
      module_name = 'workshop'
      folder = '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/workshop'
      l = 324
      i = 41
      doc_path = '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/student_applicant_fees/student_applicant_fees.json'
  File "/opt/bench/frappe-bench/apps/frappe/frappe/modules/import_file.py", line 142, in import_file_by_path
    import_doc(
      path = '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/student_applicant_fees/student_applicant_fees.json'
      force = 0
      data_import = False
      pre_process = None
      ignore_version = True
      reset_permissions = False
      docs = [{'allow_import': 1, 'autoname': 'naming_series:', 'creation': '2020-08-22 23:55:57.796292', 'doctype': 'DocType', 'document_type': 'Document', 'engine': 'InnoDB', 'fields': [{'fieldname': 'naming_series', 'fieldtype': 'Select', 'label': 'Naming Series', 'options': 'RFEE-.abbr.-.YY.-', 'print_hide': 1, 'set_only_once': 1, 'doctype': 'DocField'}, {'fieldname': 'student', 'fieldtype': 'Link', 'in_global_search': 1, 'label': 'Student Applicant', 'options': 'Student Applicant', 'reqd': 1, 'doctype': 'DocField'}, {'fetch_from': 'student.title', 'fieldname': 'student_name', 'fieldtype': 'Data', 'in_global_search': 1, 'label': 'Student Applicant Name', 'read_only': 1, 'doctype': 'DocField'}, {'fieldname': 'fee_schedule', 'fieldtype': 'Link', 'in_global_search': 1, 'label': 'Fee Schedule', 'options': 'Fee Schedule', 'print_hide': 1, 'read_only': 1, 'doctype': 'DocField'}, {'default': '0', 'fieldname': 'include_payment', 'fieldtype': 'Check', 'hidden': 1, 'label': 'Include Payment', 'print_hide...
      calculated_hash = 'd4fbaf0cc0f6d57d13092aad9a3dfd0b'
      doc = {'allow_import': 1, 'autoname': 'naming_series:', 'creation': '2020-08-22 23:55:57.796292', 'doctype': 'DocType', 'document_type': 'Document', 'engine': 'InnoDB', 'fields': [{'fieldname': 'naming_series', 'fieldtype': 'Select', 'label': 'Naming Series', 'options': 'RFEE-.abbr.-.YY.-', 'print_hide': 1, 'set_only_once': 1, 'doctype': 'DocField'}, {'fieldname': 'student', 'fieldtype': 'Link', 'in_global_search': 1, 'label': 'Student Applicant', 'options': 'Student Applicant', 'reqd': 1, 'doctype': 'DocField'}, {'fetch_from': 'student.title', 'fieldname': 'student_name', 'fieldtype': 'Data', 'in_global_search': 1, 'label': 'Student Applicant Name', 'read_only': 1, 'doctype': 'DocField'}, {'fieldname': 'fee_schedule', 'fieldtype': 'Link', 'in_global_search': 1, 'label': 'Fee Schedule', 'options': 'Fee Schedule', 'print_hide': 1, 'read_only': 1, 'doctype': 'DocField'}, {'default': '0', 'fieldname': 'include_payment', 'fieldtype': 'Check', 'hidden': 1, 'label': 'Include Payment', 'print_hide'...
      db_modified_timestamp = datetime.datetime(2024, 3, 13, 2, 16, 13, 66915)
      is_db_timestamp_latest = True
      stored_hash = None
  File "/opt/bench/frappe-bench/apps/frappe/frappe/modules/import_file.py", line 235, in import_doc
    doc.insert()
      docdict = {'allow_import': 1, 'autoname': 'naming_series:', 'creation': '2020-08-22 23:55:57.796292', 'doctype': 'DocType', 'document_type': 'Document', 'engine': 'InnoDB', 'fields': [{'fieldname': 'naming_series', 'fieldtype': 'Select', 'label': 'Naming Series', 'options': 'RFEE-.abbr.-.YY.-', 'print_hide': 1, 'set_only_once': 1, 'doctype': 'DocField'}, {'fieldname': 'student', 'fieldtype': 'Link', 'in_global_search': 1, 'label': 'Student Applicant', 'options': 'Student Applicant', 'reqd': 1, 'doctype': 'DocField'}, {'fetch_from': 'student.title', 'fieldname': 'student_name', 'fieldtype': 'Data', 'in_global_search': 1, 'label': 'Student Applicant Name', 'read_only': 1, 'doctype': 'DocField'}, {'fieldname': 'fee_schedule', 'fieldtype': 'Link', 'in_global_search': 1, 'label': 'Fee Schedule', 'options': 'Fee Schedule', 'print_hide': 1, 'read_only': 1, 'doctype': 'DocField'}, {'default': '0', 'fieldname': 'include_payment', 'fieldtype': 'Check', 'hidden': 1, 'label': 'Include Payment', 'print_hide'...
      data_import = False
      pre_process = None
      ignore_version = True
      reset_permissions = False
      path = '/opt/bench/frappe-bench/apps/csf_tz/csf_tz/csf_tz/doctype/student_applicant_fees/student_applicant_fees.json'
      controller = <class 'frappe.core.doctype.doctype.doctype.DocType'>
      doc = <DocType: Student Applicant Fees>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/model/document.py", line 314, in insert
    self.run_post_save_methods()
      self = <DocType: Student Applicant Fees>
      ignore_permissions = None
      ignore_links = None
      ignore_if_duplicate = False
      ignore_mandatory = None
      set_name = None
      set_child_names = True
      d = <DocPerm: 91c0c21ea4 parent=Student Applicant Fees>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/model/document.py", line 1129, in run_post_save_methods
    self.run_method("on_update")
      self = <DocType: Student Applicant Fees>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/model/document.py", line 963, in run_method
    out = Document.hook(fn)(self, *args, **kwargs)
      self = <DocType: Student Applicant Fees>
      method = 'on_update'
      args = ()
      kwargs = {}
      fn = <function Document.run_method.<locals>.fn at 0x7f0196360860>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/model/document.py", line 1323, in composer
    return composed(self, method, *args, **kwargs)
      self = <DocType: Student Applicant Fees>
      args = ()
      kwargs = {}
      hooks = [<function build_domain_restriced_doctype_cache at 0x7f01a47d54e0>, <function clear_doctype_notifications at 0x7f01a352f380>, <function process_workflow_actions at 0x7f01964a3b00>, <function attach_files_to_document at 0x7f01a4362160>, <function apply at 0x7f01964c28e0>, <function update_due_date at 0x7f01964c2980>, <function apply_permissions_for_non_standard_user_type at 0x7f01964d40e0>, <function run_server_script_for_doc_event at 0x7f019642f240>, <function run_visibility at 0x7f01964a0f40>]
      method = 'on_update'
      doc_events = {'*': {'on_update': ['frappe.desk.notifications.clear_doctype_notifications', 'frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions', 'frappe.core.doctype.file.utils.attach_files_to_document', 'frappe.automation.doctype.assignment_rule.assignment_rule.apply', 'frappe.automation.doctype.assignment_rule.assignment_rule.update_due_date', 'frappe.core.doctype.user_type.user_type.apply_permissions_for_non_standard_user_type', 'frappe_whatsapp.utils.run_server_script_for_doc_event', 'csf_tz.csf_tz.doctype.visibility.visibility.run_visibility'], 'after_rename': ['frappe.desk.notifications.clear_doctype_notifications'], 'on_cancel': ['frappe.desk.notifications.clear_doctype_notifications', 'frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions', 'frappe.automation.doctype.assignment_rule.assignment_rule.apply', 'frappe_whatsapp.utils.run_server_script_for_doc_event', 'csf_tz.csf_tz.doctype.visibility.visibility.run_visibility'], 'on_trash'...
      handler = 'csf_tz.csf_tz.doctype.visibility.visibility.run_visibility'
      composed = <function Document.hook.<locals>.compose.<locals>.runner at 0x7f0196360d60>
      compose = <function Document.hook.<locals>.compose at 0x7f0196360900>
      f = <function Document.run_method.<locals>.fn at 0x7f0196360860>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/model/document.py", line 1305, in runner
    add_to_return_value(self, fn(self, *args, **kwargs))
      self = <DocType: Student Applicant Fees>
      method = 'on_update'
      args = ()
      kwargs = {}
      add_to_return_value = <function Document.hook.<locals>.add_to_return_value at 0x7f01963607c0>
      fn = <function Document.run_method.<locals>.fn at 0x7f0196360860>
      hooks = (<function build_domain_restriced_doctype_cache at 0x7f01a47d54e0>, <function clear_doctype_notifications at 0x7f01a352f380>, <function process_workflow_actions at 0x7f01964a3b00>, <function attach_files_to_document at 0x7f01a4362160>, <function apply at 0x7f01964c28e0>, <function update_due_date at 0x7f01964c2980>, <function apply_permissions_for_non_standard_user_type at 0x7f01964d40e0>, <function run_server_script_for_doc_event at 0x7f019642f240>, <function run_visibility at 0x7f01964a0f40>)
  File "/opt/bench/frappe-bench/apps/frappe/frappe/model/document.py", line 960, in fn
    return method_object(*args, **kwargs)
      self = <DocType: Student Applicant Fees>
      args = ()
      kwargs = {}
      method_object = <bound method DocType.on_update of <DocType: Student Applicant Fees>>
      method = 'on_update'
  File "/opt/bench/frappe-bench/apps/frappe/frappe/core/doctype/doctype/doctype.py", line 536, in on_update
    self.run_module_method("on_doctype_update")
      self = <DocType: Student Applicant Fees>
      allow_doctype_export = False
  File "/opt/bench/frappe-bench/apps/frappe/frappe/core/doctype/doctype/doctype.py", line 634, in run_module_method
    module = load_doctype_module(self.name, self.module)
      self = <DocType: Student Applicant Fees>
      method = 'on_doctype_update'
      load_doctype_module = <function load_doctype_module at 0x7f01a46d99e0>
  File "/opt/bench/frappe-bench/apps/frappe/frappe/modules/utils.py", line 240, in load_doctype_module
    raise ImportError(msg) from e
      doctype = 'Student Applicant Fees'
      module = 'CSF TZ'
      prefix = ''
      suffix = ''
      app = 'csf_tz'
      key = ('csf_tz', 'Student Applicant Fees', '', '')
      module_name = 'csf_tz.csf_tz.doctype.student_applicant_fees.student_applicant_fees'
      msg = "Module import failed for Student Applicant Fees, the DocType you're trying to open might be deleted.<br> Error: cannot import name 'url_fix' from 'werkzeug' (/opt/bench/frappe-bench/env/lib/python3.11/site-packages/werkzeug/__init__.py)"
builtins.ImportError: Module import failed for Student Applicant Fees, the DocType you're trying to open might be deleted.<br> Error: cannot import name 'url_fix' from 'werkzeug' (/opt/bench/frappe-bench/env/lib/python3.11/site-packages/werkzeug/__init__.py)
@aakvatech
Copy link
Owner

Thanks. This is being worked up.

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

No branches or pull requests

2 participants