Giter Club home page Giter Club logo

nemo's Introduction

Code style: black Code style: djlint

PyPI - Python Version Docker Image Version (latest semver) GitHub release (latest by date) PyPI

The NEMO web application is laboratory logistics software that strives to be intuitive and easy to use, making life easier in the lab. NEMO manages tool reservations, control access to tools, and streamline logistics and communication. The code is open source and free so that other labs can benefit.

Online demo

An online version of the splash pad is available on a third party website at https://nemo-demo.atlantislabs.io.

User roles

You will be automatically logged in as superadmin "captain".
Use the impersonate feature to switch between user roles:

  • Ned Land: regular user
  • Pierre Aronnax: staff member
  • Assistant Conseil: user office
  • Commander Farragut: accounting
  • Captain Nemo: super admin

Jumbotron

The jumbotron is available at https://nemo-demo.atlantislabs.io/jumbotron/

Kiosk/Area access

You can test the kiosk and area access features by going to the following URLs:

To simulate the badge reader, press F2 then the badge number (1 for captain, 2 for professor, 3 for ned) and press F2 again.

On premise demo

You can try NEMO out using the "splash pad" Docker image, which comes preconfigured and loaded with sample data. Install Docker Community Edition (CE) and run this command:
docker run --detach --name nemo_splash_pad --publish 8000:8000 nanofab/nemo_splash_pad
... then open a web browser to http://localhost:8000. You can stop and remove the NEMO splash pad with the command:
docker rm --force nemo_splash_pad

Documentation

Documentation for NEMO resides in the GitHub wiki.

You can also download the latest NEMO Feature Manual and the NEMO Hardware Accessories document.

If you're interested in deploying NEMO at your organization, there are deployment considerations documented in the wiki. This covers what infrastructure you will need in order to have a robust production-level deployment. The installation guide provides a step-by-step guide to deploying NEMO.

The community page outlines how to ask questions and contribute to NEMO. Bugs can be reported to the issues page. If you've found a security issue with NEMO then please read our security policy and tell us discretely.

Screenshots

Here are some sample screenshots showing some of NEMO's primary features.

Landing page - the first thing a user sees when visiting NEMO Landing page

Calendar - manage tool reservations Calendar

Tool control (with hardware interlocks) - enable or disable tools, report problems, view tool status Tool control

Maintenance tasks Maintenance tasks

nemo's People

Contributors

abuckles-uci avatar amunter-nist avatar bpedersen2 avatar dependabot[bot] avatar dsbarth avatar dylan-klomparens avatar htmr80 avatar hyandell avatar jat255 avatar laytonc avatar lizhongz avatar nickishaw avatar pdessauw avatar r-xyz avatar rptmat57 avatar sbonaime avatar valilian avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nemo's Issues

Feedback and Safety emails fail to deliver if SMTP server does not allow sender spoofing

After reviewing email configuration, we are experiencing the following error when a user reports a Feedback or Safety Issue.

NEMO.utilities ERROR {'safety_or_feedback_email': (553, b'5.7.1 <user_email>: Sender address rejected: not owned by user SMTP USER')}

Traceback (most recent call last):
  None

Despite the missing traceback, we can confirm the error is due to the SMTP server rejecting any attempt to send email from an email address not associated with the SMTP user/credentials (Security measure).

Would it be possible (and would it make sense) to send the email from user office email, and add the user email in TO/CC? The recipients will always be able to reply to the user directly using Reply-All; alternatively (less practical) the user name/email could be shown in the email. Also, the users will have track record of the message submitted.

I can help with the PR if needed, still I guess it would require a small change in Email Logs to keep track of from which user the event originated (otherwise all Feedback/Issue email will only show user office as Sender).

EDIT: I am not sure if there are also other email types which might be sent on behalf of a user. Same issue would apply there.

Auth and /static problem with the docker installation (based on the wiki)

I've followed the documentation (from the wiki) for deploying the app using docker. I've managed to have it working with the 1.12.0 but the /static directory wasn't working (correctly configured, but all request to static resources failed with 404).

I've decided to update my config to the lasted image released ("3.9.2"). I can access the login page, but submitting returns a 405 on the post to /login/?next=/. I'm authenticating against an LDAP / AD server.

My settings.py is based on the one provided there : https://github.com/usnistgov/NEMO/wiki/Installation-with-Docker.

Also, when attaching to the docker container, I can see multiple errors during auth attempts :
...
[22/Apr/2021 14:49:59] django.db.backends DEBUG (0.000) SELECT "django_content_type"."id", "django_content_type"."app_label", "django_content_type"."model" FROM "django_content_type" WHERE ("django_content_type"."app_label" = 'NEMO' AND "django_content_type"."model" = 'news'); args=('NEMO', 'news')
[22/Apr/2021 14:49:59] django.template DEBUG Exception while resolving variable 'name' in template 'unknown'.
...
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/django/template/base.py", line 829, in _resolve_lookup
current = current[bit]
TypeError: 'URLResolver' object is not subscriptable
...
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/django/template/base.py", line 837, in _resolve_lookup
current = getattr(current, bit)
AttributeError: 'URLResolver' object has no attribute 'name'
...
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/django/template/base.py", line 843, in _resolve_lookup
current = current[int(bit)]
ValueError: invalid literal for int() with base 10: 'name'
...

Seems like an issue with the image itself (django templating errors?), but I'm also not sure at all if my settings.py file is valid since the doc sometimes refer to old versions of the app (the docker-compose linked in the doc uses the 1.12.0 version for example).

Maintenance records features

It would be helpful to be able to search through the maintenance records. Searching for key words, filtering by tool, and sorting by date would make the maintenance feature much more useful.

static PATH in settings.py

Hi,

After building my own docker image, I have a problem with the PATH of the static files.

My settings.py file is in /home/resadocker/nemo
My static dir is /home/resadocker/nemo/static

with this configuration

STATIC_ROOT = '/home/resadocker/nemo/static/'
STATIC_URL = '/static/'

I have the following errors

Not Found: /static/nemo.css
Not Found: /static/icons/preferences.png
Not Found: /static/moment.js
Not Found: /static/bootstrap/css/bootstrap.css
Not Found: /static/jquery.js
Not Found: /static/bootstrap/js/bootstrap.js
Not Found: /static/bootstrap/css/bootstrap.css
Not Found: /static/bootstrap/css/bootstrap-theme.css
Not Found: /static/typeahead.jquery.js
Not Found: /static/nemo.js
Not Found: /static/nemo.css

If I change to

STATIC_ROOT = '/home/resadocker/nemo/static/'
STATIC_URL = '/home/resadocker/nemo/static/'

I have other errors

Not Found: /home/resadocker/nemo/static/typeahead.jquery.js
Not Found: /home/resadocker/nemo/static/moment.js
Not Found: /home/resadocker/nemo/static/bootstrap/css/bootstrap-theme.css
Not Found: /home/resadocker/nemo/static/nemo.css
Not Found: /home/resadocker/nemo/static/jquery.js
Not Found: /home/resadocker/nemo/static/bootstrap/js/bootstrap.js
Not Found: /home/resadocker/nemo/static/bootstrap/css/bootstrap.css
Not Found: /home/resadocker/nemo/static/nemo.js
Not Found: /home/resadocker/nemo/static/icons/preferences.png
Not Found: /home/resadocker/nemo/static/bootstrap/css/bootstrap-theme.css
Not Found: /home/resadocker/nemo/static/typeahead.jquery.js
Not Found: /home/resadocker/nemo/static/nemo.js
Not Found: /home/resadocker/nemo/static/nemo.css

But those files exist !

ll /home/resadocker/nemo/static/nemo.css
-rw-r--r-- 1 root root 4.4K Feb 3 17:52 /home/resadocker/nemo/static/nemo.css

And of course, I have no graphic or colors on the main page. However, I can login !

I start the docker with or with sudo with same problem

sudo docker run --publish 8000:8000 --volume /home/resadocker/nemo:/nemo my_nemo

Email broadcast feature request

Is it possible to add an option for email broadcast to select all users who are registered (later filterable to all active users)? It seems like this would be a common way to send messages to everyone using our facility.

Feedback page not recognizing an email has been added.

Not sure if anything is missing as far as configuration goes, in order for this to function as expected.
image
After adding in a valid email address, and saving said email address, the Feedback page does not update to allow for the submission of user feedback.
image
Does anything extra need to be done, in order to have this function as expected? I see that email templates need to be added in, is this a requirement before the page is viewable?

Wrong rendering of Unicode characters in ICS invitations

Hi all,

Thanks as always for such a great tool.
I noticed a minor bug regarding calendar invitation and character encoding.
If an Unicode character (e.g. ®) is present in Site title (under Customizations-->Application) or in RESERVATION_ORGANIZER (under settings.py), the ICS invitation will be rendered incorrectly, showing strange characters.
In some cases (like for ®), the pair composed by the Unicode character and the one following (e.g. ]) is swapped for a single Japanese character (e.g. ); in other cases (like for α), only the Unicode is swapped for its code (e.g. \u03b1).

I guess the problem is somehow related to

def create_email_attachment(stream, filename=None, maintype="application", subtype="octet-stream", **content_type_params) -> MIMEBase:

Googling around, the issue seems related to missing encoding specification for the ICS file attachment (which will default to iso-8859-1 instead of UTF-8).

Below you can compare the headers proceeding the ics attachment in base64, taken from an email invitation received from NEMO:

Content-Type: text/calendar; method="REQUEST"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
[ICS in base64]

or from a correct email invitation from Google Calendar:

Content-Type: text/calendar; charset="UTF-8"; method=REQUEST
Content-Transfer-Encoding: quoted-printable
[ ICS in clear text ]
Content-Type: application/ics; name="invite.ics"
Content-Disposition: attachment; filename="invite.ics"
Content-Transfer-Encoding: base64
[ICS in base64]

or from a correct email invitation from Thunderbird;
I hope this helps to identify the problem.
Thanks.

django.db.utils.OperationalError: no such column: expired

Hi,
With the latest commit 553405b,
I don't have a login screen but an error 500 through nginx.
I have the following exception in the django_error.log

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

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/views/decorators/http.py", line 40, in inner
    return func(request, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/NEMO/views/landing.py", line 21, in landing
    delete_expired_alerts()
  File "/usr/local/lib/python3.6/site-packages/NEMO/views/alerts.py", line 59, in delete_expired_alerts
    Alert.objects.filter(expiration_time__lt=timezone.now()).update(expired=True)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 741, in update
    rows = query.get_compiler(self.db).execute_sql(CURSOR)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1462, in execute_sql
    cursor = super().execute_sql(result_type)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1133, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 67, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 76, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 383, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.OperationalError: no such column: expired

Installation for development on Ubuntu 17.10

Hi Dylan,

I am part of an electron microscopy group in Norway interested in using your software, but I'm having problems setting it up on Ubuntu 17.10 using Docker 1.13.1 and following your instructions on the wiki pages Installation with Docker and Development & test settings.py.

First i naively try:

sudo docker run --publish 8000:8000 dylanklomparens/nemo

and get the error ([...] indicate blocks of text)

[...]
  File "/usr/lib/python3.6/site-packages/django/conf/__init__.py", line 39, in _setup
    % (desc, ENVIRONMENT_VARIABLE))
django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE, but 
settings are not configured. You must either define the environment variable 
DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.

What I do next:

  1. Create directories (/email, /media, /secrets within /nemo) and files (django_secret_key.txt within /secrets, and settings.py and sqlite.db within /nemo) as per Development & test settings.py, however not in my root dir, but in my local user dir as /home/<username>/nemo/.
  2. django_secret_key.txt is left empty.
  3. Edit the following lines in the settings.py file you provide (original lines commented out) to point Django to the (presumably) correct places:
[...]

import os

# To avoid hard coding paths. If settings.py is in /home/<username>/nemo/, then
# BASE_DIR is the string '/home/<username>/nemo/'
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

[...]

#EMAIL_FILE_PATH = '/nemo/email'
EMAIL_FILE_PATH = os.path.join(BASE_DIR, 'email')

[...]

                #'NAME': '/nemo/sqlite.db',
                'NAME': os.path.join(BASE_DIR, 'sqlite.db'),

[...]

#STATIC_ROOT = '/nemo/static'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_URL = '/static/'
#MEDIA_ROOT = '/nemo/media'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

[...]

#SECRET_KEY = get_file_contents('/nemo/secrets/django_secret_key.txt')
# An arbitrary example secret key
SECRET_KEY = 'zek$#1$0@(i$t5(q*ne-r$s29#yhe9)!g$y9vc_-th4*0dxfi4'

[...]
  1. Run the following (with <username> equal to my local username, note the use of sudo):
sudo docker run --publish 8000:8000 \
  --volume /home/<username>/nemo/settings.py:/root/NEMO/settings.py \
  --env "PYTHONPATH=/root/NEMO" \
  --env "DJANGO_SETTINGS_MODULE=settings" \
  dylanklomparens/nemo

and get

[...]

Migrations for 'NEMO':
  usr/lib/python3.6/site-packages/NEMO/migrations/0001_initial.py
    - Create model Account
    - Create model ActivityHistory

[...]

Operations to perform:
  Apply all migrations: NEMO, admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK

[...]

Copying '/usr/lib/python3.6/site-packages/rest_framework/static/rest_framework/fonts/
glyphicons-halflings-regular.woff2'
Copying '/usr/lib/python3.6/site-packages/rest_framework/static/rest_framework/fonts/
glyphicons-halflings-regular.eot'

136 static files copied to '/nemo/static'.
[2018-01-24 16:05:11 +0000] [20] [INFO] Starting gunicorn 19.7.1
[2018-01-24 16:05:11 +0000] [20] [INFO] Listening at: http://127.0.0.1:8000 (20)
[2018-01-24 16:05:11 +0000] [20] [INFO] Using worker: sync
[2018-01-24 16:05:11 +0000] [23] [INFO] Booting worker with pid: 23
  1. Check http://localhost:8000, but nothing is there, and I conclude that I must have done something/a lot of things wrong.

I am unfortunately unexperienced with Docker, but have used Django off and on for three years.

A few questions:

  • Am I to leave django_secret_key.txt empty?
  • Is the settings file refered to while calling docker, /home/user/settings.py the same as created locally in the /nemo dir as per Development & test settings.py, which in my case is /home/<username>/nemo/settings.py?
  • Do I need to download the online repo and point to it somehow for NEMO to work with Docker locally?

Thank you in advance, looking forward to hear from you!

"post usage questions:" Bug ?

If I put anything in the text area "post usage questions:" in a tool, I can see an error message in the tool control page for this tool. What this text area is made for and how can we use it ?

 Unable to retrieve tool status. JSONDecodeError at /tool_status/1/ Expecting value: line 1 column 1 (char 0) Request Method: GET Request URL: http://localhost:8000/tool_status/1/ Django Version: 1.11.27 Python Executable: /usr/local/bin/python Python Version: 3.6.8 Python Path: ['/usr/local/bin', '/nemo', '/usr/local/lib/python36.zip', '/usr/local/lib/python3.6', '/usr/local/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/site-packages'] Server time: Wed, 5 Feb 2020 09:30:08 -0500 Installed Applications: ['django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', 'django.contrib.humanize', 'NEMO', 'rest_framework', 'django_filters'] Installed Middleware: ['django.middleware.security.SecurityMiddleware', 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.RemoteUserMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.common.BrokenLinkEmailsMiddleware', 'NEMO.middleware.DeviceDetectionMiddleware'] Traceback: File "/usr/local/lib/python3.6/json/decoder.py" in raw_decode 355. obj, end = self.scan_once(s, idx) During handling of the above exception (0), another exception occurred: File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner 41. response = get_response(request) File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response 187. response = self.process_exception_by_middleware(e, request) File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response 185. response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/usr/local/lib/python3.6/site-packages/django/contrib/auth/decorators.py" in _wrapped_view 23. return view_func(request, *args, **kwargs) File "/usr/local/lib/python3.6/site-packages/django/views/decorators/http.py" in inner 40. return func(request, *args, **kwargs) File "/usr/local/lib/python3.6/site-packages/NEMO/views/tool_control.py" in tool_status 58. 'post_usage_questions': DynamicForm(tool.post_usage_questions).render(), File "/usr/local/lib/python3.6/site-packages/NEMO/widgets/dynamic_form.py" in __init__ 12. self.questions = loads(questions) if questions else None File "/usr/local/lib/python3.6/json/__init__.py" in loads 354. return _default_decoder.decode(s) File "/usr/local/lib/python3.6/json/decoder.py" in decode 339. obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "/usr/local/lib/python3.6/json/decoder.py" in raw_decode 357. raise JSONDecodeError("Expecting value", s, err.value) from None Exception Type: JSONDecodeError at /tool_status/1/ Exception Value: Expecting value: line 1 column 1 (char 0) Request information: USER: Captain Nemo (captain) GET: No GET data POST: No POST data FILES: No FILES data COOKIES: csrftoken = 'XImUzd6Th0JtMl1e0YQ6Crhi3HxICpMKJP2jjJjjnCtOdpLSUXCGzt5ZnnsTXw79' sessionid = 'epiaxy3mdz78g7yeqts6c0j442ylvjtz' META: CONTENT_LENGTH = '' CONTENT_TYPE = 'text/plain' CSRF_COOKIE = 'XImUzd6Th0JtMl1e0YQ6Crhi3HxICpMKJP2jjJjjnCtOdpLSUXCGzt5ZnnsTXw79' DJANGO_SETTINGS_MODULE = 'splash_pad_settings' GATEWAY_INTERFACE = 'CGI/1.1' GPG_KEY = '0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D' HOME = '/root' HOSTNAME = '574358ebdcbf' HTTP_ACCEPT = 'text/html, */*; q=0.01' HTTP_ACCEPT_ENCODING = 'gzip, deflate, br' HTTP_ACCEPT_LANGUAGE = 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6' HTTP_CONNECTION = 'keep-alive' HTTP_COOKIE = 'csrftoken=XImUzd6Th0JtMl1e0YQ6Crhi3HxICpMKJP2jjJjjnCtOdpLSUXCGzt5ZnnsTXw79; sessionid=epiaxy3mdz78g7yeqts6c0j442ylvjtz' HTTP_DNT = '1' HTTP_HOST = 'localhost:8000' HTTP_REFERER = 'http://localhost:8000/tool_control/' HTTP_SEC_FETCH_MODE = 'cors' HTTP_SEC_FETCH_SITE = 'same-origin' HTTP_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36' HTTP_X_REQUESTED_WITH = 'XMLHttpRequest' LANG = 'C.UTF-8' PATH = '/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' PATH_INFO = '/tool_status/1/' PYTHONPATH = '/nemo/' PYTHON_PIP_VERSION = '19.1.1' PYTHON_VERSION = '3.6.8' QUERY_STRING = '' REMOTE_ADDR = '172.17.0.1' REMOTE_HOST = '' REMOTE_USER = 'captain' REQUEST_METHOD = 'GET' RUN_MAIN = 'true' SCRIPT_NAME = '' SERVER_NAME = '574358ebdcbf' SERVER_PORT = '8000' SERVER_PROTOCOL = 'HTTP/1.1' SERVER_SOFTWARE = 'WSGIServer/0.2' TZ = 'America/New_York' wsgi.errors = <_io.TextIOWrapper name='' mode='w' encoding='UTF-8'> wsgi.file_wrapper = '' wsgi.input = <_io.BufferedReader name=5> wsgi.multiprocess = False wsgi.multithread = True wsgi.run_once = False wsgi.url_scheme = 'http' wsgi.version = '(1, 0)' Settings: Using settings module splash_pad_settings ABSOLUTE_URL_OVERRIDES = {} ADMINS = [('System administrator', '[email protected]')] ALLOWED_HOSTS = [] ALLOW_CONDITIONAL_URLS = True APPEND_SLASH = True AUTHENTICATION_BACKENDS = ['NEMO.views.authentication.RemoteUserAuthenticationBackend'] AUTH_PASSWORD_VALIDATORS = '********************' AUTH_USER_MODEL = 'NEMO.User' CACHES = {'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}} CACHE_MIDDLEWARE_ALIAS = 'default' CACHE_MIDDLEWARE_KEY_PREFIX = '********************' CACHE_MIDDLEWARE_SECONDS = 600 CSRF_COOKIE_AGE = 31449600 CSRF_COOKIE_DOMAIN = None CSRF_COOKIE_HTTPONLY = False CSRF_COOKIE_NAME = 'csrftoken' CSRF_COOKIE_PATH = '/' 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': 'django.db.backends.sqlite3', 'NAME': '/nemo/sqlite.db', 'ATOMIC_REQUESTS': False, 'AUTOCOMMIT': True, 'CONN_MAX_AGE': 0, 'OPTIONS': {}, 'TIME_ZONE': None, 'USER': '', 'PASSWORD': '********************', 'HOST': '', 'PORT': '', 'TEST': {'CHARSET': None, 'COLLATION': None, 'NAME': None, 'MIRROR': None}}} DATABASE_ROUTERS = [] DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440 DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000 DATETIME_FORMAT = 'l, F jS, Y @ g:i A' DATETIME_INPUT_FORMATS = ['%m/%d/%Y %I:%M %p'] DATE_FORMAT = 'm/d/Y' DATE_INPUT_FORMATS = ['%m/%d/%Y'] DEBUG = True DEBUG_PROPAGATE_EXCEPTIONS = False DECIMAL_SEPARATOR = '.' DEFAULT_CHARSET = 'utf-8' DEFAULT_CONTENT_TYPE = 'text/html' DEFAULT_EXCEPTION_REPORTER_FILTER = 'django.views.debug.SafeExceptionReporterFilter' DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' DEFAULT_FROM_EMAIL = 'webmaster@localhost' DEFAULT_INDEX_TABLESPACE = '' DEFAULT_TABLESPACE = '' DISALLOWED_USER_AGENTS = [] EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'mail.example.org' 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_CHARSET = 'utf-8' FILE_UPLOAD_DIRECTORY_PERMISSIONS = None FILE_UPLOAD_HANDLERS = ['django.core.files.uploadhandler.MemoryFileUploadHandler', 'django.core.files.uploadhandler.TemporaryFileUploadHandler'] FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440 FILE_UPLOAD_PERMISSIONS = None FILE_UPLOAD_TEMP_DIR = None FIRST_DAY_OF_WEEK = 0 FIXTURE_DIRS = ['/nemo/'] FORCE_SCRIPT_NAME = None FORMAT_MODULE_PATH = None FORM_RENDERER = 'django.forms.renderers.DjangoTemplates' IDENTITY_SERVICE = {'available': False, 'url': 'https://identity.example.org/', 'domains': []} IGNORABLE_404_URLS = [] INSTALLED_APPS = ['django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', 'django.contrib.humanize', 'NEMO', 'rest_framework', 'django_filters'] INTERNAL_IPS = [] LANGUAGES = [('af', 'Afrikaans'), ('ar', 'Arabic'), ('ast', 'Asturian'), ('az', 'Azerbaijani'), ('bg', 'Bulgarian'), ('be', 'Belarusian'), ('bn', 'Bengali'), ('br', 'Breton'), ('bs', 'Bosnian'), ('ca', 'Catalan'), ('cs', 'Czech'), ('cy', 'Welsh'), ('da', 'Danish'), ('de', 'German'), ('dsb', 'Lower Sorbian'), ('el', 'Greek'), ('en', 'English'), ('en-au', 'Australian English'), ('en-gb', 'British English'), ('eo', 'Esperanto'), ('es', 'Spanish'), ('es-ar', 'Argentinian Spanish'), ('es-co', 'Colombian Spanish'), ('es-mx', 'Mexican Spanish'), ('es-ni', 'Nicaraguan Spanish'), ('es-ve', 'Venezuelan Spanish'), ('et', 'Estonian'), ('eu', 'Basque'), ('fa', 'Persian'), ('fi', 'Finnish'), ('fr', 'French'), ('fy', 'Frisian'), ('ga', 'Irish'), ('gd', 'Scottish Gaelic'), ('gl', 'Galician'), ('he', 'Hebrew'), ('hi', 'Hindi'), ('hr', 'Croatian'), ('hsb', 'Upper Sorbian'), ('hu', 'Hungarian'), ('ia', 'Interlingua'), ('id', 'Indonesian'), ('io', 'Ido'), ('is', 'Icelandic'), ('it', 'Italian'), ('ja', 'Japanese'), ('ka', 'Georgian'), ('kk', 'Kazakh'), ('km', 'Khmer'), ('kn', 'Kannada'), ('ko', 'Korean'), ('lb', 'Luxembourgish'), ('lt', 'Lithuanian'), ('lv', 'Latvian'), ('mk', 'Macedonian'), ('ml', 'Malayalam'), ('mn', 'Mongolian'), ('mr', 'Marathi'), ('my', 'Burmese'), ('nb', 'Norwegian Bokmål'), ('ne', 'Nepali'), ('nl', 'Dutch'), ('nn', 'Norwegian Nynorsk'), ('os', 'Ossetic'), ('pa', 'Punjabi'), ('pl', 'Polish'), ('pt', 'Portuguese'), ('pt-br', 'Brazilian Portuguese'), ('ro', 'Romanian'), ('ru', 'Russian'), ('sk', 'Slovak'), ('sl', 'Slovenian'), ('sq', 'Albanian'), ('sr', 'Serbian'), ('sr-latn', 'Serbian Latin'), ('sv', 'Swedish'), ('sw', 'Swahili'), ('ta', 'Tamil'), ('te', 'Telugu'), ('th', 'Thai'), ('tr', 'Turkish'), ('tt', 'Tatar'), ('udm', 'Udmurt'), ('uk', 'Ukrainian'), ('ur', 'Urdu'), ('vi', 'Vietnamese'), ('zh-hans', 'Simplified Chinese'), ('zh-hant', 'Traditional Chinese')] LANGUAGES_BIDI = ['he', 'ar', 'fa', 'ur'] LANGUAGE_CODE = 'en-us' LANGUAGE_COOKIE_AGE = None LANGUAGE_COOKIE_DOMAIN = None LANGUAGE_COOKIE_NAME = 'django_language' LANGUAGE_COOKIE_PATH = '/' LOCALE_PATHS = [] LOGGING = {} LOGGING_CONFIG = 'logging.config.dictConfig' LOGIN_REDIRECT_URL = 'login' LOGIN_URL = 'login' LOGOUT_REDIRECT_URL = None MANAGERS = [('System administrator', '[email protected]')] MEDIA_ROOT = '/nemo/media/' MEDIA_URL = '/media/' MESSAGE_STORAGE = 'django.contrib.messages.storage.fallback.FallbackStorage' MIDDLEWARE = ['django.middleware.security.SecurityMiddleware', 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.RemoteUserMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.common.BrokenLinkEmailsMiddleware', 'NEMO.middleware.DeviceDetectionMiddleware'] MIDDLEWARE_CLASSES = ['django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware'] MIGRATION_MODULES = {} MONTH_DAY_FORMAT = 'F j' NUMBER_GROUPING = 0 PASSWORD_HASHERS = '********************' PASSWORD_RESET_TIMEOUT_DAYS = '********************' PREPEND_WWW = False REST_FRAMEWORK = {'DEFAULT_PERMISSION_CLASSES': ('NEMO.permissions.BillingAPI',), 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',), 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 1000} ROOT_URLCONF = 'NEMO.urls' SECRET_KEY = '********************' SECURE_BROWSER_XSS_FILTER = False SECURE_CONTENT_TYPE_NOSNIFF = False SECURE_HSTS_INCLUDE_SUBDOMAINS = False SECURE_HSTS_PRELOAD = False SECURE_HSTS_SECONDS = 0 SECURE_PROXY_SSL_HEADER = None SECURE_REDIRECT_EXEMPT = [] SECURE_SSL_HOST = None SECURE_SSL_REDIRECT = False SERVER_EMAIL = 'NEMO Server Administrator ' 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_SECURE = False SESSION_ENGINE = 'django.contrib.sessions.backends.db' SESSION_EXPIRE_AT_BROWSER_CLOSE = False SESSION_FILE_PATH = None SESSION_SAVE_EVERY_REQUEST = False SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' SETTINGS_MODULE = 'splash_pad_settings' SHORT_DATETIME_FORMAT = 'm/d/Y P' SHORT_DATE_FORMAT = 'm/d/Y' SIGNING_BACKEND = 'django.core.signing.TimestampSigner' SILENCED_SYSTEM_CHECKS = [] STATICFILES_DIRS = [] STATICFILES_FINDERS = ['django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder'] STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage' STATIC_ROOT = None STATIC_URL = '/static/' TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, 'OPTIONS': {'context_processors': ['NEMO.context_processors.hide_logout_button', 'NEMO.context_processors.device', 'django.contrib.auth.context_processors.auth', 'django.template.context_processors.debug', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages']}}] TEST_NON_SERIALIZED_APPS = [] TEST_RUNNER = 'django.test.runner.DiscoverRunner' THOUSAND_SEPARATOR = ',' TIME_FORMAT = 'g:i A' TIME_INPUT_FORMATS = ['%I:%M %p'] TIME_ZONE = 'America/New_York' USE_ETAGS = False USE_I18N = False USE_L10N = False USE_THOUSAND_SEPARATOR = False USE_TZ = True USE_X_FORWARDED_HOST = False USE_X_FORWARDED_PORT = False WSGI_APPLICATION = 'NEMO.wsgi.application' X_FRAME_OPTIONS = 'SAMEORIGIN' 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.

Email broadcast bug.

I am trying to send an email broadcast but it is telling me "A generic email format has not been customized for your organization yet. You'll need to create one before you can send email broadcasts from within 4D LABS NEMO. Please visit the customizations page to upload a template.".

However, I can see the preview in the broadcast window and there is a template in the customizations page. I'm also pretty sure I was able to send an email at the end of September.

psycopg2 not available in "nanofab/nemo" docker image

The documentation recommends PostgreSQL for instances with a lot of concurrent connections but the docker image doesn't include the required python package "psycopg2".

Trying to configure the app with "nanofab/nemo:3.9.2", using the django.db.backends.postgresql_psycopg2 database engine (the engine refered to in the Django documentation) :

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/postgresql/base.py", line 20, in <module>
    import psycopg2 as Database
ModuleNotFoundError: No module named 'psycopg2'

Multiple syntax errors found in multiple files

In many files it appears the same error syntax
like this one: f'and some string'
For example here

error_message = f"The interlock command for the {tool} failed. The error message returned: {tool.interlock.most_recent_reply}"

logger.error(f'The identity service encountered a problem while attempting to delete a user. The HTTP error is {result.status_code}: {result.text}')

and many many more files

500 Server Error due to display of reservation questions

We recently noticed an issue with certain reservations returning a server error when trying to view the "event details":

image

image

After some debugging, it appears this is occurring due to groups within the reservation questions, and what happens if a value defined for some items of the group, but not others (we noticed this with the periodic table question).

Here's the structure we're using for questions:

[
 {
   "type": "textbox",
   "name": "project_id",
   "title": "Project ID",
   "title_html": "<strong>Project ID: </strong>",
   "max-width": "350",
 },
 {
   "type": "textbox",
   "name": "experiment_title",
   "title": "Title of Experiment",
   "title_html": "<strong>Title of Experiment: </strong>",
   "max-width": "350",
   "required": true
 },
 {
   "type": "textarea",
   "name": "experiment_purpose",
   "title": "Experiment Purpose",
   "title_html": "<strong>Experiment Purpose: </strong>",
   "max-width": "350",
   "rows": "5",
   "required": true
 },
 {
   "type": "radio",
   "title": "Agree to curation",
   "title_html": "<strong>Curate this session's data: </strong>",
   "choices": ["Agree", "Disagree"],
   "name": "data_consent",
   "max-width": "350",
   "required": false,
   "default_choice": "Agree"
 },
{
   "type": "group",
   "title": "Sample information",
   "title_html": "<strong>Sample information: </strong><p>Enter information for at least 1 sample below, and click the \"Add\" button to add up to 5 samples.</p>",
   "name": "sample_group",
   "max_number": 5,
   "questions":
   [
     {
       "type": "textbox",
       "name": "sample_name",
       "max-width": "350",
       "required": true,
       "title": "Sample Name / PID",
       "title_html": "<em>Sample Name / PID: </em>",
       "help": "Write in a sample name, or paste a persistent identifier (PID) for your specimen from one of the LIMS/repositories above."
     },
     {
       "type": "radio",
       "title": "Sample or PID?",
       "title_html": "<em>Is this a sample name or a PID? </em>",
       "choices": ["Sample Name", "PID"],
       "name": "sample_or_pid",
       "required": true,
       "default_choice": null
     },
     {
       "type": "textarea",
       "name": "sample_details",
       "title": "Sample Details",
       "title_html": "<em>Sample Details: </em>",
       "max-width": "350",
       "rows": "3",
       "required": false
     },
     {
       "type": "periodic-table",
       "title": "Sample elements",
       "title_html": "<em>What elements do you expect to be in your specimen? </em>",
       "name": "periodic_table",
       "required": false,
       "collapsible": true,
       "collapsed": false
      }
   ]
 }
]

And here's an example of question data on a reservation that causes an error:

{
	"project_id": {
		"type": "textbox",
		"name": "project_id",
		"title": "Project ID",
		"title_html": "<strong>Project ID: </strong>",
		"max-width": "350",
		"user_input": "test"
	},
	"experiment_title": {
		"type": "textbox",
		"name": "experiment_title",
		"title": "Title of Experiment",
		"title_html": "<strong>Title of Experiment: </strong>",
		"max-width": "350",
		"required": true,
		"user_input": "test"
	},
	"experiment_purpose": {
		"type": "textarea",
		"name": "experiment_purpose",
		"title": "Experiment Purpose",
		"title_html": "<strong>Experiment Purpose: </strong>",
		"max-width": "350",
		"rows": "5",
		"required": true,
		"user_input": "test"
	},
	"data_consent": {
		"type": "radio",
		"title": "Agree to NexusLIMS curation",
		"title_html": "<strong>Curate this session's data: </strong>",
		"choices": [
			"Agree",
			"Disagree"
		],
		"name": "data_consent",
		"max-width": "350",
		"required": false,
		"default_choice": "Agree",
		"user_input": "Agree"
	},
	"sample_group": {
		"type": "group",
		"title": "Sample information",
		"title_html": "<strong>Sample information: </strong>",
		"name": "sample_group",
		"max_number": 5,
		"questions": [
			{
				"type": "textbox",
				"name": "sample_name",
				"max-width": "350",
				"required": true,
				"title": "Sample Name / PID",
				"title_html": "<em>Sample Name / PID: </em>",
				"help": "Write in a sample name, or paste a persistent identifier (PID) for your specimen from one of the LIMS/repositories above."
			},
			{
				"type": "radio",
				"title": "Sample or PID?",
				"title_html": "<em>Is this a sample name or a PID? </em>",
				"choices": [
					"Sample Name",
					"PID"
				],
				"name": "sample_or_pid",
				"required": true,
				"default_choice": null
			},
			{
				"type": "textarea",
				"name": "sample_details",
				"title": "Sample Details",
				"title_html": "<em>Sample Details: </em>",
				"max-width": "350",
				"rows": "3",
				"required": false
			},
			{
				"type": "periodic-table",
				"title": "Sample elements",
				"title_html": "<em>What elements do you expect to be in your specimen? </em>",
				"name": "periodic_table",
				"required": false,
				"collapsible": true,
				"collapsed": false
			}
		],
		"user_input": {
			"0": {
				"sample_name": "test1",
				"sample_or_pid": "Sample Name",
				"sample_details": "test",
				"periodic_table": [
					"Ru",
					"Ag",
					"Cd"
				]
			},
			"1": {
				"sample_name": "test2",
				"sample_or_pid": "Sample Name",
				"sample_details": "test2"
			}
		}
	}
}

In particular, the issue comes from the "sample" group:

"user_input": {
        "0": {
		"sample_name": "test1",
		"sample_or_pid": "Sample Name",
		"sample_details": "test",
		"periodic_table": [
			"Ru",
			"Ag",
			"Cd"
		]
	},
	"1": {
		"sample_name": "test2",
		"sample_or_pid": "Sample Name",
		"sample_details": "test2"
	}
}

If I change this to the following by adding an empty "periodic_table" response:

"user_input": {
        "0": {
		"sample_name": "test1",
		"sample_or_pid": "Sample Name",
		"sample_details": "test",
		"periodic_table": [
			"Ru",
			"Ag",
			"Cd"
		]
	},
	"1": {
		"sample_name": "test2",
		"sample_or_pid": "Sample Name",
		"sample_details": "test2",
		"periodic_table": [ ]
	}
}

Then there is no longer an error:

image

I haven't debugged in the application code itself, but I suspect the error is coming from something I wrote (sorry!) in #97. The fastest workaround solution I see would be to make the periodic table plugin return an empty list if no elements are chosen. The proper fix would probably be to change NEMO/templates/event_details/reservation_details.html so it can handle cases like this where some elements in a group are not defined.

Rendering of '/safety/' fails when 'admin' module not present and logged as superuser

Not sure what is the best approach to fix this, so I open an issue.
When running an instance with admin interface disabled, a superuser will get an internal error ('NoReverseMatch') if no safety items are present.
Clicking on the Safety menu (/safety/) will fail when rendering safety.html due to a missing check on the adminof the url on this line:

<p>Please go to <a href="{% url 'admin:NEMO_safetyitem_changelist' %}">Administration -> Detailed Administration -> SafetyItems</a>

Easy solution should be adding a function to check if admin site is enabled on usage.py view, though I am not sure if this is an issue which might be shared by other templates and might require a more generic mitigation.

PS: I also take the chance to ask if PRs that addresses bugs should usually be made against master or against the next minor release (i.e. 4.5.0-dev).
Thanks

no such column: shop_product.category

Request Method: GET
http://127.0.0.1:8000/admin/shop/product/
3.2.3
OperationalError
no such column: shop_product.category
C:\Users\Administrator\AppData\Local\Programs\Python\Python39\lib\site-packages\django\db\backends\sqlite3\base.py, line 423, in execute
C:\Users\Administrator\AppData\Local\Programs\Python\Python39\python.exe
3.9.5
['C:\Users\Administrator\PycharmProjects\Ecommerce\mysite', 'C:\Users\Administrator\AppData\Local\Programs\Python\Python39\python39.zip', 'C:\Users\Administrator\AppData\Local\Programs\Python\Python39\DLLs', 'C:\Users\Administrator\AppData\Local\Programs\Python\Python39\lib', 'C:\Users\Administrator\AppData\Local\Programs\Python\Python39', 'C:\Users\Administrator\AppData\Local\Programs\Python\Python39\lib\site-packages']
Tue, 25 May 2021 09:16:38 +0000

UX on deleting tool documents caused confusion

Hi Mathieu,

Feel free to close this if you think it's not an issue, but we ran into some user experience challenges with one of our admin users accidentally deleting a tool while trying to manage the tool documents. As I understand it, they uploaded some tool documents, but ran into some issues uploading more due to some sort of max limit or space limitation (I'm not sure what the actual issue there was, since I don't think there should be a limit).

Anyway, to replace the uploaded documents with links, the user checked the "Delete" checkboxes, and then pressed the "Delete" button in the bottom left:

image

That delete button is for the tool record itself. It then shows a delete confirmation screen:

image

Which (in this case) was ignored, since in the user's mind, they're thinking "yes, of course I want to delete what I just selected".

This was a little bit of user error, of course, but I think there's an opportunity for improving the UX of that process (although I'm not sure how much is configurable in the detailed admin screen). Perhaps making the checkboxes for the tool documents actual delete buttons, or renaming the "Delete" button to "Delete " to make it more clear. Maybe also an explicit instruction to "Click save after checking delete boxes to delete documents" or something to that effect.

Again, feel free to close if you think this isn't an issue. Our user certainly won't make that mistake again ;)

Calendar enhancements

On the calendar, it would be great to colour-code the tools and allow someone to select multiple tools at the same time. Then, for example, a staff member could see which tools in a room are being used and differentiate them by colour.

[Bug/Feature request] Cannot easily create reservation that spans over a weekend

With the current clicking and dragging approach to making reservations on the tool calendar, it is not straightforward (or even possible?) to create a multi-day reservation that spans over a Saturday/Sunday without switching to the "Month" view. In month view, it's not possible to create one that spans to the next month.

One possible way to work around this (without a big change to the calendar code) would be to make the start and end times in the reservation details editable like the title currently is:

image

Users who are "Staff" have access to the "Customization" menu

In version 3.12.2, when the "Staff" box is checked in a user's page (in Detailed Administration), the user then has access to the Administration/Customization menu. The "Staff" user can then read/write/change the values on the Customization page.

The feature manual indicates that the Customization page should only be available to Admin users. I have tested this in impersonation mode, and with another user who has been set to "Staff."

Running docker, 3.12.2/NGINX/Postgres on Ubuntu 20.04, hosted on AWS.

Adding support for external remote_user authentication for Apache Basic Auth

Prototyped ability to authorize using remote-user authentication via Apache webserver Basic authentication.

See also: https://httpd.apache.org/docs/2.4/howto/auth.html
See also: https://stackoverflow.com/questions/18139093/base64-authentication-python

Inspecting authentication.py class 'RemoteUserAuthenticationBackend' , seems to assume kerberos authentication based on the function 'clean_username'. Was able to get it working with Apache basic authentication by using an alternate implementation of clean_username:

def clean_username_http_basic_auth(self, username):
                """
                User names arrive in the form "Basic <username>:<password>" where everything after Basic is encoded base64
                See https://stackoverflow.com/questions/18139093/base64-authentication-python
                This function decodes and extracts just the username.
                """

                #
                if not username.startswith("Basic "):
                        auth_logger.warning(
                                f"Expected username to start with Basic"
                        )
                        return "unknown_user"

                #Strip off the 'Basic' and decode
                username_password_decoded = b64decode( username[6:] ).decode('utf-8')

                parts = username_password_decoded.split(':')

                return parts[0]

This prototype works locally, but I'm way out of my expertise area here, so have no idea how kosher this approach is.

django-admin migrate has a UniqueViolation installing on a fresh postgresql database

backend_1  | Operations to perform:
backend_1  |   Apply all migrations: NEMO, admin, auth, contenttypes, sessions
backend_1  | Running migrations:
backend_1  |   Applying contenttypes.0001_initial... OK
backend_1  |   Applying contenttypes.0002_remove_content_type_name... OK
backend_1  |   Applying auth.0001_initial... OK
backend_1  |   Applying auth.0002_alter_permission_name_max_length... OK
backend_1  |   Applying auth.0003_alter_user_email_max_length... OK
backend_1  |   Applying auth.0004_alter_user_username_opts... OK
backend_1  |   Applying auth.0005_alter_user_last_login_null... OK
backend_1  |   Applying auth.0006_require_contenttypes_0002... OK
backend_1  |   Applying auth.0007_alter_validators_add_error_messages... OK
backend_1  |   Applying auth.0008_alter_user_username_max_length... OK
backend_1  |   Applying NEMO.0001_version_1_0_0... OK
backend_1  |   Applying NEMO.0002_version_1_1_0... OK
backend_1  |   Applying NEMO.0003_version_1_2_0... OK
backend_1  |   Applying NEMO.0004_version_1_3_0... OK
backend_1  |   Applying NEMO.0005_version_1_4_0... OK
backend_1  |   Applying NEMO.0006_version_1_8_0... OK
backend_1  |   Applying NEMO.0007_version_1_15_0... OK
backend_1  |   Applying NEMO.0008_version_1_16_0... OK
backend_1  |   Applying NEMO.0009_version_1_19_0... OK
backend_1  |   Applying NEMO.0010_version_1_20_0... OK
backend_1  |   Applying NEMO.0011_version_1_22_0... OK
backend_1  |   Applying NEMO.0012_version_2_0_0... OK
backend_1  |   Applying NEMO.0013_version_2_1_0... OK
backend_1  |   Applying NEMO.0014_version_2_2_0...Traceback (most recent call last):
backend_1  |   File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
backend_1  |     return self.cursor.execute(sql, params)
backend_1  | psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "NEMO_interlockcardcategory_pkey"
backend_1  | DETAIL:  Key (id)=(1) already exists.
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        #'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': os.environ.get('DB_NAME'),
        'USER': os.environ.get('DB_USER'),
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': os.environ.get('DB_HOST', 'localhost'),
        'PORT': int(os.environ.get('DB_PORT', 5432)),
    }
}

Issue with updating tool when a tool image has been uploaded

I'm having some trouble with the new tool image feature. I am able to upload a tool image, which works as expected. After that, if I want to clear the image or add a new image, this also works fine. However, when I want to make any changes to the tool that are unrelated to the image, I get a FileNotFoundError for the image. I suspect the issue is in the pre-save receiver on editing the tool, but I tried a few changes and still could not get it to work. Removing the pre-save entirely resulted in a copy of the image being saved every time any change was made in the tool admin form (even if the change had nothing to do with the image).

[Feature request] Have dedicated landing pages for reservations

As discussed with Mathieu, we have a desire for users to be able to link directly to a page descriptive of certain information within NEMO. Chief among these are reservations, but otherwise usage events and tools could be useful too.

I'm going to take a look at seeing how hard this would be implement, and try to send a PR if I'm successful.


We basically want the information in /event_details/reservation/168/, but nicely formatted as an independent page, with links back to the main app, etc.

[feature request] Make "Usage Data History" in Tool page limited to staff users

Hello,

I wanted both to propose a feature as well as reporting an inconsistency we are experiencing between our public and private instance.

The request is having the possibility, maybe through customization, to make Usage Data History tab only available to staff users.

I also bring up the point because we noticed that the Usage Data History tab is not appearing in our full instance, while on the public one it is present. The weird thing is that both instances are running the same version of NEMO.
There are no significant changes between the two settings.py, except the reduced components or plugins loaded in the public instance.

Any hints on what might be the cause of this discrepancy?

Thanks in advance

Consumables items decimal entries

Currently it is not possible to enter decimals for consumable withdraws (e.g., 5.6nm of gold). This would provide a lot more flexibility in NEMO.

Possible bug in area access page

It looks like there is a bug in the "New area access record" page, where we can manually create an area access. When choosing the area to create the record for, the options are physical access levels, not areas, and choosing any of them results in failure with this error: "Your request contained an invalid identifier."

I think what is going on is that the function get_accessible_areas_for_user is making a list of physical access levels for the user, not areas. The new_area_access_record function is then treating them as areas. It looks like get_accessible_areas_for_user is used in some places where areas are needed and others where physical access levels are needed.

Authenticating against an AD / LDAP server

Usually, apps authenticating against an AD / LDAP server provides a way to specify the ldap query parameters (ldap_host, port, bind_dn, etc), user mapping, etc. Do Nemo allow this kind of authentification? Otherwise, what would a typical AD / LDAP authentification setup look like?

Thanks,

Internal error when creating reservation from admin user

Hello,
I would like to report a minor bug we noticed on the latest version of NEMO (4.5.5). Not sure if it affected previous versions, but we did not experienced it in the past.
When creating a new reservation using the admin user, a 500 error pops up:
image

This is minor annoyance, since the reservation is still created (only calendar invitation is not sent).
Also the error does not appear in the following cases:

  • regular users making reservation
  • admin making reservations on behalf of other users (both impersonating or using the function in calendar)
  • admin cancelling other user reservations

Checking the logs, we have the following error:

Expand
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/django/template/smartif.py", line 175, in translate_token
    op = OPERATORS[token]

During handling of the above exception ("'area"), another exception occurred:
  File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.8/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/django/views/decorators/http.py", line 40, in inner
    return func(request, *args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/NEMO/views/calendar.py", line 362, in create_reservation
    return create_item_reservation(request, request.user, start, end, ReservationItemType(item_type), item_id)
  File "/usr/local/lib/python3.8/site-packages/NEMO/decorators.py", line 56, in decorator
    return function(*args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/NEMO/views/calendar.py", line 442, in create_item_reservation
    new_reservation.save_and_notify()
  File "/usr/local/lib/python3.8/site-packages/NEMO/models.py", line 1804, in save_and_notify
    send_user_created_reservation_notification(self)
  File "/usr/local/lib/python3.8/site-packages/NEMO/views/calendar.py", line 1170, in send_user_created_reservation_notification
    message = render_email_template(message, {'reservation': reservation})
  File "/usr/local/lib/python3.8/site-packages/NEMO/utilities.py", line 580, in render_email_template
    return Template(template).render(make_context(dictionary, request or EmptyHttpRequest()))
  File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 155, in __init__
    self.nodelist = self.compile_nodelist()
  File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 193, in compile_nodelist
    return parser.parse()
  File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 478, in parse
    raise self.error(token, e)
  File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 476, in parse
    compiled_result = compile_func(self, token)
  File "/usr/local/lib/python3.8/site-packages/django/template/defaulttags.py", line 967, in do_if
    condition = TemplateIfParser(parser, bits).parse()
  File "/usr/local/lib/python3.8/site-packages/django/template/defaulttags.py", line 900, in __init__
    super().__init__(*args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/django/template/smartif.py", line 166, in __init__
    mapped_tokens.append(self.translate_token(token))
  File "/usr/local/lib/python3.8/site-packages/django/template/smartif.py", line 177, in translate_token
    return self.create_var(token)
  File "/usr/local/lib/python3.8/site-packages/django/template/defaulttags.py", line 903, in create_var
    return TemplateLiteral(self.template_parser.compile_filter(value), value)
  File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 563, in compile_filter
    return FilterExpression(token, self)
  File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 662, in __init__
    raise TemplateSyntaxError("Could not parse the remainder: '%s' "

Exception Type: TemplateSyntaxError at /create_reservation/
Exception Value: Could not parse the remainder: ''area' from ''area'
Request information:
USER: <REDACTED>

GET: No GET data

POST:
start = '1688653800'
end = '1688659200'
item_type = 'tool'
item_id = '3'
csrfmiddlewaretoken = <REDACTED>
reservation_questions = 'true'
df_Confirm = 'I confirm'

FILES: No FILES data

EDIT: the user affected is marked both as Staff and Admin, just in case this might help to reproduce it.

Add AUTHENTICATION_HEADER to settings.py example in wiki

I'd like to propose that the setting for AUTHENTICATION_HEADER be added to the settings.py example in the wiki.

I beat myself up over why REMOTE_USER auth wasn't working for the better part of 6 hours before I figured out this existed lol.

Training entries

The method of entering training time in NEMO is a bit awkward. It has to be done afterwards, which means that you need to use the tool under a staff account first and then enter the training time at the end. I think it would be better if it is approached in a similar way to the staff charging system where you start a training session at the beginning and end it when you are finished. Training appears to be the only usage in NEMO that you enter at the end of the session instead of during it.

''comment'' possibility in Staff charge

Hello
Is it possible to add a comment line when applying a staff charge in order to define if it is a laboratory work (Analysis...etc) or an Administrative work?
Also, if this comment could be seen in ''view your usage'' or in the consultation ''project billing'' it would be great!

Thank you!

Syncronizing with Google calendar

Hi, thanks for the tool. Is there any API or other approach to export the calendars in NEMO to Google Calendar automatically?

Thanks!

Enhancements !

Hi,

I am using NEMO for few minutes now. It it exactly what I am looking !

However, I can see few useful enhancement :

  • option to duplicate a tool
  • modify a News in the "website" and not in the backend interface
  • modify a reservation
  • picture(s) for each tool
  • comments for each tool
  • files related to the tool like manuals or documentation

Thank you

Issue regarding setup and initialization

After following the installation guide step for step, on three different operating systems, I cannot get any function out of NEMO. When attempting to access the web page, even locally (localhost:8000), I am greeted by a timed out error. There is no error access logging, aside from when attempting to access it via a host not specified in allowed hosts. I have no issue getting the splash_pad image to function.

Some more information:
This has been tried using Ubuntu 18.04 LTS, Rhel 7, and Mojave 10.14.6.
Additionally, I tried using version 1.20.0, instead of the most recent release, but had the same issue.

Bug in Tool model methods prevents tool enables for non-staff

I found that for non-staff users, the enable tool button would not appear, even though the project selection works properly. The only change I found was the addition of the ready_to_use() model method instead of checking the conditions individually. I was able to fix it by adding parentheses after the individual method calls within ready_to_use().

This changed line 557 in models.py from:
return self.operational and not self.required_resource_is_unavailable and not self.delayed_logoff_in_progress and not self.

to:
return self.operational and not self.required_resource_is_unavailable() and not self.delayed_logoff_in_progress() and not self.scheduled_outage_in_progress()

[feature request] Making equipment issue details only visible to staff

Hello,

I would like to propose if it might be possible to have the option to only show details of tools' open issues and issue history to staff and tool responsibles.
In our facility we have a policy to encourage users to report any problem they might find and we would like to avoid a possible detrimental "shame" effect.
Also, for confidentiality, it would be preferred to avoid having the content available to everybody. This also includes attachments (e.g. photos) which might generate issues in case the user inadvertently includes in the picture details which might be confidential.

I hope this might not be too complex to implement as an option.
Thanks in advance.

Docker with PostgreSQL

I'm hoping to use PostgreSQL, but it appears that the docker image is not configured for this.

Here is the relevant part of the settings.py file.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'USER': 'username',
        'NAME': 'databasename',
        'PASSWORD': 'password',
        'TEST': {
            'NAME': 'nemotest',
        },
    },
}

When I do any docker run commands, like compiling the static files, I get the following error:
django.core.exceptions.ImproperlyConfigured: Error loading psycopg2 module: No module named 'psycopg2'

It looks like this python package is missing. This error may just be the tip of the iceberg.

Also, maybe someone can advise on weather or not SQLite would be fine for us.
~1000 users, 200+ reservable items, 150+ interlocks.

No such file or directory: '/nemo/media/rates.json'

After an update from previous realease, I have the following error


docker run  --publish 9000:8000 --volume /nemo:/nemo  nemo_ipgp

error loading rates
[Errno 2] No such file or directory: '/nemo/media/rates.json'
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/NEMO/rates.py", line 52, in load_rates
    json_data = open(rates_file)
FileNotFoundError: [Errno 2] No such file or directory: '/nemo/media/rates.json'
Operations to perform:
  Apply all migrations: NEMO, admin, auth, contenttypes, sessions
Running migrations:
  No migrations to apply.

Allow tool reservations only within time slot

Hi,
I was wondering if there's a way to only allow reservations within a set time window each day, say from 8AM to 8PM (different for each tool, slot variable on weekends, etc).
Thanks

Issues downloading feature manual

Hi. When I try to download the feature manual, it fails and gives me this message: This site can’t be reached.
raw.githubusercontent.com refused to connect.
Try:

Checking the connection
Checking the proxy and the firewall
ERR_CONNECTION_REFUSED

Error creating the DB

I want to create a new installation. I have this bug for the "Create the database" part. I am using the latest git version d150b06

docker run --volume /nemo:/nemo nemo_ipgp bash -c "django-admin makemigrations NEMO && django-admin migrate"
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 383, in execute
    return Database.Cursor.execute(self, query, params)
sqlite3.OperationalError: no such table: NEMO_customization

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

Traceback (most recent call last):
  File "/usr/local/bin/django-admin", line 8, in <module>
    sys.exit(execute_from_command_line())
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 323, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 361, in execute
    self.check()
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 390, in check
    include_deployment_checks=include_deployment_checks,
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 377, in _run_checks
    return checks.run_checks(**kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/core/checks/registry.py", line 72, in run_checks
    new_errors = check(app_configs=app_configs)
  File "/usr/local/lib/python3.6/site-packages/django/core/checks/urls.py", line 40, in check_url_namespaces_unique
    all_namespaces = _load_all_namespaces(resolver)
  File "/usr/local/lib/python3.6/site-packages/django/core/checks/urls.py", line 57, in _load_all_namespaces
    url_patterns = getattr(resolver, 'url_patterns', [])
  File "/usr/local/lib/python3.6/site-packages/django/utils/functional.py", line 80, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/usr/local/lib/python3.6/site-packages/django/urls/resolvers.py", line 584, in url_patterns
    patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
  File "/usr/local/lib/python3.6/site-packages/django/utils/functional.py", line 80, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/usr/local/lib/python3.6/site-packages/django/urls/resolvers.py", line 577, in urlconf_module
    return import_module(self.urlconf_name)
  File "/usr/local/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/usr/local/lib/python3.6/site-packages/NEMO/urls.py", line 23, in <module>
    site_title = get_customization("site_title")
  File "/usr/local/lib/python3.6/site-packages/NEMO/views/customization.py", line 80, in get_customization
    return Customization.objects.get(name=name).value
  File "/usr/local/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 402, in get
    num = len(clone)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 256, in __len__
    self._fetch_all()
  File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 1242, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 55, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1133, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 99, in execute
    return super().execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 67, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 76, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 383, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.OperationalError: no such table: NEMO_customization
```

The database file is created with a size of 0

[feature request] Hiding reservation details from non-staff users (username, etc)

Hello,
Thanks for such a useful tool.
A useful feature might be an option to hide reservation details (or at least to add the possibility to hide the User personal data) in the calendar view for non-staff users.
This would help adoption especially in scenarios with strict User Data Protection Policy, to avoid exposing user Personal Data among users if required.
I would be happy to help implementing it, so I am opening this issue to discuss at which level would make more sense to implement it.

  1. Global administration settings which affect the behavior over the whole user base.
  2. User-level setting (with possibility to set a default) to let the user choose.
  3. Combination of both, with admin setting consisting of the following options:
    a. Hide all (Details always hidden, user choice disabled)
    b. User choice (Visibility based on user choice, with default set)
    c. Show all (Details always shown, user choice disabled)

Of course, first choice seems the less complex to implement and probably already achieving the MVP, but I would appreciate some feedback.

Thanks in advance!

Minor bug in upcoming reservations on landing page

A user reported this bug, which occurs when a reservation is shortened, but a delayed logout is in effect. While the delayed logout is ongoing, the 'Late for your reservation' notification appears on the landing page. It seems this occurs because the descendant reservation created from the shortened one has an end that is still in the future, so it is picked up in the query for reservations in the landing page.

mptt_tags

I just upgrade to Nemo 3.5.0 from older version. For now, everything is ok except when I cant to see a user on the /user page. After clicl on one user I have the following error page

Request Method: | GET
-- | --
http://127.0.0.1:9000/user/1/
2.2.13
TemplateSyntaxError
'mptt_tags' is not a registered tag library. Must be one of: admin_list admin_modify admin_static admin_urls cache custom_tags_and_filters humanize i18n l10n log rest_framework static staticfiles tz
/usr/local/lib/python3.6/site-packages/django/template/defaulttags.py in find_library, line 1025
/usr/local/bin/python
3.6.10
['/usr/local/bin',  '/nemo',  '/usr/local/lib/python36.zip',  '/usr/local/lib/python3.6',  '/usr/local/lib/python3.6/lib-dynload',  '/usr/local/lib/python3.6/site-packages']
lun, 30 Nov 2020 14:53:49 +0100

And in the django_error.log

==> /nemo/logs/django_error.log <==
Internal Server Error: /user/1/
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/template/defaulttags.py", line 1021, in find_library
    return parser.libraries[name]
KeyError: 'mptt_tags'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/views/decorators/http.py", line 40, in inner
    return func(request, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/NEMO/views/users.py", line 104, in create_or_modify_user
    return render(request, 'users/create_or_modify_user.html', dictionary)
  File "/usr/local/lib/python3.6/site-packages/django/shortcuts.py", line 36, in render
    content = loader.render_to_string(template_name, context, request, using=using)
  File "/usr/local/lib/python3.6/site-packages/django/template/loader.py", line 61, in render_to_string
    template = get_template(template_name, using=using)
  File "/usr/local/lib/python3.6/site-packages/django/template/loader.py", line 15, in get_template
    return engine.get_template(template_name)
  File "/usr/local/lib/python3.6/site-packages/django/template/backends/django.py", line 34, in get_template
    return Template(self.engine.get_template(template_name), self)
  File "/usr/local/lib/python3.6/site-packages/django/template/engine.py", line 143, in get_template
    template, origin = self.find_template(template_name)
  File "/usr/local/lib/python3.6/site-packages/django/template/engine.py", line 125, in find_template
    template = loader.get_template(name, skip=skip)
  File "/usr/local/lib/python3.6/site-packages/django/template/loaders/base.py", line 30, in get_template
    contents, origin, origin.template_name, self.engine,
  File "/usr/local/lib/python3.6/site-packages/django/template/base.py", line 156, in __init__
    self.nodelist = self.compile_nodelist()
  File "/usr/local/lib/python3.6/site-packages/django/template/base.py", line 194, in compile_nodelist
    return parser.parse()
  File "/usr/local/lib/python3.6/site-packages/django/template/base.py", line 478, in parse
    raise self.error(token, e)
  File "/usr/local/lib/python3.6/site-packages/django/template/base.py", line 476, in parse
    compiled_result = compile_func(self, token)
  File "/usr/local/lib/python3.6/site-packages/django/template/loader_tags.py", line 266, in do_extends
    nodelist = parser.parse()
  File "/usr/local/lib/python3.6/site-packages/django/template/base.py", line 478, in parse
    raise self.error(token, e)
  File "/usr/local/lib/python3.6/site-packages/django/template/base.py", line 476, in parse
    compiled_result = compile_func(self, token)
  File "/usr/local/lib/python3.6/site-packages/django/template/defaulttags.py", line 1078, in load
    lib = find_library(parser, name)
  File "/usr/local/lib/python3.6/site-packages/django/template/defaulttags.py", line 1025, in find_library
    name, "\n".join(sorted(parser.libraries)),
django.template.exceptions.TemplateSyntaxError: 'mptt_tags' is not a registered tag library. Must be one of:
admin_list
admin_modify
admin_static
admin_urls
cache
custom_tags_and_filters
humanize
i18n
l10n
log
rest_framework
static
staticfiles
tz

What can I do ? The 3.5.0 upgrade went smoothly

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.