├── .github └── workflows │ └── django.yml ├── .gitignore ├── Dockerfile ├── Procfile ├── README.md ├── docker-compose.yml ├── liveup ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── main ├── __init__.py ├── admin.py ├── apps.py ├── choices.py ├── fixtures │ ├── admission.json │ ├── db.json │ ├── patient.json │ ├── prescription.json │ ├── referral.json │ ├── user.json │ └── ward.json ├── forms.py ├── migrations │ ├── 0001_initial.py │ ├── 0001_squashed_0015_auto_20220318_1205.py │ ├── 0002_auto_20220225_2123.py │ ├── 0003_auto_20220225_2125.py │ ├── 0004_alter_patient_patient_number.py │ ├── 0005_auto_20220226_1245.py │ ├── 0006_user_phone_number.py │ ├── 0007_user_roles.py │ ├── 0008_rename_roles_user_role.py │ ├── 0009_auto_20220227_0822.py │ ├── 0010_referral_status.py │ ├── 0011_auto_20220227_2314.py │ ├── 0012_auto_20220306_1903.py │ ├── 0013_auto_20220310_1451.py │ ├── 0014_auto_20220310_1453.py │ ├── 0015_auto_20220318_1205.py │ └── __init__.py ├── models.py ├── permissions.py ├── serializers.py ├── tests.py ├── urls.py ├── validators.py ├── view_helpers.py └── views.py ├── manage.py ├── requirements.txt ├── runtime.txt └── templates └── password_reset_email.html /.github/workflows/django.yml: -------------------------------------------------------------------------------- 1 | name: Django CI 2 | 3 | on: 4 | push: 5 | branches: [ main, develop ] 6 | pull_request: 7 | branches: [ main, develop ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | env: 14 | SECRET_KEY: ${{ secrets.SECRET_KEY }} 15 | DJANGO_ALLOWED_HOSTS: ${{ secrets.DJANGO_ALLOWED_HOSTS }} 16 | DEBUG: ${{ secrets.DEBUG }} 17 | POSTGRES_NAME: ${{ secrets.POSTGRES_NAME }} 18 | POSTGRES_USER: ${{ secrets.POSTGRES_USER }} 19 | POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} 20 | DB_HOST: ${{ secrets.DB_HOST }} 21 | DB_PORT: ${{ secrets.DB_PORT }} 22 | CORS_ALLOWED_ORIGINS: ${{ secrets.CORS_ALLOWED_ORIGINS }} 23 | FROM_EMAIL: ${{ secrets.FROM_EMAIL }} 24 | EMAIL_HOST_USER: ${{ secrets.EMAIL_HOST_USER }} 25 | EMAIL_HOST_PASSWORD: ${{ secrets.EMAIL_HOST_PASSWORD }} 26 | strategy: 27 | max-parallel: 4 28 | matrix: 29 | python-version: [3.9] 30 | services: 31 | postgres: 32 | image: postgres:latest 33 | env: 34 | POSTGRES_USER: postgres 35 | POSTGRES_PASSWORD: postgres 36 | POSTGRES_DB: github_actions 37 | ports: 38 | - 5432:5432 39 | options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 40 | 41 | steps: 42 | - uses: actions/checkout@v2 43 | - name: Set up Python ${{ matrix.python-version }} 44 | uses: actions/setup-python@v2 45 | with: 46 | python-version: ${{ matrix.python-version }} 47 | - name: Install Dependencies 48 | run: | 49 | python -m pip install --upgrade pip 50 | pip install -r requirements.txt 51 | - name: Run Tests 52 | run: | 53 | python manage.py test 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | *.log 4 | .env 5 | env/ 6 | 7 | 8 | 9 | # Visual Studio Code # 10 | .vscode/* 11 | !.vscode/settings.json 12 | !.vscode/tasks.json 13 | !.vscode/launch.json 14 | !.vscode/extensions.json 15 | .history 16 | 17 | # Db 18 | db.sqlite3 19 | 20 | # Docker 21 | data -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | # pull official base image 3 | FROM python:3 4 | 5 | # set environment variables 6 | ENV PYTHONDONTWRITEBYTECODE 1 7 | ENV PYTHONUNBUFFERED 1 8 | 9 | # set work directory 10 | WORKDIR /code 11 | 12 | # install dependencies 13 | RUN pip install --upgrade pip 14 | COPY requirements.txt /code/ 15 | RUN pip install -r requirements.txt 16 | COPY . /code/ -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | release: python manage.py migrate 2 | web: gunicorn liveup.wsgi --log-file - -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Django CI](https://github.com/KNehe/liveup_api/actions/workflows/django.yml/badge.svg?branch=main)](https://github.com/KNehe/liveup_api/actions/workflows/django.yml) 2 | 3 | ## Liveup_api 4 | 5 | - An API that powers the [liveup web application](https://github.com/KNehe/liveup_web) that enables doctors to track patient prescriptions, avoid redundancy and manual processes. 6 | 7 | ## Demo 8 | 9 | - Watch the demo [youtube](https://youtu.be/FrIVVXfFy-M") 10 | 11 | ## Setting Up 12 | 13 | - Clone this repository 14 | - Create .env with the following variables 15 | 16 | ``` 17 | SECRET_KEY= 18 | DJANGO_ALLOWED_HOSTS= 19 | DEBUG= 20 | POSTGRES_NAME= 21 | POSTGRES_USER= 22 | POSTGRES_PASSWORD= 23 | DB_HOST= 24 | DB_PORT= 25 | FROM_EMAIL= 26 | CORS_ALLOWED_ORIGINS= 27 | EMAIL_HOST_USER= 28 | EMAIL_HOST_PASSWORD= 29 | ``` 30 | 31 | ### Docker 32 | 33 | - Prep `.env` as described below 34 | - Run `docker compose build` 35 | - Run `docker compose up` 36 | - Visit http://127.0.0.1:8000/api/v1/swagger/ or http://127.0.0.1:8000/api/v1/redoc/ 37 | 38 | ### Or 39 | 40 | - Run `pip install -R requirements.txt` in your virtual environment 41 | - Run `python manage.py runserver` 42 | - Visit http://127.0.0.1:8000/api/v1/swagger , http://127.0.0.1:8000/api/v1/ or 43 | - Swagger docs- https://nehe-liveup-api.herokuapp.com/api/v1/swagger/ 44 | - Redoc - https://nehe-liveup-api.herokuapp.com/api/v1/redoc/ 45 | - Each of the links point to the deployed app on heroku, I do not know how long it'll be up 46 | 47 | ## Features 48 | 49 | - Login with email and password. 50 | - App User Regisration (Only admins can register receptionists, doctors, nurses, student clinicians, create wards, and perform other admin related work in the admin panel). 51 | - Receptionist can register patients, view and edit their details 52 | - Receptionist can refer a patient to a clinician. Only for patients 53 | they have registered. 54 | - A Receptionist can look up past referrals (history) of patient and edit them. 55 | They can see history of patients registered by other receptionists too but can 56 | only edit a referral they made. 57 | - App users can change their names and password. 58 | - Forgot password. 59 | - Clinicians(doctors, nurses, student doctors, etc) can view patients referred to them 60 | - Clinicians can view patient details and record prescriptions 61 | - Clinicians can admit a patient to a particular ward 62 | - Clinicians can view a patient's history (past admissions and prescriptions made by them and other clinicians). Can only edit an admission and prescription they made. 63 | - Statistics. Number of patients registered, 64 | number of referrals made by all receptionists or a particular receptionist including for the current day, number of patients admitted, number of prescriptions recorded, 65 | number of referrals made, to all doctors or a particular doctor including that for the current day, 66 | - Pagination 67 | 68 | ## Tools and technologies used 69 | 70 | - Python 71 | - Django 72 | - Django Rest Framework 73 | - PostgreSQL 74 | - Django-Heroku 75 | - JWT 76 | 77 | ## Admin Panel 78 | 79 | - Find the admin panel at http://127.0.0.1:8000/admin or https://nehe-liveup-api.herokuapp.com/admin 80 | - Ensure to create a super user to be able to test locally 81 | - Run `python manage.py load db.json` to initialize the database with users, wards, admissions, referrals, patients and prescriptions. 82 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | 3 | services: 4 | db: 5 | image: postgres 6 | ports: 7 | - "5432:5432" 8 | volumes: 9 | - ./data/db:/var/lib/postgresql/data 10 | environment: 11 | - POSTGRES_NAME=postgres 12 | - POSTGRES_USER=postgres 13 | - POSTGRES_PASSWORD=postgres 14 | - DB_HOST=127.0.0.1 15 | - DB_PORT=5432 16 | 17 | web: 18 | build: . 19 | command: python manage.py runserver 0.0.0.0:8000 20 | volumes: 21 | - .:/code 22 | ports: 23 | - "8000:8000" 24 | env_file: 25 | ./.env 26 | environment: 27 | - POSTGRES_NAME=postgres 28 | - POSTGRES_USER=postgres 29 | - POSTGRES_PASSWORD=postgres 30 | - DB_HOST=db 31 | - DB_PORT=5432 32 | depends_on: 33 | - db -------------------------------------------------------------------------------- /liveup/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KNehe/liveup_api/992628afebd0bd4f956f11e6ba935b3362868f21/liveup/__init__.py -------------------------------------------------------------------------------- /liveup/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for liveup project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'liveup.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /liveup/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for liveup project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | import environ 15 | import os 16 | from datetime import timedelta 17 | 18 | env = environ.Env(DEBUG=(bool, False)) 19 | import django_heroku 20 | 21 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 22 | BASE_DIR = Path(__file__).resolve().parent.parent 23 | 24 | environ.Env.read_env(os.path.join(BASE_DIR, ".env")) 25 | 26 | 27 | # Quick-start development settings - unsuitable for production 28 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 29 | 30 | # SECURITY WARNING: keep the secret key used in production secret! 31 | SECRET_KEY = env("SECRET_KEY") 32 | 33 | # SECURITY WARNING: don't run with debug turned on in production! 34 | DEBUG = env("DEBUG") 35 | 36 | ALLOWED_HOSTS = env("DJANGO_ALLOWED_HOSTS").split(" ") 37 | 38 | 39 | # Application definition 40 | 41 | INSTALLED_APPS = [ 42 | "jazzmin", 43 | "django.contrib.admin", 44 | "django.contrib.auth", 45 | "django.contrib.contenttypes", 46 | "django.contrib.sessions", 47 | "django.contrib.messages", 48 | "django.contrib.staticfiles", 49 | "main.apps.MainConfig", 50 | "phonenumber_field", 51 | "rest_framework", 52 | "rest_framework.authtoken", 53 | "dj_rest_auth", 54 | "drf_yasg", 55 | "corsheaders", 56 | ] 57 | 58 | MIDDLEWARE = [ 59 | "django.middleware.security.SecurityMiddleware", 60 | "django.contrib.sessions.middleware.SessionMiddleware", 61 | "corsheaders.middleware.CorsMiddleware", 62 | "django.middleware.common.CommonMiddleware", 63 | "django.middleware.csrf.CsrfViewMiddleware", 64 | "django.contrib.auth.middleware.AuthenticationMiddleware", 65 | "django.contrib.messages.middleware.MessageMiddleware", 66 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 67 | ] 68 | 69 | ROOT_URLCONF = "liveup.urls" 70 | 71 | TEMPLATES = [ 72 | { 73 | "BACKEND": "django.template.backends.django.DjangoTemplates", 74 | "DIRS": [BASE_DIR / "templates"], 75 | "APP_DIRS": True, 76 | "OPTIONS": { 77 | "context_processors": [ 78 | "django.template.context_processors.debug", 79 | "django.template.context_processors.request", 80 | "django.contrib.auth.context_processors.auth", 81 | "django.contrib.messages.context_processors.messages", 82 | ], 83 | }, 84 | }, 85 | ] 86 | 87 | WSGI_APPLICATION = "liveup.wsgi.application" 88 | 89 | 90 | # Database 91 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 92 | 93 | DATABASES = { 94 | "default": { 95 | # 'ENGINE': 'django.db.backends.sqlite3', 96 | # 'NAME': BASE_DIR / 'db.sqlite3', 97 | "ENGINE": "django.db.backends.postgresql", 98 | "NAME": env("POSTGRES_NAME"), 99 | "USER": env("POSTGRES_USER"), 100 | "PASSWORD": env("POSTGRES_PASSWORD"), 101 | "HOST": env("DB_HOST"), 102 | "PORT": env("DB_PORT"), 103 | } 104 | } 105 | import dj_database_url 106 | 107 | db_from_env = dj_database_url.config(conn_max_age=600) 108 | DATABASES["default"].update(db_from_env) 109 | 110 | if os.environ.get("GITHUB_WORKFLOW"): 111 | DATABASES = { 112 | "default": { 113 | "ENGINE": "django.db.backends.postgresql", 114 | "NAME": "github_actions", 115 | "USER": "postgres", 116 | "PASSWORD": "postgres", 117 | "HOST": "127.0.0.1", 118 | "PORT": "5432", 119 | } 120 | } 121 | 122 | 123 | # Password validation 124 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 125 | 126 | AUTH_PASSWORD_VALIDATORS = [ 127 | { 128 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 129 | }, 130 | { 131 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 132 | }, 133 | { 134 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 135 | }, 136 | { 137 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 138 | }, 139 | ] 140 | 141 | 142 | # Internationalization 143 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 144 | 145 | LANGUAGE_CODE = "en-us" 146 | 147 | TIME_ZONE = "UTC" 148 | 149 | USE_I18N = True 150 | 151 | USE_L10N = True 152 | 153 | USE_TZ = True 154 | 155 | 156 | # Static files (CSS, JavaScript, Images) 157 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 158 | 159 | STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") 160 | STATIC_URL = "/static/" 161 | django_heroku.settings(locals()) 162 | 163 | # Default primary key field type 164 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 165 | 166 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 167 | 168 | AUTH_USER_MODEL = "main.User" 169 | 170 | # REST_FRAMEWORK = { 171 | # 'DEFAULT_AUTHENTICATION_CLASSES': ( 172 | # 'dj_rest_auth.jwt_auth.JWTCookieAuthentication', 173 | # ) 174 | # } 175 | 176 | REST_USE_JWT = True 177 | 178 | SIMPLE_JWT = { 179 | "ACCESS_TOKEN_LIFETIME": timedelta(hours=48), 180 | "REFRESH_TOKEN_LIFETIME": timedelta(days=3), 181 | "SIGNING_KEY": env("SECRET_KEY"), 182 | } 183 | 184 | REST_FRAMEWORK = { 185 | "DEFAULT_AUTHENTICATION_CLASSES": ("dj_rest_auth.jwt_auth.JWTAuthentication",), 186 | "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", 187 | "PAGE_SIZE": 20, 188 | } 189 | 190 | SWAGGER_SETTINGS = { 191 | "SECURITY_DEFINITIONS": { 192 | "Bearer": {"type": "apiKey", "name": "Authorization", "in": "header"} 193 | } 194 | } 195 | 196 | # CORS_ALLOWED_ORIGINS = env('CORS_ALLOWED_ORIGINS').split(' ') 197 | CORS_ALLOW_ALL_ORIGINS = True 198 | 199 | FROM_EMAIL = env("FROM_EMAIL") 200 | 201 | REST_AUTH_SERIALIZERS = { 202 | "USER_DETAILS_SERIALIZER": "main.serializers.CustomUserDetailsSerializer", 203 | "PASSWORD_RESET_SERIALIZER": "main.serializers.CustomPasswordResetSerializer", 204 | } 205 | 206 | EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" 207 | EMAIL_USE_TLS = True 208 | EMAIL_HOST = "smtp.gmail.com" 209 | EMAIL_HOST_USER = env("EMAIL_HOST_USER") 210 | EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD") 211 | EMAIL_PORT = 587 212 | -------------------------------------------------------------------------------- /liveup/urls.py: -------------------------------------------------------------------------------- 1 | """liveup URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include 18 | 19 | 20 | urlpatterns = [ 21 | path('admin/', admin.site.urls), 22 | path('api/v1/', include('main.urls')) 23 | ] 24 | -------------------------------------------------------------------------------- /liveup/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for liveup project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'liveup.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /main/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KNehe/liveup_api/992628afebd0bd4f956f11e6ba935b3362868f21/main/__init__.py -------------------------------------------------------------------------------- /main/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.auth.admin import UserAdmin 3 | 4 | from main.models import Admission, Patient, Prescription, Referral, User, Ward 5 | from .forms import CustomUserChangeForm, CustomUserCreationForm 6 | 7 | 8 | class CustomUserAdmin(UserAdmin): 9 | add_form = CustomUserCreationForm 10 | form = CustomUserChangeForm 11 | model = User 12 | list_display = [ 13 | "username", 14 | "email", 15 | "role", 16 | "is_staff", 17 | ] 18 | fieldsets = ( 19 | ( 20 | "Personal Info", 21 | { 22 | "fields": ( 23 | "username", 24 | "email", 25 | "password", 26 | "phone_number", 27 | "first_name", 28 | "last_name", 29 | ) 30 | }, 31 | ), 32 | ( 33 | "Permissions", 34 | { 35 | "fields": ( 36 | "role", 37 | "is_staff", 38 | "is_active", 39 | ) 40 | }, 41 | ), 42 | ) 43 | add_fieldsets = ( 44 | ( 45 | None, 46 | { 47 | "classes": ("wide",), 48 | "fields": ( 49 | "username", 50 | "email", 51 | "role", 52 | "password1", 53 | "password2", 54 | "is_staff", 55 | "is_active", 56 | "phone_number", 57 | "first_name", 58 | "last_name", 59 | ), 60 | }, 61 | ), 62 | ) 63 | 64 | 65 | admin.site.register(User, CustomUserAdmin) 66 | admin.site.register(Patient) 67 | admin.site.register(Prescription) 68 | admin.site.register(Ward) 69 | admin.site.register(Admission) 70 | admin.site.register(Referral) 71 | -------------------------------------------------------------------------------- /main/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MainConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "main" 7 | -------------------------------------------------------------------------------- /main/choices.py: -------------------------------------------------------------------------------- 1 | # FOR USER MODEL 2 | 3 | RECEPTIONIST = "Receptionist" 4 | DOCTOR = "Doctor" 5 | NURSE = "Nurse" 6 | STUDENT_CLINICIAN = "Student Clinician" 7 | 8 | ROLES = ( 9 | (RECEPTIONIST, RECEPTIONIST), 10 | (DOCTOR, DOCTOR), 11 | (NURSE, NURSE), 12 | (STUDENT_CLINICIAN, STUDENT_CLINICIAN), 13 | ) 14 | 15 | # CHOICES FOR REFFERAL MODEL 16 | 17 | ADMITTED = "Admitted" 18 | DISCHARGED = "Discharged" 19 | NOT_SEEN = "Not seen" 20 | IN_PROGRESS = "In progress" 21 | 22 | REFERAL_STATUS = ( 23 | (ADMITTED, ADMITTED), 24 | (DISCHARGED, DISCHARGED), 25 | (NOT_SEEN, NOT_SEEN), 26 | (IN_PROGRESS, IN_PROGRESS), 27 | ) 28 | -------------------------------------------------------------------------------- /main/fixtures/admission.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "main.admission", 4 | "pk": 19, 5 | "fields": { 6 | "ward": 2, 7 | "patient": 31, 8 | "created_at": "2022-03-09T09:18:32.455Z", 9 | "created_by": 4, 10 | "updated_at": null, 11 | "updated_by": null 12 | } 13 | }, 14 | { 15 | "model": "main.admission", 16 | "pk": 20, 17 | "fields": { 18 | "ward": 2, 19 | "patient": 31, 20 | "created_at": "2022-03-09T12:25:13.953Z", 21 | "created_by": 4, 22 | "updated_at": "2022-03-10T10:45:24.456Z", 23 | "updated_by": 4 24 | } 25 | }, 26 | { 27 | "model": "main.admission", 28 | "pk": 24, 29 | "fields": { 30 | "ward": 2, 31 | "patient": 41, 32 | "created_at": "2022-03-16T09:27:14.836Z", 33 | "created_by": 4, 34 | "updated_at": null, 35 | "updated_by": null 36 | } 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /main/fixtures/db.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "admin.logentry", 4 | "pk": 1, 5 | "fields": { 6 | "action_time": "2022-02-25T18:29:31.637Z", 7 | "user": 1, 8 | "content_type": 7, 9 | "object_id": "1", 10 | "object_repr": "Patient name", 11 | "action_flag": 1, 12 | "change_message": "[{\"added\": {}}]" 13 | } 14 | }, 15 | { 16 | "model": "admin.logentry", 17 | "pk": 2, 18 | "fields": { 19 | "action_time": "2022-02-25T18:30:18.304Z", 20 | "user": 1, 21 | "content_type": 7, 22 | "object_id": "1", 23 | "object_repr": "Patient name", 24 | "action_flag": 2, 25 | "change_message": "[{\"changed\": {\"fields\": [\"Date of birth\"]}}]" 26 | } 27 | }, 28 | { 29 | "model": "admin.logentry", 30 | "pk": 3, 31 | "fields": { 32 | "action_time": "2022-02-25T18:30:40.714Z", 33 | "user": 1, 34 | "content_type": 7, 35 | "object_id": "1", 36 | "object_repr": "Patient name", 37 | "action_flag": 2, 38 | "change_message": "[{\"changed\": {\"fields\": [\"Date of birth\"]}}]" 39 | } 40 | }, 41 | { 42 | "model": "admin.logentry", 43 | "pk": 4, 44 | "fields": { 45 | "action_time": "2022-02-25T18:34:36.515Z", 46 | "user": 1, 47 | "content_type": 7, 48 | "object_id": "1", 49 | "object_repr": "Patient name", 50 | "action_flag": 2, 51 | "change_message": "[{\"changed\": {\"fields\": [\"Date of birth\"]}}]" 52 | } 53 | }, 54 | { 55 | "model": "admin.logentry", 56 | "pk": 5, 57 | "fields": { 58 | "action_time": "2022-02-25T18:34:47.996Z", 59 | "user": 1, 60 | "content_type": 7, 61 | "object_id": "1", 62 | "object_repr": "Patient name", 63 | "action_flag": 2, 64 | "change_message": "[{\"changed\": {\"fields\": [\"Date of birth\"]}}]" 65 | } 66 | }, 67 | { 68 | "model": "admin.logentry", 69 | "pk": 6, 70 | "fields": { 71 | "action_time": "2022-02-25T19:08:31.566Z", 72 | "user": 1, 73 | "content_type": 7, 74 | "object_id": "1", 75 | "object_repr": "Patient name", 76 | "action_flag": 2, 77 | "change_message": "[{\"changed\": {\"fields\": [\"Date of birth\"]}}]" 78 | } 79 | }, 80 | { 81 | "model": "admin.logentry", 82 | "pk": 7, 83 | "fields": { 84 | "action_time": "2022-02-25T19:08:47.921Z", 85 | "user": 1, 86 | "content_type": 7, 87 | "object_id": "1", 88 | "object_repr": "Patient name", 89 | "action_flag": 2, 90 | "change_message": "[{\"changed\": {\"fields\": [\"Date of birth\"]}}]" 91 | } 92 | }, 93 | { 94 | "model": "admin.logentry", 95 | "pk": 8, 96 | "fields": { 97 | "action_time": "2022-02-26T09:57:06.004Z", 98 | "user": 1, 99 | "content_type": 6, 100 | "object_id": "1", 101 | "object_repr": "Nehe", 102 | "action_flag": 2, 103 | "change_message": "[{\"changed\": {\"fields\": [\"Username\"]}}]" 104 | } 105 | }, 106 | { 107 | "model": "admin.logentry", 108 | "pk": 9, 109 | "fields": { 110 | "action_time": "2022-02-26T09:57:18.033Z", 111 | "user": 1, 112 | "content_type": 6, 113 | "object_id": "1", 114 | "object_repr": "Nehe", 115 | "action_flag": 2, 116 | "change_message": "[{\"changed\": {\"fields\": [\"First name\", \"Last name\", \"Phone number\"]}}]" 117 | } 118 | }, 119 | { 120 | "model": "admin.logentry", 121 | "pk": 10, 122 | "fields": { 123 | "action_time": "2022-02-26T12:38:25.965Z", 124 | "user": 1, 125 | "content_type": 7, 126 | "object_id": "7", 127 | "object_repr": "John Doe", 128 | "action_flag": 3, 129 | "change_message": "" 130 | } 131 | }, 132 | { 133 | "model": "admin.logentry", 134 | "pk": 11, 135 | "fields": { 136 | "action_time": "2022-02-26T12:38:26.068Z", 137 | "user": 1, 138 | "content_type": 7, 139 | "object_id": "6", 140 | "object_repr": "John Doe", 141 | "action_flag": 3, 142 | "change_message": "" 143 | } 144 | }, 145 | { 146 | "model": "admin.logentry", 147 | "pk": 12, 148 | "fields": { 149 | "action_time": "2022-02-26T12:38:26.069Z", 150 | "user": 1, 151 | "content_type": 7, 152 | "object_id": "5", 153 | "object_repr": "John Doe", 154 | "action_flag": 3, 155 | "change_message": "" 156 | } 157 | }, 158 | { 159 | "model": "admin.logentry", 160 | "pk": 13, 161 | "fields": { 162 | "action_time": "2022-02-26T12:38:26.071Z", 163 | "user": 1, 164 | "content_type": 7, 165 | "object_id": "4", 166 | "object_repr": "John Doe", 167 | "action_flag": 3, 168 | "change_message": "" 169 | } 170 | }, 171 | { 172 | "model": "admin.logentry", 173 | "pk": 14, 174 | "fields": { 175 | "action_time": "2022-02-26T12:38:26.073Z", 176 | "user": 1, 177 | "content_type": 7, 178 | "object_id": "3", 179 | "object_repr": "John Doe", 180 | "action_flag": 3, 181 | "change_message": "" 182 | } 183 | }, 184 | { 185 | "model": "admin.logentry", 186 | "pk": 15, 187 | "fields": { 188 | "action_time": "2022-02-26T12:38:26.075Z", 189 | "user": 1, 190 | "content_type": 7, 191 | "object_id": "2", 192 | "object_repr": "John Doe", 193 | "action_flag": 3, 194 | "change_message": "" 195 | } 196 | }, 197 | { 198 | "model": "admin.logentry", 199 | "pk": 16, 200 | "fields": { 201 | "action_time": "2022-02-26T12:52:46.935Z", 202 | "user": 1, 203 | "content_type": 7, 204 | "object_id": "15", 205 | "object_repr": "John Doe", 206 | "action_flag": 3, 207 | "change_message": "" 208 | } 209 | }, 210 | { 211 | "model": "admin.logentry", 212 | "pk": 17, 213 | "fields": { 214 | "action_time": "2022-02-26T12:52:46.972Z", 215 | "user": 1, 216 | "content_type": 7, 217 | "object_id": "14", 218 | "object_repr": "John Doe", 219 | "action_flag": 3, 220 | "change_message": "" 221 | } 222 | }, 223 | { 224 | "model": "admin.logentry", 225 | "pk": 18, 226 | "fields": { 227 | "action_time": "2022-02-26T12:52:46.974Z", 228 | "user": 1, 229 | "content_type": 7, 230 | "object_id": "13", 231 | "object_repr": "John Doe", 232 | "action_flag": 3, 233 | "change_message": "" 234 | } 235 | }, 236 | { 237 | "model": "admin.logentry", 238 | "pk": 19, 239 | "fields": { 240 | "action_time": "2022-02-26T12:52:46.975Z", 241 | "user": 1, 242 | "content_type": 7, 243 | "object_id": "12", 244 | "object_repr": "John Doe", 245 | "action_flag": 3, 246 | "change_message": "" 247 | } 248 | }, 249 | { 250 | "model": "admin.logentry", 251 | "pk": 20, 252 | "fields": { 253 | "action_time": "2022-02-26T12:52:46.978Z", 254 | "user": 1, 255 | "content_type": 7, 256 | "object_id": "11", 257 | "object_repr": "John Doe", 258 | "action_flag": 3, 259 | "change_message": "" 260 | } 261 | }, 262 | { 263 | "model": "admin.logentry", 264 | "pk": 21, 265 | "fields": { 266 | "action_time": "2022-02-26T12:52:46.980Z", 267 | "user": 1, 268 | "content_type": 7, 269 | "object_id": "10", 270 | "object_repr": "John Doe", 271 | "action_flag": 3, 272 | "change_message": "" 273 | } 274 | }, 275 | { 276 | "model": "admin.logentry", 277 | "pk": 22, 278 | "fields": { 279 | "action_time": "2022-02-26T12:52:46.981Z", 280 | "user": 1, 281 | "content_type": 7, 282 | "object_id": "9", 283 | "object_repr": "John Doe", 284 | "action_flag": 3, 285 | "change_message": "" 286 | } 287 | }, 288 | { 289 | "model": "admin.logentry", 290 | "pk": 23, 291 | "fields": { 292 | "action_time": "2022-02-26T12:52:46.983Z", 293 | "user": 1, 294 | "content_type": 7, 295 | "object_id": "8", 296 | "object_repr": "John Doe", 297 | "action_flag": 3, 298 | "change_message": "" 299 | } 300 | }, 301 | { 302 | "model": "admin.logentry", 303 | "pk": 24, 304 | "fields": { 305 | "action_time": "2022-02-26T12:57:31.852Z", 306 | "user": 1, 307 | "content_type": 3, 308 | "object_id": "1", 309 | "object_repr": "Receptionist", 310 | "action_flag": 1, 311 | "change_message": "[{\"added\": {}}]" 312 | } 313 | }, 314 | { 315 | "model": "admin.logentry", 316 | "pk": 25, 317 | "fields": { 318 | "action_time": "2022-02-26T13:09:33.480Z", 319 | "user": 1, 320 | "content_type": 3, 321 | "object_id": "1", 322 | "object_repr": "Receptionist", 323 | "action_flag": 2, 324 | "change_message": "[]" 325 | } 326 | }, 327 | { 328 | "model": "admin.logentry", 329 | "pk": 26, 330 | "fields": { 331 | "action_time": "2022-02-26T13:24:35.783Z", 332 | "user": 1, 333 | "content_type": 6, 334 | "object_id": "3", 335 | "object_repr": "doe", 336 | "action_flag": 1, 337 | "change_message": "[{\"added\": {}}]" 338 | } 339 | }, 340 | { 341 | "model": "admin.logentry", 342 | "pk": 27, 343 | "fields": { 344 | "action_time": "2022-02-26T13:57:49.864Z", 345 | "user": 1, 346 | "content_type": 6, 347 | "object_id": "1", 348 | "object_repr": "Nehe", 349 | "action_flag": 2, 350 | "change_message": "[]" 351 | } 352 | }, 353 | { 354 | "model": "admin.logentry", 355 | "pk": 28, 356 | "fields": { 357 | "action_time": "2022-02-26T13:58:17.924Z", 358 | "user": 1, 359 | "content_type": 3, 360 | "object_id": "1", 361 | "object_repr": "Receptionist", 362 | "action_flag": 3, 363 | "change_message": "" 364 | } 365 | }, 366 | { 367 | "model": "admin.logentry", 368 | "pk": 29, 369 | "fields": { 370 | "action_time": "2022-02-26T14:12:58.683Z", 371 | "user": 1, 372 | "content_type": 7, 373 | "object_id": "17", 374 | "object_repr": "Patient name 2", 375 | "action_flag": 1, 376 | "change_message": "[{\"added\": {}}]" 377 | } 378 | }, 379 | { 380 | "model": "admin.logentry", 381 | "pk": 30, 382 | "fields": { 383 | "action_time": "2022-02-26T14:13:16.785Z", 384 | "user": 1, 385 | "content_type": 7, 386 | "object_id": "17", 387 | "object_repr": "Patient name 2", 388 | "action_flag": 2, 389 | "change_message": "[{\"changed\": {\"fields\": [\"Date of birth\"]}}]" 390 | } 391 | }, 392 | { 393 | "model": "admin.logentry", 394 | "pk": 31, 395 | "fields": { 396 | "action_time": "2022-02-26T14:13:37.465Z", 397 | "user": 1, 398 | "content_type": 7, 399 | "object_id": "17", 400 | "object_repr": "Patient name 2", 401 | "action_flag": 2, 402 | "change_message": "[{\"changed\": {\"fields\": [\"Date of birth\"]}}]" 403 | } 404 | }, 405 | { 406 | "model": "admin.logentry", 407 | "pk": 32, 408 | "fields": { 409 | "action_time": "2022-02-26T20:06:57.480Z", 410 | "user": 1, 411 | "content_type": 6, 412 | "object_id": "4", 413 | "object_repr": "DocNehe", 414 | "action_flag": 1, 415 | "change_message": "[{\"added\": {}}]" 416 | } 417 | }, 418 | { 419 | "model": "admin.logentry", 420 | "pk": 33, 421 | "fields": { 422 | "action_time": "2022-02-27T05:55:53.441Z", 423 | "user": 1, 424 | "content_type": 6, 425 | "object_id": "1", 426 | "object_repr": "Nehe", 427 | "action_flag": 2, 428 | "change_message": "[{\"changed\": {\"fields\": [\"Role\"]}}]" 429 | } 430 | }, 431 | { 432 | "model": "admin.logentry", 433 | "pk": 34, 434 | "fields": { 435 | "action_time": "2022-02-27T05:59:12.882Z", 436 | "user": 1, 437 | "content_type": 6, 438 | "object_id": "1", 439 | "object_repr": "Nehe", 440 | "action_flag": 2, 441 | "change_message": "[{\"changed\": {\"fields\": [\"Role\"]}}]" 442 | } 443 | }, 444 | { 445 | "model": "admin.logentry", 446 | "pk": 35, 447 | "fields": { 448 | "action_time": "2022-02-27T08:39:29.218Z", 449 | "user": 1, 450 | "content_type": 6, 451 | "object_id": "4", 452 | "object_repr": "DocNehe", 453 | "action_flag": 2, 454 | "change_message": "[]" 455 | } 456 | }, 457 | { 458 | "model": "admin.logentry", 459 | "pk": 36, 460 | "fields": { 461 | "action_time": "2022-02-27T08:40:05.417Z", 462 | "user": 1, 463 | "content_type": 6, 464 | "object_id": "4", 465 | "object_repr": "DocNehe", 466 | "action_flag": 2, 467 | "change_message": "[{\"changed\": {\"fields\": [\"password\"]}}]" 468 | } 469 | }, 470 | { 471 | "model": "admin.logentry", 472 | "pk": 37, 473 | "fields": { 474 | "action_time": "2022-02-27T08:58:26.693Z", 475 | "user": 1, 476 | "content_type": 6, 477 | "object_id": "5", 478 | "object_repr": "doe2", 479 | "action_flag": 1, 480 | "change_message": "[{\"added\": {}}]" 481 | } 482 | }, 483 | { 484 | "model": "admin.logentry", 485 | "pk": 38, 486 | "fields": { 487 | "action_time": "2022-02-27T08:58:45.154Z", 488 | "user": 1, 489 | "content_type": 6, 490 | "object_id": "5", 491 | "object_repr": "doe2", 492 | "action_flag": 3, 493 | "change_message": "" 494 | } 495 | }, 496 | { 497 | "model": "admin.logentry", 498 | "pk": 39, 499 | "fields": { 500 | "action_time": "2022-02-27T09:06:20.864Z", 501 | "user": 1, 502 | "content_type": 6, 503 | "object_id": "3", 504 | "object_repr": "doe", 505 | "action_flag": 3, 506 | "change_message": "" 507 | } 508 | }, 509 | { 510 | "model": "admin.logentry", 511 | "pk": 40, 512 | "fields": { 513 | "action_time": "2022-02-27T09:06:54.325Z", 514 | "user": 1, 515 | "content_type": 6, 516 | "object_id": "6", 517 | "object_repr": "doey", 518 | "action_flag": 1, 519 | "change_message": "[{\"added\": {}}]" 520 | } 521 | }, 522 | { 523 | "model": "admin.logentry", 524 | "pk": 41, 525 | "fields": { 526 | "action_time": "2022-02-27T09:09:02.635Z", 527 | "user": 1, 528 | "content_type": 6, 529 | "object_id": "6", 530 | "object_repr": "doey", 531 | "action_flag": 2, 532 | "change_message": "[]" 533 | } 534 | }, 535 | { 536 | "model": "admin.logentry", 537 | "pk": 42, 538 | "fields": { 539 | "action_time": "2022-02-27T09:38:45.568Z", 540 | "user": 1, 541 | "content_type": 6, 542 | "object_id": "7", 543 | "object_repr": "fishy", 544 | "action_flag": 1, 545 | "change_message": "[{\"added\": {}}]" 546 | } 547 | }, 548 | { 549 | "model": "admin.logentry", 550 | "pk": 43, 551 | "fields": { 552 | "action_time": "2022-02-27T09:43:55.966Z", 553 | "user": 1, 554 | "content_type": 6, 555 | "object_id": "6", 556 | "object_repr": "doey", 557 | "action_flag": 3, 558 | "change_message": "" 559 | } 560 | }, 561 | { 562 | "model": "admin.logentry", 563 | "pk": 44, 564 | "fields": { 565 | "action_time": "2022-02-27T09:43:55.971Z", 566 | "user": 1, 567 | "content_type": 6, 568 | "object_id": "7", 569 | "object_repr": "fishy", 570 | "action_flag": 3, 571 | "change_message": "" 572 | } 573 | }, 574 | { 575 | "model": "admin.logentry", 576 | "pk": 45, 577 | "fields": { 578 | "action_time": "2022-02-27T09:43:55.973Z", 579 | "user": 1, 580 | "content_type": 6, 581 | "object_id": "2", 582 | "object_repr": "n", 583 | "action_flag": 3, 584 | "change_message": "" 585 | } 586 | }, 587 | { 588 | "model": "admin.logentry", 589 | "pk": 46, 590 | "fields": { 591 | "action_time": "2022-02-27T19:07:44.966Z", 592 | "user": 1, 593 | "content_type": 8, 594 | "object_id": "1", 595 | "object_repr": "Ward object (1)", 596 | "action_flag": 1, 597 | "change_message": "[{\"added\": {}}]" 598 | } 599 | }, 600 | { 601 | "model": "admin.logentry", 602 | "pk": 47, 603 | "fields": { 604 | "action_time": "2022-02-27T19:08:03.589Z", 605 | "user": 1, 606 | "content_type": 8, 607 | "object_id": "2", 608 | "object_repr": "Ward object (2)", 609 | "action_flag": 1, 610 | "change_message": "[{\"added\": {}}]" 611 | } 612 | }, 613 | { 614 | "model": "admin.logentry", 615 | "pk": 48, 616 | "fields": { 617 | "action_time": "2022-02-27T20:00:43.467Z", 618 | "user": 1, 619 | "content_type": 10, 620 | "object_id": "6", 621 | "object_repr": "Patient name admitted to None", 622 | "action_flag": 3, 623 | "change_message": "" 624 | } 625 | }, 626 | { 627 | "model": "admin.logentry", 628 | "pk": 49, 629 | "fields": { 630 | "action_time": "2022-02-27T20:00:43.475Z", 631 | "user": 1, 632 | "content_type": 10, 633 | "object_id": "5", 634 | "object_repr": "Patient name admitted to None", 635 | "action_flag": 3, 636 | "change_message": "" 637 | } 638 | }, 639 | { 640 | "model": "admin.logentry", 641 | "pk": 50, 642 | "fields": { 643 | "action_time": "2022-02-27T20:00:43.477Z", 644 | "user": 1, 645 | "content_type": 10, 646 | "object_id": "4", 647 | "object_repr": "Patient name admitted to None", 648 | "action_flag": 3, 649 | "change_message": "" 650 | } 651 | }, 652 | { 653 | "model": "admin.logentry", 654 | "pk": 51, 655 | "fields": { 656 | "action_time": "2022-02-27T20:00:43.478Z", 657 | "user": 1, 658 | "content_type": 10, 659 | "object_id": "3", 660 | "object_repr": "Patient name admitted to Maternity", 661 | "action_flag": 3, 662 | "change_message": "" 663 | } 664 | }, 665 | { 666 | "model": "admin.logentry", 667 | "pk": 52, 668 | "fields": { 669 | "action_time": "2022-02-27T20:00:43.480Z", 670 | "user": 1, 671 | "content_type": 10, 672 | "object_id": "2", 673 | "object_repr": "Patient name admitted to None", 674 | "action_flag": 3, 675 | "change_message": "" 676 | } 677 | }, 678 | { 679 | "model": "admin.logentry", 680 | "pk": 53, 681 | "fields": { 682 | "action_time": "2022-02-27T20:00:43.482Z", 683 | "user": 1, 684 | "content_type": 10, 685 | "object_id": "1", 686 | "object_repr": "Patient name admitted to None", 687 | "action_flag": 3, 688 | "change_message": "" 689 | } 690 | }, 691 | { 692 | "model": "admin.logentry", 693 | "pk": 54, 694 | "fields": { 695 | "action_time": "2022-02-27T20:01:53.737Z", 696 | "user": 1, 697 | "content_type": 10, 698 | "object_id": "8", 699 | "object_repr": "Patient name admitted to None", 700 | "action_flag": 3, 701 | "change_message": "" 702 | } 703 | }, 704 | { 705 | "model": "admin.logentry", 706 | "pk": 55, 707 | "fields": { 708 | "action_time": "2022-02-27T20:01:53.742Z", 709 | "user": 1, 710 | "content_type": 10, 711 | "object_id": "7", 712 | "object_repr": "Patient name admitted to None", 713 | "action_flag": 3, 714 | "change_message": "" 715 | } 716 | }, 717 | { 718 | "model": "admin.logentry", 719 | "pk": 56, 720 | "fields": { 721 | "action_time": "2022-02-27T20:02:19.402Z", 722 | "user": 1, 723 | "content_type": 10, 724 | "object_id": "10", 725 | "object_repr": "Patient name admitted to Casaulity", 726 | "action_flag": 3, 727 | "change_message": "" 728 | } 729 | }, 730 | { 731 | "model": "admin.logentry", 732 | "pk": 57, 733 | "fields": { 734 | "action_time": "2022-02-27T20:02:19.409Z", 735 | "user": 1, 736 | "content_type": 10, 737 | "object_id": "9", 738 | "object_repr": "Patient name admitted to Casaulity", 739 | "action_flag": 3, 740 | "change_message": "" 741 | } 742 | }, 743 | { 744 | "model": "admin.logentry", 745 | "pk": 58, 746 | "fields": { 747 | "action_time": "2022-03-01T14:46:07.211Z", 748 | "user": 1, 749 | "content_type": 6, 750 | "object_id": "1", 751 | "object_repr": "Nehe", 752 | "action_flag": 2, 753 | "change_message": "[{\"changed\": {\"fields\": [\"Role\"]}}]" 754 | } 755 | }, 756 | { 757 | "model": "admin.logentry", 758 | "pk": 59, 759 | "fields": { 760 | "action_time": "2022-03-01T14:46:33.555Z", 761 | "user": 1, 762 | "content_type": 6, 763 | "object_id": "1", 764 | "object_repr": "Nehe", 765 | "action_flag": 2, 766 | "change_message": "[{\"changed\": {\"fields\": [\"Role\"]}}]" 767 | } 768 | }, 769 | { 770 | "model": "admin.logentry", 771 | "pk": 60, 772 | "fields": { 773 | "action_time": "2022-03-01T14:50:18.988Z", 774 | "user": 1, 775 | "content_type": 6, 776 | "object_id": "1", 777 | "object_repr": "Nehe", 778 | "action_flag": 2, 779 | "change_message": "[{\"changed\": {\"fields\": [\"Role\"]}}]" 780 | } 781 | }, 782 | { 783 | "model": "admin.logentry", 784 | "pk": 61, 785 | "fields": { 786 | "action_time": "2022-03-01T14:50:49.140Z", 787 | "user": 1, 788 | "content_type": 6, 789 | "object_id": "1", 790 | "object_repr": "Nehe", 791 | "action_flag": 2, 792 | "change_message": "[{\"changed\": {\"fields\": [\"Role\"]}}]" 793 | } 794 | }, 795 | { 796 | "model": "admin.logentry", 797 | "pk": 62, 798 | "fields": { 799 | "action_time": "2022-03-01T14:50:49.363Z", 800 | "user": 1, 801 | "content_type": 6, 802 | "object_id": "1", 803 | "object_repr": "Nehe", 804 | "action_flag": 2, 805 | "change_message": "[]" 806 | } 807 | }, 808 | { 809 | "model": "admin.logentry", 810 | "pk": 63, 811 | "fields": { 812 | "action_time": "2022-03-01T15:03:17.538Z", 813 | "user": 1, 814 | "content_type": 6, 815 | "object_id": "1", 816 | "object_repr": "Nehe", 817 | "action_flag": 2, 818 | "change_message": "[{\"changed\": {\"fields\": [\"Role\"]}}]" 819 | } 820 | }, 821 | { 822 | "model": "admin.logentry", 823 | "pk": 64, 824 | "fields": { 825 | "action_time": "2022-03-01T15:03:55.231Z", 826 | "user": 1, 827 | "content_type": 6, 828 | "object_id": "1", 829 | "object_repr": "Nehe", 830 | "action_flag": 2, 831 | "change_message": "[{\"changed\": {\"fields\": [\"Role\"]}}]" 832 | } 833 | }, 834 | { 835 | "model": "admin.logentry", 836 | "pk": 65, 837 | "fields": { 838 | "action_time": "2022-03-05T11:17:30.323Z", 839 | "user": 1, 840 | "content_type": 7, 841 | "object_id": "18", 842 | "object_repr": "Patient name 3", 843 | "action_flag": 1, 844 | "change_message": "[{\"added\": {}}]" 845 | } 846 | }, 847 | { 848 | "model": "admin.logentry", 849 | "pk": 66, 850 | "fields": { 851 | "action_time": "2022-03-05T11:39:27.055Z", 852 | "user": 1, 853 | "content_type": 7, 854 | "object_id": "18", 855 | "object_repr": "Patient name 3", 856 | "action_flag": 2, 857 | "change_message": "[{\"changed\": {\"fields\": [\"Patient number\"]}}]" 858 | } 859 | }, 860 | { 861 | "model": "admin.logentry", 862 | "pk": 67, 863 | "fields": { 864 | "action_time": "2022-03-07T07:53:37.923Z", 865 | "user": 1, 866 | "content_type": 6, 867 | "object_id": "8", 868 | "object_repr": "Rhodesia", 869 | "action_flag": 1, 870 | "change_message": "[{\"added\": {}}]" 871 | } 872 | }, 873 | { 874 | "model": "admin.logentry", 875 | "pk": 68, 876 | "fields": { 877 | "action_time": "2022-03-07T08:24:25.541Z", 878 | "user": 1, 879 | "content_type": 13, 880 | "object_id": "3", 881 | "object_repr": "Patient name 2 referred to Nehe", 882 | "action_flag": 3, 883 | "change_message": "" 884 | } 885 | }, 886 | { 887 | "model": "admin.logentry", 888 | "pk": 69, 889 | "fields": { 890 | "action_time": "2022-03-09T09:07:40.411Z", 891 | "user": 1, 892 | "content_type": 10, 893 | "object_id": "15", 894 | "object_repr": "Victor Moses admitted to Casaulity", 895 | "action_flag": 3, 896 | "change_message": "" 897 | } 898 | }, 899 | { 900 | "model": "admin.logentry", 901 | "pk": 70, 902 | "fields": { 903 | "action_time": "2022-03-09T09:07:40.483Z", 904 | "user": 1, 905 | "content_type": 10, 906 | "object_id": "14", 907 | "object_repr": "Victor Moses admitted to Casaulity", 908 | "action_flag": 3, 909 | "change_message": "" 910 | } 911 | }, 912 | { 913 | "model": "admin.logentry", 914 | "pk": 71, 915 | "fields": { 916 | "action_time": "2022-03-09T09:09:39.854Z", 917 | "user": 1, 918 | "content_type": 10, 919 | "object_id": "17", 920 | "object_repr": "Victor Moses admitted to Maternity", 921 | "action_flag": 3, 922 | "change_message": "" 923 | } 924 | }, 925 | { 926 | "model": "admin.logentry", 927 | "pk": 72, 928 | "fields": { 929 | "action_time": "2022-03-09T09:09:39.880Z", 930 | "user": 1, 931 | "content_type": 10, 932 | "object_id": "16", 933 | "object_repr": "Victor Moses admitted to Casaulity", 934 | "action_flag": 3, 935 | "change_message": "" 936 | } 937 | }, 938 | { 939 | "model": "admin.logentry", 940 | "pk": 73, 941 | "fields": { 942 | "action_time": "2022-03-09T09:17:55.770Z", 943 | "user": 1, 944 | "content_type": 10, 945 | "object_id": "18", 946 | "object_repr": "Victor Moses admitted to Casaulity", 947 | "action_flag": 3, 948 | "change_message": "" 949 | } 950 | }, 951 | { 952 | "model": "admin.logentry", 953 | "pk": 74, 954 | "fields": { 955 | "action_time": "2022-03-09T09:18:20.160Z", 956 | "user": 1, 957 | "content_type": 13, 958 | "object_id": "8", 959 | "object_repr": "Victor Moses referred to DocNehe", 960 | "action_flag": 2, 961 | "change_message": "[{\"changed\": {\"fields\": [\"Status\"]}}]" 962 | } 963 | }, 964 | { 965 | "model": "admin.logentry", 966 | "pk": 75, 967 | "fields": { 968 | "action_time": "2022-03-11T15:47:25.206Z", 969 | "user": 1, 970 | "content_type": 7, 971 | "object_id": "22", 972 | "object_repr": "Tom Holland", 973 | "action_flag": 2, 974 | "change_message": "[]" 975 | } 976 | }, 977 | { 978 | "model": "admin.logentry", 979 | "pk": 76, 980 | "fields": { 981 | "action_time": "2022-03-11T15:48:05.887Z", 982 | "user": 1, 983 | "content_type": 7, 984 | "object_id": "22", 985 | "object_repr": "Tom Holland", 986 | "action_flag": 2, 987 | "change_message": "[]" 988 | } 989 | }, 990 | { 991 | "model": "admin.logentry", 992 | "pk": 77, 993 | "fields": { 994 | "action_time": "2022-03-11T15:49:05.155Z", 995 | "user": 1, 996 | "content_type": 7, 997 | "object_id": "22", 998 | "object_repr": "Tom Holland", 999 | "action_flag": 2, 1000 | "change_message": "[]" 1001 | } 1002 | }, 1003 | { 1004 | "model": "admin.logentry", 1005 | "pk": 78, 1006 | "fields": { 1007 | "action_time": "2022-03-11T15:49:48.702Z", 1008 | "user": 1, 1009 | "content_type": 7, 1010 | "object_id": "22", 1011 | "object_repr": "Tom Holland", 1012 | "action_flag": 2, 1013 | "change_message": "[]" 1014 | } 1015 | }, 1016 | { 1017 | "model": "admin.logentry", 1018 | "pk": 79, 1019 | "fields": { 1020 | "action_time": "2022-03-11T15:50:48.654Z", 1021 | "user": 1, 1022 | "content_type": 7, 1023 | "object_id": "22", 1024 | "object_repr": "Tom Holland", 1025 | "action_flag": 2, 1026 | "change_message": "[]" 1027 | } 1028 | }, 1029 | { 1030 | "model": "admin.logentry", 1031 | "pk": 80, 1032 | "fields": { 1033 | "action_time": "2022-03-11T16:07:20.431Z", 1034 | "user": 1, 1035 | "content_type": 7, 1036 | "object_id": "22", 1037 | "object_repr": "Tom Holland", 1038 | "action_flag": 2, 1039 | "change_message": "[]" 1040 | } 1041 | }, 1042 | { 1043 | "model": "admin.logentry", 1044 | "pk": 81, 1045 | "fields": { 1046 | "action_time": "2022-03-11T16:10:01.352Z", 1047 | "user": 1, 1048 | "content_type": 7, 1049 | "object_id": "22", 1050 | "object_repr": "Tom Holland", 1051 | "action_flag": 2, 1052 | "change_message": "[]" 1053 | } 1054 | }, 1055 | { 1056 | "model": "admin.logentry", 1057 | "pk": 82, 1058 | "fields": { 1059 | "action_time": "2022-03-11T16:10:27.330Z", 1060 | "user": 1, 1061 | "content_type": 7, 1062 | "object_id": "22", 1063 | "object_repr": "Tom Holland", 1064 | "action_flag": 2, 1065 | "change_message": "[]" 1066 | } 1067 | }, 1068 | { 1069 | "model": "admin.logentry", 1070 | "pk": 83, 1071 | "fields": { 1072 | "action_time": "2022-03-11T16:10:35.791Z", 1073 | "user": 1, 1074 | "content_type": 7, 1075 | "object_id": "22", 1076 | "object_repr": "Tom Holland", 1077 | "action_flag": 2, 1078 | "change_message": "[{\"changed\": {\"fields\": [\"Patient number\"]}}]" 1079 | } 1080 | }, 1081 | { 1082 | "model": "admin.logentry", 1083 | "pk": 84, 1084 | "fields": { 1085 | "action_time": "2022-03-11T16:10:46.233Z", 1086 | "user": 1, 1087 | "content_type": 7, 1088 | "object_id": "32", 1089 | "object_repr": "Fabiola J", 1090 | "action_flag": 2, 1091 | "change_message": "[]" 1092 | } 1093 | }, 1094 | { 1095 | "model": "admin.logentry", 1096 | "pk": 85, 1097 | "fields": { 1098 | "action_time": "2022-03-11T16:10:54.293Z", 1099 | "user": 1, 1100 | "content_type": 7, 1101 | "object_id": "32", 1102 | "object_repr": "Fabiola J", 1103 | "action_flag": 2, 1104 | "change_message": "[{\"changed\": {\"fields\": [\"Patient number\"]}}]" 1105 | } 1106 | }, 1107 | { 1108 | "model": "admin.logentry", 1109 | "pk": 86, 1110 | "fields": { 1111 | "action_time": "2022-03-11T16:11:54.585Z", 1112 | "user": 1, 1113 | "content_type": 7, 1114 | "object_id": "17", 1115 | "object_repr": "Patient name 2", 1116 | "action_flag": 2, 1117 | "change_message": "[{\"changed\": {\"fields\": [\"Created by\"]}}]" 1118 | } 1119 | }, 1120 | { 1121 | "model": "admin.logentry", 1122 | "pk": 87, 1123 | "fields": { 1124 | "action_time": "2022-03-11T16:12:05.905Z", 1125 | "user": 1, 1126 | "content_type": 7, 1127 | "object_id": "17", 1128 | "object_repr": "Patient name 2", 1129 | "action_flag": 2, 1130 | "change_message": "[{\"changed\": {\"fields\": [\"Patient number\"]}}]" 1131 | } 1132 | }, 1133 | { 1134 | "model": "admin.logentry", 1135 | "pk": 88, 1136 | "fields": { 1137 | "action_time": "2022-03-11T16:12:13.849Z", 1138 | "user": 1, 1139 | "content_type": 7, 1140 | "object_id": "31", 1141 | "object_repr": "Victor Moses", 1142 | "action_flag": 2, 1143 | "change_message": "[]" 1144 | } 1145 | }, 1146 | { 1147 | "model": "admin.logentry", 1148 | "pk": 89, 1149 | "fields": { 1150 | "action_time": "2022-03-11T16:12:22.059Z", 1151 | "user": 1, 1152 | "content_type": 7, 1153 | "object_id": "31", 1154 | "object_repr": "Victor Moses", 1155 | "action_flag": 2, 1156 | "change_message": "[{\"changed\": {\"fields\": [\"Patient number\"]}}]" 1157 | } 1158 | }, 1159 | { 1160 | "model": "admin.logentry", 1161 | "pk": 90, 1162 | "fields": { 1163 | "action_time": "2022-03-11T16:14:09.229Z", 1164 | "user": 1, 1165 | "content_type": 7, 1166 | "object_id": "36", 1167 | "object_repr": "Victor Moses", 1168 | "action_flag": 1, 1169 | "change_message": "[{\"added\": {}}]" 1170 | } 1171 | }, 1172 | { 1173 | "model": "admin.logentry", 1174 | "pk": 91, 1175 | "fields": { 1176 | "action_time": "2022-03-11T16:14:48.667Z", 1177 | "user": 1, 1178 | "content_type": 7, 1179 | "object_id": "36", 1180 | "object_repr": "Victor Moses", 1181 | "action_flag": 2, 1182 | "change_message": "[{\"changed\": {\"fields\": [\"Created by\"]}}]" 1183 | } 1184 | }, 1185 | { 1186 | "model": "admin.logentry", 1187 | "pk": 92, 1188 | "fields": { 1189 | "action_time": "2022-03-16T08:42:15.320Z", 1190 | "user": 1, 1191 | "content_type": 9, 1192 | "object_id": "4", 1193 | "object_repr": "Prescribed by DocNehe for fake", 1194 | "action_flag": 3, 1195 | "change_message": "" 1196 | } 1197 | }, 1198 | { 1199 | "model": "admin.logentry", 1200 | "pk": 93, 1201 | "fields": { 1202 | "action_time": "2022-03-16T08:42:30.898Z", 1203 | "user": 1, 1204 | "content_type": 10, 1205 | "object_id": "22", 1206 | "object_repr": "fake admitted to Maternity", 1207 | "action_flag": 3, 1208 | "change_message": "" 1209 | } 1210 | }, 1211 | { 1212 | "model": "admin.logentry", 1213 | "pk": 94, 1214 | "fields": { 1215 | "action_time": "2022-03-16T08:42:30.903Z", 1216 | "user": 1, 1217 | "content_type": 10, 1218 | "object_id": "21", 1219 | "object_repr": "fake admitted to Casaulity", 1220 | "action_flag": 3, 1221 | "change_message": "" 1222 | } 1223 | }, 1224 | { 1225 | "model": "admin.logentry", 1226 | "pk": 95, 1227 | "fields": { 1228 | "action_time": "2022-03-16T11:45:05.063Z", 1229 | "user": 1, 1230 | "content_type": 13, 1231 | "object_id": "11", 1232 | "object_repr": "Fabiola J referred to Rhodesia", 1233 | "action_flag": 3, 1234 | "change_message": "" 1235 | } 1236 | }, 1237 | { 1238 | "model": "sessions.session", 1239 | "pk": "03kmzoq06u0jedmm1dethshwrhrs5r2a", 1240 | "fields": { 1241 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3Iy:tHQKt1z0ipAkdrlAqZ6ibd4_qi7Bqexao7jamnneXRA", 1242 | "expire_date": "2022-03-18T08:28:52.214Z" 1243 | } 1244 | }, 1245 | { 1246 | "model": "sessions.session", 1247 | "pk": "17jocirx0syxw0ctvjhy6tyixcftxs0v", 1248 | "fields": { 1249 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3Jb:k6eQD9KmJvIxbzRJFAAPA6CcnBxN_JwC63d_T2ZJpPE", 1250 | "expire_date": "2022-03-18T08:29:31.453Z" 1251 | } 1252 | }, 1253 | { 1254 | "model": "sessions.session", 1255 | "pk": "18fp8cimxs955o0s0czec5udszie0f5w", 1256 | "fields": { 1257 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nRUoe:OeREVnbmhKpz9I3u-92U2u1ak0Jfy-FLaXp9FT9Pgsw", 1258 | "expire_date": "2022-03-22T08:03:32.839Z" 1259 | } 1260 | }, 1261 | { 1262 | "model": "sessions.session", 1263 | "pk": "19usqzq5j9siootagvzop8yso5dthuk9", 1264 | "fields": { 1265 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ5v4:sU15BPmH2BevrQMkEr3fX7B2Bn98UtRe6NSsTQXNHTM", 1266 | "expire_date": "2022-03-18T11:16:22.719Z" 1267 | } 1268 | }, 1269 | { 1270 | "model": "sessions.session", 1271 | "pk": "1ho5f4dmx7199a1r6lxkjapc0gqsarub", 1272 | "fields": { 1273 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nREAY:D1FSfWaxJ69kc4Vquva3GlpIwzX27egEsU5YbW8x2pY", 1274 | "expire_date": "2022-03-21T14:17:02.994Z" 1275 | } 1276 | }, 1277 | { 1278 | "model": "sessions.session", 1279 | "pk": "1podmsebx8yltfrwnn4nxyth1mc0bria", 1280 | "fields": { 1281 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRC8P:adY27XC9n1KggMMq1zr0xrlZD3jXLdJNcIoZJ9molzo", 1282 | "expire_date": "2022-03-21T12:06:41.258Z" 1283 | } 1284 | }, 1285 | { 1286 | "model": "sessions.session", 1287 | "pk": "2bgdu9dssel4sdgndkm41sv7yj4junkg", 1288 | "fields": { 1289 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nNwei:iJX685WfHQaIrJm8AJBpVxwOz0Rc4xzdZ31_LQkAYNE", 1290 | "expire_date": "2022-03-12T12:58:36.020Z" 1291 | } 1292 | }, 1293 | { 1294 | "model": "sessions.session", 1295 | "pk": "2jpcjv3g9cn5qnh75q8swhlzc01n5n5v", 1296 | "fields": { 1297 | "session_data": ".eJxVjE0OwiAYRO_C2pACBYpL9z0D-X7AVg0kpV0Z726bdKG7ybw38xYRtnWKW0tLnFlchRKX3w6BnqkcgB9Q7lVSLesyozwUedImx8rpdTvdv4MJ2rSvNSub8wAhYyYd2JH1QJq6gcApjUajtR6hz478nlQYUgeaTY-dsb0Sny8BbDf2:1nU551:2ltd2xWbR5M5dX01wO3VWZA1SuWs9e851uCoMKib6S0", 1298 | "expire_date": "2022-03-29T11:11:07.515Z" 1299 | } 1300 | }, 1301 | { 1302 | "model": "sessions.session", 1303 | "pk": "2rm1mqg5ta1y7mug5kkjh0v4c9c56ha1", 1304 | "fields": { 1305 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ8VN:eoIUn4WDOLonzGMVp0lBJXPHpnZBs6DtbdXaM9BMjnI", 1306 | "expire_date": "2022-03-18T14:02:01.333Z" 1307 | } 1308 | }, 1309 | { 1310 | "model": "sessions.session", 1311 | "pk": "32f3ogmkkly7fsu7ok7swbe7g52q03gj", 1312 | "fields": { 1313 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nSI4X:-xL-0nAFgT25fTl3eUIsXy-2ULis45_dQO9jQzN3Zjs", 1314 | "expire_date": "2022-03-24T12:39:13.285Z" 1315 | } 1316 | }, 1317 | { 1318 | "model": "sessions.session", 1319 | "pk": "3mnoe0fb7imdf5appiqfgk9zdw4frhmz", 1320 | "fields": { 1321 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRTz4:mtnsPMQt7oyhE9sZ9ubGhEEZqC3Dvv3RNXMTxZvkXgI", 1322 | "expire_date": "2022-03-22T07:10:14.668Z" 1323 | } 1324 | }, 1325 | { 1326 | "model": "sessions.session", 1327 | "pk": "3pdwhtlnyhmrim1w6ltgbu61eoxnragm", 1328 | "fields": { 1329 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nUP8k:zkeh3HydFejlkwO934QAj4t-BU6vLkRBPWY9ASgBCfk", 1330 | "expire_date": "2022-03-30T08:36:18.975Z" 1331 | } 1332 | }, 1333 | { 1334 | "model": "sessions.session", 1335 | "pk": "3q3qy7gzox03qca9ate62jp7qhwpfgcq", 1336 | "fields": { 1337 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRTwG:ENRL4eCFvoSDWQjvsboEGV8CP7ZpvZtUDrC5AM0ljyI", 1338 | "expire_date": "2022-03-22T07:07:20.935Z" 1339 | } 1340 | }, 1341 | { 1342 | "model": "sessions.session", 1343 | "pk": "4me1dhsxuqrc0tn831yrhgny53zy1870", 1344 | "fields": { 1345 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3J3:0tBnTDwUbE_rEcTJvC5MyWljs0McW2LRV-VgmFpKSAQ", 1346 | "expire_date": "2022-03-18T08:28:57.077Z" 1347 | } 1348 | }, 1349 | { 1350 | "model": "sessions.session", 1351 | "pk": "5ay47blu7zbxn7vhvj5jq0i6gty642v8", 1352 | "fields": { 1353 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRBu0:fDXUWDUoLno0nZyg64GY39bgxhmxNPp0WCmBW6E9uHU", 1354 | "expire_date": "2022-03-21T11:51:48.213Z" 1355 | } 1356 | }, 1357 | { 1358 | "model": "sessions.session", 1359 | "pk": "5hcrxx8eo2u1bcp5bilnm20nq69neqzo", 1360 | "fields": { 1361 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nTL7J:cPxbOJTA4XY_uEGRfO3LSCC_pnzfCtpqHotRWZlG4Is", 1362 | "expire_date": "2022-03-27T10:06:25.739Z" 1363 | } 1364 | }, 1365 | { 1366 | "model": "sessions.session", 1367 | "pk": "5vibiviewndupwdo7zi45xjz7l9xi8eh", 1368 | "fields": { 1369 | "session_data": ".eJxVjE0OwiAYRO_C2pACBYpL9z0D-X7AVg0kpV0Z726bdKG7ybw38xYRtnWKW0tLnFlchRKX3w6BnqkcgB9Q7lVSLesyozwUedImx8rpdTvdv4MJ2rSvNSub8wAhYyYd2JH1QJq6gcApjUajtR6hz478nlQYUgeaTY-dsb0Sny8BbDf2:1nTl8F:dGemR79twZhodsUQ6JFm6pk_Uc8sydw3-tNTXfnwOcs", 1370 | "expire_date": "2022-03-28T13:53:07.111Z" 1371 | } 1372 | }, 1373 | { 1374 | "model": "sessions.session", 1375 | "pk": "604phlsq0w2hv9gk5b6c1h6om533vii2", 1376 | "fields": { 1377 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3Iq:ZrTLs9Aqf29ubBA9PGC-0j3z8vGFKxfCfiOi0wJggvE", 1378 | "expire_date": "2022-03-18T08:28:44.382Z" 1379 | } 1380 | }, 1381 | { 1382 | "model": "sessions.session", 1383 | "pk": "68lmkics3b0rd2py1uhf4oa9h0mqzr4a", 1384 | "fields": { 1385 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nSYoA:ZVPSB7AouUjA4__jQFOM7cUYA1yEdbMtIMfvOZww_g4", 1386 | "expire_date": "2022-03-25T06:31:26.467Z" 1387 | } 1388 | }, 1389 | { 1390 | "model": "sessions.session", 1391 | "pk": "69019egjqum9j8k7rbwomi5u5hqil5pj", 1392 | "fields": { 1393 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRDzP:OxY-yRFzNaUyPxqaSP6CXN3RzpOcXaEK9lUfQL5HWXY", 1394 | "expire_date": "2022-03-21T14:05:31.679Z" 1395 | } 1396 | }, 1397 | { 1398 | "model": "sessions.session", 1399 | "pk": "6ghi6i3o5f5x1queiwd9xgmoc9i4ayln", 1400 | "fields": { 1401 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ5u9:qvcdCCLN0FO8rbzjy_gXK3UqLJU36fIKmaZ84D8pWUI", 1402 | "expire_date": "2022-03-18T11:15:25.902Z" 1403 | } 1404 | }, 1405 | { 1406 | "model": "sessions.session", 1407 | "pk": "6sx7nlfckj15cj2jjcwz7kb2letwddia", 1408 | "fields": { 1409 | "session_data": ".eJxVjE0OwiAYRO_C2pACBYpL9z0D-X7AVg0kpV0Z726bdKG7ybw38xYRtnWKW0tLnFlchRKX3w6BnqkcgB9Q7lVSLesyozwUedImx8rpdTvdv4MJ2rSvNSub8wAhYyYd2JH1QJq6gcApjUajtR6hz478nlQYUgeaTY-dsb0Sny8BbDf2:1nUPHO:3TRKZB2y2tWgmXFZsUa3deuyBIvB4s9cjFea5IMA2Ng", 1410 | "expire_date": "2022-03-30T08:45:14.787Z" 1411 | } 1412 | }, 1413 | { 1414 | "model": "sessions.session", 1415 | "pk": "6z5qqirvl8vdhjkpgow3sr5v2oghtd6k", 1416 | "fields": { 1417 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nReBB:l2nPLDt4__WczAw-gMzhu3hXxYgJ0dXZh3eunSD1SSI", 1418 | "expire_date": "2022-03-22T18:03:25.282Z" 1419 | } 1420 | }, 1421 | { 1422 | "model": "sessions.session", 1423 | "pk": "83nfyghn5dm8c1kr9z8ouhtedb3jnavt", 1424 | "fields": { 1425 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nU35s:FFta4LXvFqp1lnqcqjTRuo_qONq1NGj5vU2b9rkAJOI", 1426 | "expire_date": "2022-03-29T09:03:52.996Z" 1427 | } 1428 | }, 1429 | { 1430 | "model": "sessions.session", 1431 | "pk": "8fw96veim18mrzv3ll6y3v9almwnlgn2", 1432 | "fields": { 1433 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ43U:ox4KTprA-nvVZeuMS2VmMSI-DEysGLLDRoIx4UCFQrw", 1434 | "expire_date": "2022-03-18T09:16:56.440Z" 1435 | } 1436 | }, 1437 | { 1438 | "model": "sessions.session", 1439 | "pk": "93gijapjg0vvqweihj2fm8muobja96ap", 1440 | "fields": { 1441 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3Iz:D_Ct_0Q5h0XBs18BdLOzsHp4HYyNNpcBKkgANuWN478", 1442 | "expire_date": "2022-03-18T08:28:53.844Z" 1443 | } 1444 | }, 1445 | { 1446 | "model": "sessions.session", 1447 | "pk": "971bvmie0erzmb7rxi14atli2ffrqpt4", 1448 | "fields": { 1449 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nReEg:f_wmEtbJP3GxuXBtzBxuWvr1wGsMiEhTaAiJSajY0Zs", 1450 | "expire_date": "2022-03-22T18:07:02.133Z" 1451 | } 1452 | }, 1453 | { 1454 | "model": "sessions.session", 1455 | "pk": "98vfi0coobqxddw31hv96kvcj5tdm1n4", 1456 | "fields": { 1457 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRCSo:ILtd-DUcxRhqywh6EWMROT-kGMg8idyKlqwys5G2kh8", 1458 | "expire_date": "2022-03-21T12:27:46.524Z" 1459 | } 1460 | }, 1461 | { 1462 | "model": "sessions.session", 1463 | "pk": "99sdsm56jgu9j0vhxyl8i182w2ck0ebb", 1464 | "fields": { 1465 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRERk:YVndS2Q_6EpzwC5Ai_hkvRj_uDKEbR5-S8f2SJX8qms", 1466 | "expire_date": "2022-03-21T14:34:48.542Z" 1467 | } 1468 | }, 1469 | { 1470 | "model": "sessions.session", 1471 | "pk": "9cx82b4181aewdw9n1e10q5imnvhu24u", 1472 | "fields": { 1473 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nTLLO:F9X_kfa-WXqRxr-TGbD7XkpUE0trBSNCP7-ytX55KC8", 1474 | "expire_date": "2022-03-27T10:20:58.849Z" 1475 | } 1476 | }, 1477 | { 1478 | "model": "sessions.session", 1479 | "pk": "9qfdyrue5lhbhmmckh3oo8jlzzf0qbsy", 1480 | "fields": { 1481 | "session_data": ".eJwNyMsRgCAMBcBeqOCF8IvNMNHE4Sxycuxd97hP6Lru0df0qw-dI2zhYK_ALrlEEjTik5Gicqr0h0YRs9yykjhSRW4FvivcIDUaU3g_UQMX4A:1nTiix:NVA7NvnCCW4LJlJxQ1XkHxYvY6rC8rnsaL0lMlYoO1M", 1482 | "expire_date": "2022-03-28T11:18:51.431Z" 1483 | } 1484 | }, 1485 | { 1486 | "model": "sessions.session", 1487 | "pk": "9qmqjkxjwodgsolf45ejpe1ox6h8ygid", 1488 | "fields": { 1489 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3J0:zGdOjxv0cJukXQYU_ErX56G7aFySe2RcoCeCwPWx2ko", 1490 | "expire_date": "2022-03-18T08:28:54.668Z" 1491 | } 1492 | }, 1493 | { 1494 | "model": "sessions.session", 1495 | "pk": "a5nfp9sslulpd0yud3qnck3yhubhukkt", 1496 | "fields": { 1497 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nUQga:6y1DZ6-uQY58-3gG23uftn9LyxbGbt3EkyfW5A9_LUY", 1498 | "expire_date": "2022-03-30T10:15:20.025Z" 1499 | } 1500 | }, 1501 | { 1502 | "model": "sessions.session", 1503 | "pk": "aa1oyj0v52ax5genxta5pr2lzqhwtbwb", 1504 | "fields": { 1505 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ5sK:x3sBEZcQZ5jBVQOuiuxNSjEdNiW_CckXJ_QXQzDoagg", 1506 | "expire_date": "2022-03-18T11:13:32.903Z" 1507 | } 1508 | }, 1509 | { 1510 | "model": "sessions.session", 1511 | "pk": "aoo5zk3ufqji20d5ixglz0bgqlnvoma5", 1512 | "fields": { 1513 | "session_data": ".eJwFwcENwCAIAMBdnAAUInYZAyLxXeur6e69e1PX86x-9rz70r3SldSpqGXkJpW9UaiUqFBtaHUWJC4ThoXARAMTIjQeqoDRIpOn7weWeRnC:1nTM4X:Na5ndK1YrstcjC2vAzpSBT5lkExx2o2aCaUPg6SEQR8", 1514 | "expire_date": "2022-03-27T11:07:37.709Z" 1515 | } 1516 | }, 1517 | { 1518 | "model": "sessions.session", 1519 | "pk": "bmz373e5mddzx2swbmi8jdkle1lwp96a", 1520 | "fields": { 1521 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nREpL:zY6S7K1CChjTAYOd0tY3MBWaztjfAE4FM0WqJHz9Ucs", 1522 | "expire_date": "2022-03-21T14:59:11.849Z" 1523 | } 1524 | }, 1525 | { 1526 | "model": "sessions.session", 1527 | "pk": "c42nu9b1j9ib4woryn67pc2uf7vqmreg", 1528 | "fields": { 1529 | "session_data": ".eJwFwcERgCAMBMBeqIBESMBmmMCF4S36cuzd3Tc0e-7Vnu1XW7ZXOAPHQRU-0jDOwkaCTD2WiinCXQ3I45ilkrl3zKSqQGWQqHtJ4fsBphwaPA:1nTM1D:fbHKRuVRn9ANkLwq3c8bFZelzrX8VWm9-LQ1i5PxHjM", 1530 | "expire_date": "2022-03-27T11:04:11.306Z" 1531 | } 1532 | }, 1533 | { 1534 | "model": "sessions.session", 1535 | "pk": "ckil8lki9hhedn18f5llyiueaezhr5sr", 1536 | "fields": { 1537 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3T7:cm6xgvNq9C3SojfkWVJO6OIbmq5v7NhKAHgTBxZhqsI", 1538 | "expire_date": "2022-03-18T08:39:21.662Z" 1539 | } 1540 | }, 1541 | { 1542 | "model": "sessions.session", 1543 | "pk": "cni9zofc9tl8sti7l1qaw8tn5eeqzl5b", 1544 | "fields": { 1545 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRCIE:CesdUiJ2_HhVKk-cER5WKYavdu-xObp-gUYoS1VnNMM", 1546 | "expire_date": "2022-03-21T12:16:50.976Z" 1547 | } 1548 | }, 1549 | { 1550 | "model": "sessions.session", 1551 | "pk": "cue5ns9eccb3z7oyamwg7dbrjcnb5kb0", 1552 | "fields": { 1553 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3F5:Uze3Y0ngGVvsAHLydZPU_9cmbAU9e72feS9yVnuE7yY", 1554 | "expire_date": "2022-03-18T08:24:51.817Z" 1555 | } 1556 | }, 1557 | { 1558 | "model": "sessions.session", 1559 | "pk": "d7ikafk6hcgjeuomj9guxytxuqjhxexe", 1560 | "fields": { 1561 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3Hs:H9uYdeb-D8TsMwl2hnVJwSmMcU6QjY1hQluurPd4Zhg", 1562 | "expire_date": "2022-03-18T08:27:44.072Z" 1563 | } 1564 | }, 1565 | { 1566 | "model": "sessions.session", 1567 | "pk": "dfjdpaa0x0nt7qqnnxbsfrqngsjvgfzk", 1568 | "fields": { 1569 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRU1u:9sbElZoJGh94BHdc82eS6vG1-L9uX0U5ZOdodM-Tbow", 1570 | "expire_date": "2022-03-22T07:13:10.427Z" 1571 | } 1572 | }, 1573 | { 1574 | "model": "sessions.session", 1575 | "pk": "dii2ecylts2gfiy7cs7shpr3ilo9es4w", 1576 | "fields": { 1577 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ44R:wguDNjHxHMHqUuS9p6VQkYmQt4wJFCPFxn3PTj-juAM", 1578 | "expire_date": "2022-03-18T09:17:55.310Z" 1579 | } 1580 | }, 1581 | { 1582 | "model": "sessions.session", 1583 | "pk": "dlstzyuxl8ojcdlxcm8a9tj3fv97l3hu", 1584 | "fields": { 1585 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRByD:8A5bMB44fagu7nvNmXf9uIDm-LdYDmQghxE7P45yLA8", 1586 | "expire_date": "2022-03-21T11:56:09.133Z" 1587 | } 1588 | }, 1589 | { 1590 | "model": "sessions.session", 1591 | "pk": "dxged7shhn4gfmkpwfujsqbenl3n3e9l", 1592 | "fields": { 1593 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3FJ:znBJfuKJed2GK2pO0UFZtZ3xQZOXjgtDby1kke_hSwI", 1594 | "expire_date": "2022-03-18T08:25:05.298Z" 1595 | } 1596 | }, 1597 | { 1598 | "model": "sessions.session", 1599 | "pk": "e2hhxa9ke0cl92s3lj2k4876ih0fq76a", 1600 | "fields": { 1601 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3DY:Ufi94xJTwngEX7VScCuZuTHrbAm5OEO3lvMWXqf3gMM", 1602 | "expire_date": "2022-03-18T08:23:16.746Z" 1603 | } 1604 | }, 1605 | { 1606 | "model": "sessions.session", 1607 | "pk": "e6gtpycto40xsutyg4f119kfhwhrb8td", 1608 | "fields": { 1609 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nRfHj:nbKWj22dmlg0QYc7pZ0PpZ965EVawrDWFKbDh5ObhG4", 1610 | "expire_date": "2022-03-22T19:14:15.424Z" 1611 | } 1612 | }, 1613 | { 1614 | "model": "sessions.session", 1615 | "pk": "f4zz4f0sotm71deej817o11zum5nzvmg", 1616 | "fields": { 1617 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ8pw:-WhL47-nBGE_w5x91dadDD2M1TL1SbLFNK9_-TfzkM0", 1618 | "expire_date": "2022-03-18T14:23:16.394Z" 1619 | } 1620 | }, 1621 | { 1622 | "model": "sessions.session", 1623 | "pk": "f66q1deusbft94pgl6amg78sel5tznbs", 1624 | "fields": { 1625 | "session_data": ".eJwFwcENwCAIAMBdnAAQAbuMUdH4rvXVdPfevaHU86xy9rjLqnuFK7Am8YgmakMQIytWI5iTWJqbQ1UgTdocBoB0S0jZaczO03rm8P1bzhiJ:1nTM1U:lsaNbg_Ejbis87PagyLKa9c20UIQLaP3Y6vjcRqk0Uc", 1626 | "expire_date": "2022-03-27T11:04:28.870Z" 1627 | } 1628 | }, 1629 | { 1630 | "model": "sessions.session", 1631 | "pk": "fb6mdljxh114544icvfbs9zsbjyugb7d", 1632 | "fields": { 1633 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRfH2:_Gu6DOgEaxdlbBg3TVkYg8Y90K--zqbb3zsP9cQqppc", 1634 | "expire_date": "2022-03-22T19:13:32.798Z" 1635 | } 1636 | }, 1637 | { 1638 | "model": "sessions.session", 1639 | "pk": "fjm1t2rbdnd3mx0kpfmmi5oejvgsl5ae", 1640 | "fields": { 1641 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nREuN:HVGzS7ixkk06Ue78vXpEM9jKSdsYkRGVakg4NK3_Sto", 1642 | "expire_date": "2022-03-21T15:04:23.507Z" 1643 | } 1644 | }, 1645 | { 1646 | "model": "sessions.session", 1647 | "pk": "fjue9cdyl68ymckoerf7mhmvqc8x7k8o", 1648 | "fields": { 1649 | "session_data": ".eJxVjE0OwiAYRO_C2pACBYpL9z0D-X7AVg0kpV0Z726bdKG7ybw38xYRtnWKW0tLnFlchRKX3w6BnqkcgB9Q7lVSLesyozwUedImx8rpdTvdv4MJ2rSvNSub8wAhYyYd2JH1QJq6gcApjUajtR6hz478nlQYUgeaTY-dsb0Sny8BbDf2:1nUP5O:62QRqfFYAHId5KG08z33IKLUgCVRHgMiAoSMWYlQUQs", 1650 | "expire_date": "2022-03-30T08:32:50.644Z" 1651 | } 1652 | }, 1653 | { 1654 | "model": "sessions.session", 1655 | "pk": "g3x2i1emcwbiuqav4ygh1hn23r3dk1pn", 1656 | "fields": { 1657 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nTLO7:r4V_jNHPQxqV3oNYajCUuGOd9Lyicd3nUb2k8MnnEic", 1658 | "expire_date": "2022-03-27T10:23:47.883Z" 1659 | } 1660 | }, 1661 | { 1662 | "model": "sessions.session", 1663 | "pk": "g4n1db369tokf99eypdd8h7jade6jz0x", 1664 | "fields": { 1665 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nSKvK:iNbgpFsJ2rF3ifWyTEJU5XKKDxILZ2DwS-3PLmztxbM", 1666 | "expire_date": "2022-03-24T15:41:54.949Z" 1667 | } 1668 | }, 1669 | { 1670 | "model": "sessions.session", 1671 | "pk": "gclb000csnxcyjvupd6yupubqagkuygb", 1672 | "fields": { 1673 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ94q:AsLViX7sGZk8FksbXp4U65ulfo3Dfgwp-aN9y7txyOg", 1674 | "expire_date": "2022-03-18T14:38:40.562Z" 1675 | } 1676 | }, 1677 | { 1678 | "model": "sessions.session", 1679 | "pk": "htsguwg827mmwjpco7547nx8ez224e69", 1680 | "fields": { 1681 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nUP3q:_BKiJw_d67SUTU5Vqgganl-qGO6V_SrWBkDvF2Ejqwk", 1682 | "expire_date": "2022-03-30T08:31:14.983Z" 1683 | } 1684 | }, 1685 | { 1686 | "model": "sessions.session", 1687 | "pk": "hzgiu9dqqrn6v2ego7gppww0s842og0d", 1688 | "fields": { 1689 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ8ok:vynYlT8Z-k7yqE7fnyuSIV2ErRFXMSGgZz8oJrxFsnw", 1690 | "expire_date": "2022-03-18T14:22:02.939Z" 1691 | } 1692 | }, 1693 | { 1694 | "model": "sessions.session", 1695 | "pk": "i9he1hk9yj13mqqf6wbww0h1okgtmczi", 1696 | "fields": { 1697 | "session_data": ".eJwFwcENwCAIAMBdnEAUFLqMQcT4rvXVdPfevaHpeVY72--2dK9wBfWJ4lw0aU-MFJMWqBRZY5wZhmQzGE6JALiaMzuhGXYS9l4lfD-LXBlH:1nTLqf:5SATqlfkEJxN6t4upREh9fGChKjzBcB89M11JuiIph4", 1698 | "expire_date": "2022-03-27T10:53:17.765Z" 1699 | } 1700 | }, 1701 | { 1702 | "model": "sessions.session", 1703 | "pk": "izkc8kq5212ue9joqd10k7246lrigcay", 1704 | "fields": { 1705 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nU39n:CaTq4pYuUIjfPGHpjk5ov78SX7zpiBQqtOdKPc-aj0Q", 1706 | "expire_date": "2022-03-29T09:07:55.277Z" 1707 | } 1708 | }, 1709 | { 1710 | "model": "sessions.session", 1711 | "pk": "jxp20j9gsjfdbsqwh23gyqr9uruszh5a", 1712 | "fields": { 1713 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3J1:J_0vfq79KopKLr6J2JFuTEOWTWgigFMIhESUdFv4qdo", 1714 | "expire_date": "2022-03-18T08:28:55.344Z" 1715 | } 1716 | }, 1717 | { 1718 | "model": "sessions.session", 1719 | "pk": "k2l6ocjjefvtcdhl4exwxyrn6nq4q9dk", 1720 | "fields": { 1721 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nTLNH:zCwuFnEtcxOoLzlP92RtDEPz_BZXXcwFfInD5zZXaNI", 1722 | "expire_date": "2022-03-27T10:22:55.744Z" 1723 | } 1724 | }, 1725 | { 1726 | "model": "sessions.session", 1727 | "pk": "k4jd3rete30rl50272kuumub12gmh2du", 1728 | "fields": { 1729 | "session_data": ".eJwFwcERgCAMBMBeqCAEIjmbYSKE4S3ycuzd3TdU28-se_ldp60ZzgBEbWXAxYrYAeLYzcVbZM0F3um6snWhRhCmgUxJkcCqRBIlfD-Eyhhf:1nTLor:oHSpmCOSbALFygVE6bADtMQkIIfFWya_-PHO3_IskYg", 1730 | "expire_date": "2022-03-27T10:51:25.314Z" 1731 | } 1732 | }, 1733 | { 1734 | "model": "sessions.session", 1735 | "pk": "k4l887g3duee6mvhw8k6bmjymr5vvhvv", 1736 | "fields": { 1737 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nSILj:cXO135znuEKq1XStY_Z4tXjB5EPFpY2sNlmIdAzzWEg", 1738 | "expire_date": "2022-03-24T12:56:59.475Z" 1739 | } 1740 | }, 1741 | { 1742 | "model": "sessions.session", 1743 | "pk": "kd1timmchsha0601m7abiae9tffqskgr", 1744 | "fields": { 1745 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3J2:eJO6x2ceZXEbv2oAtxv2kgm1ntrFR6BvcJyEzF_Hr4s", 1746 | "expire_date": "2022-03-18T08:28:56.617Z" 1747 | } 1748 | }, 1749 | { 1750 | "model": "sessions.session", 1751 | "pk": "l9xvnevvdzn4irqo2cqewvaj6rs1vjlr", 1752 | "fields": { 1753 | "session_data": ".eJwNyUESgCAIAMC_8IIkQewzDhiM58xT099rr_tA03WPtqZfbegccABXOck0qZUo6lJpT1xUMaRrqmHB_4a5M7qho3HIRsGSCXN3eD-7eRqd:1nTLvm:tktdmz8_izDJKxVYZkqjGawCYPJB6m0KsgXFWFbg-4c", 1754 | "expire_date": "2022-03-27T10:58:34.601Z" 1755 | } 1756 | }, 1757 | { 1758 | "model": "sessions.session", 1759 | "pk": "lhvc79z5xm0874im36cr59fvqfv0wdjl", 1760 | "fields": { 1761 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nSKxC:bJYEZbefPVN6XA0t8_ykAo0kIFD2gl8uWEnD17Hs7dA", 1762 | "expire_date": "2022-03-24T15:43:50.311Z" 1763 | } 1764 | }, 1765 | { 1766 | "model": "sessions.session", 1767 | "pk": "lsdhiu1rucrggx9r4sbwc60pb0r4kxev", 1768 | "fields": { 1769 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ8nD:eP70qWLskYHyn_huVtqPPaJop8tLy2S_PKhB__BNc0M", 1770 | "expire_date": "2022-03-18T14:20:27.798Z" 1771 | } 1772 | }, 1773 | { 1774 | "model": "sessions.session", 1775 | "pk": "m4jyfnczg16o1plwix15m7ccwl19yl1m", 1776 | "fields": { 1777 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ5ty:pi50Rxq5QJp6rmUKa_Jqm5fSOjQ1GGBLyNtULmLlaGM", 1778 | "expire_date": "2022-03-18T11:15:14.904Z" 1779 | } 1780 | }, 1781 | { 1782 | "model": "sessions.session", 1783 | "pk": "me5vao4r6isaijiiup7wp0n1c852tweq", 1784 | "fields": { 1785 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRCRu:u0fNdQobdHSamSXHnj8dY5zA3O_F_i6LGQLRg4RMamI", 1786 | "expire_date": "2022-03-21T12:26:50.918Z" 1787 | } 1788 | }, 1789 | { 1790 | "model": "sessions.session", 1791 | "pk": "ml6fzcthuf1ldic1ud47usvv58rh5cnz", 1792 | "fields": { 1793 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nUPHA:8TREmP7Ul7Dkr7DxbMsMAI6zZJJITUnNs1ZMGuEBg3c", 1794 | "expire_date": "2022-03-30T08:45:00.423Z" 1795 | } 1796 | }, 1797 | { 1798 | "model": "sessions.session", 1799 | "pk": "mt3a3esnuyl6jdkopgv1ttj3srd8ycgc", 1800 | "fields": { 1801 | "session_data": ".eJxVjE0OwiAYRO_C2pACBYpL9z0D-X7AVg0kpV0Z726bdKG7ybw38xYRtnWKW0tLnFlchRKX3w6BnqkcgB9Q7lVSLesyozwUedImx8rpdTvdv4MJ2rSvNSub8wAhYyYd2JH1QJq6gcApjUajtR6hz478nlQYUgeaTY-dsb0Sny8BbDf2:1nU3A3:1S2DMTocEItNDh85xG02peIVUDxd_BcVNT6Y8ewR0k4", 1802 | "expire_date": "2022-03-29T09:08:11.660Z" 1803 | } 1804 | }, 1805 | { 1806 | "model": "sessions.session", 1807 | "pk": "nxiarik6p22ioxx9nzn1od8eeb1zmkyl", 1808 | "fields": { 1809 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRCZD:tcdBzwa3IVJf-NyL9-Abofp9tFvFgnsANn9GKG3Zfvc", 1810 | "expire_date": "2022-03-21T12:34:23.983Z" 1811 | } 1812 | }, 1813 | { 1814 | "model": "sessions.session", 1815 | "pk": "nyw24r5u497eyuj3qiepy79r7t31swl0", 1816 | "fields": { 1817 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3Fn:LriKcqnWLkHZnyZN9d0mKqWQOob2XQ7udlCYqtnHWx4", 1818 | "expire_date": "2022-03-18T08:25:35.819Z" 1819 | } 1820 | }, 1821 | { 1822 | "model": "sessions.session", 1823 | "pk": "o3csvqi6ldf5yoamou7h80dld7ifnavj", 1824 | "fields": { 1825 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ5kU:DIzSR1K3FG3Vlic84iG-jJKx7zUvcowS3g4OUfpHTkg", 1826 | "expire_date": "2022-03-18T11:05:26.335Z" 1827 | } 1828 | }, 1829 | { 1830 | "model": "sessions.session", 1831 | "pk": "oupyszp6mxazx0j8vgqx1j2iu5pdvu4y", 1832 | "fields": { 1833 | "session_data": ".eJxVjL0OwjAQg98lM4rgkuaHkb3PEN3lLqSAWqlpJ8S700odQPJif7bfKuG61LQ2mdPA6qou6vSbEeanjDvgB473SedpXOaB9F7RB226n1het6P7d1Cx1W3tzh452JyJohB7u9u4KbBAZwyAK4DOGVu6LoD3EnMxwAELBSBSny_0DjhR:1nTign:4szaCwTyIvK4fOEVAHVcUgyfTsoQsPcokboZwxl428E", 1834 | "expire_date": "2022-03-28T11:16:37.374Z" 1835 | } 1836 | }, 1837 | { 1838 | "model": "sessions.session", 1839 | "pk": "oz45ymg3p669zenqnosm0ullykb5f1k9", 1840 | "fields": { 1841 | "session_data": ".eJxVjEsOwiAUAO_C2hDhiW1duu8ZyPshVUOT0q6Md1eSLnQ7M5mXibitOW5VlziJuRhnDr-MkB9ampA7lttseS7rMpFtid1tteMs-rzu7d8gY81ty3gePFFAYUzkgn6JBGBSoJDAsXQE4Ac-QndKTvokibwCKHHPat4fJAI5xQ:1nTie7:KzM2_zqF1lKsW_Veq9NjJII1aGlHvGOFUEqZ8AU3wiU", 1842 | "expire_date": "2022-03-28T11:13:51.206Z" 1843 | } 1844 | }, 1845 | { 1846 | "model": "sessions.session", 1847 | "pk": "p8ytizy2a7z8cq8bsay823rx6wbhbavp", 1848 | "fields": { 1849 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ43r:SkDxClTXNRENHmDeRooXuGFYbMkrDEilOwwverSGL_E", 1850 | "expire_date": "2022-03-18T09:17:19.813Z" 1851 | } 1852 | }, 1853 | { 1854 | "model": "sessions.session", 1855 | "pk": "qbgxtniklphcfi6fp0jjq2d9galfrc3l", 1856 | "fields": { 1857 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ8UA:dqR02zXS65cbjiLQjxNhgbMeYNWoden6mzlDeOJFodY", 1858 | "expire_date": "2022-03-18T14:00:46.721Z" 1859 | } 1860 | }, 1861 | { 1862 | "model": "sessions.session", 1863 | "pk": "qkio97kxli6q5bb7erkqtons73p3yq63", 1864 | "fields": { 1865 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ5v8:h54aphHoqBqMk3wFd_iMv1jjRRPbr6uV834t_hGn-Zg", 1866 | "expire_date": "2022-03-18T11:16:26.168Z" 1867 | } 1868 | }, 1869 | { 1870 | "model": "sessions.session", 1871 | "pk": "qqug9g9krtav0s2rj6wsou6qqcgjqqoo", 1872 | "fields": { 1873 | "session_data": ".eJxVjE0OwiAYRO_C2pACBYpL9z0D-X7AVg0kpV0Z726bdKG7ybw38xYRtnWKW0tLnFlchRKX3w6BnqkcgB9Q7lVSLesyozwUedImx8rpdTvdv4MJ2rSvNSub8wAhYyYd2JH1QJq6gcApjUajtR6hz478nlQYUgeaTY-dsb0Sny8BbDf2:1nUQDc:D1LhedWuARtYSeOnbzGS1weYOMV8I2ipfV48Eg5yLUk", 1874 | "expire_date": "2022-03-30T09:45:24.326Z" 1875 | } 1876 | }, 1877 | { 1878 | "model": "sessions.session", 1879 | "pk": "r6gfrus680ap3cqllec1ycp5c09v4q15", 1880 | "fields": { 1881 | "session_data": ".eJwFwcERgCAMBMBe0kHIoWAzTEjI8BZ5Ofbu7ktN9zPbXuNuU9eki8ykREIgh0Q4mJ3j4LOy1OSajmFQFyuuEEYV1N45PGcMlFTo-wGdPhl3:1nTLmE:Opgh9o74UyyyGu7a-Q-KJr7xuCjBEIkR2Bp7aolJn30", 1882 | "expire_date": "2022-03-27T10:48:42.463Z" 1883 | } 1884 | }, 1885 | { 1886 | "model": "sessions.session", 1887 | "pk": "rgu78n02jql80bjzdvav2vd4q23myzyy", 1888 | "fields": { 1889 | "session_data": ".eJxVjE0OwiAYRO_C2pACBYpL9z0D-X7AVg0kpV0Z726bdKG7ybw38xYRtnWKW0tLnFlchRKX3w6BnqkcgB9Q7lVSLesyozwUedImx8rpdTvdv4MJ2rSvNSub8wAhYyYd2JH1QJq6gcApjUajtR6hz478nlQYUgeaTY-dsb0Sny8BbDf2:1nURDB:MHDNf5MhwXFhilYYEqDA2c784XWqX8FCHI9iNRo1doE", 1890 | "expire_date": "2022-03-30T10:49:01.950Z" 1891 | } 1892 | }, 1893 | { 1894 | "model": "sessions.session", 1895 | "pk": "sllloxa9idyhkv92hk2fsj4todamzw0f", 1896 | "fields": { 1897 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQsOd:raY_w69UPDq78zcAYNq4CZbEkCPqZmIh4Mk8nmL3UmI", 1898 | "expire_date": "2022-03-20T15:02:07.496Z" 1899 | } 1900 | }, 1901 | { 1902 | "model": "sessions.session", 1903 | "pk": "sr12mh1gyrmgigfep81052y4ko8f2glf", 1904 | "fields": { 1905 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3Ia:1F5ZiupHCGOVPjbV-Lp341Xw2hxrBVKcGk9Z0a5kSds", 1906 | "expire_date": "2022-03-18T08:28:28.691Z" 1907 | } 1908 | }, 1909 | { 1910 | "model": "sessions.session", 1911 | "pk": "svegu319y8hq2vae4u0wd5rnc2hivc1e", 1912 | "fields": { 1913 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3J1:J_0vfq79KopKLr6J2JFuTEOWTWgigFMIhESUdFv4qdo", 1914 | "expire_date": "2022-03-18T08:28:55.722Z" 1915 | } 1916 | }, 1917 | { 1918 | "model": "sessions.session", 1919 | "pk": "t9mq60vl8nl19icaeywvmfuyxneuy58k", 1920 | "fields": { 1921 | "session_data": ".eJwNxskRgCAMAMBeUgGBmMNmmKBheIu8HHvXfe0D1dc96ppx1eFzwA7W2LtSliMVQRdlMwpWcvaSwv9Q6ciMho23pEjdXEKznsYS8H5u3xh1:1nTLrY:q7_aUctNDosvvnbB_u24e29aGtD8Qr6w9RPuAWKXhB8", 1922 | "expire_date": "2022-03-27T10:54:12.414Z" 1923 | } 1924 | }, 1925 | { 1926 | "model": "sessions.session", 1927 | "pk": "tefe1weptyq7vj9loba1x7o3s87ycz6u", 1928 | "fields": { 1929 | "session_data": ".eJxVjE0OwiAYRO_C2pACBYpL9z0D-X7AVg0kpV0Z726bdKG7ybw38xYRtnWKW0tLnFlchRKX3w6BnqkcgB9Q7lVSLesyozwUedImx8rpdTvdv4MJ2rSvNSub8wAhYyYd2JH1QJq6gcApjUajtR6hz478nlQYUgeaTY-dsb0Sny8BbDf2:1nUPcq:G-v7VJJ_Tokl_imda_KvQbl4VeYCOuLAWQPiaoR_reY", 1930 | "expire_date": "2022-03-30T09:07:24.385Z" 1931 | } 1932 | }, 1933 | { 1934 | "model": "sessions.session", 1935 | "pk": "tom607q26jx0sqpzf8cqkfkyyc76sffr", 1936 | "fields": { 1937 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRCOJ:urRZFNW4lwP7ctxnfLSNYbvJnTH76jRcSg4WqxwK_-g", 1938 | "expire_date": "2022-03-21T12:23:07.922Z" 1939 | } 1940 | }, 1941 | { 1942 | "model": "sessions.session", 1943 | "pk": "u2jgplz1knwf3vewmnd69dhvcll12731", 1944 | "fields": { 1945 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3J2:eJO6x2ceZXEbv2oAtxv2kgm1ntrFR6BvcJyEzF_Hr4s", 1946 | "expire_date": "2022-03-18T08:28:56.147Z" 1947 | } 1948 | }, 1949 | { 1950 | "model": "sessions.session", 1951 | "pk": "uaews0f7ftmmr20hrihzicb2bse18wzo", 1952 | "fields": { 1953 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nUPuy:gvZTpI0ro4ubv3fBhlrg_nVFKHUytQBxNLW1JK40LbY", 1954 | "expire_date": "2022-03-30T09:26:08.877Z" 1955 | } 1956 | }, 1957 | { 1958 | "model": "sessions.session", 1959 | "pk": "uksq7iu8graaf1b5q1zj4zzv9mgyh4bq", 1960 | "fields": { 1961 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ8rN:LGvwGjsq1dIM7Bt2wtK3Cva2w8plkAB64am97h1coNU", 1962 | "expire_date": "2022-03-18T14:24:45.367Z" 1963 | } 1964 | }, 1965 | { 1966 | "model": "sessions.session", 1967 | "pk": "ungyz6fmdtubfahp1ooshlj17qwjuwtv", 1968 | "fields": { 1969 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRCP2:En0lz5qraVc0gZyhlkKe3u_JYwcq-ExFHF2YgxV_4lg", 1970 | "expire_date": "2022-03-21T12:23:52.979Z" 1971 | } 1972 | }, 1973 | { 1974 | "model": "sessions.session", 1975 | "pk": "uu9bub9e00nbjj4dl1q4hsrgron35i82", 1976 | "fields": { 1977 | "session_data": ".eJxVjE0OwiAYRO_C2pACBYpL9z0D-X7AVg0kpV0Z726bdKG7ybw38xYRtnWKW0tLnFlchRKX3w6BnqkcgB9Q7lVSLesyozwUedImx8rpdTvdv4MJ2rSvNSub8wAhYyYd2JH1QJq6gcApjUajtR6hz478nlQYUgeaTY-dsb0Sny8BbDf2:1nUPYX:qrD3LNQIj60qPqjywQMcYWreuZBz9hLS7PVfzFtq_Sc", 1978 | "expire_date": "2022-03-30T09:02:57.372Z" 1979 | } 1980 | }, 1981 | { 1982 | "model": "sessions.session", 1983 | "pk": "v4qxjfasyt8j7v3oi95ofpafruca86vt", 1984 | "fields": { 1985 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nOF65:2MAq1_jt_iTmzQ8_uEr891deEFU3fCQwgHzD1JjimWA", 1986 | "expire_date": "2022-03-13T08:40:05.431Z" 1987 | } 1988 | }, 1989 | { 1990 | "model": "sessions.session", 1991 | "pk": "vcdhp0qyi7ngirlm0yaxk5pcvuz4v2bz", 1992 | "fields": { 1993 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nREJk:9iLMmmcGkf0cnwotIcP5bNnHnlXxidIzP9QzkyG2Pnc", 1994 | "expire_date": "2022-03-21T14:26:32.009Z" 1995 | } 1996 | }, 1997 | { 1998 | "model": "sessions.session", 1999 | "pk": "vxcr2hr6v2eo2eompx1khp6x6gfkuv0k", 2000 | "fields": { 2001 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nSIMB:Gu36wUzYaKex8l-min9s5YcMtmx6nZTkJArYXvs_8LI", 2002 | "expire_date": "2022-03-24T12:57:27.903Z" 2003 | } 2004 | }, 2005 | { 2006 | "model": "sessions.session", 2007 | "pk": "w5ow5wmfyfbiey3fkvtvo6q0oet2rqv5", 2008 | "fields": { 2009 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nREGn:RLlRiddG4KBWQN6T_RU4Nb0SnZK1W7nSGSnwOWBokWA", 2010 | "expire_date": "2022-03-21T14:23:29.926Z" 2011 | } 2012 | }, 2013 | { 2014 | "model": "sessions.session", 2015 | "pk": "w7goky837b4bh8ibg3v3uxq3f7bg8dav", 2016 | "fields": { 2017 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRCQM:LzJckqVKRh30JLfGIM13PIw1RpDwZeL5Mqs1n82jON8", 2018 | "expire_date": "2022-03-21T12:25:14.495Z" 2019 | } 2020 | }, 2021 | { 2022 | "model": "sessions.session", 2023 | "pk": "wc7sat662u6ckelg1orit72zd7c3vj7i", 2024 | "fields": { 2025 | "session_data": ".eJxVjEEOwiAQRe_C2pAMDBVcuvcMZGAYqRqalHbVeHdt0oVu_3vvbyrSutS49jLHkdVFgTr9bonys7Qd8IPafdJ5ass8Jr0r-qBd3yYur-vh_h1U6vVb52y9GBR0YkUYARhkgHMAGwyTGUpGYps9E1rAYDGkBMLOYUFvvHp_AO3VN9c:1nTLoZ:gcRKpIAiXpfvt3Yc5Qci0aVlaNKj4byxaK1EHME0wQ0", 2026 | "expire_date": "2022-03-27T10:51:07.818Z" 2027 | } 2028 | }, 2029 | { 2030 | "model": "sessions.session", 2031 | "pk": "wn560n0p0b5dos33eqpfd32coimlhcw4", 2032 | "fields": { 2033 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRCcR:CQ7TpJBopvuJZ5VdPpu9pTq21pOS46svxDZHhGqeyLk", 2034 | "expire_date": "2022-03-21T12:37:43.219Z" 2035 | } 2036 | }, 2037 | { 2038 | "model": "sessions.session", 2039 | "pk": "wt2ls9t9m63dnb0sy0zv508ug47q9fha", 2040 | "fields": { 2041 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nSYnO:UK3sIJMDaPU2LiUqJmQy0wTCOi8DkumQZls9sFtwaS8", 2042 | "expire_date": "2022-03-25T06:30:38.126Z" 2043 | } 2044 | }, 2045 | { 2046 | "model": "sessions.session", 2047 | "pk": "wtkbx1vbb88y5vu993q1ho14iaed5rz8", 2048 | "fields": { 2049 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nREVt:3OFBCiMvrdWkp3VFCGLjlXcN8k08a-MHWMEgHDjSZ5c", 2050 | "expire_date": "2022-03-21T14:39:05.076Z" 2051 | } 2052 | }, 2053 | { 2054 | "model": "sessions.session", 2055 | "pk": "x3jub7ic70e4v6awq8e0nzzk8fs6qbf8", 2056 | "fields": { 2057 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nR6XL:uUMI1jD42ScNxIE8IRiHFYwhNPgk3iJruVfm8Q4zz7E", 2058 | "expire_date": "2022-03-21T06:08:03.733Z" 2059 | } 2060 | }, 2061 | { 2062 | "model": "sessions.session", 2063 | "pk": "y0aks5swrfmiuy5gz5h8bwrgnfwqd4a9", 2064 | "fields": { 2065 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nTL6c:R2mq-lBSSAsvaKNFDPyZxtRwdCLbhapuEh68XdU9P9k", 2066 | "expire_date": "2022-03-27T10:05:42.325Z" 2067 | } 2068 | }, 2069 | { 2070 | "model": "sessions.session", 2071 | "pk": "y3xj4sgjr32liicvp1julrxr62hbi1cx", 2072 | "fields": { 2073 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nREiW:KMqEdHY9EUCtWE0djY8eNN5hnpPMSmjcGsDRTwWyUT8", 2074 | "expire_date": "2022-03-21T14:52:08.616Z" 2075 | } 2076 | }, 2077 | { 2078 | "model": "sessions.session", 2079 | "pk": "ydie8c801hzl1akrlj2yepojep8h2tbz", 2080 | "fields": { 2081 | "session_data": ".eJxVjMsOgjAURP-la9P0FkvBpXu_obm9D4saSCisjP8uJCw0s5tzZt4m4bqUtFaZ08DmYs7m9NtlpKeMO-AHjvfJ0jQu85DtrtiDVnubWF7Xw_07KFjLtm6IWHsACI4j9RoaUc-yBQgZfGaQyCEqqQNl1yJA0zkv2LUsWc3nCxoeOXA:1nUPHp:phANPNPNgFugGnfHUcY_xmaW_xnmn6seE2LeM6X8cMc", 2082 | "expire_date": "2022-03-30T08:45:41.544Z" 2083 | } 2084 | }, 2085 | { 2086 | "model": "sessions.session", 2087 | "pk": "yfmme99u2g1ubmlyejd0mz6n4jvvdmxx", 2088 | "fields": { 2089 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRCe3:__50FRMGs1uDDn68FSCG5Wac09zGaTqAj7wsyqOlYuM", 2090 | "expire_date": "2022-03-21T12:39:23.486Z" 2091 | } 2092 | }, 2093 | { 2094 | "model": "sessions.session", 2095 | "pk": "ykc13sv40z67dlzj5xxvmeucr7m82tdh", 2096 | "fields": { 2097 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nQ3Hf:hwfpg4NqRN9eQqWtXObXr5MQudmfJCRRWU8jtHCJFcA", 2098 | "expire_date": "2022-03-18T08:27:31.835Z" 2099 | } 2100 | }, 2101 | { 2102 | "model": "sessions.session", 2103 | "pk": "zc5fjcr70qi46scfw0opbwamggporl9y", 2104 | "fields": { 2105 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nREb4:XzzxI6eOtwtoonjfrDeJ507lVlgrffWN8aXDQ9cTJvI", 2106 | "expire_date": "2022-03-21T14:44:26.739Z" 2107 | } 2108 | }, 2109 | { 2110 | "model": "sessions.session", 2111 | "pk": "zh97ezf21t86dnhadtwmqyla90p7le5o", 2112 | "fields": { 2113 | "session_data": ".eJwFwckRgCAMAMBeUgEQCMRmGHIwvEVejr27-0If51n9bL_7GnvBBe5M2WZMOmk0qU1lomHJMadaxdxFGM2opDI8aNIYiKVRRWcxhu8Ht4QaQg:1nTM73:Vu1zVYmPbg_ak95YWvirNvFX_fn-FlvyEIoyQzCmAY4", 2114 | "expire_date": "2022-03-27T11:10:13.141Z" 2115 | } 2116 | }, 2117 | { 2118 | "model": "sessions.session", 2119 | "pk": "zz4bv5tzbe3b3kr8eid3seg3wvmy8u6y", 2120 | "fields": { 2121 | "session_data": ".eJxVjM0OwiAQhN-FsyHlx-3i0bvPQBYWpGogKe3J-O62SQ96mmS-b-YtPK1L8WtPs59YXIQSp98uUHymugN-UL03GVtd5inIXZEH7fLWOL2uh_t3UKiXbQ2Bk0ZWBjS6SAbckAA4ZmuVUZQxxNEaxRkBMyneQp9HZ6zBIbBl8fkC3gs3hg:1nRCQq:DnUlV3uRUv-E27P1liU0APs4di6cTxRK99DfsNdY5aU", 2122 | "expire_date": "2022-03-21T12:25:44.789Z" 2123 | } 2124 | }, 2125 | { 2126 | "model": "main.user", 2127 | "pk": 1, 2128 | "fields": { 2129 | "password": "pbkdf2_sha256$260000$akgmBrQRA4X5bbDGQx5rWf$diO1cGRbo0czLhSOiDirudcLdHDJNWr+fmqmjF/Mk2A=", 2130 | "last_login": "2022-03-16T10:49:01.836Z", 2131 | "is_superuser": true, 2132 | "username": "Nehe", 2133 | "first_name": "Nehemiah", 2134 | "last_name": "Kamolu", 2135 | "is_staff": true, 2136 | "is_active": true, 2137 | "date_joined": "2022-02-25T17:10:32Z", 2138 | "email": "kamolunehemiah@gmail.com", 2139 | "phone_number": "+256774225437", 2140 | "role": "Receptionist", 2141 | "groups": [], 2142 | "user_permissions": [] 2143 | } 2144 | }, 2145 | { 2146 | "model": "main.user", 2147 | "pk": 4, 2148 | "fields": { 2149 | "password": "pbkdf2_sha256$260000$XTHY1NP3y9toA1JHupqMCt$hcXYl8+3VuTRMAbcM1ZdFQ4gg6KM9+0BaACInVIw1TY=", 2150 | "last_login": "2022-03-16T10:15:19.600Z", 2151 | "is_superuser": false, 2152 | "username": "DocNehe", 2153 | "first_name": "Nehe Fname", 2154 | "last_name": "Nehe Kname", 2155 | "is_staff": false, 2156 | "is_active": true, 2157 | "date_joined": "2022-02-26T20:05:52Z", 2158 | "email": "nehedoc@gmail.com", 2159 | "phone_number": null, 2160 | "role": "Doctor", 2161 | "groups": [], 2162 | "user_permissions": [] 2163 | } 2164 | }, 2165 | { 2166 | "model": "main.user", 2167 | "pk": 8, 2168 | "fields": { 2169 | "password": "pbkdf2_sha256$260000$4zt5bhINbkbYB1TkNeSH4e$fwj62Moi43aCr69KLJ0QCNhcPn1gCd89RwmX+Di5Q2w=", 2170 | "last_login": null, 2171 | "is_superuser": false, 2172 | "username": "Rhodesia", 2173 | "first_name": "Rhoda", 2174 | "last_name": "Maxy", 2175 | "is_staff": false, 2176 | "is_active": true, 2177 | "date_joined": "2022-03-07T07:53:37.198Z", 2178 | "email": "rhoda@gmail.com", 2179 | "phone_number": "+256774225437", 2180 | "role": "Student Clinician", 2181 | "groups": [], 2182 | "user_permissions": [] 2183 | } 2184 | }, 2185 | { 2186 | "model": "main.patient", 2187 | "pk": 22, 2188 | "fields": { 2189 | "patient_number": "P-22", 2190 | "next_of_kin": "Iron man", 2191 | "address": "New York City", 2192 | "date_of_birth": "2018-10-09", 2193 | "age": 3, 2194 | "contacts": "+256 774 205 433", 2195 | "patient_name": "Tom Holland", 2196 | "created_at": "2022-03-06T19:41:47.735Z", 2197 | "created_by": 1, 2198 | "updated_at": null, 2199 | "updated_by": null 2200 | } 2201 | }, 2202 | { 2203 | "model": "main.patient", 2204 | "pk": 31, 2205 | "fields": { 2206 | "patient_number": "P-31", 2207 | "next_of_kin": "Yaya Toure", 2208 | "address": "Cameroon", 2209 | "date_of_birth": "2022-03-07", 2210 | "age": 0, 2211 | "contacts": "+256 774 205 434", 2212 | "patient_name": "Victor Moses", 2213 | "created_at": "2022-03-07T16:08:32.600Z", 2214 | "created_by": 1, 2215 | "updated_at": null, 2216 | "updated_by": null 2217 | } 2218 | }, 2219 | { 2220 | "model": "main.patient", 2221 | "pk": 32, 2222 | "fields": { 2223 | "patient_number": "P-32", 2224 | "next_of_kin": "Nehe K", 2225 | "address": "Kampala Ug Ntinda", 2226 | "date_of_birth": "2019-11-07", 2227 | "age": 2, 2228 | "contacts": "+256 774 205 434", 2229 | "patient_name": "Fabiola J", 2230 | "created_at": "2022-03-07T16:09:59.409Z", 2231 | "created_by": 1, 2232 | "updated_at": null, 2233 | "updated_by": null 2234 | } 2235 | }, 2236 | { 2237 | "model": "main.patient", 2238 | "pk": 41, 2239 | "fields": { 2240 | "patient_number": "P-41", 2241 | "next_of_kin": "Yaya Toure", 2242 | "address": "Cameroon", 2243 | "date_of_birth": "2022-03-16", 2244 | "age": 0, 2245 | "contacts": "+256 774 205 434", 2246 | "patient_name": "Victor Moses aha", 2247 | "created_at": "2022-03-16T09:24:39.796Z", 2248 | "created_by": 1, 2249 | "updated_at": null, 2250 | "updated_by": null 2251 | } 2252 | }, 2253 | { 2254 | "model": "main.prescription", 2255 | "pk": 2, 2256 | "fields": { 2257 | "patient": 31, 2258 | "start_datetime": "2022-03-10T14:32:00Z", 2259 | "end_datetime": "2022-03-11T14:32:00Z", 2260 | "description": "7 tablets a day everyday for 2 weeks", 2261 | "created_at": "2022-03-09T11:02:47.378Z", 2262 | "created_by": 4, 2263 | "updated_at": "2022-03-10T11:48:14.085Z", 2264 | "updated_by": 4 2265 | } 2266 | }, 2267 | { 2268 | "model": "main.prescription", 2269 | "pk": 3, 2270 | "fields": { 2271 | "patient": 31, 2272 | "start_datetime": "2022-03-09T13:16:00Z", 2273 | "end_datetime": "2022-03-09T14:16:00Z", 2274 | "description": "Take 3 tablets every day for many days", 2275 | "created_at": "2022-03-09T11:18:11.384Z", 2276 | "created_by": 4, 2277 | "updated_at": null, 2278 | "updated_by": null 2279 | } 2280 | }, 2281 | { 2282 | "model": "main.prescription", 2283 | "pk": 6, 2284 | "fields": { 2285 | "patient": 41, 2286 | "start_datetime": "2022-03-16T12:27:00Z", 2287 | "end_datetime": "2022-03-16T12:28:00Z", 2288 | "description": "jkjskdnsjdnsjkndjsndjksndjknsdjknsjkdnsjkdnjknjknjk", 2289 | "created_at": "2022-03-16T09:27:36.760Z", 2290 | "created_by": 4, 2291 | "updated_at": null, 2292 | "updated_by": null 2293 | } 2294 | }, 2295 | { 2296 | "model": "main.ward", 2297 | "pk": 1, 2298 | "fields": { 2299 | "name": "Maternity", 2300 | "created_at": "2022-02-27T19:07:44.961Z", 2301 | "created_by": 4, 2302 | "updated_at": null, 2303 | "updated_by": null 2304 | } 2305 | }, 2306 | { 2307 | "model": "main.ward", 2308 | "pk": 2, 2309 | "fields": { 2310 | "name": "Casaulity", 2311 | "created_at": "2022-02-27T19:08:03.586Z", 2312 | "created_by": 4, 2313 | "updated_at": null, 2314 | "updated_by": null 2315 | } 2316 | }, 2317 | { 2318 | "model": "main.admission", 2319 | "pk": 19, 2320 | "fields": { 2321 | "ward": 2, 2322 | "patient": 31, 2323 | "created_at": "2022-03-09T09:18:32.455Z", 2324 | "created_by": 4, 2325 | "updated_at": null, 2326 | "updated_by": null 2327 | } 2328 | }, 2329 | { 2330 | "model": "main.admission", 2331 | "pk": 20, 2332 | "fields": { 2333 | "ward": 2, 2334 | "patient": 31, 2335 | "created_at": "2022-03-09T12:25:13.953Z", 2336 | "created_by": 4, 2337 | "updated_at": "2022-03-10T10:45:24.456Z", 2338 | "updated_by": 4 2339 | } 2340 | }, 2341 | { 2342 | "model": "main.admission", 2343 | "pk": 24, 2344 | "fields": { 2345 | "ward": 2, 2346 | "patient": 41, 2347 | "created_at": "2022-03-16T09:27:14.836Z", 2348 | "created_by": 4, 2349 | "updated_at": null, 2350 | "updated_by": null 2351 | } 2352 | }, 2353 | { 2354 | "model": "main.referral", 2355 | "pk": 4, 2356 | "fields": { 2357 | "patient": 22, 2358 | "doctor": 8, 2359 | "status": "Not seen", 2360 | "created_at": "2022-03-07T09:46:36.783Z", 2361 | "created_by": 1, 2362 | "updated_at": null, 2363 | "updated_by": null 2364 | } 2365 | }, 2366 | { 2367 | "model": "main.referral", 2368 | "pk": 5, 2369 | "fields": { 2370 | "patient": 22, 2371 | "doctor": 4, 2372 | "status": "Not seen", 2373 | "created_at": "2022-03-07T09:49:35.162Z", 2374 | "created_by": 1, 2375 | "updated_at": "2022-03-11T09:51:03.325Z", 2376 | "updated_by": 1 2377 | } 2378 | }, 2379 | { 2380 | "model": "main.referral", 2381 | "pk": 8, 2382 | "fields": { 2383 | "patient": 31, 2384 | "doctor": 4, 2385 | "status": "Admitted", 2386 | "created_at": "2022-03-08T19:13:45.614Z", 2387 | "created_by": 1, 2388 | "updated_at": "2022-03-11T09:49:02.165Z", 2389 | "updated_by": 1 2390 | } 2391 | }, 2392 | { 2393 | "model": "main.referral", 2394 | "pk": 10, 2395 | "fields": { 2396 | "patient": 41, 2397 | "doctor": 4, 2398 | "status": "Admitted", 2399 | "created_at": "2022-03-16T09:25:21.307Z", 2400 | "created_by": 1, 2401 | "updated_at": "2022-03-16T09:27:14.938Z", 2402 | "updated_by": 4 2403 | } 2404 | }, 2405 | { 2406 | "model": "authtoken.token", 2407 | "pk": "987852f1d1e0f8375b88218ee6756400e373beba", 2408 | "fields": { 2409 | "user": 1, 2410 | "created": "2022-02-26T10:25:36.744Z" 2411 | } 2412 | } 2413 | ] 2414 | -------------------------------------------------------------------------------- /main/fixtures/patient.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "main.patient", 4 | "pk": 22, 5 | "fields": { 6 | "patient_number": "P-22", 7 | "next_of_kin": "Iron man", 8 | "address": "New York City", 9 | "date_of_birth": "2018-10-09", 10 | "age": 3, 11 | "contacts": "+256 774 205 433", 12 | "patient_name": "Tom Holland", 13 | "created_at": "2022-03-06T19:41:47.735Z", 14 | "created_by": 1, 15 | "updated_at": null, 16 | "updated_by": null 17 | } 18 | }, 19 | { 20 | "model": "main.patient", 21 | "pk": 31, 22 | "fields": { 23 | "patient_number": "P-31", 24 | "next_of_kin": "Yaya Toure", 25 | "address": "Cameroon", 26 | "date_of_birth": "2022-03-07", 27 | "age": 0, 28 | "contacts": "+256 774 205 434", 29 | "patient_name": "Victor Moses", 30 | "created_at": "2022-03-07T16:08:32.600Z", 31 | "created_by": 1, 32 | "updated_at": null, 33 | "updated_by": null 34 | } 35 | }, 36 | { 37 | "model": "main.patient", 38 | "pk": 32, 39 | "fields": { 40 | "patient_number": "P-32", 41 | "next_of_kin": "Nehe K", 42 | "address": "Kampala Ug Ntinda", 43 | "date_of_birth": "2019-11-07", 44 | "age": 2, 45 | "contacts": "+256 774 205 434", 46 | "patient_name": "Fabiola J", 47 | "created_at": "2022-03-07T16:09:59.409Z", 48 | "created_by": 1, 49 | "updated_at": null, 50 | "updated_by": null 51 | } 52 | }, 53 | { 54 | "model": "main.patient", 55 | "pk": 41, 56 | "fields": { 57 | "patient_number": "P-41", 58 | "next_of_kin": "Yaya Toure", 59 | "address": "Cameroon", 60 | "date_of_birth": "2022-03-16", 61 | "age": 0, 62 | "contacts": "+256 774 205 434", 63 | "patient_name": "Victor Moses aha", 64 | "created_at": "2022-03-16T09:24:39.796Z", 65 | "created_by": 1, 66 | "updated_at": null, 67 | "updated_by": null 68 | } 69 | } 70 | ] 71 | -------------------------------------------------------------------------------- /main/fixtures/prescription.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "main.prescription", 4 | "pk": 2, 5 | "fields": { 6 | "patient": 31, 7 | "start_datetime": "2022-03-10T14:32:00Z", 8 | "end_datetime": "2022-03-11T14:32:00Z", 9 | "description": "7 tablets a day everyday for 2 weeks", 10 | "created_at": "2022-03-09T11:02:47.378Z", 11 | "created_by": 4, 12 | "updated_at": "2022-03-10T11:48:14.085Z", 13 | "updated_by": 4 14 | } 15 | }, 16 | { 17 | "model": "main.prescription", 18 | "pk": 3, 19 | "fields": { 20 | "patient": 31, 21 | "start_datetime": "2022-03-09T13:16:00Z", 22 | "end_datetime": "2022-03-09T14:16:00Z", 23 | "description": "Take 3 tablets every day for many days", 24 | "created_at": "2022-03-09T11:18:11.384Z", 25 | "created_by": 4, 26 | "updated_at": null, 27 | "updated_by": null 28 | } 29 | }, 30 | { 31 | "model": "main.prescription", 32 | "pk": 6, 33 | "fields": { 34 | "patient": 41, 35 | "start_datetime": "2022-03-16T12:27:00Z", 36 | "end_datetime": "2022-03-16T12:28:00Z", 37 | "description": "jkjskdnsjdnsjkndjsndjksndjknsdjknsjkdnsjkdnjknjknjk", 38 | "created_at": "2022-03-16T09:27:36.760Z", 39 | "created_by": 4, 40 | "updated_at": null, 41 | "updated_by": null 42 | } 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /main/fixtures/referral.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "main.referral", 4 | "pk": 4, 5 | "fields": { 6 | "patient": 22, 7 | "doctor": 8, 8 | "status": "Not seen", 9 | "created_at": "2022-03-07T09:46:36.783Z", 10 | "created_by": 1, 11 | "updated_at": null, 12 | "updated_by": null 13 | } 14 | }, 15 | { 16 | "model": "main.referral", 17 | "pk": 5, 18 | "fields": { 19 | "patient": 22, 20 | "doctor": 4, 21 | "status": "Not seen", 22 | "created_at": "2022-03-07T09:49:35.162Z", 23 | "created_by": 1, 24 | "updated_at": "2022-03-11T09:51:03.325Z", 25 | "updated_by": 1 26 | } 27 | }, 28 | { 29 | "model": "main.referral", 30 | "pk": 8, 31 | "fields": { 32 | "patient": 31, 33 | "doctor": 4, 34 | "status": "Admitted", 35 | "created_at": "2022-03-08T19:13:45.614Z", 36 | "created_by": 1, 37 | "updated_at": "2022-03-11T09:49:02.165Z", 38 | "updated_by": 1 39 | } 40 | }, 41 | { 42 | "model": "main.referral", 43 | "pk": 10, 44 | "fields": { 45 | "patient": 41, 46 | "doctor": 4, 47 | "status": "Admitted", 48 | "created_at": "2022-03-16T09:25:21.307Z", 49 | "created_by": 1, 50 | "updated_at": "2022-03-16T09:27:14.938Z", 51 | "updated_by": 4 52 | } 53 | } 54 | ] 55 | -------------------------------------------------------------------------------- /main/fixtures/user.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "main.user", 4 | "pk": 1, 5 | "fields": { 6 | "password": "pbkdf2_sha256$260000$akgmBrQRA4X5bbDGQx5rWf$diO1cGRbo0czLhSOiDirudcLdHDJNWr+fmqmjF/Mk2A=", 7 | "last_login": "2022-03-16T10:49:01.836Z", 8 | "is_superuser": true, 9 | "username": "Nehe", 10 | "first_name": "Nehemiah", 11 | "last_name": "Kamolu", 12 | "is_staff": true, 13 | "is_active": true, 14 | "date_joined": "2022-02-25T17:10:32Z", 15 | "email": "kamolunehemiah@gmail.com", 16 | "phone_number": "+256774225437", 17 | "role": "Receptionist", 18 | "groups": [], 19 | "user_permissions": [] 20 | } 21 | }, 22 | { 23 | "model": "main.user", 24 | "pk": 4, 25 | "fields": { 26 | "password": "pbkdf2_sha256$260000$XTHY1NP3y9toA1JHupqMCt$hcXYl8+3VuTRMAbcM1ZdFQ4gg6KM9+0BaACInVIw1TY=", 27 | "last_login": "2022-03-16T10:15:19.600Z", 28 | "is_superuser": false, 29 | "username": "DocNehe", 30 | "first_name": "Nehe Fname", 31 | "last_name": "Nehe Kname", 32 | "is_staff": false, 33 | "is_active": true, 34 | "date_joined": "2022-02-26T20:05:52Z", 35 | "email": "nehedoc@gmail.com", 36 | "phone_number": null, 37 | "role": "Doctor", 38 | "groups": [], 39 | "user_permissions": [] 40 | } 41 | }, 42 | { 43 | "model": "main.user", 44 | "pk": 8, 45 | "fields": { 46 | "password": "pbkdf2_sha256$260000$4zt5bhINbkbYB1TkNeSH4e$fwj62Moi43aCr69KLJ0QCNhcPn1gCd89RwmX+Di5Q2w=", 47 | "last_login": null, 48 | "is_superuser": false, 49 | "username": "Rhodesia", 50 | "first_name": "Rhoda", 51 | "last_name": "Maxy", 52 | "is_staff": false, 53 | "is_active": true, 54 | "date_joined": "2022-03-07T07:53:37.198Z", 55 | "email": "rhoda@gmail.com", 56 | "phone_number": "+256774225437", 57 | "role": "Student Clinician", 58 | "groups": [], 59 | "user_permissions": [] 60 | } 61 | } 62 | ] 63 | -------------------------------------------------------------------------------- /main/fixtures/ward.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "main.ward", 4 | "pk": 1, 5 | "fields": { 6 | "name": "Maternity", 7 | "created_at": "2022-02-27T19:07:44.961Z", 8 | "created_by": 4, 9 | "updated_at": null, 10 | "updated_by": null 11 | } 12 | }, 13 | { 14 | "model": "main.ward", 15 | "pk": 2, 16 | "fields": { 17 | "name": "Casaulity", 18 | "created_at": "2022-02-27T19:08:03.586Z", 19 | "created_by": 4, 20 | "updated_at": null, 21 | "updated_by": null 22 | } 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /main/forms.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.forms import UserCreationForm, UserChangeForm 2 | from main.models import User 3 | 4 | 5 | class CustomUserCreationForm(UserCreationForm): 6 | class Meta(UserCreationForm.Meta): 7 | model = User 8 | fields = UserCreationForm.Meta.fields 9 | 10 | 11 | class CustomUserChangeForm(UserChangeForm): 12 | class Meta(UserChangeForm.Meta): 13 | model = User 14 | fields = UserChangeForm.Meta.fields 15 | -------------------------------------------------------------------------------- /main/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-02-25 16:42 2 | 3 | import django.contrib.auth.models 4 | import django.contrib.auth.validators 5 | from django.db import migrations, models 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('auth', '0012_alter_user_first_name_max_length'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='User', 20 | fields=[ 21 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('password', models.CharField(max_length=128, verbose_name='password')), 23 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 24 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 25 | ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), 26 | ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), 27 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), 28 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 29 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), 30 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 31 | ('email', models.EmailField(max_length=254, unique=True)), 32 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), 33 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), 34 | ], 35 | options={ 36 | 'verbose_name': 'user', 37 | 'verbose_name_plural': 'users', 38 | 'abstract': False, 39 | }, 40 | managers=[ 41 | ('objects', django.contrib.auth.models.UserManager()), 42 | ], 43 | ), 44 | ] 45 | -------------------------------------------------------------------------------- /main/migrations/0001_squashed_0015_auto_20220318_1205.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-03-18 09:21 2 | 3 | import datetime 4 | from django.conf import settings 5 | import django.contrib.auth.models 6 | import django.contrib.auth.validators 7 | import django.core.validators 8 | from django.db import migrations, models 9 | import django.db.models.deletion 10 | import django.utils.timezone 11 | from django.utils.timezone import utc 12 | import phonenumber_field.modelfields 13 | 14 | 15 | class Migration(migrations.Migration): 16 | 17 | replaces = [('main', '0001_initial'), ('main', '0002_auto_20220225_2123'), ('main', '0003_auto_20220225_2125'), ('main', '0004_alter_patient_patient_number'), ('main', '0005_auto_20220226_1245'), ('main', '0006_user_phone_number'), ('main', '0007_user_roles'), ('main', '0008_rename_roles_user_role'), ('main', '0009_auto_20220227_0822'), ('main', '0010_referral_status'), ('main', '0011_auto_20220227_2314'), ('main', '0012_auto_20220306_1903'), ('main', '0013_auto_20220310_1451'), ('main', '0014_auto_20220310_1453'), ('main', '0015_auto_20220318_1205')] 18 | 19 | initial = True 20 | 21 | dependencies = [ 22 | ('auth', '0012_alter_user_first_name_max_length'), 23 | ] 24 | 25 | operations = [ 26 | migrations.CreateModel( 27 | name='User', 28 | fields=[ 29 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 30 | ('password', models.CharField(max_length=128, verbose_name='password')), 31 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 32 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 33 | ('username', models.CharField(default=datetime.datetime(2022, 2, 26, 9, 45, 59, 941879, tzinfo=utc), error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), 34 | ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), 35 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), 36 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 37 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), 38 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 39 | ('email', models.EmailField(max_length=254, unique=True)), 40 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), 41 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), 42 | ('phone_number', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None)), 43 | ('role', models.CharField(choices=[('Receptionist', 'Receptionist'), ('Doctor', 'Doctor'), ('Nurse', 'Nurse'), ('Student Clinician', 'Student Clinician')], default='Receptionist', max_length=50)), 44 | ], 45 | options={ 46 | 'verbose_name': 'user', 47 | 'verbose_name_plural': 'users', 48 | 'abstract': False, 49 | }, 50 | managers=[ 51 | ('objects', django.contrib.auth.models.UserManager()), 52 | ], 53 | ), 54 | migrations.CreateModel( 55 | name='Ward', 56 | fields=[ 57 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 58 | ('name', models.CharField(max_length=100)), 59 | ('created_at', models.DateTimeField(auto_now_add=True)), 60 | ('updated_at', models.DateTimeField(blank=True, null=True)), 61 | ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ward_created_by', to=settings.AUTH_USER_MODEL)), 62 | ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='updated_created_by', to=settings.AUTH_USER_MODEL)), 63 | ], 64 | options={ 65 | 'ordering': ['-created_at'], 66 | }, 67 | ), 68 | migrations.CreateModel( 69 | name='Patient', 70 | fields=[ 71 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 72 | ('patient_number', models.CharField(blank=True, max_length=10)), 73 | ('next_of_kin', models.CharField(max_length=50)), 74 | ('address', models.CharField(max_length=50)), 75 | ('date_of_birth', models.DateField(validators=[django.core.validators.MaxValueValidator(datetime.date(2022, 3, 18))])), 76 | ('age', models.IntegerField(blank=True)), 77 | ('contacts', models.CharField(max_length=20)), 78 | ('patient_name', models.CharField(max_length=100)), 79 | ('created_at', models.DateTimeField(auto_now_add=True)), 80 | ('updated_at', models.DateTimeField(blank=True, null=True)), 81 | ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patient_created_by', to=settings.AUTH_USER_MODEL)), 82 | ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patient_updated_by', to=settings.AUTH_USER_MODEL)), 83 | ], 84 | options={ 85 | 'ordering': ['-created_at'], 86 | }, 87 | ), 88 | migrations.CreateModel( 89 | name='Admission', 90 | fields=[ 91 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 92 | ('created_at', models.DateTimeField(auto_now_add=True)), 93 | ('updated_at', models.DateTimeField(blank=True, null=True)), 94 | ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='admission_created_by', to=settings.AUTH_USER_MODEL)), 95 | ('patient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='patient_admitted', to='main.patient')), 96 | ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='admission_updated_by', to=settings.AUTH_USER_MODEL)), 97 | ('ward', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ward_admitted', to='main.ward')), 98 | ], 99 | options={ 100 | 'ordering': ['-created_at'], 101 | }, 102 | ), 103 | migrations.CreateModel( 104 | name='Referral', 105 | fields=[ 106 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 107 | ('created_at', models.DateTimeField(auto_now_add=True)), 108 | ('updated_at', models.DateTimeField(blank=True, null=True)), 109 | ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='referral_created_by', to=settings.AUTH_USER_MODEL)), 110 | ('doctor', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='doctor_referred_to', to=settings.AUTH_USER_MODEL)), 111 | ('patient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='patient_referred', to='main.patient')), 112 | ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='referral_updated_by', to=settings.AUTH_USER_MODEL)), 113 | ('status', models.CharField(choices=[('Admitted', 'Admitted'), ('Discharged', 'Discharged'), ('Not seen', 'Not seen'), ('In progress', 'In progress')], default='Not seen', max_length=20)), 114 | ], 115 | options={ 116 | 'ordering': ['-created_at'], 117 | }, 118 | ), 119 | migrations.CreateModel( 120 | name='Prescription', 121 | fields=[ 122 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 123 | ('start_datetime', models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2022, 3, 18, 9, 5, 36, 229676, tzinfo=utc))])), 124 | ('end_datetime', models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2022, 3, 18, 9, 5, 36, 229676, tzinfo=utc))])), 125 | ('description', models.TextField(max_length=400, validators=[django.core.validators.MinLengthValidator(20)])), 126 | ('created_at', models.DateTimeField(auto_now_add=True)), 127 | ('updated_at', models.DateTimeField(blank=True, null=True)), 128 | ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='presciption_created_by', to=settings.AUTH_USER_MODEL)), 129 | ('patient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='patient_prescribed', to='main.patient')), 130 | ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='presciption_updated_by', to=settings.AUTH_USER_MODEL)), 131 | ], 132 | options={ 133 | 'ordering': ['-created_at'], 134 | }, 135 | ), 136 | ] 137 | -------------------------------------------------------------------------------- /main/migrations/0002_auto_20220225_2123.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-02-25 18:23 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('main', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Patient', 17 | fields=[ 18 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('patient_number', models.CharField(max_length=10)), 20 | ('next_of_kin', models.CharField(max_length=50)), 21 | ('address', models.CharField(max_length=50)), 22 | ('date_of_birth', models.DateField()), 23 | ('age', models.IntegerField(blank=True)), 24 | ('contacts', models.CharField(max_length=20)), 25 | ('patient_name', models.CharField(max_length=100)), 26 | ('created_at', models.DateTimeField(auto_now_add=True)), 27 | ('updated_at', models.DateTimeField(auto_now=True)), 28 | ], 29 | ), 30 | migrations.AlterField( 31 | model_name='user', 32 | name='username', 33 | field=models.CharField(blank=True, max_length=50, null=True), 34 | ), 35 | migrations.CreateModel( 36 | name='Ward', 37 | fields=[ 38 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 39 | ('name', models.CharField(max_length=100)), 40 | ('created_at', models.DateTimeField(auto_now_add=True)), 41 | ('updated_at', models.DateTimeField(auto_now=True)), 42 | ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ward_created_by', to=settings.AUTH_USER_MODEL)), 43 | ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='updated_created_by', to=settings.AUTH_USER_MODEL)), 44 | ], 45 | ), 46 | migrations.CreateModel( 47 | name='Prescription', 48 | fields=[ 49 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 50 | ('start_datetime', models.DateTimeField()), 51 | ('end_datetime', models.DateTimeField()), 52 | ('description', models.TextField(max_length=400)), 53 | ('created_at', models.DateTimeField(auto_now_add=True)), 54 | ('updated_at', models.DateTimeField(auto_now=True)), 55 | ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='presciption_created_by', to=settings.AUTH_USER_MODEL)), 56 | ('patient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='patient_prescribed', to='main.patient')), 57 | ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='presciption_updated_by', to=settings.AUTH_USER_MODEL)), 58 | ], 59 | ), 60 | migrations.AddField( 61 | model_name='patient', 62 | name='created_by', 63 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patient_created_by', to=settings.AUTH_USER_MODEL), 64 | ), 65 | migrations.AddField( 66 | model_name='patient', 67 | name='updated_by', 68 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patient_updated_by', to=settings.AUTH_USER_MODEL), 69 | ), 70 | migrations.CreateModel( 71 | name='Admission', 72 | fields=[ 73 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 74 | ('created_at', models.DateTimeField(auto_now_add=True)), 75 | ('updated_at', models.DateTimeField(auto_now=True)), 76 | ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='admission_created_by', to=settings.AUTH_USER_MODEL)), 77 | ('patient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='patient_admitted', to='main.patient')), 78 | ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='admission_updated_by', to=settings.AUTH_USER_MODEL)), 79 | ('ward', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ward_admitted', to='main.ward')), 80 | ], 81 | ), 82 | ] 83 | -------------------------------------------------------------------------------- /main/migrations/0003_auto_20220225_2125.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-02-25 18:25 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('main', '0002_auto_20220225_2123'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='admission', 15 | name='updated_at', 16 | field=models.DateTimeField(blank=True, null=True), 17 | ), 18 | migrations.AlterField( 19 | model_name='patient', 20 | name='updated_at', 21 | field=models.DateTimeField(blank=True, null=True), 22 | ), 23 | migrations.AlterField( 24 | model_name='prescription', 25 | name='updated_at', 26 | field=models.DateTimeField(blank=True, null=True), 27 | ), 28 | migrations.AlterField( 29 | model_name='ward', 30 | name='updated_at', 31 | field=models.DateTimeField(blank=True, null=True), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /main/migrations/0004_alter_patient_patient_number.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-02-25 18:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('main', '0003_auto_20220225_2125'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='patient', 15 | name='patient_number', 16 | field=models.CharField(blank=True, max_length=10), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /main/migrations/0005_auto_20220226_1245.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-02-26 09:45 2 | 3 | import datetime 4 | import django.contrib.auth.validators 5 | import django.core.validators 6 | from django.db import migrations, models 7 | from django.utils.timezone import utc 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('main', '0004_alter_patient_patient_number'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AlterField( 18 | model_name='patient', 19 | name='date_of_birth', 20 | field=models.DateField(validators=[django.core.validators.MaxValueValidator(datetime.date(2022, 2, 26))]), 21 | ), 22 | migrations.AlterField( 23 | model_name='user', 24 | name='username', 25 | field=models.CharField(default=datetime.datetime(2022, 2, 26, 9, 45, 59, 941879, tzinfo=utc), error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'), 26 | preserve_default=False, 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /main/migrations/0006_user_phone_number.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-02-26 09:55 2 | 3 | from django.db import migrations 4 | import phonenumber_field.modelfields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('main', '0005_auto_20220226_1245'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='user', 16 | name='phone_number', 17 | field=phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /main/migrations/0007_user_roles.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-02-26 13:57 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('main', '0006_user_phone_number'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='user', 15 | name='roles', 16 | field=models.CharField(choices=[('Receptionist', 'Receptionist'), ('Doctor', 'Doctor'), ('Nurse', 'Nurse'), ('Student Clinician', 'Student Clinician')], default='Receptionist', max_length=50), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /main/migrations/0008_rename_roles_user_role.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-02-26 14:00 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('main', '0007_user_roles'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField( 14 | model_name='user', 15 | old_name='roles', 16 | new_name='role', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /main/migrations/0009_auto_20220227_0822.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-02-27 05:22 2 | 3 | import datetime 4 | from django.conf import settings 5 | import django.core.validators 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('main', '0008_rename_roles_user_role'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AlterField( 18 | model_name='patient', 19 | name='date_of_birth', 20 | field=models.DateField(validators=[django.core.validators.MaxValueValidator(datetime.date(2022, 2, 27))]), 21 | ), 22 | migrations.CreateModel( 23 | name='Referral', 24 | fields=[ 25 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 26 | ('created_at', models.DateTimeField(auto_now_add=True)), 27 | ('updated_at', models.DateTimeField(blank=True, null=True)), 28 | ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='referral_created_by', to=settings.AUTH_USER_MODEL)), 29 | ('doctor', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='doctor_referred_to', to=settings.AUTH_USER_MODEL)), 30 | ('patient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='patient_referred', to='main.patient')), 31 | ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='referral_updated_by', to=settings.AUTH_USER_MODEL)), 32 | ], 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /main/migrations/0010_referral_status.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-02-27 06:14 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('main', '0009_auto_20220227_0822'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='referral', 15 | name='status', 16 | field=models.CharField(choices=[('Admitted', 'Admitted'), ('Discharged', 'Discharged'), ('Not seen', 'Not seen'), ('In progress', 'In progress')], default='Not seen', max_length=20), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /main/migrations/0011_auto_20220227_2314.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-02-27 20:14 2 | 3 | import datetime 4 | import django.core.validators 5 | from django.db import migrations, models 6 | from django.utils.timezone import utc 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('main', '0010_referral_status'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='prescription', 18 | name='description', 19 | field=models.TextField(max_length=400, validators=[django.core.validators.MinLengthValidator(20)]), 20 | ), 21 | migrations.AlterField( 22 | model_name='prescription', 23 | name='end_datetime', 24 | field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2022, 2, 27, 20, 14, 34, 465392, tzinfo=utc))]), 25 | ), 26 | migrations.AlterField( 27 | model_name='prescription', 28 | name='start_datetime', 29 | field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2022, 2, 27, 20, 14, 34, 465392, tzinfo=utc))]), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /main/migrations/0012_auto_20220306_1903.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-03-06 16:03 2 | 3 | import datetime 4 | import django.core.validators 5 | from django.db import migrations, models 6 | from django.utils.timezone import utc 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('main', '0011_auto_20220227_2314'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterModelOptions( 17 | name='admission', 18 | options={'ordering': ['-created_at']}, 19 | ), 20 | migrations.AlterModelOptions( 21 | name='patient', 22 | options={'ordering': ['-created_at']}, 23 | ), 24 | migrations.AlterModelOptions( 25 | name='prescription', 26 | options={'ordering': ['-created_at']}, 27 | ), 28 | migrations.AlterModelOptions( 29 | name='referral', 30 | options={'ordering': ['-created_at']}, 31 | ), 32 | migrations.AlterModelOptions( 33 | name='ward', 34 | options={'ordering': ['-created_at']}, 35 | ), 36 | migrations.AlterField( 37 | model_name='patient', 38 | name='date_of_birth', 39 | field=models.DateField(validators=[django.core.validators.MaxValueValidator(datetime.date(2022, 3, 6))]), 40 | ), 41 | migrations.AlterField( 42 | model_name='prescription', 43 | name='end_datetime', 44 | field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2022, 3, 6, 16, 3, 0, 803707, tzinfo=utc))]), 45 | ), 46 | migrations.AlterField( 47 | model_name='prescription', 48 | name='start_datetime', 49 | field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2022, 3, 6, 16, 3, 0, 803707, tzinfo=utc))]), 50 | ), 51 | ] 52 | -------------------------------------------------------------------------------- /main/migrations/0013_auto_20220310_1451.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-03-10 11:51 2 | 3 | import datetime 4 | import django.core.validators 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('main', '0012_auto_20220306_1903'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='patient', 17 | name='date_of_birth', 18 | field=models.DateField(validators=[django.core.validators.MaxValueValidator(datetime.date(2022, 3, 10))]), 19 | ), 20 | migrations.AlterField( 21 | model_name='prescription', 22 | name='end_datetime', 23 | field=models.DateTimeField(), 24 | ), 25 | migrations.AlterField( 26 | model_name='prescription', 27 | name='start_datetime', 28 | field=models.DateTimeField(), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /main/migrations/0014_auto_20220310_1453.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-03-10 11:53 2 | 3 | import datetime 4 | import django.core.validators 5 | from django.db import migrations, models 6 | from django.utils.timezone import utc 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('main', '0013_auto_20220310_1451'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='prescription', 18 | name='end_datetime', 19 | field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2022, 3, 10, 11, 53, 43, 457539, tzinfo=utc))]), 20 | ), 21 | migrations.AlterField( 22 | model_name='prescription', 23 | name='start_datetime', 24 | field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2022, 3, 10, 11, 53, 43, 457539, tzinfo=utc))]), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /main/migrations/0015_auto_20220318_1205.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2022-03-18 09:05 2 | 3 | import datetime 4 | import django.core.validators 5 | from django.db import migrations, models 6 | from django.utils.timezone import utc 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('main', '0014_auto_20220310_1453'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='patient', 18 | name='date_of_birth', 19 | field=models.DateField(validators=[django.core.validators.MaxValueValidator(datetime.date(2022, 3, 18))]), 20 | ), 21 | migrations.AlterField( 22 | model_name='prescription', 23 | name='end_datetime', 24 | field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2022, 3, 18, 9, 5, 36, 229676, tzinfo=utc))]), 25 | ), 26 | migrations.AlterField( 27 | model_name='prescription', 28 | name='start_datetime', 29 | field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2022, 3, 18, 9, 5, 36, 229676, tzinfo=utc))]), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /main/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KNehe/liveup_api/992628afebd0bd4f956f11e6ba935b3362868f21/main/migrations/__init__.py -------------------------------------------------------------------------------- /main/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import AbstractUser 3 | from django.utils import timezone 4 | from django.core.validators import ( 5 | MaxValueValidator, 6 | MinValueValidator, 7 | MinLengthValidator, 8 | ) 9 | from django.utils import timezone 10 | from phonenumber_field.modelfields import PhoneNumberField 11 | from django.db.models.signals import post_save 12 | 13 | from main.choices import NOT_SEEN, RECEPTIONIST, REFERAL_STATUS, ROLES 14 | 15 | 16 | class User(AbstractUser): 17 | email = models.EmailField(unique=True) 18 | phone_number = PhoneNumberField(blank=True, null=True) 19 | role = models.CharField(max_length=50, choices=ROLES, default=RECEPTIONIST) 20 | 21 | USERNAME_FIELD = "email" 22 | REQUIRED_FIELDS = [ 23 | "username", 24 | ] 25 | 26 | def __str__(self) -> str: 27 | return self.username 28 | 29 | 30 | class Patient(models.Model): 31 | patient_number = models.CharField(max_length=10, blank=True) 32 | next_of_kin = models.CharField(max_length=50) 33 | address = models.CharField(max_length=50) 34 | date_of_birth = models.DateField( 35 | validators=[MaxValueValidator(timezone.now().date())] 36 | ) 37 | age = models.IntegerField(blank=True) 38 | contacts = models.CharField(max_length=20) 39 | patient_name = models.CharField(max_length=100) 40 | created_at = models.DateTimeField(auto_now_add=True) 41 | created_by = models.ForeignKey( 42 | User, on_delete=models.SET_NULL, null=True, related_name="patient_created_by" 43 | ) 44 | updated_at = models.DateTimeField(null=True, blank=True) 45 | updated_by = models.ForeignKey( 46 | User, 47 | on_delete=models.SET_NULL, 48 | null=True, 49 | blank=True, 50 | related_name="patient_updated_by", 51 | ) 52 | 53 | class Meta: 54 | ordering = ["-created_at"] 55 | 56 | def __str__(self) -> str: 57 | return self.patient_name 58 | 59 | def calculate_age(self): 60 | today = timezone.now() 61 | born = self.date_of_birth 62 | self.age = ( 63 | today.year - born.year - ((today.month, today.day) < (born.month, born.day)) 64 | ) 65 | 66 | def generate_patient_number(self): 67 | self.patient_number = f"P-{self.pk}" 68 | 69 | def save(self, *args, **kwargs): 70 | self.calculate_age() 71 | super().save(*args, **kwargs) 72 | 73 | 74 | def patient_post_save(sender, instance, created, *args, **kwargs): 75 | if created: 76 | instance.generate_patient_number() 77 | instance.save() 78 | 79 | 80 | post_save.connect(patient_post_save, sender=Patient) 81 | 82 | 83 | class Prescription(models.Model): 84 | patient = models.ForeignKey( 85 | Patient, on_delete=models.CASCADE, related_name="patient_prescribed" 86 | ) 87 | start_datetime = models.DateTimeField( 88 | validators=[MinValueValidator(timezone.now())] 89 | ) 90 | end_datetime = models.DateTimeField(validators=[MinValueValidator(timezone.now())]) 91 | description = models.TextField(max_length=400, validators=[MinLengthValidator(20)]) 92 | created_at = models.DateTimeField(auto_now_add=True) 93 | created_by = models.ForeignKey( 94 | User, 95 | on_delete=models.SET_NULL, 96 | null=True, 97 | related_name="presciption_created_by", 98 | ) 99 | updated_at = models.DateTimeField(null=True, blank=True) 100 | updated_by = models.ForeignKey( 101 | User, 102 | on_delete=models.SET_NULL, 103 | null=True, 104 | blank=True, 105 | related_name="presciption_updated_by", 106 | ) 107 | 108 | class Meta: 109 | ordering = ["-created_at"] 110 | 111 | def __str__(self) -> str: 112 | return f"Prescribed by {self.created_by} for {self.patient}" 113 | 114 | 115 | class Ward(models.Model): 116 | name = models.CharField(max_length=100) 117 | created_at = models.DateTimeField(auto_now_add=True) 118 | created_by = models.ForeignKey( 119 | User, on_delete=models.SET_NULL, null=True, related_name="ward_created_by" 120 | ) 121 | updated_at = models.DateTimeField( 122 | null=True, 123 | blank=True, 124 | ) 125 | updated_by = models.ForeignKey( 126 | User, 127 | on_delete=models.SET_NULL, 128 | null=True, 129 | blank=True, 130 | related_name="updated_created_by", 131 | ) 132 | 133 | class Meta: 134 | ordering = ["-created_at"] 135 | 136 | def __str__(self) -> str: 137 | return self.name 138 | 139 | 140 | class Admission(models.Model): 141 | ward = models.ForeignKey( 142 | Ward, on_delete=models.SET_NULL, null=True, related_name="ward_admitted" 143 | ) 144 | patient = models.ForeignKey( 145 | Patient, on_delete=models.CASCADE, related_name="patient_admitted" 146 | ) 147 | created_at = models.DateTimeField(auto_now_add=True) 148 | created_by = models.ForeignKey( 149 | User, on_delete=models.SET_NULL, null=True, related_name="admission_created_by" 150 | ) 151 | updated_at = models.DateTimeField(null=True, blank=True) 152 | updated_by = models.ForeignKey( 153 | User, 154 | on_delete=models.SET_NULL, 155 | null=True, 156 | blank=True, 157 | related_name="admission_updated_by", 158 | ) 159 | 160 | class Meta: 161 | ordering = ["-created_at"] 162 | 163 | def __str__(self) -> str: 164 | return f"{self.patient} admitted to {self.ward}" 165 | 166 | 167 | class Referral(models.Model): 168 | patient = models.ForeignKey( 169 | Patient, on_delete=models.CASCADE, related_name="patient_referred" 170 | ) 171 | doctor = models.ForeignKey( 172 | User, on_delete=models.SET_NULL, null=True, related_name="doctor_referred_to" 173 | ) 174 | status = models.CharField(max_length=20, choices=REFERAL_STATUS, default=NOT_SEEN) 175 | created_at = models.DateTimeField(auto_now_add=True) 176 | created_by = models.ForeignKey( 177 | User, on_delete=models.SET_NULL, null=True, related_name="referral_created_by" 178 | ) 179 | updated_at = models.DateTimeField(null=True, blank=True) 180 | updated_by = models.ForeignKey( 181 | User, 182 | on_delete=models.SET_NULL, 183 | null=True, 184 | blank=True, 185 | related_name="referral_updated_by", 186 | ) 187 | 188 | class Meta: 189 | ordering = ["-created_at"] 190 | 191 | def __str__(self) -> str: 192 | return f"{self.patient} referred to {self.doctor}" 193 | -------------------------------------------------------------------------------- /main/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework import permissions 2 | 3 | from main.choices import DOCTOR, NURSE, RECEPTIONIST, STUDENT_CLINICIAN 4 | 5 | 6 | class IsReceptionist(permissions.BasePermission): 7 | def has_permission(self, request, view): 8 | return perform_check(request, RECEPTIONIST) 9 | 10 | 11 | class IsDoctor(permissions.BasePermission): 12 | def has_permission(self, request, view): 13 | return perform_check(request, DOCTOR) 14 | 15 | 16 | class IsNurse(permissions.BasePermission): 17 | def has_permission(self, request, view): 18 | return perform_check(request, NURSE) 19 | 20 | 21 | class IsStudent_Clinician(permissions.BasePermission): 22 | def has_permission(self, request, view): 23 | return perform_check(request, STUDENT_CLINICIAN) 24 | 25 | 26 | def perform_check(request, role): 27 | if not request.user.is_authenticated: 28 | return False 29 | return request.user.role == role 30 | -------------------------------------------------------------------------------- /main/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from main.models import Admission, Patient, Prescription, Referral, User, Ward 4 | 5 | from dj_rest_auth.serializers import UserDetailsSerializer 6 | from dj_rest_auth.serializers import PasswordResetSerializer 7 | 8 | 9 | class PatientSerializer(serializers.HyperlinkedModelSerializer): 10 | class Meta: 11 | model = Patient 12 | fields = [ 13 | "url", 14 | "patient_number", 15 | "next_of_kin", 16 | "address", 17 | "date_of_birth", 18 | "age", 19 | "contacts", 20 | "patient_name", 21 | "created_by", 22 | "created_at", 23 | "updated_by", 24 | "updated_at", 25 | ] 26 | read_only_fields = [ 27 | "age", 28 | "patient_number", 29 | "created_by", 30 | "created_at", 31 | "updated_by", 32 | "updated_at", 33 | ] 34 | 35 | 36 | class UserSerializer(serializers.HyperlinkedModelSerializer): 37 | class Meta: 38 | model = User 39 | fields = [ 40 | "url", 41 | "email", 42 | "username", 43 | "first_name", 44 | "last_name", 45 | "phone_number", 46 | "role", 47 | ] 48 | read_only_fields = [ 49 | "email", 50 | "username", 51 | "first_name", 52 | "last_name", 53 | "phone_number", 54 | "role", 55 | ] 56 | 57 | 58 | class ReferralSerializer(serializers.HyperlinkedModelSerializer): 59 | class Meta: 60 | model = Referral 61 | fields = [ 62 | "url", 63 | "patient", 64 | "status", 65 | "doctor", 66 | "created_at", 67 | "created_by", 68 | "updated_at", 69 | "updated_by", 70 | ] 71 | read_only_fields = ["created_by", "updated_at", "updated_by", "created_at"] 72 | 73 | 74 | class PrescriptionSerializer(serializers.HyperlinkedModelSerializer): 75 | class Meta: 76 | model = Prescription 77 | fields = [ 78 | "url", 79 | "patient", 80 | "start_datetime", 81 | "end_datetime", 82 | "description", 83 | "created_at", 84 | "created_by", 85 | "updated_at", 86 | "updated_by", 87 | ] 88 | read_only_fields = ["created_at", "created_by", "updated_at", "updated_by"] 89 | 90 | 91 | class WardSerializer(serializers.HyperlinkedModelSerializer): 92 | class Meta: 93 | model = Ward 94 | fields = ["url", "name"] 95 | 96 | 97 | class AdmissionSerializer(serializers.HyperlinkedModelSerializer): 98 | class Meta: 99 | model = Admission 100 | fields = [ 101 | "url", 102 | "ward", 103 | "patient", 104 | "created_at", 105 | "created_by", 106 | "updated_at", 107 | "updated_by", 108 | ] 109 | read_only_fields = ["created_at", "created_by", "updated_at", "updated_by"] 110 | 111 | 112 | class CustomUserDetailsSerializer(UserDetailsSerializer): 113 | class Meta(UserDetailsSerializer.Meta): 114 | fields = UserDetailsSerializer.Meta.fields + ( 115 | "role", 116 | "username", 117 | "phone_number", 118 | ) 119 | 120 | 121 | # Nested Hyperlinked Model Serialiazers 122 | 123 | 124 | class AdmissionNestedSerializer(serializers.HyperlinkedModelSerializer): 125 | created_by = UserSerializer() 126 | updated_by = UserSerializer() 127 | ward = WardSerializer() 128 | patient = PatientSerializer() 129 | 130 | class Meta: 131 | model = Admission 132 | fields = [ 133 | "url", 134 | "id", 135 | "ward", 136 | "patient", 137 | "created_at", 138 | "created_by", 139 | "updated_at", 140 | "updated_by", 141 | ] 142 | read_only_fields = ["created_at", "created_by", "updated_at", "updated_by"] 143 | 144 | 145 | class PrescriptionNestedSerializer(serializers.HyperlinkedModelSerializer): 146 | created_by = UserSerializer() 147 | updated_by = UserSerializer() 148 | patient = PatientSerializer() 149 | 150 | class Meta: 151 | model = Prescription 152 | fields = [ 153 | "url", 154 | "patient", 155 | "start_datetime", 156 | "end_datetime", 157 | "description", 158 | "created_at", 159 | "created_by", 160 | "updated_at", 161 | "updated_by", 162 | ] 163 | read_only_fields = ["created_at", "created_by", "updated_at", "updated_by"] 164 | 165 | 166 | class ReferralNestederializer(serializers.HyperlinkedModelSerializer): 167 | patient = PatientSerializer() 168 | doctor = UserSerializer() 169 | created_by = UserSerializer() 170 | updated_by = UserSerializer() 171 | 172 | class Meta: 173 | model = Referral 174 | fields = [ 175 | "url", 176 | "patient", 177 | "status", 178 | "doctor", 179 | "created_at", 180 | "created_by", 181 | "updated_at", 182 | "updated_by", 183 | ] 184 | read_only_fields = ["created_by", "updated_at", "updated_by", "created_at"] 185 | 186 | 187 | class CustomPasswordResetSerializer(PasswordResetSerializer): 188 | def get_email_options(self): 189 | return {"email_template_name": "password_reset_email.html"} 190 | -------------------------------------------------------------------------------- /main/tests.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | 3 | from rest_framework.test import APITestCase 4 | from rest_framework import status 5 | 6 | from main.choices import DOCTOR, RECEPTIONIST, STUDENT_CLINICIAN 7 | from main.models import User 8 | 9 | 10 | class LoginTestCase(APITestCase): 11 | def setUp(self) -> None: 12 | self.dummy_user = { 13 | "email": "knehe@gmail.com", 14 | "phone_number": "+256554332456", 15 | "role": STUDENT_CLINICIAN, 16 | "username": "nehe8kk", 17 | "first_name": "nehe", 18 | "last_name": "nehe", 19 | "password": "#$23msnAB#$&", 20 | } 21 | 22 | def test_should_login_user(self): 23 | User.objects.create_user(**self.dummy_user) 24 | 25 | response = self.client.post(reverse("rest_login"), self.dummy_user) 26 | 27 | self.assertEqual(response.status_code, status.HTTP_200_OK) 28 | self.assertEqual( 29 | response.data.get("user").get("email"), self.dummy_user["email"] 30 | ) 31 | self.assertEqual(response.data.get("user").get("role"), self.dummy_user["role"]) 32 | self.assertIsNotNone(response.data.get("access_token")) 33 | self.assertIsNotNone(response.data.get("refresh_token")) 34 | 35 | def test_should_not_login_user_when_password_is_missing(self): 36 | User.objects.create_user(**self.dummy_user) 37 | 38 | self.dummy_user["password"] = "" 39 | 40 | response = self.client.post(reverse("rest_login"), self.dummy_user) 41 | 42 | self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) 43 | self.assertEqual( 44 | response.data.get("password")[0], "This field may not be blank." 45 | ) 46 | self.assertEqual(response.data.get("password")[0].code, "blank") 47 | 48 | def test_should_not_login_user_when_email_is_missing(self): 49 | User.objects.create_user(**self.dummy_user) 50 | 51 | del self.dummy_user["email"] 52 | 53 | response = self.client.post(reverse("rest_login"), self.dummy_user) 54 | self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) 55 | self.assertEqual( 56 | response.data.get("non_field_errors")[0], 57 | "Unable to log in with provided credentials.", 58 | ) 59 | self.assertEqual(response.data.get("non_field_errors")[0].code, "invalid") 60 | 61 | def test_should_not_login_user_when_user_not_registered(self): 62 | response = self.client.post(reverse("rest_login"), self.dummy_user) 63 | 64 | self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) 65 | self.assertEqual( 66 | response.data.get("non_field_errors")[0], 67 | "Unable to log in with provided credentials.", 68 | ) 69 | self.assertEqual(response.data.get("non_field_errors")[0].code, "invalid") 70 | 71 | 72 | class PatientViewSetTestCase(APITestCase): 73 | def setUp(self) -> None: 74 | self.dummy_user = { 75 | "email": "knehe@gmail.com", 76 | "phone_number": "+256554332456", 77 | "role": STUDENT_CLINICIAN, 78 | "username": "nehe8kk", 79 | "first_name": "nehe", 80 | "last_name": "nehe", 81 | "password": "#$23msnAB#$&", 82 | } 83 | self.patient = { 84 | "next_of_kin": "next_of_kin", 85 | "address": "address", 86 | "date_of_birth": "2022-02-25", 87 | "contacts": "+256 774 332 423", 88 | "patient_name": "John Doe", 89 | } 90 | 91 | def authenticate(self): 92 | User.objects.create_user(**self.dummy_user) 93 | 94 | response = self.client.post(reverse("rest_login"), self.dummy_user) 95 | 96 | self.client.credentials( 97 | HTTP_AUTHORIZATION=f"Bearer {response.data.get('access_token')}" 98 | ) 99 | 100 | def test_should_register_patient(self): 101 | self.dummy_user["role"] = RECEPTIONIST 102 | 103 | self.authenticate() 104 | 105 | response = self.client.post(reverse("patient-list"), self.patient) 106 | 107 | self.assertEqual(response.status_code, status.HTTP_201_CREATED) 108 | self.assertEqual(response.data.get("next_of_kin"), self.patient["next_of_kin"]) 109 | self.assertEqual(response.data.get("address"), self.patient["address"]) 110 | self.assertEqual( 111 | response.data.get("date_of_birth"), self.patient["date_of_birth"] 112 | ) 113 | self.assertEqual( 114 | response.data.get("patient_name"), self.patient["patient_name"] 115 | ) 116 | self.assertEqual(response.data.get("contacts"), self.patient["contacts"]) 117 | self.assertIsNotNone(response.data.get("created_by")) 118 | self.assertIsNotNone(response.data.get("created_at")) 119 | self.assertIsNotNone(response.data.get("url")) 120 | self.assertIsNone(response.data.get("updated_by")) 121 | self.assertIsNone(response.data.get("updated_at")) 122 | 123 | def test_should_register_patient_if_user_not_authorized(self): 124 | self.authenticate() 125 | 126 | response = self.client.post(reverse("patient-list"), self.patient) 127 | 128 | self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) 129 | self.assertEqual( 130 | response.data.get("detail"), 131 | "You do not have permission to perform this action.", 132 | ) 133 | 134 | def test_should_register_patient_if_required_data_missing(self): 135 | self.dummy_user["role"] = RECEPTIONIST 136 | 137 | self.patient["next_of_kin"] = "" 138 | self.patient["address"] = "" 139 | self.patient["date_of_birth"] = "" 140 | self.patient["patient_name"] = "" 141 | self.patient["contacts"] = "" 142 | 143 | self.authenticate() 144 | 145 | response = self.client.post(reverse("patient-list"), self.patient) 146 | 147 | self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) 148 | self.assertEqual( 149 | response.data.get("next_of_kin")[0], "This field may not be blank." 150 | ) 151 | self.assertEqual( 152 | response.data.get("address")[0], "This field may not be blank." 153 | ) 154 | self.assertEqual( 155 | response.data.get("date_of_birth")[0], 156 | "Date has wrong format. Use one of these formats instead: YYYY-MM-DD.", 157 | ) 158 | self.assertEqual( 159 | response.data.get("patient_name")[0], "This field may not be blank." 160 | ) 161 | self.assertEqual( 162 | response.data.get("contacts")[0], "This field may not be blank." 163 | ) 164 | 165 | def test_should_register_patient_if_not_authenticated(self): 166 | response = self.client.post(reverse("patient-list"), self.patient) 167 | 168 | self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) 169 | self.assertEqual( 170 | response.data.get("detail"), 171 | "Authentication credentials were not provided.", 172 | ) 173 | 174 | def test_should_update_patient(self): 175 | self.dummy_user["role"] = RECEPTIONIST 176 | 177 | self.authenticate() 178 | 179 | response = self.client.post(reverse("patient-list"), self.patient) 180 | 181 | self.assertEqual(response.status_code, status.HTTP_201_CREATED) 182 | 183 | self.patient["patient_name"] = "new name" 184 | self.patient["next_of_kin"] = "new next of kin name" 185 | 186 | patient_id = response.data.get("url").split("/")[-2] 187 | 188 | response2 = self.client.patch( 189 | reverse("patient-detail", kwargs={"pk": patient_id}), self.patient 190 | ) 191 | 192 | self.assertEqual(response2.status_code, status.HTTP_200_OK) 193 | self.assertEqual(response2.data.get("next_of_kin"), self.patient["next_of_kin"]) 194 | self.assertEqual( 195 | response2.data.get("patient_name"), self.patient["patient_name"] 196 | ) 197 | self.assertEqual(response2.data.get("url"), response.data.get("url")) 198 | 199 | def test_should_not_update_patient_if_not_found(self): 200 | self.dummy_user["role"] = RECEPTIONIST 201 | 202 | self.authenticate() 203 | 204 | response = self.client.post(reverse("patient-list"), self.patient) 205 | 206 | self.assertEqual(response.status_code, status.HTTP_201_CREATED) 207 | 208 | self.patient["patient_name"] = "new name" 209 | self.patient["next_of_kin"] = "new next of kin name" 210 | 211 | patient_id = 212 # not registered patient id 212 | 213 | response2 = self.client.patch( 214 | reverse("patient-detail", kwargs={"pk": patient_id}), self.patient 215 | ) 216 | 217 | self.assertEqual(response2.status_code, status.HTTP_404_NOT_FOUND) 218 | self.assertEqual(response2.data.get("detail"), "Not found.") 219 | 220 | def test_should_not_update_patient_if_not_authorized(self): 221 | self.dummy_user["role"] = RECEPTIONIST 222 | 223 | self.authenticate() 224 | 225 | response = self.client.post(reverse("patient-list"), self.patient) 226 | 227 | self.assertEqual(response.status_code, status.HTTP_201_CREATED) 228 | 229 | self.patient["patient_name"] = "new name" 230 | self.patient["next_of_kin"] = "new next of kin name" 231 | 232 | self.dummy_user["role"] = STUDENT_CLINICIAN 233 | self.dummy_user["email"] = "rando@gmail.com" 234 | self.dummy_user["username"] = "randohaha" 235 | 236 | self.authenticate() 237 | 238 | patient_id = response.data.get("url").split("/")[-2] 239 | 240 | response2 = self.client.patch( 241 | reverse("patient-detail", kwargs={"pk": patient_id}), self.patient 242 | ) 243 | 244 | self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) 245 | self.assertEqual( 246 | response2.data.get("detail"), 247 | "You do not have permission to perform this action.", 248 | ) 249 | 250 | def test_should_not_update_patient_if_not_authenticated(self): 251 | response = self.client.patch( 252 | reverse("patient-detail", kwargs={"pk": 1}), self.patient 253 | ) 254 | 255 | self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) 256 | self.assertEqual( 257 | response.data.get("detail"), 258 | "Authentication credentials were not provided.", 259 | ) 260 | 261 | def test_should_retrieve_a_patient(self): 262 | self.dummy_user["role"] = RECEPTIONIST 263 | 264 | self.authenticate() 265 | 266 | response = self.client.post(reverse("patient-list"), self.patient) 267 | 268 | self.assertEqual(response.status_code, status.HTTP_201_CREATED) 269 | 270 | patient_id = response.data.get("url").split("/")[-2] 271 | 272 | response2 = self.client.get( 273 | reverse("patient-detail", kwargs={"pk": patient_id}), self.patient 274 | ) 275 | 276 | self.assertEqual(response2.status_code, status.HTTP_200_OK) 277 | self.assertEqual(response2.data.get("next_of_kin"), self.patient["next_of_kin"]) 278 | self.assertEqual( 279 | response2.data.get("patient_name"), self.patient["patient_name"] 280 | ) 281 | self.assertEqual(response2.data.get("url"), response.data.get("url")) 282 | 283 | def test_should_not_retrieve_patient_if_not_authorized(self): 284 | self.dummy_user["role"] = RECEPTIONIST 285 | 286 | self.authenticate() 287 | 288 | response = self.client.post(reverse("patient-list"), self.patient) 289 | 290 | self.assertEqual(response.status_code, status.HTTP_201_CREATED) 291 | 292 | self.dummy_user["role"] = STUDENT_CLINICIAN 293 | self.dummy_user["email"] = "rando@gmail.com" 294 | self.dummy_user["username"] = "randohaha" 295 | 296 | self.authenticate() 297 | 298 | patient_id = response.data.get("url").split("/")[-2] 299 | 300 | response2 = self.client.get( 301 | reverse("patient-detail", kwargs={"pk": patient_id}), self.patient 302 | ) 303 | 304 | self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) 305 | self.assertEqual( 306 | response2.data.get("detail"), 307 | "You do not have permission to perform this action.", 308 | ) 309 | 310 | def test_should_not_retrieve_patient_if_not_authenticated(self): 311 | response = self.client.get( 312 | reverse("patient-detail", kwargs={"pk": 1}), self.patient 313 | ) 314 | 315 | self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) 316 | self.assertEqual( 317 | response.data.get("detail"), 318 | "Authentication credentials were not provided.", 319 | ) 320 | 321 | def test_should_get_all_patients(self): 322 | self.dummy_user["role"] = RECEPTIONIST 323 | 324 | self.authenticate() 325 | 326 | response = self.client.post(reverse("patient-list"), self.patient) 327 | 328 | self.assertEqual(response.status_code, status.HTTP_201_CREATED) 329 | 330 | response = self.client.get(reverse("patient-list")) 331 | 332 | self.assertEqual(response.status_code, status.HTTP_200_OK) 333 | self.assertEqual(response.data.get("count"), 1) 334 | self.assertIsNone(response.data.get("next")) 335 | self.assertIsNone(response.data.get("previous")) 336 | self.assertEqual(len(response.data.get("results")), 1) 337 | 338 | def test_should_not_get_all_patients_if_not_authorized(self): 339 | self.dummy_user["role"] = RECEPTIONIST 340 | 341 | self.authenticate() 342 | 343 | self.client.post(reverse("patient-list"), self.patient) 344 | 345 | self.dummy_user["role"] = STUDENT_CLINICIAN 346 | self.dummy_user["email"] = "rando@gmail.com" 347 | self.dummy_user["username"] = "randohaha" 348 | 349 | self.authenticate() 350 | 351 | response = self.client.get(reverse("patient-list"), self.patient) 352 | 353 | self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) 354 | self.assertEqual( 355 | response.data.get("detail"), 356 | "You do not have permission to perform this action.", 357 | ) 358 | 359 | def test_should_not_get_all_patients_if_not_authorized(self): 360 | response = self.client.get(reverse("patient-list"), self.patient) 361 | 362 | self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) 363 | self.assertEqual( 364 | response.data.get("detail"), 365 | "Authentication credentials were not provided.", 366 | ) 367 | 368 | 369 | class UserViewSetTestCase(APITestCase): 370 | def setUp(self) -> None: 371 | self.dummy_user = { 372 | "email": "knehe@gmail.com", 373 | "phone_number": "+256554332456", 374 | "role": STUDENT_CLINICIAN, 375 | "username": "nehe8kk", 376 | "first_name": "nehe", 377 | "last_name": "nehe", 378 | "password": "#$23msnAB#$&", 379 | } 380 | 381 | def authenticate(self): 382 | User.objects.create_user(**self.dummy_user) 383 | 384 | response = self.client.post(reverse("rest_login"), self.dummy_user) 385 | 386 | self.client.credentials( 387 | HTTP_AUTHORIZATION=f"Bearer {response.data.get('access_token')}" 388 | ) 389 | 390 | def test_should_get_all_users(self): 391 | self.authenticate() 392 | 393 | response = self.client.get(reverse("user-list")) 394 | 395 | self.assertEqual(response.status_code, status.HTTP_200_OK) 396 | self.assertEqual(response.data.get("count"), 1) 397 | self.assertIsNone(response.data.get("next")) 398 | self.assertIsNone(response.data.get("previous")) 399 | self.assertEqual(len(response.data.get("results")), 1) 400 | 401 | def test_should_not_get_all_users_when_not_athenticated(self): 402 | response = self.client.get(reverse("user-list")) 403 | 404 | self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) 405 | self.assertEqual( 406 | response.data.get("detail"), 407 | "Authentication credentials were not provided.", 408 | ) 409 | 410 | def test_should_not_get_one_user(self): 411 | self.authenticate() 412 | 413 | user_id = 12212 # unknown user id 414 | response = self.client.get(reverse("user-detail", kwargs={"pk": user_id})) 415 | 416 | self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) 417 | 418 | def test_should_get_one_user(self): 419 | self.authenticate() 420 | 421 | self.dummy_user["email"] = "email@gmail.com" 422 | self.dummy_user["username"] = "username" 423 | 424 | user_id = User.objects.create_user(**self.dummy_user).id 425 | response = self.client.get(reverse("user-detail", kwargs={"pk": user_id})) 426 | 427 | self.assertEqual(response.status_code, status.HTTP_200_OK) 428 | self.assertEqual(response.data.get("email"), self.dummy_user["email"]) 429 | self.assertEqual(response.data.get("first_name"), self.dummy_user["first_name"]) 430 | 431 | 432 | class ReceptionistPatientViewTestCase(APITestCase): 433 | def setUp(self) -> None: 434 | self.dummy_user = { 435 | "email": "knehe@gmail.com", 436 | "phone_number": "+256554332456", 437 | "role": RECEPTIONIST, 438 | "username": "nehe8kk", 439 | "first_name": "nehe", 440 | "last_name": "nehe", 441 | "password": "#$23msnAB#$&", 442 | } 443 | self.patient = { 444 | "next_of_kin": "next_of_kin", 445 | "address": "address", 446 | "date_of_birth": "2022-02-25", 447 | "contacts": "+256 774 332 423", 448 | "patient_name": "John Doe", 449 | } 450 | 451 | def authenticate(self): 452 | User.objects.create_user(**self.dummy_user) 453 | 454 | response = self.client.post(reverse("rest_login"), self.dummy_user) 455 | 456 | self.client.credentials( 457 | HTTP_AUTHORIZATION=f"Bearer {response.data.get('access_token')}" 458 | ) 459 | 460 | def test_should_get_patients_registered_by_receptionist(self): 461 | self.authenticate() 462 | 463 | self.client.post(reverse("patient-list"), self.patient) 464 | 465 | response = self.client.get(reverse("registered-patient-list"), self.patient) 466 | 467 | self.assertEqual(response.status_code, status.HTTP_200_OK) 468 | self.assertEqual(response.data.get("count"), 1) 469 | self.assertIsNone(response.data.get("next")) 470 | self.assertIsNone(response.data.get("previous")) 471 | self.assertEqual(len(response.data.get("results")), 1) 472 | 473 | def test_should_not_get_patients_registered_by_receptionist_when_not_authenticated( 474 | self, 475 | ): 476 | response = self.client.get(reverse("registered-patient-list"), self.patient) 477 | 478 | self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) 479 | self.assertEqual( 480 | response.data.get("detail"), 481 | "Authentication credentials were not provided.", 482 | ) 483 | 484 | def test_should_not_get_patients_registered_by_receptionist_when_not_authorized( 485 | self, 486 | ): 487 | self.dummy_user["role"] = STUDENT_CLINICIAN 488 | 489 | self.authenticate() 490 | 491 | response = self.client.get(reverse("registered-patient-list"), self.patient) 492 | 493 | self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) 494 | self.assertEqual( 495 | response.data.get("detail"), 496 | "You do not have permission to perform this action.", 497 | ) 498 | 499 | def test_should_get_one_registered_patient(self): 500 | self.authenticate() 501 | 502 | response = self.client.post(reverse("patient-list"), self.patient) 503 | 504 | patient_id = response.data.get("url").split("/")[-2] 505 | 506 | response = self.client.get(reverse("patient-detail", kwargs={"pk": patient_id})) 507 | 508 | self.assertEqual(response.status_code, status.HTTP_200_OK) 509 | self.assertIsNotNone(response.data.get("patient_number")) 510 | self.assertIsNotNone(response.data.get("patient_name")) 511 | self.assertIsNotNone(response.data.get("next_of_kin")) 512 | self.assertIsNotNone(response.data.get("date_of_birth")) 513 | 514 | def test_should_not_get_one_unknown_patient(self): 515 | self.authenticate() 516 | 517 | patient_id = 12212 # unknown patient id 518 | response = self.client.get(reverse("patient-detail", kwargs={"pk": patient_id})) 519 | 520 | self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) 521 | 522 | def test_should_not_get_one_registered_patient(self): 523 | self.dummy_user["role"] = STUDENT_CLINICIAN 524 | self.authenticate() 525 | 526 | response = self.client.get(reverse("patient-detail", kwargs={"pk": 22})) 527 | 528 | self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) 529 | self.assertEqual( 530 | response.data.get("detail"), 531 | "You do not have permission to perform this action.", 532 | ) 533 | 534 | 535 | class ReferralViewSetTestCase(APITestCase): 536 | def setUp(self) -> None: 537 | self.dummy_user = { 538 | "email": "knehe@gmail.com", 539 | "phone_number": "+256554332456", 540 | "role": RECEPTIONIST, 541 | "username": "nehe8kk", 542 | "first_name": "nehe", 543 | "last_name": "nehe", 544 | "password": "#$23msnAB#$&", 545 | } 546 | self.patient = { 547 | "next_of_kin": "next_of_kin", 548 | "address": "address", 549 | "date_of_birth": "2022-02-25", 550 | "contacts": "+256 774 332 423", 551 | "patient_name": "John Doe", 552 | } 553 | 554 | def authenticate(self): 555 | User.objects.create_user(**self.dummy_user) 556 | 557 | response = self.client.post(reverse("rest_login"), self.dummy_user) 558 | 559 | self.client.credentials( 560 | HTTP_AUTHORIZATION=f"Bearer {response.data.get('access_token')}" 561 | ) 562 | 563 | def create_doctor(self): 564 | self.dummy_user["email"] = "doctor@gmail.com" 565 | self.dummy_user["username"] = "doctor1" 566 | self.dummy_user["role"] = DOCTOR 567 | 568 | doc = User.objects.create_user(**self.dummy_user) 569 | return doc 570 | 571 | def create_second_doctor(self): 572 | self.dummy_user["email"] = "doctor2@gmail.com" 573 | self.dummy_user["username"] = "doctor2" 574 | self.dummy_user["role"] = DOCTOR 575 | 576 | doc = User.objects.create_user(**self.dummy_user) 577 | return doc 578 | 579 | def test_should_refer_patient(self): 580 | self.authenticate() 581 | 582 | patient_response = self.client.post(reverse("patient-list"), self.patient) 583 | 584 | doctor_id = self.create_doctor().id 585 | 586 | doc_response = self.client.get(reverse("user-detail", kwargs={"pk": doctor_id})) 587 | 588 | response = self.client.post( 589 | reverse("referral-list"), 590 | { 591 | "doctor": doc_response.data.get("url"), 592 | "patient": patient_response.data.get("url"), 593 | }, 594 | ) 595 | 596 | self.assertEqual(response.status_code, status.HTTP_201_CREATED) 597 | self.assertEqual(response.data.get("status"), "Not seen") 598 | self.assertEqual(response.data.get("doctor"), doc_response.data.get("url")) 599 | self.assertIsNotNone(response.data.get("created_by")) 600 | self.assertIsNotNone(response.data.get("created_at")) 601 | self.assertIsNone(response.data.get("updated_at")) 602 | self.assertIsNone(response.data.get("updated_by")) 603 | 604 | def test_should_not_refer_patient_when_not_authenticated(self): 605 | 606 | response = self.client.post( 607 | reverse("referral-list"), 608 | { 609 | "doctor": "will not be checked", 610 | "patient": "will not be checked", 611 | }, 612 | ) 613 | 614 | self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) 615 | self.assertEqual( 616 | response.data.get("detail"), 617 | "Authentication credentials were not provided.", 618 | ) 619 | 620 | def test_should_not_refer_patient_with_invalid_urls(self): 621 | self.authenticate() 622 | 623 | response = self.client.post( 624 | reverse("referral-list"), 625 | { 626 | "doctor": "bad_url", 627 | "patient": "invalid_url", 628 | }, 629 | ) 630 | 631 | self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) 632 | self.assertEqual( 633 | response.data.get("patient")[0], "Invalid hyperlink - No URL match." 634 | ) 635 | self.assertEqual( 636 | response.data.get("doctor")[0], "Invalid hyperlink - No URL match." 637 | ) 638 | 639 | def test_should_update_referral_data(self): 640 | self.authenticate() 641 | 642 | patient_response = self.client.post(reverse("patient-list"), self.patient) 643 | 644 | doctor_id = self.create_doctor().id 645 | doc_response = self.client.get(reverse("user-detail", kwargs={"pk": doctor_id})) 646 | 647 | response = self.client.post( 648 | reverse("referral-list"), 649 | { 650 | "doctor": doc_response.data.get("url"), 651 | "patient": patient_response.data.get("url"), 652 | }, 653 | ) 654 | 655 | doctor_id = self.create_second_doctor().id 656 | 657 | doc_response = self.client.get(reverse("user-detail", kwargs={"pk": doctor_id})) 658 | 659 | refferal_id = response.data.get("url").split("/")[-2] 660 | 661 | response2 = self.client.patch( 662 | reverse("referral-detail", kwargs={"pk": refferal_id}), 663 | {"doctor": doc_response.data.get("url")}, 664 | ) 665 | 666 | self.assertEqual(response2.status_code, status.HTTP_200_OK) 667 | self.assertEqual(response2.data.get("status"), "Not seen") 668 | self.assertNotEqual(response.data.get("doctor"), response2.data.get("doctor")) 669 | self.assertIsNotNone(response2.data.get("updated_at")) 670 | self.assertIsNotNone(response2.data.get("updated_by")) 671 | 672 | def test_should_not_update_referral_data_when_not_authenticated(self): 673 | response = self.client.patch( 674 | reverse("referral-detail", kwargs={"pk": 22}), 675 | {"doctor": "will not vause trouble"}, 676 | ) 677 | 678 | self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) 679 | self.assertEqual( 680 | response.data.get("detail"), 681 | "Authentication credentials were not provided.", 682 | ) 683 | 684 | def test_should_not_update_referral_data_with_invalid_urls(self): 685 | self.authenticate() 686 | 687 | patient_response = self.client.post(reverse("patient-list"), self.patient) 688 | 689 | doctor_id = self.create_doctor().id 690 | doc_response = self.client.get(reverse("user-detail", kwargs={"pk": doctor_id})) 691 | 692 | response = self.client.post( 693 | reverse("referral-list"), 694 | { 695 | "doctor": doc_response.data.get("url"), 696 | "patient": patient_response.data.get("url"), 697 | }, 698 | ) 699 | 700 | doctor_id = self.create_second_doctor().id 701 | 702 | doc_response = self.client.get(reverse("user-detail", kwargs={"pk": doctor_id})) 703 | 704 | refferal_id = response.data.get("url").split("/")[-2] 705 | 706 | response2 = self.client.patch( 707 | reverse("referral-detail", kwargs={"pk": refferal_id}), 708 | {"doctor": "invalid_url", "patient": "invalid_url"}, 709 | ) 710 | 711 | self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) 712 | self.assertEqual( 713 | response2.data.get("patient")[0], "Invalid hyperlink - No URL match." 714 | ) 715 | self.assertEqual( 716 | response2.data.get("doctor")[0], "Invalid hyperlink - No URL match." 717 | ) 718 | 719 | 720 | # IF YOU'RE HAVE REACHED HERE, THANKS FOR READING MY TESTS 721 | # I DID NOT TEST ALL VIEWS BECAUSE THIS PROJECT WAS MEANT FOR LEARNING 722 | # I THINK THE REST OF THE TEST LOGIC FOR NON-TESTED VIEWS WILL BE VERY SIMILAR and I'VE UNDERSTOOD ENOUGH 723 | # IN PRODUCTION IT'D BE WISE TO TEST ALL 724 | # FEEL FREE TO CLONE AND CONTINUE IF INTERESTED 725 | -------------------------------------------------------------------------------- /main/urls.py: -------------------------------------------------------------------------------- 1 | from email.mime import base 2 | from django.urls import include, path, re_path 3 | from rest_framework.routers import DefaultRouter 4 | from rest_framework.permissions import AllowAny 5 | from drf_yasg.views import get_schema_view 6 | from drf_yasg import openapi 7 | from django.conf import settings 8 | 9 | from main.views import ( 10 | AdmissionViewSet, 11 | ClinicianAssignedPatientsViewSet, 12 | ClinicianStatAPIView, 13 | PatientAdmissionInfoViewSet, 14 | PatientPrescriptionInfoViewSet, 15 | PatientReferralInfoViewSet, 16 | PatientViewSet, 17 | PatientsByName, 18 | PrescriptionViewSet, 19 | ReceptionistPatientView, 20 | ReceptionistStatAPIView, 21 | ReferralViewSet, 22 | UserViewSet, 23 | WardViewSet, 24 | ClinicianInfoViewSet, 25 | ) 26 | 27 | router = DefaultRouter() 28 | router.register(r"patients", PatientViewSet) 29 | router.register(r"users", UserViewSet) 30 | router.register(r"referrals", ReferralViewSet) 31 | router.register(r"prescriptions", PrescriptionViewSet) 32 | router.register(r"wards", WardViewSet) 33 | router.register(r"admissions", AdmissionViewSet), 34 | router.register(r"clinicians", ClinicianInfoViewSet, "clinician") 35 | router.register(r"receptionist-patients", ReceptionistPatientView, "registered-patient") 36 | router.register( 37 | r"assigned-patients", ClinicianAssignedPatientsViewSet, basename="assigned-patient" 38 | ) 39 | router.register( 40 | r"admissions-info", PatientAdmissionInfoViewSet, basename="admission-info" 41 | ) 42 | router.register( 43 | r"prescriptions-info", PatientPrescriptionInfoViewSet, basename="prescription-info" 44 | ) 45 | router.register(r"referrals-info", PatientReferralInfoViewSet, basename="referral-info") 46 | 47 | schema_view = get_schema_view( 48 | openapi.Info( 49 | title="LiveUp API", 50 | default_version="v1", 51 | description="RESTFUL API for LiveUp", 52 | contact=openapi.Contact(email=settings.FROM_EMAIL), 53 | license=openapi.License(name="BSD License"), 54 | ), 55 | public=True, 56 | permission_classes=[AllowAny], 57 | ) 58 | 59 | urlpatterns = [ 60 | path("browsable-api-auth/", include("rest_framework.urls")), 61 | path("auth/", include("dj_rest_auth.urls")), 62 | path("receptionists/stats/", ReceptionistStatAPIView.as_view()), 63 | path("medics/stats/", ClinicianStatAPIView.as_view()), 64 | path("patient/by-name/", PatientsByName.as_view()), 65 | path("", include(router.urls)), 66 | re_path( 67 | r"^swagger(?P\.json|\.yaml)$", 68 | schema_view.without_ui(cache_timeout=0), 69 | name="schema-json", 70 | ), 71 | re_path( 72 | r"^swagger/$", 73 | schema_view.with_ui("swagger", cache_timeout=0), 74 | name="schema-swagger-ui", 75 | ), 76 | re_path( 77 | r"^redoc/$", schema_view.with_ui("redoc", cache_timeout=0), name="schema-redoc" 78 | ), 79 | ] 80 | -------------------------------------------------------------------------------- /main/validators.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ValidationError 2 | from django.utils.translation import gettext_lazy as _ 3 | from django.utils import timezone 4 | 5 | 6 | def validate_dob(value): 7 | if value > timezone.now().date(): 8 | raise ValidationError( 9 | _(f"Date should be less than or equal to {timezone.now().date()}") 10 | ) 11 | -------------------------------------------------------------------------------- /main/view_helpers.py: -------------------------------------------------------------------------------- 1 | from django.utils import timezone 2 | from .models import Referral, Patient, Prescription, Admission 3 | 4 | 5 | def generate_receptionist_stats(request): 6 | today = timezone.now().date() 7 | 8 | referrals = Referral.objects.all().count() 9 | referrals_by_user = Referral.objects.filter(created_by=request.user).count() 10 | referrals_today_by_user = Referral.objects.filter( 11 | created_by=request.user, created_at__date=today 12 | ).count() 13 | 14 | patients = Patient.objects.all().count() 15 | patients_by_user = Patient.objects.filter(created_by=request.user).count() 16 | patients_today_by_user = Patient.objects.filter( 17 | created_by=request.user, created_at__date=today 18 | ).count() 19 | 20 | stats = { 21 | "referrals": referrals, 22 | "referrals_by_user": referrals_by_user, 23 | "referrals_today_by_user": referrals_today_by_user, 24 | "patients": patients, 25 | "patients_by_user": patients_by_user, 26 | "patients_today_by_user": patients_today_by_user, 27 | } 28 | return stats 29 | 30 | 31 | def generate_clinician_stats(request): 32 | today = timezone.now().date() 33 | 34 | referrals = Referral.objects.all().count() 35 | referrals_to_user = Referral.objects.filter(doctor=request.user).count() 36 | referrals_today_to_user = Referral.objects.filter( 37 | doctor=request.user, created_at__date=today 38 | ).count() 39 | 40 | admissions = Admission.objects.all().count() 41 | admissions_by_user = Admission.objects.filter(created_by=request.user).count() 42 | admissions_today_by_user = Admission.objects.filter( 43 | created_by=request.user, created_at__date=today 44 | ).count() 45 | 46 | prescriptions = Prescription.objects.all().count() 47 | prescriptions_by_user = Prescription.objects.filter(created_by=request.user).count() 48 | prescriptions_today_by_user = Prescription.objects.filter( 49 | created_by=request.user, created_at__date=today 50 | ).count() 51 | 52 | stats = { 53 | "referrals": referrals, 54 | "referrals_to_user": referrals_to_user, 55 | "referrals_today_to_user": referrals_today_to_user, 56 | "admissions": admissions, 57 | "admissions_by_user": admissions_by_user, 58 | "admissions_today_by_user": admissions_today_by_user, 59 | "prescriptions": prescriptions, 60 | "prescriptions_by_user": prescriptions_by_user, 61 | "prescriptions_today_by_user": prescriptions_today_by_user, 62 | } 63 | return stats 64 | -------------------------------------------------------------------------------- /main/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets 2 | from rest_framework.views import APIView 3 | from rest_framework.response import Response 4 | from rest_framework.permissions import IsAuthenticated 5 | from django.utils import timezone 6 | from rest_framework import generics 7 | 8 | from main.choices import DOCTOR, NURSE, STUDENT_CLINICIAN 9 | 10 | from main.models import Admission, Patient, Prescription, Referral, User, Ward 11 | from main.view_helpers import generate_clinician_stats, generate_receptionist_stats 12 | from .serializers import ( 13 | AdmissionNestedSerializer, 14 | AdmissionSerializer, 15 | PatientSerializer, 16 | PrescriptionNestedSerializer, 17 | PrescriptionSerializer, 18 | ReferralNestederializer, 19 | ReferralSerializer, 20 | UserSerializer, 21 | WardSerializer, 22 | ) 23 | from .permissions import IsNurse, IsReceptionist, IsDoctor, IsStudent_Clinician 24 | 25 | 26 | class PatientViewSet(viewsets.ModelViewSet): 27 | """List, create, retreive and destroy operations for a patient""" 28 | 29 | serializer_class = PatientSerializer 30 | queryset = Patient.objects.all() 31 | permission_classes = [IsReceptionist | IsDoctor] 32 | 33 | def perform_create(self, serializer): 34 | serializer.save(created_by=self.request.user) 35 | 36 | def perform_update(self, serializer): 37 | serializer.save(updated_by=self.request.user, updated_at=timezone.now()) 38 | 39 | 40 | class UserViewSet(viewsets.ReadOnlyModelViewSet): 41 | """List, create, retreive and destroy operations for a user""" 42 | 43 | serializer_class = UserSerializer 44 | queryset = User.objects.all().order_by("-id") 45 | permission_classes = [IsAuthenticated] 46 | 47 | 48 | class ReceptionistPatientView(viewsets.ReadOnlyModelViewSet): 49 | """Get patients registered by a particular receptionist""" 50 | 51 | serializer_class = PatientSerializer 52 | permission_classes = [IsReceptionist] 53 | 54 | def get_queryset(self): 55 | user = self.request.user 56 | return Patient.objects.filter(created_by=user) 57 | 58 | 59 | class ReferralViewSet(viewsets.ModelViewSet): 60 | """ 61 | List, create, retreive and destroy 62 | operations for a patient referred to a clinician 63 | """ 64 | 65 | serializer_class = ReferralSerializer 66 | permission_classes = [IsAuthenticated] 67 | queryset = Referral.objects.all() 68 | 69 | def perform_create(self, serializer): 70 | serializer.save(created_by=self.request.user) 71 | 72 | def perform_update(self, serializer): 73 | serializer.save(updated_by=self.request.user, updated_at=timezone.now()) 74 | 75 | 76 | class ClinicianAssignedPatientsViewSet(viewsets.ReadOnlyModelViewSet): 77 | """Get patients assigned to a particular clinician""" 78 | 79 | serializer_class = ReferralSerializer 80 | permission_classes = [IsDoctor | IsNurse | IsStudent_Clinician] 81 | 82 | def get_queryset(self): 83 | return Referral.objects.filter(doctor=self.request.user) 84 | 85 | 86 | class PrescriptionViewSet(viewsets.ModelViewSet): 87 | """ 88 | List, create, retreive and destroy 89 | operations for a patient's prescription 90 | made by a clinician 91 | """ 92 | 93 | serializer_class = PrescriptionSerializer 94 | queryset = Prescription.objects.all() 95 | 96 | def get_permissions(self): 97 | if self.action == "destroy": 98 | permission_classes = [IsDoctor] 99 | else: 100 | permission_classes = [IsDoctor | IsNurse | IsStudent_Clinician] 101 | return [permission() for permission in permission_classes] 102 | 103 | def perform_create(self, serializer): 104 | serializer.save(created_by=self.request.user) 105 | 106 | def perform_update(self, serializer): 107 | serializer.save(updated_at=timezone.now(), updated_by=self.request.user) 108 | 109 | 110 | class AdmissionViewSet(viewsets.ModelViewSet): 111 | """ 112 | List, create, retreive and destroy operations for an admitted patient to 113 | a particular ward 114 | """ 115 | 116 | serializer_class = AdmissionSerializer 117 | queryset = Admission.objects.all() 118 | 119 | def get_permissions(self): 120 | if self.action == "destroy": 121 | permission_classes = [IsDoctor] 122 | else: 123 | permission_classes = [IsDoctor | IsNurse | IsStudent_Clinician] 124 | return [permission() for permission in permission_classes] 125 | 126 | def perform_create(self, serializer): 127 | serializer.save(created_by=self.request.user) 128 | 129 | def perform_update(self, serializer): 130 | serializer.save(updated_at=timezone.now(), updated_by=self.request.user) 131 | 132 | 133 | class WardViewSet(viewsets.ReadOnlyModelViewSet): 134 | """ 135 | List, create, retreive and destroy operations for a ward 136 | """ 137 | 138 | serializer_class = WardSerializer 139 | queryset = Ward.objects.all() 140 | permission_classes = [IsAuthenticated] 141 | pagination_class = None 142 | 143 | 144 | class ReceptionistStatAPIView(APIView): 145 | """ 146 | Fetch statistics for a particular receptionist 147 | e.g Number of patients registered today 148 | """ 149 | 150 | permission_classes = [IsReceptionist] 151 | 152 | def get(self, request, format=None): 153 | stats = generate_receptionist_stats(request) 154 | return Response(stats) 155 | 156 | 157 | class ClinicianStatAPIView(APIView): 158 | """ 159 | Fetch statistics for a particular clinician 160 | e.g Number of patients admitted today 161 | """ 162 | 163 | permission_classes = [IsDoctor | IsNurse | IsStudent_Clinician] 164 | 165 | def get(self, request, format=None): 166 | stats = generate_clinician_stats(request) 167 | return Response(stats) 168 | 169 | 170 | class ClinicianInfoViewSet(viewsets.ReadOnlyModelViewSet): 171 | """ 172 | Fetch users who are only clinicians 173 | A clinician is either a Doctor, Nurse 174 | or Student Clinician 175 | """ 176 | 177 | serializer_class = UserSerializer 178 | permission_classes = [IsAuthenticated] 179 | pagination_class = None 180 | 181 | def get_queryset(self): 182 | return User.objects.filter(role__in=[DOCTOR, NURSE, STUDENT_CLINICIAN]) 183 | 184 | 185 | class PatientAdmissionInfoViewSet(viewsets.ReadOnlyModelViewSet): 186 | """ 187 | Fetch admission data for a particular patient 188 | A patient id is required as query param 189 | Else fetch all admission data for all patients 190 | """ 191 | 192 | permission_classes = [IsDoctor | IsNurse | IsStudent_Clinician] 193 | serializer_class = AdmissionNestedSerializer 194 | pagination_class = None 195 | 196 | def get_queryset(self): 197 | patient_id = self.request.query_params.get("patient_id") 198 | 199 | if not patient_id: 200 | return Admission.objects.all() 201 | 202 | return Admission.objects.filter(patient=patient_id) 203 | 204 | 205 | class PatientPrescriptionInfoViewSet(viewsets.ReadOnlyModelViewSet): 206 | """ 207 | Fetch prescription data for a particular patient 208 | A patient id is required as query param 209 | Else fetch all prescription data for all patients 210 | """ 211 | 212 | serializer_class = PrescriptionNestedSerializer 213 | permission_classes = [IsDoctor | IsNurse | IsStudent_Clinician] 214 | pagination_class = None 215 | 216 | def get_queryset(self): 217 | patient_id = self.request.query_params.get("patient_id") 218 | 219 | if not patient_id: 220 | return Prescription.objects.all() 221 | 222 | return Prescription.objects.filter(patient=patient_id) 223 | 224 | 225 | class PatientReferralInfoViewSet(viewsets.ReadOnlyModelViewSet): 226 | """ 227 | View referral history of a patient or all patients 228 | Add patient_id as query param to get history for a patient 229 | """ 230 | 231 | serializer_class = ReferralNestederializer 232 | permission_classes = [IsDoctor | IsNurse | IsStudent_Clinician | IsReceptionist] 233 | pagination_class = None 234 | 235 | def get_queryset(self): 236 | patient_id = self.request.query_params.get("patient_id") 237 | 238 | if not patient_id: 239 | return Referral.objects.all() 240 | 241 | return Referral.objects.filter(patient=patient_id) 242 | 243 | 244 | class PatientsByName(generics.ListAPIView): 245 | """ 246 | Fetch a patient's data using their name 247 | patient_name query param is required 248 | """ 249 | 250 | serializer_class = PatientSerializer 251 | permission_classes = [IsAuthenticated] 252 | pagination_class = None 253 | 254 | def get_queryset(self): 255 | patient_name = self.request.query_params.get("patient_name") 256 | queryset = Patient.objects.filter(patient_name__iexact=patient_name) 257 | if not queryset or len(queryset) == 0: 258 | queryset = Patient.objects.filter(patient_name__icontains=patient_name) 259 | return queryset 260 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'liveup.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KNehe/liveup_api/992628afebd0bd4f956f11e6ba935b3362868f21/requirements.txt -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.9.10 -------------------------------------------------------------------------------- /templates/password_reset_email.html: -------------------------------------------------------------------------------- 1 | {% load i18n %}{% autoescape off %} 2 | {% blocktranslate %}You're receiving this email because you requested a password reset for your user account at LiveUp.{% endblocktranslate %} 3 | 4 | {% translate "Please go to the following page and choose a new password:" %} 5 | {% block reset_link %} 6 | {{ protocol }}://localhost:3000/password_reset_confirm/{{uid}}/{{token}} 7 | {% endblock %} 8 | {% translate 'Your username, in case you’ve forgotten:' %} {{ user.get_username }} 9 | 10 | {% translate "Thanks for using our site!" %} 11 | 12 | {% blocktranslate %}The LiveUp team{% endblocktranslate %} 13 | 14 | {% endautoescape %} 15 | --------------------------------------------------------------------------------