specify / specify7

Specify 7
https://www.specifysoftware.org/products/specify-7/
GNU General Public License v2.0
63 stars 36 forks source link

Cannot save Collecting Trip or Storage with an attachment #3289

Closed grantfitzsimmons closed 1 month ago

grantfitzsimmons commented 1 year ago

https://fwri-edge.test.specifysystems.org/specify/view/collectingtrip/991/

After uploading an attachment, you are unable to save without a crash.

Short error:

TypeError at /api/specify/collectingtrip/991/ cannot unpack non-iterable NoneType object 

Full error:

TypeError at /api/specify/collectingtrip/991/ cannot unpack non-iterable NoneType object Request Method: PUT Request URL: http://fwri-edge.test.specifysystems.org/api/specify/collectingtrip/991/ Django Version: 3.2.15 Python Executable: /opt/specify7/ve/bin/python3.8 Python Version: 3.8.0 Python Path: ['/opt/specify7', '/opt/specify7', '/opt/specify7/ve/bin', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '/opt/specify7/ve/lib/python3.8/site-packages', '/opt/specify7'] Server time: Mon, 03 Apr 2023 18:45:01 -0500 Installed Applications: ('django.contrib.sessions', 'django.contrib.staticfiles', 'django.contrib.contenttypes', 'django.contrib.auth', 'specifyweb.specify', 'specifyweb.permissions', 'specifyweb.accounts', 'specifyweb.stored_queries', 'specifyweb.businessrules', 'specifyweb.express_search', 'specifyweb.context', 'specifyweb.attachment_gw', 'specifyweb.frontend', 'specifyweb.barvis', 'specifyweb.report_runner', 'specifyweb.interactions', 'specifyweb.workbench', 'specifyweb.notifications', 'specifyweb.export', 'specifyweb.raven_placeholder') Installed Middleware: ['django.middleware.gzip.GZipMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'specifyweb.context.middleware.ContextMiddleware', 'specifyweb.permissions.middleware.PermissionsMiddleware', 'specifyweb.middleware.general.GeneralMiddleware'] Traceback (most recent call last): File "/opt/specify7/ve/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner response = get_response(request) File "/opt/specify7/ve/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/opt/specify7/specifyweb/specify/views.py", line 29, in wrapped return view(request, *args, **kwargs) File "/opt/specify7/ve/lib/python3.8/site-packages/django/views/decorators/cache.py", line 31, in _cache_controlled response = viewfunc(request, *args, **kw) File "/opt/specify7/specifyweb/specify/views.py", line 62, in view return dispatch_func(request, *args, **kwargs) File "/opt/specify7/specifyweb/specify/api.py", line 132, in resource_dispatch obj = put_resource(request.specify_collection, File "/usr/lib/python3.8/contextlib.py", line 75, in inner return func(*args, **kwds) File "/opt/specify7/specifyweb/specify/api.py", line 612, in put_resource return update_obj(collection, agent, name, id, version, data) File "/opt/specify7/specifyweb/specify/api.py", line 639, in update_obj handle_to_many(collection, agent, obj, data) File "/opt/specify7/specifyweb/specify/api.py", line 571, in handle_to_many rel_obj = create_obj(collection, agent, rel_model, rel_data, parent_obj=obj) File "/opt/specify7/specifyweb/specify/api.py", line 386, in create_obj autonumber_and_save(collection, agent.specifyuser, obj) File "/opt/specify7/specifyweb/specify/autonumbering.py", line 28, in autonumber_and_save obj.save() File "/opt/specify7/specifyweb/specify/build_models.py", line 63, in save return super(model, self).save(*args, **kwargs) File "/opt/specify7/ve/lib/python3.8/site-packages/django/db/models/base.py", line 739, in save self.save_base(using=using, force_insert=force_insert, File "/opt/specify7/ve/lib/python3.8/site-packages/django/db/models/base.py", line 763, in save_base pre_save.send( File "/opt/specify7/ve/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 180, in send return [ File "/opt/specify7/ve/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 181, in (receiver, receiver(signal=self, sender=sender, **named)) File "/opt/specify7/specifyweb/businessrules/orm_signal_handler.py", line 19, in handler rule(sender, kwargs['instance']) File "/opt/specify7/specifyweb/businessrules/attachment_rules.py", line 25, in attachment_jointable_save obj.attachment.scopetype, obj.attachment.scopeid = Scoping(attachee)() Exception Type: TypeError at /api/specify/collectingtrip/991/ Exception Value: cannot unpack non-iterable NoneType object Request information: USER: Specifyuser object (1) GET: No GET data POST: No POST data FILES: No FILES data COOKIES: csrftoken = 'eLhqRqBmQAxdhMNfJuz3UWT1tMMFmmb5DNmjE5oOcgV2lMWqaBva34zPzl5Qxe2K' sessionid = '39wft9ktzpnkushd23gvgfe8xmeqmchw' collection = '4' META: CONTENT_LENGTH = '1239' CONTENT_TYPE = 'application/json' CSRF_COOKIE = 'eLhqRqBmQAxdhMNfJuz3UWT1tMMFmmb5DNmjE5oOcgV2lMWqaBva34zPzl5Qxe2K' HTTP_ACCEPT = 'application/json' HTTP_ACCEPT_ENCODING = 'gzip, deflate, br' HTTP_ACCEPT_LANGUAGE = 'en-US,en;q=0.5' HTTP_CONNECTION = 'close' HTTP_COOKIE = 'csrftoken=eLhqRqBmQAxdhMNfJuz3UWT1tMMFmmb5DNmjE5oOcgV2lMWqaBva34zPzl5Qxe2K; sessionid=39wft9ktzpnkushd23gvgfe8xmeqmchw; collection=4' HTTP_DNT = '1' HTTP_HOST = 'fwri-edge.test.specifysystems.org' HTTP_ORIGIN = 'https://fwri-edge.test.specifysystems.org' HTTP_REFERER = 'https://fwri-edge.test.specifysystems.org/specify/view/collectingtrip/991/' HTTP_SEC_FETCH_DEST = 'empty' HTTP_SEC_FETCH_MODE = 'cors' HTTP_SEC_FETCH_SITE = 'same-origin' HTTP_SEC_GPC = '1' HTTP_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/112.0' HTTP_X_CSRFTOKEN = '********************' HTTP_X_FORWARDED_FOR = '129.237.90.91' HTTP_X_REAL_IP = '129.237.90.91' PATH_INFO = '/api/specify/collectingtrip/991/' QUERY_STRING = '' RAW_URI = '/api/specify/collectingtrip/991/' REMOTE_ADDR = '172.28.0.22' REMOTE_PORT = '58304' REQUEST_METHOD = 'PUT' SCRIPT_NAME = '' SERVER_NAME = '0.0.0.0' SERVER_PORT = '8000' SERVER_PROTOCOL = 'HTTP/1.0' SERVER_SOFTWARE = 'gunicorn/20.1.0' gunicorn.socket = wsgi.errors = wsgi.file_wrapper = wsgi.input = wsgi.input_terminated = True wsgi.multiprocess = True wsgi.multithread = False wsgi.run_once = False wsgi.url_scheme = 'http' wsgi.version = '(1, 0)' Settings: Using settings module settings ABSOLUTE_URL_OVERRIDES = {} ADMINS = '()' ADMIN_MEDIA_PREFIX = '/static/admin/' ALLOWED_HOSTS = ['*'] ALLOW_SPECIFY6_PASSWORDS = '********************' ALLOW_SUPPORT_LOGIN = False ANONYMOUS_USER = None APPEND_SLASH = True AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend'] AUTH_LDAP_SERVER_URI = None AUTH_PASSWORD_VALIDATORS = '********************' AUTH_USER_MODEL = 'specify.Specifyuser' CACHES = {'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}} CACHE_MIDDLEWARE_ALIAS = 'default' CACHE_MIDDLEWARE_KEY_PREFIX = '********************' CACHE_MIDDLEWARE_SECONDS = 600 CELERY_BROKER_URL = 'redis://redis/0' CELERY_RESULT_BACKEND = 'redis://redis/1' CELERY_TASK_DEFAULT_QUEUE = 'fwri-edge' CSRF_COOKIE_AGE = 31449600 CSRF_COOKIE_DOMAIN = None CSRF_COOKIE_HTTPONLY = False CSRF_COOKIE_NAME = 'csrftoken' CSRF_COOKIE_PATH = '/' CSRF_COOKIE_SAMESITE = 'Lax' CSRF_COOKIE_SECURE = False CSRF_FAILURE_VIEW = 'django.views.csrf.csrf_failure' CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN' CSRF_TRUSTED_ORIGINS = [] CSRF_USE_SESSIONS = False DATABASES = {'default': {'ENGINE': 'specifyweb.hibernateboolsbackend.backends.mysql', 'NAME': 'fwri', 'USER': 'root', 'PASSWORD': '********************', 'HOST': 'mariadb', 'PORT': '', 'OPTIONS': {}, 'TEST': {'CHARSET': None, 'COLLATION': None, 'MIGRATE': True, 'MIRROR': None, 'NAME': None}, 'ATOMIC_REQUESTS': False, 'AUTOCOMMIT': True, 'CONN_MAX_AGE': 0, 'TIME_ZONE': None}} DATABASE_HOST = 'mariadb' DATABASE_NAME = 'fwri' DATABASE_OPTIONS = {} DATABASE_PORT = '' DATABASE_ROUTERS = [] DATA_UPLOAD_MAX_MEMORY_SIZE = 419430400 DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000 DATETIME_FORMAT = 'N j, Y, P' DATETIME_INPUT_FORMATS = ['%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S.%f', '%Y-%m-%d %H:%M', '%m/%d/%Y %H:%M:%S', '%m/%d/%Y %H:%M:%S.%f', '%m/%d/%Y %H:%M', '%m/%d/%y %H:%M:%S', '%m/%d/%y %H:%M:%S.%f', '%m/%d/%y %H:%M'] DATE_FORMAT = 'N j, Y' DATE_INPUT_FORMATS = ['%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', '%b %d %Y', '%b %d, %Y', '%d %b %Y', '%d %b, %Y', '%B %d %Y', '%B %d, %Y', '%d %B %Y', '%d %B, %Y'] DEBUG = True DEBUG_PROPAGATE_EXCEPTIONS = False DECIMAL_SEPARATOR = '.' DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' DEFAULT_CHARSET = 'utf-8' DEFAULT_EXCEPTION_REPORTER = 'django.views.debug.ExceptionReporter' DEFAULT_EXCEPTION_REPORTER_FILTER = 'django.views.debug.SafeExceptionReporterFilter' DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' DEFAULT_FROM_EMAIL = 'webmaster@localhost' DEFAULT_HASHING_ALGORITHM = 'sha256' DEFAULT_INDEX_TABLESPACE = '' DEFAULT_TABLESPACE = '' DEPOSITORY_DIR = '/volumes/static-files/depository' DISABLE_AUDITING = False DISALLOWED_USER_AGENTS = [] EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'localhost' EMAIL_HOST_PASSWORD = '********************' EMAIL_HOST_USER = '' EMAIL_PORT = 25 EMAIL_SSL_CERTFILE = None EMAIL_SSL_KEYFILE = '********************' EMAIL_SUBJECT_PREFIX = '[Django] ' EMAIL_TIMEOUT = None EMAIL_USE_LOCALTIME = False EMAIL_USE_SSL = False EMAIL_USE_TLS = False FILE_UPLOAD_DIRECTORY_PERMISSIONS = None FILE_UPLOAD_HANDLERS = ['django.core.files.uploadhandler.MemoryFileUploadHandler', 'django.core.files.uploadhandler.TemporaryFileUploadHandler'] FILE_UPLOAD_MAX_MEMORY_SIZE = 104857600 FILE_UPLOAD_PERMISSIONS = 420 FILE_UPLOAD_TEMP_DIR = None FIRST_DAY_OF_WEEK = 0 FIXTURE_DIRS = [] FORCE_SCRIPT_NAME = None FORMAT_MODULE_PATH = None FORM_RENDERER = 'django.forms.renderers.DjangoTemplates' IGNORABLE_404_URLS = [] INSTALLED_APPS = "('django.contrib.sessions', 'django.contrib.staticfiles', 'django.contrib.contenttypes', 'django.contrib.auth', 'specifyweb.specify', 'specifyweb.permissions', 'specifyweb.accounts', 'specifyweb.stored_queries', 'specifyweb.businessrules', 'specifyweb.express_search', 'specifyweb.context', 'specifyweb.attachment_gw', 'specifyweb.frontend', 'specifyweb.barvis', 'specifyweb.report_runner', 'specifyweb.interactions', 'specifyweb.workbench', 'specifyweb.notifications', 'specifyweb.export', 'specifyweb.raven_placeholder')" INTERNAL_IPS = [] JAVA_PATH = '/usr/bin/java' LANGUAGES = [('en-us', 'English'), ('ru-ru', 'русский'), ('uk-ua', 'українська'), ('fr-fr', 'français'), ('es-es', 'español')] LANGUAGES_BIDI = ['he', 'ar', 'ar-dz', 'fa', 'ur'] LANGUAGE_CODE = 'en-us' LANGUAGE_COOKIE_AGE = None LANGUAGE_COOKIE_DOMAIN = None LANGUAGE_COOKIE_HTTPONLY = False LANGUAGE_COOKIE_NAME = 'language' LANGUAGE_COOKIE_PATH = '/' LANGUAGE_COOKIE_SAMESITE = None LANGUAGE_COOKIE_SECURE = False LOCALE_PATHS = "('/opt/specify7/frontend/locale',)" LOGGING = {'version': 1, 'disable_existing_loggers': False, 'formatters': {'standard': {'format': '[%(asctime)s] [%(levelname)s] [%(name)s:%(lineno)s] %(message)s', 'datefmt': '%d/%b/%Y %H:%M:%S'}}, 'handlers': {'console': {'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'standard'}}, 'loggers': {'django.request': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': False}, 'specifyweb': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': False}}} LOGGING_CONFIG = 'logging.config.dictConfig' LOGIN_REDIRECT_URL = '/' LOGIN_URL = '/accounts/login/' LOGOUT_REDIRECT_URL = None MANAGERS = '()' MASTER_NAME = 'root' MASTER_PASSWORD = '********************' MEDIA_ROOT = '' MEDIA_URL = '/' MESSAGE_STORAGE = 'django.contrib.messages.storage.fallback.FallbackStorage' MIDDLEWARE = ['django.middleware.gzip.GZipMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'specifyweb.context.middleware.ContextMiddleware', 'specifyweb.permissions.middleware.PermissionsMiddleware', 'specifyweb.middleware.general.GeneralMiddleware'] MIGRATION_MODULES = {} MONTH_DAY_FORMAT = 'F j' NOTIFICATION_TTL_DAYS = 7 NUMBER_GROUPING = 0 OAUTH_LOGIN_PROVIDERS = {} PASSWORD_HASHERS = '********************' PASSWORD_RESET_TIMEOUT = '********************' PASSWORD_RESET_TIMEOUT_DAYS = '********************' PREPEND_WWW = False RAVEN_CONFIG = None REPORT_RUNNER_HOST = 'report-runner' REPORT_RUNNER_PORT = '8080' ROOT_URLCONF = 'specifyweb.urls' RO_MODE = False SA_DATABASE_URL = 'mysql://root:root@mariadb:3306/fwri?charset=utf8' SA_POOL_RECYCLE = 3600 SECRET_KEY = '********************' SECURE_BROWSER_XSS_FILTER = False SECURE_CONTENT_TYPE_NOSNIFF = True SECURE_HSTS_INCLUDE_SUBDOMAINS = False SECURE_HSTS_PRELOAD = False SECURE_HSTS_SECONDS = 0 SECURE_PROXY_SSL_HEADER = None SECURE_REDIRECT_EXEMPT = [] SECURE_REFERRER_POLICY = 'same-origin' SECURE_SSL_HOST = None SECURE_SSL_REDIRECT = False SEPARATE_WEB_ATTACHMENT_FOLDERS = None SERVER_EMAIL = 'root@localhost' SESSION_CACHE_ALIAS = 'default' SESSION_COOKIE_AGE = 1209600 SESSION_COOKIE_DOMAIN = None SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_NAME = 'sessionid' SESSION_COOKIE_PATH = '/' SESSION_COOKIE_SAMESITE = 'Lax' SESSION_COOKIE_SECURE = False SESSION_ENGINE = 'django.contrib.sessions.backends.file' SESSION_EXPIRE_AT_BROWSER_CLOSE = True SESSION_FILE_PATH = None SESSION_SAVE_EVERY_REQUEST = False SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' SETTINGS_MODULE = 'settings' SHORT_DATETIME_FORMAT = 'm/d/Y P' SHORT_DATE_FORMAT = 'm/d/Y' SIGNING_BACKEND = 'django.core.signing.TimestampSigner' SILENCED_SYSTEM_CHECKS = [] SITE_ID = 1 SPECIFY_CONFIG_DIR = '/opt/Specify/config' SPECIFY_THICK_CLIENT = '/opt/Specify' STATICFILES_DIRS = "(('config', '/opt/Specify/config'),)" STATICFILES_FINDERS = "('django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder')" STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage' STATIC_ROOT = '' STATIC_URL = '/static/' STATS_URL = 'https://stats.specifycloud.org/capture' SUPPORT_LOGIN_TTL = 300 TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': {'context_processors': ['django.contrib.auth.context_processors.auth', 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages', 'django.template.context_processors.request']}}] TEST_NON_SERIALIZED_APPS = [] TEST_RUNNER = 'django.test.runner.DiscoverRunner' THICK_CLIENT_LOCATION = '/opt/Specify' THOUSAND_SEPARATOR = ',' TIME_FORMAT = 'P' TIME_INPUT_FORMATS = ['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] TIME_ZONE = 'America/Chicago' USE_I18N = True USE_L10N = True USE_THOUSAND_SEPARATOR = False USE_TZ = False USE_X_FORWARDED_HOST = False USE_X_FORWARDED_PORT = False VERSION = 'edge(debug)' WB_UPLOAD_LOG_DIR = '/home/specify/wb_upload_logs' WEBPACK_LOADER = {'MANIFEST_FILE': '/static/manifest.json'} WEB_ATTACHMENT_COLLECTION = 'sp7demofish' WEB_ATTACHMENT_KEY = '********************' WEB_ATTACHMENT_REQUIRES_KEY_FOR_GET = '********************' WEB_ATTACHMENT_URL = 'https://demo-assets.specifycloud.org/web_asset_store.xml' WSGI_APPLICATION = None X_FRAME_OPTIONS = 'DENY' YEAR_MONTH_FORMAT = 'F Y' You’re seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard page generated by the handler for this status code. 

Specify 7 Crash Report - 2023-04-03T18 45 35.340Z.txt

https://user-images.githubusercontent.com/37256050/229598925-40b92f9d-95bc-453e-8348-bf0553deb3fe.mov

Reported By: @Plarson at the Speciforum

It seems like saving certain types of records after creating attachments for them frequently (or maybe always) results in a crash on save. I can’t list all of the tables where it happens off the top of my head, but it is happening consistently for me with the Collecting Trip table. I’m trying to attach a .jpg map but get the attached crash report when I save. Specify 7 Crash Report - 2023-03-30T19_48_48.843Z.txt (801.4 KB) This is not a problem, thankfully, for the collection object tables or the collecting information/event tables, but several others

grantfitzsimmons commented 1 year ago

There are likely other tables with this issue. We need to go through each of the tables that have attachments and try it out.

specifysoftware commented 1 year ago

This issue has been mentioned on Specify Community Forum. There might be relevant details there:

https://discourse.specifysoftware.org/t/attachment-save-errors/1126/2

melton-jason commented 1 year ago

Perhaps a duplicate of #2409 and #2477. Also related to #2525

maxpatiiuk commented 1 year ago

@grantfitzsimmons can you please merge the duplicate issues?

grantfitzsimmons commented 1 year ago

Once you fix the issue :wink: /s

Will do!

grantfitzsimmons commented 1 year ago

This does not happen with a new CT record.

image

https://willemcatno-edge.test.specifysystems.org/specify/view/collectingtrip/1057/

Steps to Recreate:

  1. Edit the Latitude field (text5) (I have been able to recreate this with any field, but this was in the initial bug report)

  2. Save

  3. See error:

Invalid response code 500. Expected one of 200, 201, and 409. Response:
TypeError at /api/specify/collectingtrip/1057/
cannot unpack non-iterable NoneType object

Same database as https://github.com/specify/specify7/issues/2409. Seems to be a back-end error, but needs to be resolved ASAP.

Reported By: Willem at SAIAB

grantfitzsimmons commented 1 year ago

https://user-images.githubusercontent.com/37256050/200051240-8a0c28ad-2f36-41cf-8e81-618070cee456.mp4

This is a problem in edge and testability.

TypeError at /api/specify/storage/91843/
cannot unpack non-iterable NoneType object

Specify 7 Crash Report - 2022-11-04T18_42_26.470Z.txt

grantfitzsimmons commented 1 year ago

@melton-jason Is the fix you used here relevant to these issues?

https://github.com/specify/specify7/pull/2744

melton-jason commented 1 year ago

@melton-jason Is the fix you used here relevant to these issues?

2744

I believe so. I have not looked at the Storage or Collecting Trip issue and uncovered the cause, but I think it is the same issue which means the fix should be the same as the fix for exchange in/exchange out.

The problem occurs as an attachment business rule problem and occurs at this line. https://github.com/specify/specify7/blob/0345888388ccf2d0366c96117b3c58469e770b80/specifyweb/businessrules/attachment_rules.py#L25

More specifically, Scoping(attachee)() is returning None, which python then tries to unpack into the scopetype and scopeid of the object's attachment

For quick reference, here is the Scoping class.

https://github.com/specify/specify7/blob/0345888388ccf2d0366c96117b3c58469e770b80/specifyweb/specify/scoping.py#L11-L96

I tried to alleviate potential issues like this in #2744 by implementing a way to infer the table's scoping, but (obviously) it seems there is a bug in the code. According to the code's intended behavior, Collecting Trip (and thus its attachments) would be scoped to Discipline.

Aside from also fixing the bug, I will add scoping tests on the backend to reduce the possibility of this happening again.

maxpatiiuk commented 1 year ago

That scoping.py file is criminally bug-prone. @melton-jason Can it be refactored to scan whether a table has collectionmemberid, division_id or discipline_id or etc and automatically determine scope based on that rather than have the programmer manually add scope like this to every table?

def locality(self): return self._simple_discipline_scope()

It is needlesly bug prone and causes issues like https://github.com/specify/specify7/issues/2525

melton-jason commented 1 year ago

That scoping.py file is criminally bug-prone. @melton-jason Can it be refactored to scan whether a table has collectionmemberid, division_id or discipline_id or etc and automatically determine scope based on that rather than have the programmer manually add scope like this to every table?

def locality(self): return self._simple_discipline_scope()

It is needlesly bug prone and causes issues like #2525

@maxpatiiuk That is what I tried to do by adding an _infer_scope() method in #2744: it checks if the object has collectionmemberid, division_id, or discipline_id attributes and returns the respective scoping. If the object does not have any of those attributes, I assume the scoping is institution-wide. I can fix the bug, add testing, then remove the redundant table methods and save that space for any tables that we wish to manually override.

maxpatiiuk commented 1 year ago

oh that would be awesome! I didn't notice that. Thanks!

grantfitzsimmons commented 1 year ago

Same issue happens with DNA Sequencing Run attachments

emenslin commented 1 month ago

Fixed