diff --git a/.gitignore b/.gitignore index 351bcd0..d13529d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ obc_db.sqlite3 dev_settings.py settings.py +.venv + # C extensions *.so @@ -196,4 +198,6 @@ hosts.cfg deploy_key.pub deploy_key_ed.pub -src/currency/** \ No newline at end of file +src/currency/** +src/intrepid/prod_settings.py +.aider* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d73aae4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +## 2023-11-04 +* Typo fixes +* Remove all links to create new account/hide the backend dashboard + +## 2023-10-28 +* Fixes to Thoth sync and orphaned works +* Fixes to stats on dashboard \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..49fccc4 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +Copyright © 2023 Open Book Collective + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index af37952..e01444c 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,57 @@ Intrepid makes use of Fluid Permissions that lets you pair a view with a group a one = Site.objects.all()[0] one.name = 'Open Book Collective' one.save() + +# Model Translations +This is current a WIP. Here are instructions for setting up model translations. + +## Models +Limited, currently, to the SiteText model. This will need to be expanded to cover all texts that appear on the front end. + +## Languages +Initially I've enabled English and German, this can be extended in the future. + +## Fallback +English is the designated fallback language, it will display when a user has not specified another language. Text must also be inserted in English before other languanges. + +## Setup +To setup model translations follow these instructions: + +### Requirements +Modeltranslations is now a requirement so ensure you run: + +```shell +pip3 install -r requirements.txt +``` + +### Settings +Add the following to settings.py + +```python + +INSTALLED_APPS = [ + 'modeltranslation', + ... +] + +# LANGUAGES +LANGUAGE_CODE = 'en' +USE_I18N = True +gettext = lambda s: s +LANGUAGES = ( + ('de', gettext('German')), + ('en', gettext('English')), +) +MODELTRANSLATION_DEFAULT_LANGUAGE = 'en' +MODELTRANSLATION_LANGUAGES = ('en', 'de') +MODELTRANSLATION_FALLBACK_LANGUAGES = ('en',) +MODELTRANSLATION_PREPOPULATE_LANGUAGE = 'en' +``` + +### Migrations +Once this is added to your settings.py run the following: + +```shell +python3 src/manage.py migrate +python src/manage.py update_translation_fields +``` diff --git a/docker-compose-dev.yaml b/docker-compose-dev.yaml new file mode 100644 index 0000000..3c279ae --- /dev/null +++ b/docker-compose-dev.yaml @@ -0,0 +1,38 @@ +services: + postgres: + image: postgres:12.2-alpine + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} + POSTGRES_DB: postgres + PGDATA: /data/postgres + volumes: + - postgres:/data/postgres + ports: + - "5432:5432" + networks: + - postgres + restart: unless-stopped + + pgadmin: + image: dpage/pgadmin4:4.21 + environment: + PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org} + PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin} + volumes: + - pgadmin:/root/.pgadmin + ports: + - "5050:80" + networks: + - postgres + depends_on: + - postgres + restart: unless-stopped + +networks: + postgres: + driver: bridge + +volumes: + postgres: + pgadmin: diff --git a/fixtures/site_text_3.json b/fixtures/site_text_3.json new file mode 100644 index 0000000..e96ff9c --- /dev/null +++ b/fixtures/site_text_3.json @@ -0,0 +1,6612 @@ +[ + { + "model": "cms.sitetext", + "pk": 67, + "fields": { + "key": "activate_new_account", + "body": "Activate a new account", + "help_text": "Text of link to activate new account.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 68, + "fields": { + "key": "activate_account", + "body": "Activate account", + "help_text": "Text of button to activate an account.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 69, + "fields": { + "key": "sign_in_to_account", + "body": "Sign into your account", + "help_text": "Text of heading to sign in.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 70, + "fields": { + "key": "register", + "body": "Register", + "help_text": "Alt text and other locations that display 'Register'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 71, + "fields": { + "key": "login", + "body": "Login", + "help_text": "Alt text and other locations that display 'Login'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 72, + "fields": { + "key": "2fa_calling", + "body": "We are calling your phone right now, please enter the digits you hear.", + "help_text": "Text to be displayed on the 2FA calling page.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 73, + "fields": { + "key": "2fa_text", + "body": "We sent you a text message, please enter the tokens we sent.", + "help_text": "Text to be displayed on the 2FA texting page.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 74, + "fields": { + "key": "2fa_token", + "body": "Please enter the tokens generated by your token generator.", + "help_text": "Text to be displayed on the 2FA token page.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 75, + "fields": { + "key": "2fa_backup", + "body": "Use this form for entering backup tokens for logging in. These tokens have been generated for you to print and keep safe. Please enter one of these backup tokens to login to your account.", + "help_text": "Text to be displayed on the 2FA backup page.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 76, + "fields": { + "key": "2fa_backup_phone", + "body": "Or, alternatively, use one of your backup phones:", + "help_text": "Text to be displayed on the 2FA page to use a backup phone.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 77, + "fields": { + "key": "2fa_last_resort", + "body": "As a last resort, you can use a backup token:", + "help_text": "Text to be displayed on the 2FA page to use a last resort backup token.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 78, + "fields": { + "key": "2fa_last_resort_button", + "body": "Use Backup Token", + "help_text": "Text to be displayed on the 2FA page button to use a last resort backup token.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 79, + "fields": { + "key": "forgot_your_password", + "body": "Forgot your password?", + "help_text": "Text of links to recover password page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 80, + "fields": { + "key": "profile", + "body": "Profile", + "help_text": "Text of heading on the profile page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 81, + "fields": { + "key": "banding_setup", + "body": "Banding Setup", + "help_text": "Text of heading that reads 'Banding Setup'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 82, + "fields": { + "key": "unspecified", + "body": "Unspecified", + "help_text": "Text of dropdown option 'Unspecified'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 83, + "fields": { + "key": "save_to_profile", + "body": "Save to Profile", + "help_text": "Text of button that reads 'Save to Profile'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 84, + "fields": { + "key": "institution", + "body": "Institution", + "help_text": "Text of label for institution field", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 85, + "fields": { + "key": "manage_accounts", + "body": "Manage Accounts", + "help_text": "Text for the heading 'Manage Accounts'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 86, + "fields": { + "key": "username", + "body": "Username", + "help_text": "Text for the table heading 'Username'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 87, + "fields": { + "key": "email", + "body": "Email Address", + "help_text": "Text for the table heading 'Email Address'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 88, + "fields": { + "key": "first_name", + "body": "First Name", + "help_text": "Text for the table heading 'First Name'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 89, + "fields": { + "key": "last_name", + "body": "Last Name", + "help_text": "Text for the table heading 'Last Name'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 90, + "fields": { + "key": "affiliation", + "body": "Affiliation", + "help_text": "Text for the table heading 'Affiliation'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 91, + "fields": { + "key": "permissions", + "body": "Permissions", + "help_text": "Text for the heading 'Permissions'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 92, + "fields": { + "key": "manager", + "body": "Manager", + "help_text": "Text for the breadcrumb 'Manager'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 93, + "fields": { + "key": "permission_management", + "body": "Permission Management", + "help_text": "Text for the heading 'Permission Management'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 94, + "fields": { + "key": "permission_management_help", + "body": "Manage permissions across the system by granting groups access to views.", + "help_text": "Text for the help for the permission management page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 95, + "fields": { + "key": "save", + "body": "Save", + "help_text": "Text that says 'Save'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 96, + "fields": { + "key": "view_name", + "body": "View Name", + "help_text": "Text that says 'View Name'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 97, + "fields": { + "key": "full_name", + "body": "Full Name", + "help_text": "Text that says 'Full Name'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 98, + "fields": { + "key": "2fa_enabled", + "body": "Two Factor Authentication Enabled", + "help_text": "Text that says 'Two Factor Authentication Enabled'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 99, + "fields": { + "key": "is_enabled", + "body": "Enabled", + "help_text": "Text that says 'Enabled'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 100, + "fields": { + "key": "disabled", + "body": "Disabled", + "help_text": "Text that says 'Disabled'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 101, + "fields": { + "key": "bandings", + "body": "Bandings", + "help_text": "Text that says 'Bandings'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 102, + "fields": { + "key": "configure_bandings", + "body": "Configure Bandings", + "help_text": "Text that says 'Configure Bandings'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 103, + "fields": { + "key": "image_of", + "body": "Image of", + "help_text": "Alt text of images that says 'Image of'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 104, + "fields": { + "key": "edit_profile", + "body": "Edit Profile", + "help_text": "Link text that reads 'Edit Profile'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 105, + "fields": { + "key": "manage_account_security", + "body": "Manage Account Security", + "help_text": "Link text that reads 'Manage Account Security'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 106, + "fields": { + "key": "register_a_new_account", + "body": "Register a New Account", + "help_text": "Heading text that reads 'Register a New Account'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 107, + "fields": { + "key": "account_email", + "body": "Please note that after initial account creation an email will be sent to the address you supply, please click the link in this email to activate your account. You will not be able to login until you have activated your account.", + "help_text": "Help text re. emails after new account creation", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 108, + "fields": { + "key": "create_account", + "body": "Create Account", + "help_text": "Text of a button that reads 'Create Account'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 109, + "fields": { + "key": "site_backups", + "body": "Site Backups", + "help_text": "Headings that read 'Site Backups'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 110, + "fields": { + "key": "sql_backups", + "body": "SQL Backups", + "help_text": "Headings that read 'SQL Backups'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 111, + "fields": { + "key": "date_modified", + "body": "Date Modified", + "help_text": "Table heading that read 'Date Modified'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 112, + "fields": { + "key": "version_id", + "body": "Version ID", + "help_text": "Table heading that read 'Version ID'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 113, + "fields": { + "key": "size", + "body": "Size", + "help_text": "Table heading that read 'Size' (referring to file size)", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 114, + "fields": { + "key": "download", + "body": "Download", + "help_text": "Table heading that read 'Download'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 115, + "fields": { + "key": "data_file_backup", + "body": "Data File Backup", + "help_text": "Heading that reads 'Data File Backup'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 116, + "fields": { + "key": "thoth_backup", + "body": "Thoth Backups", + "help_text": "Heading that reads 'Thoth Backups'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 117, + "fields": { + "key": "home", + "body": "Home", + "help_text": "Breadcrumb that reads 'Home'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 118, + "fields": { + "key": "logout", + "body": "Logout", + "help_text": "Menu option that reads 'Logout'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 119, + "fields": { + "key": "confirm_delete", + "body": "Are you sure you want to delete this object?", + "help_text": "Text of confirm deletion popup", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 120, + "fields": { + "key": "dashboard_caps", + "body": "DASHBOARD", + "help_text": "A menu heading for the dashboard, in capitals", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 121, + "fields": { + "key": "dashboard", + "body": "Dashboard", + "help_text": "A menu heading for the dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 122, + "fields": { + "key": "my_information", + "body": "My Information", + "help_text": "A menu heading for 'My Information'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 123, + "fields": { + "key": "my_saved_quotes", + "body": "My Saved Quotes", + "help_text": "A menu heading for 'My Saved Quotes'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 124, + "fields": { + "key": "my_searches", + "body": "My Searches/Feed", + "help_text": "A menu heading for 'My Searches/Feed'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 125, + "fields": { + "key": "payment_processing", + "body": "Payment Processing", + "help_text": "A menu heading for 'Payment Processing'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 126, + "fields": { + "key": "initiative_manager", + "body": "Initiative Manager", + "help_text": "A menu heading for 'Initiative Manager'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 127, + "fields": { + "key": "my_initiatives", + "body": "My Initiatives", + "help_text": "A menu heading for 'My Initiatives'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 128, + "fields": { + "key": "signups", + "body": "Signups", + "help_text": "A menu heading for 'Signups'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 129, + "fields": { + "key": "price_debugger", + "body": "Price Debugger", + "help_text": "A menu heading for 'Price Debugger'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 130, + "fields": { + "key": "staff", + "body": "Staff", + "help_text": "A menu heading for 'Staff'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 131, + "fields": { + "key": "order_management", + "body": "Order Management", + "help_text": "A menu heading for 'Order Management'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 132, + "fields": { + "key": "email_logs", + "body": "Email Logs", + "help_text": "A menu heading for 'Email Logs'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 133, + "fields": { + "key": "payment_processors", + "body": "Payment Processors", + "help_text": "A menu heading for 'Payment Processors'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 134, + "fields": { + "key": "content_management", + "body": "Content Management", + "help_text": "A menu heading for 'Content Management'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 135, + "fields": { + "key": "all_pages", + "body": "All Pages", + "help_text": "A menu heading for 'All Pages'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 136, + "fields": { + "key": "site_wide_news", + "body": "Site Wide News", + "help_text": "A menu heading for 'Site Wide News'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 137, + "fields": { + "key": "page_update_feed", + "body": "Page Update Feed", + "help_text": "A menu heading for 'Page Update Feed'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 138, + "fields": { + "key": "who_we_are", + "body": "Who We Are", + "help_text": "A menu heading for 'who we are'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 139, + "fields": { + "key": "homepage_quotes", + "body": "Homepage Quotes", + "help_text": "A menu heading for 'Homepage Quotes'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 140, + "fields": { + "key": "platform_management", + "body": "Platform Management", + "help_text": "A menu heading for 'Platform Management'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 141, + "fields": { + "key": "add_initiative", + "body": "Add Initiative", + "help_text": "A menu heading for 'Add Initiative'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 142, + "fields": { + "key": "standards_attestation", + "body": "Standards Attestation", + "help_text": "A menu heading for 'Standards Attestation'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 143, + "fields": { + "key": "form_elements", + "body": "Form Elements", + "help_text": "A menu heading for 'Form Elements'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 144, + "fields": { + "key": "banding_vocabularies", + "body": "Banding Vocabularies", + "help_text": "A menu heading for 'Banding Vocabularies'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 145, + "fields": { + "key": "view_permissions", + "body": "View Permissions", + "help_text": "A menu heading for 'View Permissions'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 146, + "fields": { + "key": "subjects", + "body": "Subjects", + "help_text": "A menu heading for 'Subjects'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 147, + "fields": { + "key": "backups", + "body": "Backups", + "help_text": "A menu heading for 'Backups'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 148, + "fields": { + "key": "navbar", + "body": "Navbar", + "help_text": "A menu heading for 'Navbar'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 149, + "fields": { + "key": "toggle_navigation", + "body": "Toggle Navigation", + "help_text": "A menu heading for 'Toggle Navigation'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 150, + "fields": { + "key": "quotations", + "body": "Quotations", + "help_text": "A menu heading for 'Quotations'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 151, + "fields": { + "key": "open_book_collective", + "body": "Open Book Collective", + "help_text": "A page heading with the name of the site", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 152, + "fields": { + "key": "biography_of", + "body": "Biography of", + "help_text": "An alt-text for images that reads 'Biography of'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 153, + "fields": { + "key": "news", + "body": "News", + "help_text": "News alt-text for images", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 154, + "fields": { + "key": "open_quote", + "body": "Open quotation mark", + "help_text": "Alt-text for images that reads 'Open quotation mark'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 155, + "fields": { + "key": "close_quote", + "body": "Close quotation mark", + "help_text": "Alt-text for images that reads 'Close quotation mark'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 156, + "fields": { + "key": "what_we_stand_for", + "body": "What We Stand For", + "help_text": "What We Stand For text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 157, + "fields": { + "key": "membership", + "body": "Membership", + "help_text": "Membership text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 158, + "fields": { + "key": "governance", + "body": "Governance", + "help_text": "Governance text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 159, + "fields": { + "key": "how_it_works", + "body": "How It Works", + "help_text": "How It Works text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 160, + "fields": { + "key": "funding_and_fees", + "body": "Funding and Fees", + "help_text": "Funding and Fees text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 161, + "fields": { + "key": "supporters", + "body": "Supporters", + "help_text": "Supporters text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 162, + "fields": { + "key": "platform", + "body": "Platform", + "help_text": "Platform text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 163, + "fields": { + "key": "initiatives", + "body": "Initiatives", + "help_text": "Initiatives text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 164, + "fields": { + "key": "catalogue", + "body": "Catalogue", + "help_text": "Catalogue text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 165, + "fields": { + "key": "twitter", + "body": "Twitter/X", + "help_text": "Twitter text in footer", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 166, + "fields": { + "key": "mastodon", + "body": "Mastodon", + "help_text": "Mastodon text in footer", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 167, + "fields": { + "key": "email_contact", + "body": "Email", + "help_text": "Email text in footer", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 168, + "fields": { + "key": "open_book_collective_abbreviation", + "body": "OBC", + "help_text": "Abbreviation for Open Book Collective", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 169, + "fields": { + "key": "terms_of_use", + "body": "Terms of Use", + "help_text": "Footer link for terms of use", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 170, + "fields": { + "key": "privacy_policy", + "body": "Privacy Policy", + "help_text": "Privacy policy link for terms of use", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 171, + "fields": { + "key": "accessibility", + "body": "Accessibility", + "help_text": "Accessibility link for terms of use", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 172, + "fields": { + "key": "watch_video", + "body": "Watch video", + "help_text": "Watch video link on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 173, + "fields": { + "key": "stronger_oa", + "body": "Stronger OA Communities", + "help_text": "Stronger OA Communities text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 174, + "fields": { + "key": "build_your_subscription", + "body": "Build your Subscription", + "help_text": "Build Your Subscription button on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 175, + "fields": { + "key": "sustainable_funding_model", + "body": "Sustainable Funding Model", + "help_text": "Sustainable Funding Model text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 176, + "fields": { + "key": "search_the_catalogue", + "body": "Search the catalogue", + "help_text": "Search the catalogue link on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 177, + "fields": { + "key": "robust_infrastructure", + "body": "Robust Infrastructure", + "help_text": "Robust Infrastructure text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 178, + "fields": { + "key": "apply_for_membership", + "body": "Apply for Membership", + "help_text": "Apply for Membership link on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 179, + "fields": { + "key": "diversity_in_oa", + "body": "Diversity in OA", + "help_text": "Diversity in OA text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 180, + "fields": { + "key": "libraries", + "body": "Libraries", + "help_text": "Libraries text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 181, + "fields": { + "key": "authors_and_readers", + "body": "Authors and Readers", + "help_text": "Authors and Readers text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 182, + "fields": { + "key": "publishers", + "body": "Publishers", + "help_text": "Publishers text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 183, + "fields": { + "key": "a_news_item", + "body": "A News Item", + "help_text": "A News Item alt-text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 184, + "fields": { + "key": "funders", + "body": "Funders", + "help_text": "Funders header on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 185, + "fields": { + "key": "image_community", + "body": "An image representing community", + "help_text": "Community image alt-text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 186, + "fields": { + "key": "image_sustainability", + "body": "An image representing sustainability", + "help_text": "Sustainability image alt-text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 187, + "fields": { + "key": "image_infrastructure", + "body": "An image representing infrastructure", + "help_text": "Infrastructure image alt-text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 188, + "fields": { + "key": "image_diversity", + "body": "An image representing diversity", + "help_text": "Diversity image alt-text on homepage", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 189, + "fields": { + "key": "featured_books", + "body": "Featured Books", + "help_text": "Header that reads 'Featured Books'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 190, + "fields": { + "key": "news_updates", + "body": "News Updates", + "help_text": "Header that reads 'News Updates'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 191, + "fields": { + "key": "initiatives_subhead", + "body": "At the core of the Open Book Collective are our initiatives. These range from open-access book publishers to indexing services and directories. You can find out more about these initiatives here.", + "help_text": "Initiatives page subheading", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 192, + "fields": { + "key": "collections_and_collectives", + "body": "Collections and Collectives", + "help_text": "Alt-text on initiatives page that reads 'Collections and Collectives'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 193, + "fields": { + "key": "publishers_and_service_providers", + "body": "Publishers and Service Providers", + "help_text": "Text on initiatives page that reads 'Publishers and Service Providers'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 194, + "fields": { + "key": "your_quotes", + "body": "Your Quotes", + "help_text": "Text on nav page that reads 'Your Quotes'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 195, + "fields": { + "key": "more", + "body": "More", + "help_text": "Text on nav page that reads 'More'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 196, + "fields": { + "key": "updates_from_the_open_book_collective", + "body": "Updates from the Open Book Collective", + "help_text": "Text on news page that reads 'Updates from the Open Book Collective'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 197, + "fields": { + "key": "register_for_the_open_book_collective", + "body": "Register for the Open Book Collective", + "help_text": "Text on news page that reads 'Register for the Open Book Collective'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 198, + "fields": { + "key": "repeat_email", + "body": "Repeat email", + "help_text": "Placeholder form text that says 'repeat email'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 199, + "fields": { + "key": "password", + "body": "Password", + "help_text": "Placeholder form text that says 'password'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 200, + "fields": { + "key": "repeat_password", + "body": "Repeat password", + "help_text": "Placeholder form text that says 'Repeat password'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 201, + "fields": { + "key": "sign_up", + "body": "Sign up", + "help_text": "Placeholder form text that says 'Sign up'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 202, + "fields": { + "key": "who_we_are_subhead", + "body": "We are a team of librarians, publishers, publishing service providers and academics committed to collectively reimagining and reconfiguring the funding and circulation of open access monographs.", + "help_text": "Subheader on Who We Are page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 203, + "fields": { + "key": "board_of_stewards", + "body": "Board of Stewards", + "help_text": "Subheader for Board of Stewards", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 204, + "fields": { + "key": "view_biography", + "body": "View Biography", + "help_text": "Link text to View Biography", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 205, + "fields": { + "key": "membership_committee", + "body": "Membership Committee", + "help_text": "Subheader for Membership Committee", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 206, + "fields": { + "key": "management_team", + "body": "Management Team", + "help_text": "Subheader for Management Team", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 207, + "fields": { + "key": "secretariat", + "body": "Secretariat", + "help_text": "Subheader for Secretariat", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 208, + "fields": { + "key": "finance_committee", + "body": "Finance Committee", + "help_text": "Subheader for Finance Committee", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 209, + "fields": { + "key": "previous_board_members", + "body": "Previous Board Members", + "help_text": "Subheader for Previous Board Members", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 210, + "fields": { + "key": "find_out_more", + "body": "Find out more about the OBC and who is behind it.", + "help_text": "Explainer text on Who We Are mobile page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 211, + "fields": { + "key": "pricing_country", + "body": "Pricing on the page below is based on your detected or set country:", + "help_text": "Explanation re. pricing currency", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 212, + "fields": { + "key": "change_country", + "body": "Change your country.", + "help_text": "Button to change country", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 213, + "fields": { + "key": "build_your_quote", + "body": "Build Your Quote", + "help_text": "alt-text for images that reads 'Build Your Quote'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 214, + "fields": { + "key": "all_initiatives", + "body": "All Initiatives", + "help_text": "All Initiatives text for dropdown", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 215, + "fields": { + "key": "all_subjects", + "body": "All Subjects", + "help_text": "All Subjects text for dropdown", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 216, + "fields": { + "key": "recommended", + "body": "Recommended", + "help_text": "Recommended text for dropdown", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 217, + "fields": { + "key": "no_results", + "body": "No results to match your search", + "help_text": "No results text on packages page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 218, + "fields": { + "key": "more_information", + "body": "More Information", + "help_text": "Button in collective element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 219, + "fields": { + "key": "collective_signup_disabled", + "body": "Collective package signup is disabled.", + "help_text": "Message for when collective package signup is disabled", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 220, + "fields": { + "key": "learn_more", + "body": "Learn More", + "help_text": "Button in initiative element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 221, + "fields": { + "key": "edit_package", + "body": "Edit package", + "help_text": "Button for staff to edit package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 222, + "fields": { + "key": "initiative_only", + "body": "Initiative only", + "help_text": "Text in dropdown to support just a single initiative", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 223, + "fields": { + "key": "initiative_package_signup_disabled", + "body": "Initiative package signup is disabled.", + "help_text": "Text to use when initiative package signup is disabled", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 224, + "fields": { + "key": "list", + "body": "List", + "help_text": "The word 'List' for the heading on CMS management pages", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 225, + "fields": { + "key": "layout_preserve", + "body": "In order to preserve the layout on the landing page, initiatives should have either 1 page, 2 pages, or 5 pages", + "help_text": "A statement about the number of pages and preserving the layout", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 226, + "fields": { + "key": "title", + "body": "Title", + "help_text": "The word 'Title' for the heading on CMS management pages", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 227, + "fields": { + "key": "created", + "body": "Created", + "help_text": "The word 'Created' for the heading on CMS management pages", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 228, + "fields": { + "key": "last_updated_by", + "body": "Last Updated By", + "help_text": "The words 'Last Updated By' for the heading on CMS management pages", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 229, + "fields": { + "key": "display", + "body": "Display", + "help_text": "The word 'Display' for the heading on CMS management pages", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 230, + "fields": { + "key": "url", + "body": "URL", + "help_text": "The word 'URL' for the heading on CMS management pages", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 231, + "fields": { + "key": "edit", + "body": "Edit", + "help_text": "The word 'Edit' for the heading on CMS management pages", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 232, + "fields": { + "key": "diff", + "body": "Diff", + "help_text": "The word 'Diff' for the CMS diffing page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 233, + "fields": { + "key": "create", + "body": "Create", + "help_text": "The word 'Create' for the CMS edit/create page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 234, + "fields": { + "key": "manage_data", + "body": "You can manage the data for this page below. There are two save options available.", + "help_text": "Help text on the create/edit CMS page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 235, + "fields": { + "key": "save_page", + "body": "Save - creates a new version of your page but does not make it live.", + "help_text": "Help text on the create/edit CMS page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 236, + "fields": { + "key": "save_page_make_current", + "body": "Save and Make Current Version - creates a new version and also makes it the live version.", + "help_text": "Help text on the create/edit CMS page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 237, + "fields": { + "key": "can_control_versions", + "body": "You can control versions, and which of them is live, from the", + "help_text": "Help text on the create/edit CMS page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 238, + "fields": { + "key": "detail_page", + "body": "detail page", + "help_text": "Help text on the create/edit CMS page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 239, + "fields": { + "key": "institution_target", + "body": "Institution (Optional: targets this update at a specific institution)", + "help_text": "Help text on the create/edit CMS page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 240, + "fields": { + "key": "save_and_make_current", + "body": "Save and Make Current Version", + "help_text": "Help text on the create/edit CMS page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 241, + "fields": { + "key": "edit_homepage_quotation", + "body": "Edit homepage quotation", + "help_text": "Breadcrumb on the edit homepage quotation page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 242, + "fields": { + "key": "homepage_quotation_configuration", + "body": "Homepage Quotation Configuration", + "help_text": "Breadcrumb on the homepage quotation configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 243, + "fields": { + "key": "who_we_are_entry", + "body": "Who We Are entry", + "help_text": "Breadcrumb on the homepage quotation configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 244, + "fields": { + "key": "edit_site_text", + "body": "Edit Site Text:", + "help_text": "Explanatory text on the edit site text page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 245, + "fields": { + "key": "featured_thoth_titles", + "body": "Featured Thoth Titles", + "help_text": "Text on the Thoth titles page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 246, + "fields": { + "key": "thoth_display", + "body": "Note: these display as three per line, so multiples of three will look best.", + "help_text": "Text on the Thoth titles page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 247, + "fields": { + "key": "view_book", + "body": "View book", + "help_text": "Text used on Thoth titles page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 248, + "fields": { + "key": "delete_featured_title", + "body": "Delete this Featured Title", + "help_text": "Text used on Thoth titles page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 249, + "fields": { + "key": "no_featured_books", + "body": "No featured books", + "help_text": "Text used on Thoth titles page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 250, + "fields": { + "key": "fixed_pages_explainer", + "body": "Certain locations within the CMS are fixed (e.g. \"Terms and Conditions\"). This page allows you to map these entries to CMS entries.", + "help_text": "Explanatory text used on the Fixed Pages page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 251, + "fields": { + "key": "control_fixed_versions", + "body": "You can control versions, and which of them is live, from the", + "help_text": "Explanatory text used on the Fixed Pages page. Goes just before 'detail page'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 252, + "fields": { + "key": "homepage_quotation_configuration_explainer", + "body": "This page allows you to configure the display of quotations on the home page. You can add as many quotations as you like, but only the first four will show on the homepage.", + "help_text": "Explanatory text used on the Homepage Quotation Configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 253, + "fields": { + "key": "quote", + "body": "Quote", + "help_text": "Text used on the Homepage Quotation Configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 254, + "fields": { + "key": "person", + "body": "Person", + "help_text": "Text used on the Homepage Quotation Configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 255, + "fields": { + "key": "organization", + "body": "Organization", + "help_text": "Text used on the Homepage Quotation Configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 256, + "fields": { + "key": "no_quotations", + "body": "No quotations.", + "help_text": "Text used on the Homepage Quotation Configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 257, + "fields": { + "key": "details", + "body": "Details", + "help_text": "Text for use in headings and tables", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 258, + "fields": { + "key": "target_institution", + "body": "Target Institution", + "help_text": "Text for use in headings and tables", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 259, + "fields": { + "key": "sequence", + "body": "Sequence", + "help_text": "Text for use in headings and tables", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 260, + "fields": { + "key": "current_version", + "body": "Current version", + "help_text": "Text for use on page details page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 261, + "fields": { + "key": "view_current_version", + "body": "View Current Version", + "help_text": "Text for use on page details page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 262, + "fields": { + "key": "id", + "body": "ID", + "help_text": "Text for abbreviation of ID (identifier)", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 263, + "fields": { + "key": "created_by", + "body": "Created by", + "help_text": "Text for use in headings and tables (created by)", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 264, + "fields": { + "key": "view_version", + "body": "View version", + "help_text": "Text for use in headings and tables (view version)", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 265, + "fields": { + "key": "set_as_current_version", + "body": "Set as Current Version", + "help_text": "Text for use in headings and tables (set as current version)", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 266, + "fields": { + "key": "this_is_current", + "body": "This is the current version", + "help_text": "Text for use in headings and tables (set as current version)", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 267, + "fields": { + "key": "no_other_versions", + "body": "No other versions", + "help_text": "Text for use in headings and tables (no other versions)", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 268, + "fields": { + "key": "preview", + "body": "Preview", + "help_text": "Text for use in headings and tables (preview)", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 269, + "fields": { + "key": "not_found", + "body": "not found", + "help_text": "Text for use in headings and tables (not found)", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 270, + "fields": { + "key": "site_wide_news_config", + "body": "Site-Wide News Configuration", + "help_text": "Heading for the site-wide news configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 271, + "fields": { + "key": "site_wide_news_config_items", + "body": "Configures Site-Wide News Items", + "help_text": "Heading for the site-wide news configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 272, + "fields": { + "key": "page", + "body": "Page", + "help_text": "Heading for the site-wide news configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 273, + "fields": { + "key": "remove_from_homepage", + "body": "Remove from Homepage", + "help_text": "Button action text on the site-wide news configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 274, + "fields": { + "key": "date_time", + "body": "Date/Time", + "help_text": "Table text on the site-wide news configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 275, + "fields": { + "key": "add_to_homepage", + "body": "Add to Homepage", + "help_text": "Table text on the site-wide news configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 276, + "fields": { + "key": "update_feed", + "body": "Update Feed", + "help_text": "Header text on the update feed page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 277, + "fields": { + "key": "lists_updates_to_pages", + "body": "Lists updates to pages and versions.", + "help_text": "Header text on the update feed page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 278, + "fields": { + "key": "page_update", + "body": "Page/Update", + "help_text": "Table text on the update feed page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 279, + "fields": { + "key": "who_we_are_configuration", + "body": "Who We Are Configuration", + "help_text": "Header text on the Who We Are configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 280, + "fields": { + "key": "contacts_for_initiatives", + "body": "Contacts for your initiatives", + "help_text": "Header text on the contacts for initiatives page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 281, + "fields": { + "key": "contacts_for_initiatives_explainer", + "body": "Contacts for the initiative can be assigned to receive notifications on signups and on changes to access controls. Note that anybody listed here can use their access code to view signups and access controls for the listed initiative.", + "help_text": "Explainer text on the contacts for initiatives page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 282, + "fields": { + "key": "contacts_add_edit", + "body": "Add new and edit existing contacts.", + "help_text": "Subheader text on the contacts for initiatives page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 283, + "fields": { + "key": "view_quotes_in_progress", + "body": "View Quotes in Progress", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 284, + "fields": { + "key": "books_in_packages", + "body": "Books in Packages at OBC", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 285, + "fields": { + "key": "authors_from_institution", + "body": "Authors from Your Institution", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 286, + "fields": { + "key": "funded_presses", + "body": "Funded Presses at OBC", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 287, + "fields": { + "key": "books_indexed", + "body": "Books Indexed", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 288, + "fields": { + "key": "books_published", + "body": "Books Published by OBC Presses", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 289, + "fields": { + "key": "books_authorship_map", + "body": "Book Authorship Map", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 290, + "fields": { + "key": "collapse", + "body": "Collapse", + "help_text": "Alt text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 291, + "fields": { + "key": "disable_signup_note", + "body": "Note: Disabling all signups will overrule the individual options.", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 292, + "fields": { + "key": "status", + "body": "Status", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 293, + "fields": { + "key": "no_documents", + "body": "No Documents", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 294, + "fields": { + "key": "complete_order_form", + "body": "Complete Order Form", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 295, + "fields": { + "key": "change_current_country", + "body": "Change Current Country", + "help_text": "Text on update country element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 296, + "fields": { + "key": "we_have_detected_your_country_as", + "body": "We have detected your current country as:", + "help_text": "Text on update country element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 297, + "fields": { + "key": "update_country", + "body": "Update Country", + "help_text": "Text on update country element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 298, + "fields": { + "key": "delete_text", + "body": "Are you sure you want to delete this object? NB: No data can be recovered from this operation.", + "help_text": "Text on delete_check element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 299, + "fields": { + "key": "adding", + "body": "Adding", + "help_text": "Text on manage basket element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 300, + "fields": { + "key": "existing_quotations", + "body": "Existing Quotations", + "help_text": "Text on manage basket element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 301, + "fields": { + "key": "no_existing_quotations", + "body": "You do not have any existing quotations.", + "help_text": "Text on manage basket element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 302, + "fields": { + "key": "add_to", + "body": "Add to", + "help_text": "Text on manage basket element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 303, + "fields": { + "key": "create_new_quotation", + "body": "Create New Quotation", + "help_text": "Text on manage basket element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 304, + "fields": { + "key": "your_reference_number_is", + "body": "Your reference number is", + "help_text": "Text on order form element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 305, + "fields": { + "key": "please_quote_it", + "body": "Please quote it if you need to get in touch with us.", + "help_text": "Text on order form element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 306, + "fields": { + "key": "initiative_details", + "body": "Initiative Details", + "help_text": "Heading on Initiative Details page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 307, + "fields": { + "key": "expanded_initiative", + "body": "Expanded information of a given initiative including name and accounts associated with it.", + "help_text": "Helper text on Initiative Details page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 308, + "fields": { + "key": "basic_information", + "body": "Basic Information", + "help_text": "Helper text on Initiative Details page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 309, + "fields": { + "key": "description", + "body": "Description", + "help_text": "Helper text on Initiative Details page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 310, + "fields": { + "key": "accounts", + "body": "Accounts", + "help_text": "Header text on Initiative Details page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 311, + "fields": { + "key": "accounts_helper", + "body": "Below are the accounts linked to this Initiative. Linked accounts have permission to create and edit initiative pages and updates.", + "help_text": "Helper text on Initiative Details page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 312, + "fields": { + "key": "unlink", + "body": "Unlink", + "help_text": "Button text on Initiative Details page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 313, + "fields": { + "key": "edit_initiative", + "body": "Edit Initiative", + "help_text": "Header text on Edit Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 314, + "fields": { + "key": "initiative_packages", + "body": "Initiative Packages", + "help_text": "Header text on Edit Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 315, + "fields": { + "key": "packages_owned_by", + "body": "Packages Owned By", + "help_text": "Header text on Edit Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 316, + "fields": { + "key": "data_fields", + "body": "Data Fields", + "help_text": "Table text on Edit Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 317, + "fields": { + "key": "access_control", + "body": "Access Control", + "help_text": "Table text on Edit Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 318, + "fields": { + "key": "initiative_list", + "body": "Initiative List", + "help_text": "Header text on Edit Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 319, + "fields": { + "key": "initiative_list_helper", + "body": "View, edit, delete and create initiative objects.", + "help_text": "Header text on Edit Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 320, + "fields": { + "key": "initiatives_staff", + "body": "As a staff member you will see all Initiatives listed here.", + "help_text": "Helper text on User Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 321, + "fields": { + "key": "initiatives_manager", + "body": "A list of Initiatives for which you are a manager.", + "help_text": "Helper text on User Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 322, + "fields": { + "key": "managers", + "body": "Managers", + "help_text": "Helper text on User Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 323, + "fields": { + "key": "orders", + "body": "Orders", + "help_text": "Helper text on User Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 324, + "fields": { + "key": "invoicing", + "body": "Invoicing", + "help_text": "Helper text on User Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 325, + "fields": { + "key": "pages", + "body": "Pages", + "help_text": "Helper text on User Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 326, + "fields": { + "key": "updates", + "body": "Updates", + "help_text": "Helper text on User Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 327, + "fields": { + "key": "invoice_management", + "body": "Invoice Management", + "help_text": "Heading text on Invoice Management page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 328, + "fields": { + "key": "order_helper", + "body": "Listed here are the ordered packages that require invoicing. Note, these packages may not make up the whole order, but contain those that you are a payment processor for.", + "help_text": "Helper text on Invoice Management page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 329, + "fields": { + "key": "latest_status_change", + "body": "Latest Status Change", + "help_text": "Table text on Invoice Management page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 330, + "fields": { + "key": "change_status", + "body": "Change Status", + "help_text": "Table text on Invoice Management page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 331, + "fields": { + "key": "new", + "body": "New", + "help_text": "Option text on Invoice Management page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 332, + "fields": { + "key": "invoice_sent", + "body": "Invoice Sent", + "help_text": "Option text on Invoice Management page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 333, + "fields": { + "key": "invoice_paid", + "body": "Invoice Paid", + "help_text": "Option text on Invoice Management page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 334, + "fields": { + "key": "begin_processing_signup", + "body": "Begin Processing Signup", + "help_text": "Option text on Invoice Management page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 335, + "fields": { + "key": "no_status_updates", + "body": "No Status Updates", + "help_text": "Text on Invoice Management page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 336, + "fields": { + "key": "edit_payment_processor", + "body": "Edit Payment Processor", + "help_text": "Text on Edit Payment Processors page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 337, + "fields": { + "key": "add_payment_processor", + "body": "Add Payment Processor", + "help_text": "Text on Edit Payment Processors page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 338, + "fields": { + "key": "hold_ctrl", + "body": "Hold control (ctrl) to select multiple managers and countries.", + "help_text": "Text on Edit Payment Processors page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 339, + "fields": { + "key": "order_helper_invoices", + "body": "Listed below are the orders that contain a package for which you are listed as one of the payment processors for. You will be able to see the details of the order that relate to you including billing information and agreed pricing.", + "help_text": "Text on Order Invoices page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 340, + "fields": { + "key": "customer", + "body": "Customer", + "help_text": "Text on Order Invoices page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 341, + "fields": { + "key": "invoice_statuses", + "body": "Invoice Statuses", + "help_text": "Text on Order Invoices page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 342, + "fields": { + "key": "amount", + "body": "Amount", + "help_text": "Text on Order Invoices page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 343, + "fields": { + "key": "countries", + "body": "Countries", + "help_text": "Text on List Payment Processors page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 344, + "fields": { + "key": "form_elements_setup", + "body": "Form Elements Setup", + "help_text": "Breadcrumb text for form elements setup", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 345, + "fields": { + "key": "email_log_entries", + "body": "Email Log Entries", + "help_text": "Title for email log entries page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 346, + "fields": { + "key": "email_logs_description", + "body": "This page allows platform managers to view email logs.", + "help_text": "Description for email logs page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 347, + "fields": { + "key": "success", + "body": "Success", + "help_text": "Label for successful email status", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 348, + "fields": { + "key": "failed", + "body": "Failed", + "help_text": "Label for failed email status", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 349, + "fields": { + "key": "sending", + "body": "Sending", + "help_text": "Label for sending email status", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 350, + "fields": { + "key": "update_statuses", + "body": "Update Statuses", + "help_text": "Button text for updating email statuses", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 351, + "fields": { + "key": "to", + "body": "To", + "help_text": "Table heading for recipient", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 352, + "fields": { + "key": "subject", + "body": "Subject", + "help_text": "Table heading for email subject", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 353, + "fields": { + "key": "quotation_name", + "body": "Quotation Name", + "help_text": "Table heading for quotation name", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 354, + "fields": { + "key": "data_field", + "body": "Data Field", + "help_text": "Table heading for data field", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 355, + "fields": { + "key": "package", + "body": "Package", + "help_text": "Table heading for package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 356, + "fields": { + "key": "user", + "body": "User", + "help_text": "Table heading for user", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 357, + "fields": { + "key": "view_data", + "body": "View Data", + "help_text": "Table heading for view data action", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 358, + "fields": { + "key": "signup_date", + "body": "Signup Date", + "help_text": "Table heading for signup date", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 359, + "fields": { + "key": "access", + "body": "Access", + "help_text": "Table heading for access", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 360, + "fields": { + "key": "debugger_intro", + "body": "The pricing structure in OBC is complicated. This tool allows you to test your pricing logic works as expected.", + "help_text": "Introduction text for the debugger tool.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 361, + "fields": { + "key": "debugger_prerequisite", + "body": "Understanding how OBC pricings are controlled is a prerequisite:", + "help_text": "Prerequisite text for the debugger tool.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 362, + "fields": { + "key": "debugger_flow_intro", + "body": "The default flow for applying prices is:", + "help_text": "Introduction to the default flow for applying prices.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 363, + "fields": { + "key": "debug_button", + "body": "Debug", + "help_text": "Button text for debugging.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 364, + "fields": { + "key": "debug_result", + "body": "Debug result", + "help_text": "Label for the debug result section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 365, + "fields": { + "key": "signups_lead", + "body": "Signups to Your Initiatives", + "help_text": "Lead text for the signups page.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 366, + "fields": { + "key": "export_csv", + "body": "You can also export this data to CSV", + "help_text": "Text for exporting data to CSV.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 367, + "fields": { + "key": "export_csv_link", + "body": "export this data to CSV", + "help_text": "Link text for exporting data to CSV.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 368, + "fields": { + "key": "gdpr_warning", + "body": "(Warning: This file may contain sensitive personal data subject to GDPR or other data protection legislation.)", + "help_text": "GDPR warning text.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 369, + "fields": { + "key": "approved_by_org", + "body": "Approved by Org", + "help_text": "Table heading for organization approval status.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 370, + "fields": { + "key": "form_elements_lead", + "body": "This page allows platform managers to specify form elements that will be available to initiatives.", + "help_text": "Lead text for form elements setup page.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 371, + "fields": { + "key": "add_new", + "body": "Add New", + "help_text": "Button text for adding a new form element.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 373, + "fields": { + "key": "signup_to", + "body": "Signup to", + "help_text": "Text for signup to a package.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 374, + "fields": { + "key": "by", + "body": "by", + "help_text": "Text for indicating by whom.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 375, + "fields": { + "key": "signup_information", + "body": "Signup Information", + "help_text": "Lead text for signup information section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 376, + "fields": { + "key": "organisation_approval_status", + "body": "Organisation Approval Status", + "help_text": "Header text for organisation approval status section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 377, + "fields": { + "key": "organisation_approval_explanation", + "body": "If the organisation signing up has approved then this means they are not requesting any changes to the package terms and you can go ahead an approve it.", + "help_text": "Explanation text for organisation approval.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 378, + "fields": { + "key": "approved_on", + "body": "Approved on", + "help_text": "Text for indicating approval date.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 379, + "fields": { + "key": "not_approved", + "body": "Not approved", + "help_text": "Text for indicating not approved status.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 380, + "fields": { + "key": "initiative_approval_status", + "body": "Initiative Approval Status", + "help_text": "Header text for initiative approval status section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 381, + "fields": { + "key": "signup_approved_on", + "body": "This signup was approved on", + "help_text": "Text for indicating signup approval date.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 382, + "fields": { + "key": "signup_not_approved", + "body": "This signup has not been approved.", + "help_text": "Text for indicating signup not approved status.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 383, + "fields": { + "key": "approve_signup", + "body": "Approve Signup", + "help_text": "Button text for approving signup.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 384, + "fields": { + "key": "upload_custom_document", + "body": "You can upload a custom document for the package if the organisation signing up has requested changes to standard terms. This document must incorporate all documents for this agreement as it will override all others.", + "help_text": "Explanation text for uploading custom documents.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 385, + "fields": { + "key": "signups_and_access_control", + "body": "Signups and Access Control", + "help_text": "Header text for signups and access control section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 386, + "fields": { + "key": "access_control_lead", + "body": "Some types of open access deal have unique perks for specific institutions (e.g. Opening the Future). This page allows for the generation of an access control list for subscription portions of such programmes.", + "help_text": "Lead text for access control section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 387, + "fields": { + "key": "access_control_download", + "body": "The access control list is downloadable by", + "help_text": "Text for downloading access control list.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 388, + "fields": { + "key": "all_contacts", + "body": "all contacts", + "help_text": "Link text for all contacts.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 389, + "fields": { + "key": "create_add_packages", + "body": "Create and add packages to your quotations.", + "help_text": "Text for creating and adding packages to quotations.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 390, + "fields": { + "key": "new_form_element", + "body": "New Form Element", + "help_text": "Breadcrumb text for new form element.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 391, + "fields": { + "key": "add_edit_form_element", + "body": "Add a new or edit an existing form element", + "help_text": "Lead text for adding or editing form elements.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 392, + "fields": { + "key": "save_element", + "body": "Save Element", + "help_text": "Button text for saving form element.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 393, + "fields": { + "key": "uncompleted_quotations", + "body": "A list of your uncompleted quotations.", + "help_text": "Lead text for uncompleted quotations section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 394, + "fields": { + "key": "view_select_packages", + "body": "View and select packages available for signup.", + "help_text": "Lead text for viewing and selecting packages.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 395, + "fields": { + "key": "individual_packages", + "body": "Individual packages.", + "help_text": "Text for individual packages section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 396, + "fields": { + "key": "no_packages_display", + "body": "There are no Packages to display.", + "help_text": "Text for indicating no packages to display.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 397, + "fields": { + "key": "groups_of_packages", + "body": "Groups of packages.", + "help_text": "Text for groups of packages section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 398, + "fields": { + "key": "no_meta_packages_display", + "body": "There are no Meta Packages to display.", + "help_text": "Text for indicating no meta packages to display.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 399, + "fields": { + "key": "add_access_control_entry", + "body": "Add an Access Control Entry", + "help_text": "Header text for adding access control entry.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 400, + "fields": { + "key": "add_access_control_instruction", + "body": "Add a new access control instruction.", + "help_text": "Lead text for adding access control instruction.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 401, + "fields": { + "key": "access_control_warning", + "body": "Warning: These entries cannot be deleted, but they can be reversed by adding the opposite instruction (i.e. revoke).", + "help_text": "Warning text for access control entries.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 402, + "fields": { + "key": "revoke", + "body": "Revoke", + "help_text": "Revoke button.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 403, + "fields": { + "key": "grant", + "body": "Grant", + "help_text": "Grant button.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 404, + "fields": { + "key": "membership_offer", + "body": "Membership Offer", + "help_text": "Header for membership offer section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 405, + "fields": { + "key": "included_packages", + "body": "Included Packages", + "help_text": "Header for included packages section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 406, + "fields": { + "key": "process_signup", + "body": "Process Signup", + "help_text": "Link text to process signup.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 407, + "fields": { + "key": "no_active_quotes", + "body": "No active quotes to display.", + "help_text": "Text on view baskets page.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 408, + "fields": { + "key": "manage_redirect_for", + "body": "Manage Redirect for", + "help_text": "Title for managing redirect for a banding.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 409, + "fields": { + "key": "redirect_description", + "body": "This allows you to specify that the user should be directed to an external site (such as the Jisc Collections catalogue page) at the end of the signup. To undo this, simply leave the redirect blank.", + "help_text": "Description for redirect functionality.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 410, + "fields": { + "key": "create_banding_type", + "body": "Create Banding Type", + "help_text": "Title for creating a new banding type.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 411, + "fields": { + "key": "create_banding_type_description", + "body": "Create a new Banding Type by providing required metadata. Once the banding type has been created you can select the relevant banding vocabularies.", + "help_text": "Description for creating a new banding type.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 412, + "fields": { + "key": "currencies_for", + "body": "Currencies for", + "help_text": "Title for currencies associated with a banding.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 413, + "fields": { + "key": "in", + "body": "in", + "help_text": "Preposition used in titles.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 414, + "fields": { + "key": "currencies_associated", + "body": "The following currencies are associated with this banding.", + "help_text": "Description for currencies associated with a banding.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 415, + "fields": { + "key": "select_payment_processor", + "body": "Select a Payment Processor", + "help_text": "Option text for selecting a payment processor.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 416, + "fields": { + "key": "payment_processors_listed", + "body": "Listed below are payment processors for", + "help_text": "Description for payment processors listed for a country.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 417, + "fields": { + "key": "set_or_leave_empty", + "body": "You can set one or leave it empty to handle billing yourself.", + "help_text": "Description for setting or leaving payment processor empty.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 418, + "fields": { + "key": "bandings_for", + "body": "Bandings for", + "help_text": "Title for bandings associated with a package.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 419, + "fields": { + "key": "no_pricing_info", + "body": "This package does not have pricing information set for the default currency", + "help_text": "Warning text for missing pricing information.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 420, + "fields": { + "key": "bandings_associated", + "body": "The following bandings are associated with this package.", + "help_text": "Description for bandings associated with a package.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 421, + "fields": { + "key": "basic_price_overview", + "body": "Basic Price Overview", + "help_text": "Title for basic price overview section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 422, + "fields": { + "key": "price_overview_description", + "body": "Here you can see, at a glance, all prices associated with this package.", + "help_text": "Description for basic price overview section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 423, + "fields": { + "key": "create_new_banding_type", + "body": "Create New Banding Type", + "help_text": "Breadcrumb text for creating a new banding type.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 424, + "fields": { + "key": "save_new_banding_type", + "body": "Save New Banding Type", + "help_text": "Button text for saving a new banding type.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 425, + "fields": { + "key": "banding_types_list", + "body": "Banding Types List", + "help_text": "Breadcrumb text for the banding types list link.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 426, + "fields": { + "key": "edit_bandings", + "body": "Edit Bandings", + "help_text": "Breadcrumb text for editing bandings.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 427, + "fields": { + "key": "add_this_banding", + "body": "Add this banding", + "help_text": "Button text for adding a banding.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 428, + "fields": { + "key": "edit_currencies", + "body": "Edit Currencies", + "help_text": "Button text for editing currencies.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 429, + "fields": { + "key": "remove_banding_and_prices", + "body": "Remove Banding and Prices", + "help_text": "Button text for removing banding and prices.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 430, + "fields": { + "key": "add_this_currency", + "body": "Add this currency", + "help_text": "Button text for adding a currency.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 431, + "fields": { + "key": "edit_prices", + "body": "Edit Prices", + "help_text": "Button text for editing prices.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 432, + "fields": { + "key": "remove_currency_and_prices", + "body": "Remove Currency and Prices", + "help_text": "Button text for removing currency and prices.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 433, + "fields": { + "key": "payment_processor", + "body": "Payment Processor", + "help_text": "Table heading for payment processor.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 434, + "fields": { + "key": "manage_payment_processor_for", + "body": "Manage Payment Processor for", + "help_text": "Title for managing payment processor for a country.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 435, + "fields": { + "key": "current_payment_processor", + "body": "For this banding and currency the payment processor is currently set to", + "help_text": "Text indicating the current payment processor for a banding and currency.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 436, + "fields": { + "key": "prices_for", + "body": "Prices for", + "help_text": "Title for prices associated with a currency and banding.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 437, + "fields": { + "key": "for", + "body": "for", + "help_text": "Preposition used in titles.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 438, + "fields": { + "key": "set_prices_for_band", + "body": "Please set the prices for this band.", + "help_text": "Instruction to set prices for a band.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 439, + "fields": { + "key": "save_prices", + "body": "Save Prices", + "help_text": "Button text for saving prices.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 440, + "fields": { + "key": "manage_redirect", + "body": "Manage Redirect", + "help_text": "Text for managing redirect", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 441, + "fields": { + "key": "banding_types_list_description", + "body": "A list of all of the Banding Types available for package creation.", + "help_text": "Description for banding types list", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 442, + "fields": { + "key": "banding_types_warning", + "body": "Warning: package banding types cannot be changed after they are created as this will destroy current quotations.", + "help_text": "Warning for banding types", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 443, + "fields": { + "key": "add_new_banding_type", + "body": "Add New Banding Type", + "help_text": "Button text for adding a new banding type", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 444, + "fields": { + "key": "is_fte", + "body": "Is FTE?", + "help_text": "Column heading for FTE status", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 445, + "fields": { + "key": "linked_vocabs", + "body": "# Linked Vocabs", + "help_text": "Column heading for linked vocabularies", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 446, + "fields": { + "key": "manage_banding_type_vocabs_for", + "body": "Manage Banding Type Vocabs for", + "help_text": "Title for managing banding type vocabs", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 447, + "fields": { + "key": "manage_vocabs_for", + "body": "Manage Vocabs for", + "help_text": "Breadcrumb text for managing vocabs", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 448, + "fields": { + "key": "select_vocabs_description", + "body": "Select vocabs for this Banding Type. When a Initiative creates packages they will use these as the basis for their Banding/Prices.", + "help_text": "Description for selecting vocabs", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 449, + "fields": { + "key": "add_new_vocabulary", + "body": "Add a new vocabulary", + "help_text": "Button text for adding a new vocabulary", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 450, + "fields": { + "key": "text", + "body": "Text", + "help_text": "Column heading for text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 451, + "fields": { + "key": "upper_limit", + "body": "Upper Limit", + "help_text": "Column heading for upper limit", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 452, + "fields": { + "key": "lower_limit", + "body": "Lower Limit", + "help_text": "Column heading for lower limit", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 453, + "fields": { + "key": "order_complete_message", + "body": "Thanks for your interest in supporting Open Access book publishing. Your quotation is now complete. Your reference number is", + "help_text": "Message displayed when an order is complete", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 454, + "fields": { + "key": "quotation_details", + "body": "Quotation Details", + "help_text": "Heading for quotation details section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 455, + "fields": { + "key": "download_quotation", + "body": "Download Quotation", + "help_text": "Button text for downloading the quotation", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 456, + "fields": { + "key": "thanks_for_your_interest", + "body": "Thanks for your interest in supporting Open Access book publishing. Your quotation is now complete. Your reference number is", + "help_text": "Help text for quotation complete", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 457, + "fields": { + "key": "order_complete_body_partial", + "body": "is now complete. If you require assistance with your order ensure you quote this order number.", + "help_text": "Message displayed when an order is complete", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 458, + "fields": { + "key": "order_documents_info", + "body": "Here you can find the documents related to your order.", + "help_text": "Information about order documents", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 459, + "fields": { + "key": "download_order_documents", + "body": "Download Order Documents", + "help_text": "Button text for downloading order documents", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 460, + "fields": { + "key": "order_complete_contact", + "body": "If you have any questions, please contact us.", + "help_text": "Contact information for order completion", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 461, + "fields": { + "key": "contact_email", + "body": "Contact Email", + "help_text": "Table heading for contact email", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 462, + "fields": { + "key": "cost", + "body": "Annual Cost", + "help_text": "Table heading for annual cost", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 463, + "fields": { + "key": "invoice_status", + "body": "Invoice Status", + "help_text": "Table heading for invoice status", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 464, + "fields": { + "key": "package_media_files", + "body": "Package Media Files", + "help_text": "Heading on order complete page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 465, + "fields": { + "key": "your_order_pre", + "body": "Your order with order number", + "help_text": "Heading on order complete page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 466, + "fields": { + "key": "your_order_post", + "body": "is now complete. If you require assistance with your order ensure you quote this order number.", + "help_text": "Helper text on order complete", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 469, + "fields": { + "key": "your_quotation", + "body": "Your quotation", + "help_text": "Title for the quotation section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 470, + "fields": { + "key": "quotation_generated", + "body": "We have generated a quotation for you to review, with a unique quotation number", + "help_text": "Text for the generated quotation", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 471, + "fields": { + "key": "quotation_reference", + "body": "Please refer to this quotation number if you would like to discuss any details with us.", + "help_text": "Text for referring to the quotation number", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 472, + "fields": { + "key": "order_documents_description", + "body": "We've zipped up the documents for all of the packages you are signing up for. Please read them and then either approve the signup below or contact the initiative to discuss the terms. Also, some types of signup (those going through Jisc Collections) require you to input your details on an external site. Please check the links below carefully under the 'external' column.", + "help_text": "Description for order documents", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 473, + "fields": { + "key": "contact_details_description", + "body": "Once you have read the order documents you can contact the initiatives to discuss them or approve the signup. If necessary the package manager can upload new terms. Once you and the package manager have both approved the signup terms will be locked in place.", + "help_text": "Description for contact details", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 474, + "fields": { + "key": "approval_description", + "body": "When you are happy with the package terms you can mark the signup as approved. You may need to wait for the Initiative to approve the signup also in order to finalise the signup.", + "help_text": "Description for approval process", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 475, + "fields": { + "key": "approve_this_signup", + "body": "Approve this Signup", + "help_text": "Button text for approving the signup", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 476, + "fields": { + "key": "approved_by_provider", + "body": "Approved by Provider?", + "help_text": "Table heading for provider approval status.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 477, + "fields": { + "key": "external", + "body": "External", + "help_text": "Table heading for external links.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 478, + "fields": { + "key": "agree_terms_of_signup", + "body": "Agree to Terms of Signup", + "help_text": "Button text for agreeing to terms of signup.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 479, + "fields": { + "key": "revise_document", + "body": "Revise Document", + "help_text": "Text for revising a document.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 480, + "fields": { + "key": "add_new_document", + "body": "Add New Document", + "help_text": "Text for adding a new document.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 481, + "fields": { + "key": "revise_document_for", + "body": "Revise document for", + "help_text": "Text for revising a document for a specific package.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 482, + "fields": { + "key": "add_new_document_to", + "body": "Add a new document to", + "help_text": "Text for adding a new document to a specific initiative's package.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 483, + "fields": { + "key": "s_package", + "body": "'s package,", + "help_text": "Text for indicating possession of a package.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 484, + "fields": { + "key": "metadata", + "body": "Metadata", + "help_text": "Text for metadata section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 485, + "fields": { + "key": "versions", + "body": "Versions", + "help_text": "Text for documents page.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 486, + "fields": { + "key": "document", + "body": "Document", + "help_text": "Text for documents page.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 487, + "fields": { + "key": "packages", + "body": "Packages", + "help_text": "Breadcrumb text for packages", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 488, + "fields": { + "key": "documents", + "body": "Documents", + "help_text": "Breadcrumb text for documents", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 489, + "fields": { + "key": "orders_and_documents", + "body": "Orders and Documents", + "help_text": "A menu heading for 'Orders and Documents'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 490, + "fields": { + "key": "contacts", + "body": "Contacts", + "help_text": "A menu heading for 'Contacts'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 491, + "fields": { + "key": "fixed_pages", + "body": "Fixed Pages", + "help_text": "A menu heading for 'Fixed Pages'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 492, + "fields": { + "key": "contact_details", + "body": "Contact Details", + "help_text": "A menu heading for 'Contact Details'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 493, + "fields": { + "key": "site_setup", + "body": "Site Setup", + "help_text": "A menu heading for 'Site Setup'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 494, + "fields": { + "key": "banding_types", + "body": "Banding Types", + "help_text": "A menu heading for 'Banding Types'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 495, + "fields": { + "key": "add", + "body": "Add", + "help_text": "Add button on mobile", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 496, + "fields": { + "key": "initiative", + "body": "Initiative", + "help_text": "Heading for the site-wide news configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 497, + "fields": { + "key": "quotes", + "body": "Quotes", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 498, + "fields": { + "key": "order_number", + "body": "Order Number", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 499, + "fields": { + "key": "number_of_packages", + "body": "Number of Packages", + "help_text": "Text on dashboard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 500, + "fields": { + "key": "active", + "body": "Active", + "help_text": "Table text on Edit Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 501, + "fields": { + "key": "price", + "body": "Price", + "help_text": "Table text on Invoice Management page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 502, + "fields": { + "key": "approved_by_initiative", + "body": "Approved by Initiative", + "help_text": "Table heading for initiative approval status.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 503, + "fields": { + "key": "order_documents_button", + "body": "Download Order Documents", + "help_text": "Button text for downloading order documents", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 504, + "fields": { + "key": "approval", + "body": "Approval", + "help_text": "Table heading for offer", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 505, + "fields": { + "key": "offer", + "body": "Offer", + "help_text": "Table heading for offer", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 506, + "fields": { + "key": "duplicate_offers", + "body": "Duplicate Offers: ", + "help_text": "Label for duplicate offers", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 507, + "fields": { + "key": "duplicate_offers_description", + "body": "Your selection includes one or more duplicate offers. Any duplicate offers are automatically removed from your quote to ensure you are only charged once.", + "help_text": "Description for duplicate offers", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 508, + "fields": { + "key": "totals_ignore_duplicates", + "body": "Totals ignore duplicate offers.", + "help_text": "Note that totals ignore duplicate offers", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 509, + "fields": { + "key": "total", + "body": "Total", + "help_text": "Label for total", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 510, + "fields": { + "key": "indicative_price_warning", + "body": "*No offers have yet been set up in your chosen currency. Instead, we have provided an indicative price. We recommend you get in touch with us at info@openbookcollective.org so we can provide you with a custom quotation.", + "help_text": "Warning about indicative pricing", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 511, + "fields": { + "key": "no_banding_or_price_found", + "body": "No banding or price found.", + "help_text": "Message displayed when no banding or price is found.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 513, + "fields": { + "key": "contract", + "body": "Contract", + "help_text": "Link text for contract.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 517, + "fields": { + "key": "remove_from_basket", + "body": "Remove from Basket", + "help_text": "Alt text on mobile", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 518, + "fields": { + "key": "form_elements_setup_for", + "body": "Form Elements Setup for", + "help_text": "Title for form elements setup page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 519, + "fields": { + "key": "select_data_for_signup", + "body": "Please select the data that you require from users in order to signup to this package.", + "help_text": "Instruction for selecting data for signup", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 520, + "fields": { + "key": "add_new_package", + "body": "Add New Package", + "help_text": "Heading text for adding a new package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 521, + "fields": { + "key": "package_review_notice", + "body": "Before this package is displayed for signup it will be reviewed by a member of staff.", + "help_text": "Notice about package review before signup", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 522, + "fields": { + "key": "standard_attestation_configuration", + "body": "Standard Attestation Configuration", + "help_text": "Title for standard attestation configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 523, + "fields": { + "key": "save_attestation", + "body": "Save Attestation", + "help_text": "Button text for saving attestation", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 524, + "fields": { + "key": "standards_attested_to_by", + "body": "Standards Attested to by", + "help_text": "Title for standards attested to by a package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 525, + "fields": { + "key": "standards_attested_description", + "body": "The following standards are attested to by this package.", + "help_text": "Description for standards attested to by a package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 526, + "fields": { + "key": "attest_to_this_standard", + "body": "Attest to this standard", + "help_text": "Button text for attesting to a standard", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 527, + "fields": { + "key": "edit_attestation", + "body": "Edit Attestation", + "help_text": "Button text for editing an attestation", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 528, + "fields": { + "key": "remove_attestation", + "body": "Remove Attestation", + "help_text": "Button text for removing an attestation", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 529, + "fields": { + "key": "select_banding_type", + "body": "Select a banding type for this package", + "help_text": "Instruction to select a banding type", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 530, + "fields": { + "key": "or", + "body": "or", + "help_text": "Conjunction 'or'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 531, + "fields": { + "key": "manage_available_bandings", + "body": "manage available bandings", + "help_text": "Link text to manage available bandings", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 532, + "fields": { + "key": "uses_fte", + "body": "Uses FTE", + "help_text": "Column heading for Uses FTE", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 533, + "fields": { + "key": "select", + "body": "Select", + "help_text": "Button text for selecting an option", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 534, + "fields": { + "key": "password_set_info", + "body": "Your password has been set. You may now log in.", + "help_text": "Information text after password reset", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 535, + "fields": { + "key": "log_in", + "body": "Log in", + "help_text": "Text for login link", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 536, + "fields": { + "key": "highlights", + "body": "Highlights", + "help_text": "Title for the highlights section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 537, + "fields": { + "key": "view_our_full_catalogue", + "body": "View Our Full Catalogue", + "help_text": "Button text to view the full catalogue", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 538, + "fields": { + "key": "manage_contacts_for", + "body": "Manage Contacts for", + "help_text": "Title for managing contacts for a banding type", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 539, + "fields": { + "key": "manage_contacts", + "body": "Manage Contacts", + "help_text": "Breadcrumb text for managing contacts", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 540, + "fields": { + "key": "select_contacts_for_events", + "body": "Select contacts for event types. These contacts will be sent an email when the event occurs.", + "help_text": "Instruction for selecting contacts for events", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 541, + "fields": { + "key": "contacts_access_warning", + "body": "Warning: Contacts have access to signup data which may be sensitive.", + "help_text": "Warning about contacts having access to sensitive data", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 542, + "fields": { + "key": "add_new_contact", + "body": "Add a New Contact", + "help_text": "Button text for adding a new contact", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 543, + "fields": { + "key": "signup_notification", + "body": "Signup Notification", + "help_text": "Table heading for signup notification", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 544, + "fields": { + "key": "access_control_notification", + "body": "Access Control Notification", + "help_text": "Table heading for access control notification", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 545, + "fields": { + "key": "save_contacts_to_package", + "body": "Save Contacts to Package", + "help_text": "Button text for contacts page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 546, + "fields": { + "key": "revert", + "body": "Revert", + "help_text": "Button text for reverting a file", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 547, + "fields": { + "key": "revert_warning", + "body": "Reverting will overwrite the current document details with the version's.", + "help_text": "Revert warning", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 548, + "fields": { + "key": "no_versions", + "body": "No versions recorded for this document.", + "help_text": "No versions text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 549, + "fields": { + "key": "packages_not_approved", + "body": "Listed here are packages that have not been approved for display.", + "help_text": "Description for packages not approved", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 550, + "fields": { + "key": "banding_type", + "body": "Banding Type", + "help_text": "Table heading for banding type", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 551, + "fields": { + "key": "default_country_currency", + "body": "Default Country/Currency", + "help_text": "Table heading for default country/currency", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 552, + "fields": { + "key": "approve", + "body": "Approve", + "help_text": "Button text for approve", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 553, + "fields": { + "key": "manage_meta_package", + "body": "Manage Meta Package", + "help_text": "Title for managing a meta package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 554, + "fields": { + "key": "create_new_meta_package", + "body": "Create New Meta Package", + "help_text": "Title for creating a new meta package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 555, + "fields": { + "key": "meta_packages_description", + "body": "A list of all the meta packages available (both active and inactive). As meta packages span multiple initiatives they can only be managed by staff.", + "help_text": "Description for meta packages section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 556, + "fields": { + "key": "add_new_meta_package", + "body": "Add New Meta Package", + "help_text": "Button text for adding a new meta package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 557, + "fields": { + "key": "meta_packages_group_description", + "body": "Meta Packages are a group of other packages that a library can subscribe to.", + "help_text": "Description for meta packages group", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 558, + "fields": { + "key": "username_email_info", + "body": "Your username will be the email address you used to register your account.", + "help_text": "Information about username being the email address", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 559, + "fields": { + "key": "login_error", + "body": "Your username and password didn't match. Please try again.", + "help_text": "Error message for login failure", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 560, + "fields": { + "key": "no_access_info", + "body": "Your account doesn't have access to this page. To proceed, please login with an account that has access.", + "help_text": "Information about no access to a page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 561, + "fields": { + "key": "login_to_see_page", + "body": "Please login to see this page.", + "help_text": "Prompt to login to see a page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 562, + "fields": { + "key": "enter_new_password_twice", + "body": "Please enter your new password twice so we can verify you typed it in correctly.", + "help_text": "Instruction text for entering new password twice.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 563, + "fields": { + "key": "new_password", + "body": "New password:", + "help_text": "Label for new password field.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 564, + "fields": { + "key": "confirm_password", + "body": "Confirm password:", + "help_text": "Label for confirm password field.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 565, + "fields": { + "key": "change_my_password", + "body": "Change my password", + "help_text": "Button text for changing password.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 566, + "fields": { + "key": "change", + "body": "Change", + "help_text": "Button text for change action.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 567, + "fields": { + "key": "password_reset_instructions", + "body": "If an account with the email you entered exists we've sent you instructions for resetting your password. You should receive this message shortly.", + "help_text": "Instructions for password reset email.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 568, + "fields": { + "key": "password_reset_no_email", + "body": "If you don\u2019t receive an email, please make sure you\u2019ve entered the address you registered with, and check your spam folder.", + "help_text": "Instructions if no password reset email is received.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 569, + "fields": { + "key": "email_greeting", + "body": "Dear", + "help_text": "Greeting in the password reset email.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 570, + "fields": { + "key": "password_reset_request", + "body": "You're receiving this email because you requested a password reset for your user account at ", + "help_text": "Notification of password reset request.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 571, + "fields": { + "key": "password_reset_instruction", + "body": "Please go to the following page and choose a new password:", + "help_text": "Instruction to reset password.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 572, + "fields": { + "key": "username_reminder", + "body": "Your username, in case you\u2019ve forgotten: ", + "help_text": "Reminder of username in password reset email.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 574, + "fields": { + "key": "email_us", + "body": "Email Us", + "help_text": "Text for email us link", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 575, + "fields": { + "key": "contact", + "body": "Contact", + "help_text": "Menu option that reads 'Contact'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 576, + "fields": { + "key": "package_approval", + "body": "Package Approval", + "help_text": "A menu heading for 'Package Approval'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 577, + "fields": { + "key": "meta_packages", + "body": "Meta Packages", + "help_text": "A menu heading for 'Meta Packages'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 578, + "fields": { + "key": "summary", + "body": "Summary", + "help_text": "Text on nav page that reads 'Summary'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 579, + "fields": { + "key": "pricing", + "body": "Pricing", + "help_text": "Text on nav page and elsewhere that reads 'Pricing'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 580, + "fields": { + "key": "name", + "body": "Name", + "help_text": "Name text for dropdown", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 581, + "fields": { + "key": "standards", + "body": "Standards", + "help_text": "Table text on Edit Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 582, + "fields": { + "key": "media_files", + "body": "Media Files", + "help_text": "Table text on Edit Initiative page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 583, + "fields": { + "key": "no_packages_standards", + "body": "No packages adhere to our standards.", + "help_text": "Text for indicating no packages adhere to standards.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 584, + "fields": { + "key": "no_standards", + "body": "This package adheres to none of our standards.", + "help_text": "Text for indicating package adheres to no standards.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 585, + "fields": { + "key": "view_attestation_history", + "body": "View attestation history", + "help_text": "Link text for viewing attestation history.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 586, + "fields": { + "key": "only_in_collectives", + "body": "Only available in collectives", + "help_text": "Text for indicating availability only in collectives.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 587, + "fields": { + "key": "standards_attestation_history", + "body": "Standards Attestation History", + "help_text": "Modal title for standards attestation history.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 589, + "fields": { + "key": "package_name", + "body": "Package Name", + "help_text": "Table heading for package name", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 590, + "fields": { + "key": "close", + "body": "Close", + "help_text": "Close text for modals", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 591, + "fields": { + "key": "month_contract", + "body": "month contract", + "help_text": "Appended after the number of months the contract runs for", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 592, + "fields": { + "key": "add_to_quote", + "body": "Add to Quote", + "help_text": "Button in collective element", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 593, + "fields": { + "key": "included_in", + "body": "Included in", + "help_text": "Preamble text to specify which packages are included in which metapackages", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 595, + "fields": { + "key": "create_new_package", + "body": "Create New Package", + "help_text": "Breadcrumb text for creating a new package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 596, + "fields": { + "key": "add_edit_package_lead", + "body": "Add new and edit existing package.", + "help_text": "Lead text for adding or editing a package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 597, + "fields": { + "key": "list_documents", + "body": "List Documents", + "help_text": "Title for listing documents", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 598, + "fields": { + "key": "media_files_affiliated_with", + "body": "Media Files Affiliated with", + "help_text": "Title for media files affiliated with a package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 599, + "fields": { + "key": "media_files_accessible", + "body": "Media Files are accessible only to libraries who have subscribed to one of your packages.", + "help_text": "Description for media files accessibility", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 600, + "fields": { + "key": "load_new_file", + "body": "Load New File", + "help_text": "Heading for loading a new file", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 601, + "fields": { + "key": "upload_file", + "body": "Upload File", + "help_text": "Button text for uploading a file", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 602, + "fields": { + "key": "date_uploaded", + "body": "Date Uploaded", + "help_text": "Table heading for date uploaded", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 603, + "fields": { + "key": "download_file", + "body": "Download File", + "help_text": "Link text for downloading a file", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 604, + "fields": { + "key": "delete_file", + "body": "Delete File", + "help_text": "Button text for deleting a file", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 605, + "fields": { + "key": "reset_your_password", + "body": "Reset your password", + "help_text": "Title for the password reset complete page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 606, + "fields": { + "key": "password_reset_form_instructions", + "body": "Enter your email address below. If a matching account has been found a reset email will be sent to your inbox. Please ensure you check any junk mail or filter folders.", + "help_text": "Instructions for the password reset form", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 607, + "fields": { + "key": "reset_password", + "body": "Reset Password", + "help_text": "Button text for resetting password", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 608, + "fields": { + "key": "more_info", + "body": "More Info", + "help_text": "Button text for more information", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 609, + "fields": { + "key": "build_advanced_search", + "body": "Build an Advanced Search", + "help_text": "Title for building an advanced search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 610, + "fields": { + "key": "collective_catalogue_description", + "body": "The collective catalogue displays books that are in the Thoth system and that are supported by the work and funding model of the Open Book Collective.", + "help_text": "Description of the collective catalogue", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 611, + "fields": { + "key": "add_to_search", + "body": "Add to Search", + "help_text": "Button text for adding to search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 612, + "fields": { + "key": "free_text", + "body": "Free text", + "help_text": "Option for free text search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 613, + "fields": { + "key": "keyword", + "body": "Keyword", + "help_text": "Option for keyword search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 614, + "fields": { + "key": "author", + "body": "Author", + "help_text": "Option for author search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 615, + "fields": { + "key": "add_search", + "body": "Add search", + "help_text": "Button text for adding a search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 616, + "fields": { + "key": "author_search_note", + "body": "Note: to search for author names, place your query in quotation marks to match a full name", + "help_text": "Note for searching author names", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 617, + "fields": { + "key": "search_configuration", + "body": "Search Configuration", + "help_text": "Title for search configuration section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 618, + "fields": { + "key": "type", + "body": "Type", + "help_text": "Column heading for type", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 619, + "fields": { + "key": "save_this_search", + "body": "Save this search", + "help_text": "Button text for saving a search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 620, + "fields": { + "key": "reset_search", + "body": "Reset search", + "help_text": "Button text for resetting a search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 621, + "fields": { + "key": "delete_all_terms", + "body": "Delete all terms from this search", + "help_text": "Button text for deleting all search terms", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 622, + "fields": { + "key": "manage_your_searches", + "body": "Manage your searches", + "help_text": "Link text for managing searches", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 623, + "fields": { + "key": "first", + "body": "first", + "help_text": "Pagination text for first page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 624, + "fields": { + "key": "previous", + "body": "previous", + "help_text": "Pagination text for previous page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 625, + "fields": { + "key": "next", + "body": "next", + "help_text": "Pagination text for next page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 626, + "fields": { + "key": "last", + "body": "last", + "help_text": "Pagination text for last page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 627, + "fields": { + "key": "page_of", + "body": "Page", + "help_text": "Pagination text for current page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 628, + "fields": { + "key": "of", + "body": "of", + "help_text": "Pagination text for 'of' in page count", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 629, + "fields": { + "key": "collective_catalogue", + "body": "Collective Catalogue", + "help_text": "Collective Catalogue heading and alt text", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 630, + "fields": { + "key": "search_placeholder", + "body": "Search", + "help_text": "Placeholder text for search input", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 631, + "fields": { + "key": "search_name_placeholder", + "body": "Search name to save", + "help_text": "Placeholder text for search name input", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 632, + "fields": { + "key": "date_newest", + "body": "Date Newest", + "help_text": "Option text in advanced Thoth search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 633, + "fields": { + "key": "date_oldest", + "body": "Date Oldest", + "help_text": "Option text in advanced Thoth search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 634, + "fields": { + "key": "no_abstract", + "body": "No abstract.", + "help_text": "Text displayed when there is no abstract available.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 635, + "fields": { + "key": "download_formats", + "body": "Download Formats", + "help_text": "Heading for download formats section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 636, + "fields": { + "key": "included_in_packages", + "body": "Included in Packages", + "help_text": "Heading for included in packages section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 637, + "fields": { + "key": "publisher_links", + "body": "Publisher Links", + "help_text": "Heading for publisher links section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 638, + "fields": { + "key": "publisher_landing_page", + "body": "Publisher landing page", + "help_text": "Link text for publisher landing page.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 639, + "fields": { + "key": "doi", + "body": "DOI", + "help_text": "Link text for DOI.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 640, + "fields": { + "key": "topics", + "body": "Topics", + "help_text": "Heading for topics section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 641, + "fields": { + "key": "metadata_formats", + "body": "Metadata Formats", + "help_text": "Heading for metadata formats section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 642, + "fields": { + "key": "keywords", + "body": "Keywords", + "help_text": "Heading for keywords section.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 643, + "fields": { + "key": "syncing_process_description", + "body": "Syncing with Thoth is a multi-stage process:", + "help_text": "Description of the syncing process with Thoth", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 644, + "fields": { + "key": "run_test_import", + "body": "Run a test import. This verifies that the remote Thoth server is compatible.", + "help_text": "Instruction to run a test import", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 645, + "fields": { + "key": "run_full_import", + "body": "Run a full import. This pulls data into the local Thoth database.", + "help_text": "Instruction to run a full import", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 646, + "fields": { + "key": "notify_users", + "body": "Notify users.", + "help_text": "Instruction to notify users", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 647, + "fields": { + "key": "run_test", + "body": "Run test", + "help_text": "Button text for running a test", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 648, + "fields": { + "key": "run_import", + "body": "Run import", + "help_text": "Button text for running an import", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 649, + "fields": { + "key": "notify_users_button", + "body": "Notify users", + "help_text": "Button text for notifying users", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 650, + "fields": { + "key": "test_results", + "body": "Test results", + "help_text": "Label for test results section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 651, + "fields": { + "key": "last_real_thoth_import", + "body": "Last real Thoth import", + "help_text": "Label for last real Thoth import section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 652, + "fields": { + "key": "subject_list_description", + "body": " subject list. Initiatives can pick one or more subjects for their packages.", + "help_text": "Description of the subject list.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 653, + "fields": { + "key": "permission_denied", + "body": "Permission Denied", + "help_text": "Text displayed when access is denied.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 654, + "fields": { + "key": "select_an_initiative", + "body": "Select an Initiative", + "help_text": "Option text for selecting an initiative", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 655, + "fields": { + "key": "sort", + "body": "Sort", + "help_text": "Label for sort section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 656, + "fields": { + "key": "latest_titles", + "body": "Latest Titles", + "help_text": "Heading for latest titles section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 657, + "fields": { + "key": "or_try_the", + "body": "Or try the", + "help_text": "Text for suggesting an alternative search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 658, + "fields": { + "key": "advanced_search", + "body": "advanced search", + "help_text": "Link text for advanced search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 659, + "fields": { + "key": "filters", + "body": "Filters", + "help_text": "Heading text in Thoth", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 660, + "fields": { + "key": "books_from", + "body": "Books from", + "help_text": "Heading for books from a specific publisher", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 661, + "fields": { + "key": "featured_books_page_description", + "body": "This page allows you to add a new featured title from the Thoth shared catalogue to your initiative's page.", + "help_text": "Description for the featured books page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 662, + "fields": { + "key": "find_the_book", + "body": "find the book that you wish to use", + "help_text": "Link text to find the book", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 663, + "fields": { + "key": "featured_books_page_instruction", + "body": "In the URL bar, you will see a URL that ends with '/books/book/1309/'. Put that last number in the below box to add the title.", + "help_text": "Instruction for adding a book on the featured books page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 664, + "fields": { + "key": "thoth_import_id_placeholder", + "body": "The Thoth import ID", + "help_text": "Placeholder text for Thoth import ID input", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 665, + "fields": { + "key": "save_to_initiative", + "body": "Save to Initiative", + "help_text": "Button text for saving to initiative", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 666, + "fields": { + "key": "saved_searches", + "body": "Saved Searches", + "help_text": "Title for saved searches page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 667, + "fields": { + "key": "saved_searches_description", + "body": "These are the searches that you have made previously and saved. You can here re-run these searches or sign up to email alerts.", + "help_text": "Description for saved searches page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 668, + "fields": { + "key": "search_name", + "body": "Search name", + "help_text": "Table heading for search name", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 669, + "fields": { + "key": "terms", + "body": "Terms", + "help_text": "Table heading for search terms", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 670, + "fields": { + "key": "email_updates", + "body": "Email updates", + "help_text": "Table heading for email updates", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 671, + "fields": { + "key": "run_search", + "body": "Run search", + "help_text": "Table heading for run search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 672, + "fields": { + "key": "subscribe_email_updates", + "body": "Subscribe for email updates", + "help_text": "Button text for subscribing to email updates", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 673, + "fields": { + "key": "unsubscribe_email_updates", + "body": "Unsubscribe from email updates", + "help_text": "Button text for unsubscribing from email updates", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 674, + "fields": { + "key": "run_this_search", + "body": "Run this search", + "help_text": "Button text for running a search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 675, + "fields": { + "key": "delete_this_search", + "body": "Delete this Search", + "help_text": "Button text for deleting a search", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 676, + "fields": { + "key": "thoth_feed", + "body": "Thoth Feed", + "help_text": "Title for Thoth feed section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 677, + "fields": { + "key": "thoth_feed_description", + "body": "These are the books matched by all of the above searches", + "help_text": "Description for Thoth feed section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 678, + "fields": { + "key": "authors", + "body": "Authors", + "help_text": "Table heading for authors", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 679, + "fields": { + "key": "book_name", + "body": "Book Name", + "help_text": "Table heading for book name", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 680, + "fields": { + "key": "publisher", + "body": "Publisher", + "help_text": "Table heading for publisher", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 681, + "fields": { + "key": "catalogue_record", + "body": "Catalogue record", + "help_text": "Link text for catalogue record", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 682, + "fields": { + "key": "institutional_updates", + "body": "Institutional Updates", + "help_text": "Title for institutional updates section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 683, + "fields": { + "key": "institutional_updates_description", + "body": "These are updates directed specifically at your institution (as set in your profile).", + "help_text": "Description for institutional updates section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 684, + "fields": { + "key": "your_profile", + "body": "your profile", + "help_text": "Link text for your profile", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 685, + "fields": { + "key": "view_update", + "body": "View update", + "help_text": "Link text for viewing an update", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 686, + "fields": { + "key": "login_info", + "body": "Before logging in to OBC please ensure you have activated your account by following the activation link that has been sent to your registered email address.", + "help_text": "Information before logging in.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 687, + "fields": { + "key": "post_login_info", + "body": "Once you have logged in you will be taken to the OBC Dashboard, where you can manage your account.", + "help_text": "Information after logging in.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 688, + "fields": { + "key": "auth_step_info", + "body": "", + "help_text": "Information for the authentication step.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 689, + "fields": { + "key": "manage_banding_type_vocabs", + "body": "Manage Banding Type Vocabs", + "help_text": "Title for managing banding type vocabs", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 690, + "fields": { + "key": "manage_banded_vocabularies", + "body": "Manage Banded Vocabularies", + "help_text": "Lead text for managing banded vocabularies", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 691, + "fields": { + "key": "drag_and_drop_vocab", + "body": "Drag and drop the vocabs into groups, this way they will appear in the correct order on the front end.", + "help_text": "Instruction for dragging and dropping vocabularies", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 692, + "fields": { + "key": "add_new_vocab", + "body": "Add new vocab", + "help_text": "Button text for adding a new vocabulary", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 693, + "fields": { + "key": "banding_vocabulary_configuration", + "body": "Banding Vocabulary Configuration", + "help_text": "Title for the banding vocabulary configuration page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 694, + "fields": { + "key": "add_or_edit_banding_vocabulary", + "body": "Add a new or edit an existing banding vocabulary", + "help_text": "Lead text for adding or editing a banding vocabulary", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 695, + "fields": { + "key": "manage_subject_vocabs", + "body": "Manage Subject Vocabs", + "help_text": "Title for managing subject vocabularies", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 696, + "fields": { + "key": "edit_subject", + "body": "Edit Subject", + "help_text": "Text for editing a subject", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 697, + "fields": { + "key": "add_new_subject", + "body": "Add New Subject", + "help_text": "Text for adding a new subject", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 698, + "fields": { + "key": "manage_standards", + "body": "Manage Standards", + "help_text": "Title for managing standards", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 699, + "fields": { + "key": "manage_standards_for_attestation", + "body": "Manage Standards for Attestation", + "help_text": "Lead text for managing standards for attestation", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 700, + "fields": { + "key": "add_new_standard", + "body": "Add new standard", + "help_text": "Button text for adding standards for attestation", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 701, + "fields": { + "key": "thoth_sync", + "body": "Thoth Sync", + "help_text": "A menu heading for 'Thoth Sync'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 702, + "fields": { + "key": "search", + "body": "Search", + "help_text": "A menu heading for 'Search'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 703, + "fields": { + "key": "update", + "body": "Update", + "help_text": "Table text on the update feed page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 704, + "fields": { + "key": "date", + "body": "Date", + "help_text": "Table heading for date sent", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 705, + "fields": { + "key": "value", + "body": "Value", + "help_text": "Table heading for value", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 706, + "fields": { + "key": "remove", + "body": "Remove", + "help_text": "Table heading for remove", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 707, + "fields": { + "key": "add_edit_standard_attestation", + "body": "Add a new or edit an existing standard attestation", + "help_text": "Lead text for adding or editing standard attestations", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 708, + "fields": { + "key": "view", + "body": "View", + "help_text": "The word 'View' for the heading on CMS management pages", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 709, + "fields": { + "key": "delete", + "body": "Delete", + "help_text": "Text used in tables", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 710, + "fields": { + "key": "books", + "body": "Books", + "help_text": "Title for the books section", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 711, + "fields": { + "key": "remove_banding_and_currencies", + "body": "Remove Banding and Currencies", + "help_text": "Button text for removing banding and currencies.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 712, + "fields": { + "key": "revert_document_to_version", + "body": "Revert document to version", + "help_text": "Button text for reverting a file", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 713, + "fields": { + "key": "debugger_packages_set_default_banding", + "body": "Packages can set the default banding that they wish to use.", + "help_text": "Text on the debugger page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 714, + "fields": { + "key": "order_form_footer", + "body": "", + "help_text": "Text on the order page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 714, + "fields": { + "key": "debugger_packages_associated_bandings", + "body": "Packages have associated bandings (“Open Book Publishers Membership” can say it uses “Jisc” and “Carnegie”).", + "help_text": "Text on the debugger page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 715, + "fields": { + "key": "latest_publications", + "body": "Latest Publications", + "help_text": "Header on the Latest Publications page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 716, + "fields": { + "key": "debugger_platform_managers", + "body": "Platform Managers at OBC setup bandings that have tiers that are either banded or based on FTE (“Jisc Collections” → “Band 1”, “Band 2” / “Carnegie” → “Big (1000 FTE)”, “Medium (500 FTE)”, “Small (100 FTE)”).", + "help_text": "Text on the debugger page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 717, + "fields": { + "key": "required", + "body": "Required", + "help_text": "Table text on the list_order_invoices page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 718, + "fields": { + "key": "change_access", + "body": "Change Access", + "help_text": "Breadcrumb for change access", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 719, + "fields": { + "key": "order", + "body": "Order", + "help_text": "Text for 'order'", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 720, + "fields": { + "key": "debugger_check_match_user_info", + "body": "Check if we can match this against the user’s stored information (FTE/bandings).", + "help_text": "Step in the flow to check match against user's stored information.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 721, + "fields": { + "key": "view_versions", + "body": "View Versions", + "help_text": "Link text for viewing document versions", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 722, + "fields": { + "key": "debugger_packages_specify_currencies", + "body": "Packages can specify which currencies they provide an associated banding in. So Jisc Collections, “Band 1”, could be paid for in GBP or USD.", + "help_text": "Explanation of packages specifying currencies.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 723, + "fields": { + "key": "reverting_warning", + "body": "Reverting will overwrite the current document details with the version's.", + "help_text": "Warning about reverting document versions", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 724, + "fields": { + "key": "debugger_packages_set_prices", + "body": "Packages set prices for currency-associated-bandings (BandingTypeCurrencyEntries). (E.g. a price for Jisc Band 1 in GBP).", + "help_text": "Explanation of packages setting prices.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 725, + "fields": { + "key": "documents_affiliated_with", + "body": "Documents Affiliated with", + "help_text": "Heading for documents affiliated with a package", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 726, + "fields": { + "key": "order_form_totals", + "body": "Order Totals", + "help_text": "Heading for totals on the Order Form page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 727, + "fields": { + "key": "debugger_fallback_default_banding", + "body": "If not, fall back to package default banding and currency.", + "help_text": "Step in the flow to fall back to default banding and currency.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 728, + "fields": { + "key": "no_versions_recorded", + "body": "No versions recorded for this document.", + "help_text": "Message when no document versions are recorded", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 729, + "fields": { + "key": "debugger_ask_user_details", + "body": "If not, ask user to give their details.", + "help_text": "Step in the flow to ask user for details.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 730, + "fields": { + "key": "debugger_payment_processors_associated", + "body": "Payment processors are associated with a currency-associated banding. So LYRASIS are set to handle all prices in Jisc Band 1 in USD.", + "help_text": "Explanation of payment processors associated with banding.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 731, + "fields": { + "key": "manage_vocabs", + "body": "Manage Vocabs", + "help_text": "Breadcrumb text for managing vocabs", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 732, + "fields": { + "key": "choices", + "body": "Choices", + "help_text": "Table text for choices", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 733, + "fields": { + "key": "documents_warning", + "body": "Warning: all documents here will be public and visible to everybody. These are designed to be contracts and other details of the package.", + "help_text": "Warning about document visibility", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 734, + "fields": { + "key": "debugger_find_prices", + "body": "Find any/all prices in the user’s preferred currency.", + "help_text": "Step in the flow to find prices in user's preferred currency.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 735, + "fields": { + "key": "view_document", + "body": "View Document", + "help_text": "Link text for viewing a document", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 736, + "fields": { + "key": "order_complete_number", + "body": "Your order with order number ", + "help_text": "Message displayed when an order is complete", + "rich_text": false + } + } +] \ No newline at end of file diff --git a/fixtures/site_text_4.json b/fixtures/site_text_4.json new file mode 100644 index 0000000..d337eb4 --- /dev/null +++ b/fixtures/site_text_4.json @@ -0,0 +1,156 @@ +[ + { + "model": "cms.sitetext", + "pk": 737, + "fields": { + "key": "no_offers", + "body": "There are no offers in this quote.", + "help_text": "Text for when a quote contains no offers.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 738, + "fields": { + "key": "complete_text", + "body": "Complete form below to see price.", + "help_text": "Text to ask the user to complete a form to see a price", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 739, + "fields": { + "key": "proceed", + "body": "Proceed", + "help_text": "Button text to proceed with a quotation", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 740, + "fields": { + "key": "order_form_footer", + "body": "Once you have entered your details, you will be given the option to save a document with your quotation details. If you have any questions, please get in touch with us at: info@openbookcollective.org", + "help_text": "Info text on the bottom of the 'proceed' page.", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 741, + "fields": { + "key": "order_amount", + "body": "Offers for term length: ", + "help_text": "Text that precedes the offer amount", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 743, + "fields": { + "key": "platform_fee", + "body": "Platform fee for term length: ", + "help_text": "Text that precedes the platform fee", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 745, + "fields": { + "key": "total_term_length", + "body": "Total for term length: ", + "help_text": "Text that precedes the total fee", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 746, + "fields": { + "key": "about_term_length", + "body": "You can choose to support OBC initiatives for either 12 (1 year) or 36 months (3 years). For 36 months terms, annual prices are fixed at the Annual Cost displayed above. By default, we request payment for subscriptions on an annual basis, however if preferred we can alternatively issue a payment request for the full subscription amount.", + "help_text": "Text to explain the term lengths", + "rich_text": true + } + }, + { + "model": "cms.sitetext", + "pk": 747, + "fields": { + "key": "months", + "body": "months", + "help_text": "Text to append after the term length", + "rich_text": true + } + }, + { + "model": "cms.sitetext", + "pk": 748, + "fields": { + "key": "about_term_length_next_page", + "body": "You can choose to support OBC initiatives for either 12 (1 year) or 36 months (3 years). For 36 months terms, annual prices are fixed at the Annual Cost displayed above. By default, we request payment for subscriptions on an annual basis, however if preferred we can alternatively issue a payment request for the full subscription amount. You can set the term length on the next page.", + "help_text": "Text to explain the term lengths", + "rich_text": true + } + }, + {"model": "cms.sitetext", + "pk": 749, + "fields": { + "key": "obc_percentage_annual", + "body": "Annual OBC Processing Fee", + "help_text": "Text to explain the fee length", + "rich_text": true + } + }, + {"model": "cms.sitetext", + "pk": 750, + "fields": { + "key": "annual_sub_total", + "body": "Annual Sub Total", + "help_text": "Text to explain the annual sub-total", + "rich_text": true + } + }, + {"model": "cms.sitetext", + "pk": 751, + "fields": { + "key": "annual_total", + "body": "Annual Total", + "help_text": "Text to explain the annual total", + "rich_text": true + } + }, + {"model": "cms.sitetext", + "pk": 752, + "fields": { + "key": "based_on_term", + "body": "Based on the institution information details you entered, your quotation is as follows:", + "help_text": "Text to explain the the quotation", + "rich_text": true + } + }, + {"model": "cms.sitetext", + "pk": 753, + "fields": { + "key": "months_contract", + "body": "12 or 36 months contract", + "help_text": "Text to explain the term lengths", + "rich_text": true + } + }, + {"model": "cms.sitetext", + "pk": 754, + "fields": { + "key": "per_year", + "body": "per year", + "help_text": "Text that says 'per year'", + "rich_text": true + } + } +] diff --git a/fixtures/site_text_5.json b/fixtures/site_text_5.json new file mode 100644 index 0000000..4e226e5 --- /dev/null +++ b/fixtures/site_text_5.json @@ -0,0 +1,22 @@ +[ + { + "model": "cms.sitetext", + "pk": 755, + "fields": { + "key": "term_length", + "body": "Term length:", + "help_text": "Text to display the term length", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 756, + "fields": { + "key": "dummy", + "body": "Dummy", + "help_text": "Dummy site CMS entry", + "rich_text": false + } + } +] \ No newline at end of file diff --git a/fixtures/site_text_6.json b/fixtures/site_text_6.json new file mode 100644 index 0000000..df905aa --- /dev/null +++ b/fixtures/site_text_6.json @@ -0,0 +1,132 @@ +[ + { + "model": "cms.sitetext", + "pk": 758, + "fields": { + "key": "after_currency_bandings_info", + "body": "After you enter your currency you will be asked to supple information on bandings", + "help_text": "Quotation page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 759, + "fields": { + "key": "select_currency_note", + "body": "Select your preferred currency. Note: a package may not have a price set for your chosen currency.", + "help_text": "Quotation page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 760, + "fields": { + "key": "institution_details", + "body": "Your institution Details", + "help_text": "Quotation page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 761, + "fields": { + "key": "institutions_fte", + "body": "Institutions FTE", + "help_text": "Quotation page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 762, + "fields": { + "key": "fte_student_count_prompt", + "body": "Please enter your institution's full time equivalent student count.", + "help_text": "Quotation page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 763, + "fields": { + "key": "jisc_banding", + "body": "Jisc Banding", + "help_text": "Quotation page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 764, + "fields": { + "key": "jisc_banding_info", + "body": "*UK institutions can find their Jisc band here. If you do not have a Jisc banding, please get in touch with us so we can provide you with a custom quotation.", + "help_text": "Quotation page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 765, + "fields": { + "key": "classification_by_size_income", + "body": "Classification by organisation size (FTE) & country income (based on World Bank classifications).", + "help_text": "Quotation page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 766, + "fields": { + "key": "org_size_country_classification_note", + "body": "*Organisation size is your full time equivalent student count. You can find the latest Country Classifications by income here.", + "help_text": "Quotation page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 767, + "fields": { + "key": "save_button", + "body": "Save ", + "help_text": "Save button on 'Contact details' page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 768, + "fields": { + "key": "email_address", + "body": "Email address*", + "help_text": "'Contact details' page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 770, + "fields": { + "key": "contact_name", + "body": "Contact name*", + "help_text": "'Contact details' page", + "rich_text": false + } + }, + { + "model": "cms.sitetext", + "pk": 771, + "fields": { + "key": "included_within_offers", + "body": "Included within these offers:", + "help_text": "Quotation page", + "rich_text": false + } + } +] \ No newline at end of file diff --git a/fixtures/site_text_7.json b/fixtures/site_text_7.json new file mode 100644 index 0000000..b1ecfa3 --- /dev/null +++ b/fixtures/site_text_7.json @@ -0,0 +1,13 @@ +[ + { + "model": "cms.sitetext", + "pk": 772, + "fields": { + "key": "your_currency", + "body": "Your Currency", + "frontend": true, + "help_text": "Basket page: 'Your currency'", + "rich_text": false + } + } +] \ No newline at end of file diff --git a/fixtures/test_package.json b/fixtures/test_package.json new file mode 100644 index 0000000..42f1db1 --- /dev/null +++ b/fixtures/test_package.json @@ -0,0 +1,18828 @@ +[ + { + "model": "initiatives.initiative", + "pk": 1, + "fields": { + "name": "Open Access Publishing", + "description": "Test initiative: Open Access Publishing", + "active": true, + "contact_email": "contact1@example.com", + "short_code": "I001" + } + }, + { + "model": "initiatives.initiative", + "pk": 2, + "fields": { + "name": "Digital Scholarship Fund", + "description": "Test initiative: Digital Scholarship Fund", + "active": true, + "contact_email": "contact2@example.com", + "short_code": "I002" + } + }, + { + "model": "initiatives.initiative", + "pk": 3, + "fields": { + "name": "Research Infrastructure Alliance", + "description": "Test initiative: Research Infrastructure Alliance", + "active": true, + "contact_email": "contact3@example.com", + "short_code": "I003" + } + }, + { + "model": "initiatives.initiative", + "pk": 4, + "fields": { + "name": "Global Knowledge Network", + "description": "Test initiative: Global Knowledge Network", + "active": true, + "contact_email": "contact4@example.com", + "short_code": "I004" + } + }, + { + "model": "initiatives.initiative", + "pk": 5, + "fields": { + "name": "Academic Commons Project", + "description": "Test initiative: Academic Commons Project", + "active": true, + "contact_email": "contact5@example.com", + "short_code": "I005" + } + }, + { + "model": "initiatives.initiative", + "pk": 6, + "fields": { + "name": "Scholarly Communication Hub", + "description": "Test initiative: Scholarly Communication Hub", + "active": true, + "contact_email": "contact6@example.com", + "short_code": "I006" + } + }, + { + "model": "initiatives.initiative", + "pk": 7, + "fields": { + "name": "Library Futures Initiative", + "description": "Test initiative: Library Futures Initiative", + "active": true, + "contact_email": "contact7@example.com", + "short_code": "I007" + } + }, + { + "model": "initiatives.initiative", + "pk": 8, + "fields": { + "name": "Open Science Collective", + "description": "Test initiative: Open Science Collective", + "active": true, + "contact_email": "contact8@example.com", + "short_code": "I008" + } + }, + { + "model": "initiatives.initiative", + "pk": 9, + "fields": { + "name": "Knowledge Equity Programme", + "description": "Test initiative: Knowledge Equity Programme", + "active": true, + "contact_email": "contact9@example.com", + "short_code": "I009" + } + }, + { + "model": "initiatives.initiative", + "pk": 10, + "fields": { + "name": "Institutional Repository Network", + "description": "Test initiative: Institutional Repository Network", + "active": true, + "contact_email": "contact10@example.com", + "short_code": "I010" + } + }, + { + "model": "initiatives.initiative", + "pk": 11, + "fields": { + "name": "Digital Preservation Trust", + "description": "Test initiative: Digital Preservation Trust", + "active": true, + "contact_email": "contact11@example.com", + "short_code": "I011" + } + }, + { + "model": "initiatives.initiative", + "pk": 12, + "fields": { + "name": "Open Monograph Press", + "description": "Test initiative: Open Monograph Press", + "active": true, + "contact_email": "contact12@example.com", + "short_code": "I012" + } + }, + { + "model": "initiatives.initiative", + "pk": 13, + "fields": { + "name": "Humanities Digital Library", + "description": "Test initiative: Humanities Digital Library", + "active": true, + "contact_email": "contact13@example.com", + "short_code": "I013" + } + }, + { + "model": "initiatives.initiative", + "pk": 14, + "fields": { + "name": "Science Access Foundation", + "description": "Test initiative: Science Access Foundation", + "active": true, + "contact_email": "contact14@example.com", + "short_code": "I014" + } + }, + { + "model": "initiatives.initiative", + "pk": 15, + "fields": { + "name": "Transformative Agreements Consortium", + "description": "Test initiative: Transformative Agreements Consortium", + "active": true, + "contact_email": "contact15@example.com", + "short_code": "I015" + } + }, + { + "model": "initiatives.initiative", + "pk": 16, + "fields": { + "name": "Community-Led Open Publishing", + "description": "Test initiative: Community-Led Open Publishing", + "active": true, + "contact_email": "contact16@example.com", + "short_code": "I016" + } + }, + { + "model": "initiatives.initiative", + "pk": 17, + "fields": { + "name": "Data Sharing Alliance", + "description": "Test initiative: Data Sharing Alliance", + "active": true, + "contact_email": "contact17@example.com", + "short_code": "I017" + } + }, + { + "model": "initiatives.initiative", + "pk": 18, + "fields": { + "name": "Open Educational Resources", + "description": "Test initiative: Open Educational Resources", + "active": true, + "contact_email": "contact18@example.com", + "short_code": "I018" + } + }, + { + "model": "initiatives.initiative", + "pk": 19, + "fields": { + "name": "Cross-Border Research Exchange", + "description": "Test initiative: Cross-Border Research Exchange", + "active": true, + "contact_email": "contact19@example.com", + "short_code": "I019" + } + }, + { + "model": "initiatives.initiative", + "pk": 20, + "fields": { + "name": "Sustainable Scholarship Fund", + "description": "Test initiative: Sustainable Scholarship Fund", + "active": true, + "contact_email": "contact20@example.com", + "short_code": "I020" + } + }, + { + "model": "package.country", + "pk": 999, + "fields": { + "name": "United Kingdom", + "code": "GB", + "currency": "GBP", + "catch_all": false + } + }, + { + "model": "package.country", + "pk": 998, + "fields": { + "name": "United States", + "code": "USA", + "currency": "USD", + "catch_all": false + } + }, + { + "model": "package.country", + "pk": 997, + "fields": { + "name": "Germany", + "code": "DE", + "currency": "EUR", + "catch_all": false + } + }, + { + "model": "package.bandingtype", + "pk": 100, + "fields": { + "is_fte": true, + "name": "Institution Size (FTE)", + "description": "Classification based on full-time equivalent student count", + "active": true, + "vocabs": [ + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107 + ] + } + }, + { + "model": "package.bandingtype", + "pk": 101, + "fields": { + "is_fte": false, + "name": "Country Income Classification", + "description": "Pricing based on World Bank country income classifications", + "active": true, + "vocabs": [ + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115 + ] + } + }, + { + "model": "vocab.bandingvocab", + "pk": 100, + "fields": { + "text": "Micro (0-1,000 FTE)", + "lower_limit": 0, + "upper_limit": 1000 + } + }, + { + "model": "vocab.bandingvocab", + "pk": 101, + "fields": { + "text": "Small (1,001-5,000 FTE)", + "lower_limit": 1001, + "upper_limit": 5000 + } + }, + { + "model": "vocab.bandingvocab", + "pk": 102, + "fields": { + "text": "Medium (5,001-10,000 FTE)", + "lower_limit": 5001, + "upper_limit": 10000 + } + }, + { + "model": "vocab.bandingvocab", + "pk": 103, + "fields": { + "text": "Large (10,001-20,000 FTE)", + "lower_limit": 10001, + "upper_limit": 20000 + } + }, + { + "model": "vocab.bandingvocab", + "pk": 104, + "fields": { + "text": "Very Large (20,001-35,000 FTE)", + "lower_limit": 20001, + "upper_limit": 35000 + } + }, + { + "model": "vocab.bandingvocab", + "pk": 105, + "fields": { + "text": "Extra Large (35,001-50,000 FTE)", + "lower_limit": 35001, + "upper_limit": 50000 + } + }, + { + "model": "vocab.bandingvocab", + "pk": 106, + "fields": { + "text": "Mega (50,001-100,000 FTE)", + "lower_limit": 50001, + "upper_limit": 100000 + } + }, + { + "model": "vocab.bandingvocab", + "pk": 107, + "fields": { + "text": "Consortium (100,001+ FTE)", + "lower_limit": 100001, + "upper_limit": null + } + }, + { + "model": "vocab.bandingvocab", + "pk": 108, + "fields": { + "text": "Band A - Least Developed", + "lower_limit": null, + "upper_limit": null + } + }, + { + "model": "vocab.bandingvocab", + "pk": 109, + "fields": { + "text": "Band B - Low Income", + "lower_limit": null, + "upper_limit": null + } + }, + { + "model": "vocab.bandingvocab", + "pk": 110, + "fields": { + "text": "Band C - Lower-Middle Income", + "lower_limit": null, + "upper_limit": null + } + }, + { + "model": "vocab.bandingvocab", + "pk": 111, + "fields": { + "text": "Band D - Upper-Middle Income", + "lower_limit": null, + "upper_limit": null + } + }, + { + "model": "vocab.bandingvocab", + "pk": 112, + "fields": { + "text": "Band E - High Income (Developing)", + "lower_limit": null, + "upper_limit": null + } + }, + { + "model": "vocab.bandingvocab", + "pk": 113, + "fields": { + "text": "Band F - High Income (OECD)", + "lower_limit": null, + "upper_limit": null + } + }, + { + "model": "vocab.bandingvocab", + "pk": 114, + "fields": { + "text": "Band G - Premium", + "lower_limit": null, + "upper_limit": null + } + }, + { + "model": "vocab.bandingvocab", + "pk": 115, + "fields": { + "text": "Band H - Elite", + "lower_limit": null, + "upper_limit": null + } + }, + { + "model": "package.basepackage", + "pk": 100, + "fields": { + "name": "Package 100 - Core (FTE+Choice/all)", + "description": "Test package 100", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 100, + "fields": { + "basepackage_ptr": 100, + "initiative": 1, + "default_country": 997, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 100, + "fields": { + "package": 100, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 100, + "fields": { + "package": 100, + "banding_type_entry": 100, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 101, + "fields": { + "package": 100, + "banding_type_entry": 100, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 102, + "fields": { + "package": 100, + "banding_type_entry": 100, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 100, + "fields": { + "package": 100, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 100, + "fields": { + "banding": 100, + "country": 999, + "value": 50, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 101, + "fields": { + "banding": 100, + "country": 998, + "value": 65, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 102, + "fields": { + "banding": 100, + "country": 997, + "value": 57, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 101, + "fields": { + "package": 100, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 103, + "fields": { + "banding": 101, + "country": 999, + "value": 80, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 104, + "fields": { + "banding": 101, + "country": 998, + "value": 104, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 105, + "fields": { + "banding": 101, + "country": 997, + "value": 92, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 102, + "fields": { + "package": 100, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 106, + "fields": { + "banding": 102, + "country": 999, + "value": 120, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 107, + "fields": { + "banding": 102, + "country": 998, + "value": 156, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 108, + "fields": { + "banding": 102, + "country": 997, + "value": 138, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 103, + "fields": { + "package": 100, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 109, + "fields": { + "banding": 103, + "country": 999, + "value": 160, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 110, + "fields": { + "banding": 103, + "country": 998, + "value": 208, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 111, + "fields": { + "banding": 103, + "country": 997, + "value": 184, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 104, + "fields": { + "package": 100, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 112, + "fields": { + "banding": 104, + "country": 999, + "value": 200, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 113, + "fields": { + "banding": 104, + "country": 998, + "value": 260, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 114, + "fields": { + "banding": 104, + "country": 997, + "value": 230, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 105, + "fields": { + "package": 100, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 115, + "fields": { + "banding": 105, + "country": 999, + "value": 260, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 116, + "fields": { + "banding": 105, + "country": 998, + "value": 338, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 117, + "fields": { + "banding": 105, + "country": 997, + "value": 299, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 106, + "fields": { + "package": 100, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 118, + "fields": { + "banding": 106, + "country": 999, + "value": 340, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 119, + "fields": { + "banding": 106, + "country": 998, + "value": 442, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 120, + "fields": { + "banding": 106, + "country": 997, + "value": 391, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 107, + "fields": { + "package": 100, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 121, + "fields": { + "banding": 107, + "country": 999, + "value": 440, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 122, + "fields": { + "banding": 107, + "country": 998, + "value": 572, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 123, + "fields": { + "banding": 107, + "country": 997, + "value": 506, + "default_currency": "EUR" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 101, + "fields": { + "package": 100, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 103, + "fields": { + "package": 100, + "banding_type_entry": 101, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 104, + "fields": { + "package": 100, + "banding_type_entry": 101, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 105, + "fields": { + "package": 100, + "banding_type_entry": 101, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 108, + "fields": { + "package": 100, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 124, + "fields": { + "banding": 108, + "country": 999, + "value": 30, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 125, + "fields": { + "banding": 108, + "country": 998, + "value": 39, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 126, + "fields": { + "banding": 108, + "country": 997, + "value": 34, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 109, + "fields": { + "package": 100, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 127, + "fields": { + "banding": 109, + "country": 999, + "value": 50, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 128, + "fields": { + "banding": 109, + "country": 998, + "value": 65, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 129, + "fields": { + "banding": 109, + "country": 997, + "value": 57, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 110, + "fields": { + "package": 100, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 130, + "fields": { + "banding": 110, + "country": 999, + "value": 80, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 131, + "fields": { + "banding": 110, + "country": 998, + "value": 104, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 132, + "fields": { + "banding": 110, + "country": 997, + "value": 92, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 111, + "fields": { + "package": 100, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 133, + "fields": { + "banding": 111, + "country": 999, + "value": 110, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 134, + "fields": { + "banding": 111, + "country": 998, + "value": 143, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 135, + "fields": { + "banding": 111, + "country": 997, + "value": 126, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 112, + "fields": { + "package": 100, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 136, + "fields": { + "banding": 112, + "country": 999, + "value": 140, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 137, + "fields": { + "banding": 112, + "country": 998, + "value": 182, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 138, + "fields": { + "banding": 112, + "country": 997, + "value": 161, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 113, + "fields": { + "package": 100, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 139, + "fields": { + "banding": 113, + "country": 999, + "value": 170, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 140, + "fields": { + "banding": 113, + "country": 998, + "value": 221, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 141, + "fields": { + "banding": 113, + "country": 997, + "value": 195, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 114, + "fields": { + "package": 100, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 142, + "fields": { + "banding": 114, + "country": 999, + "value": 200, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 143, + "fields": { + "banding": 114, + "country": 998, + "value": 260, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 144, + "fields": { + "banding": 114, + "country": 997, + "value": 230, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 115, + "fields": { + "package": 100, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 145, + "fields": { + "banding": 115, + "country": 999, + "value": 280, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 146, + "fields": { + "banding": 115, + "country": 998, + "value": 364, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 147, + "fields": { + "banding": 115, + "country": 997, + "value": 322, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 101, + "fields": { + "name": "Package 101 - Plus (FTE/GBP)", + "description": "Test package 101", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 101, + "fields": { + "basepackage_ptr": 101, + "initiative": 1, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 102, + "fields": { + "package": 101, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 106, + "fields": { + "package": 101, + "banding_type_entry": 102, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 116, + "fields": { + "package": 101, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 148, + "fields": { + "banding": 116, + "country": 999, + "value": 74, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 117, + "fields": { + "package": 101, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 149, + "fields": { + "banding": 117, + "country": 999, + "value": 119, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 118, + "fields": { + "package": 101, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 150, + "fields": { + "banding": 118, + "country": 999, + "value": 178, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 119, + "fields": { + "package": 101, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 151, + "fields": { + "banding": 119, + "country": 999, + "value": 238, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 120, + "fields": { + "package": 101, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 152, + "fields": { + "banding": 120, + "country": 999, + "value": 297, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 121, + "fields": { + "package": 101, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 153, + "fields": { + "banding": 121, + "country": 999, + "value": 386, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 122, + "fields": { + "package": 101, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 154, + "fields": { + "banding": 122, + "country": 999, + "value": 505, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 123, + "fields": { + "package": 101, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 155, + "fields": { + "banding": 123, + "country": 999, + "value": 653, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 102, + "fields": { + "name": "Package 102 - Premium (FTE/GBP+USD)", + "description": "Test package 102", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 102, + "fields": { + "basepackage_ptr": 102, + "initiative": 1, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 103, + "fields": { + "package": 102, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 107, + "fields": { + "package": 102, + "banding_type_entry": 103, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 108, + "fields": { + "package": 102, + "banding_type_entry": 103, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 124, + "fields": { + "package": 102, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 156, + "fields": { + "banding": 124, + "country": 999, + "value": 98, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 157, + "fields": { + "banding": 124, + "country": 998, + "value": 128, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 125, + "fields": { + "package": 102, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 158, + "fields": { + "banding": 125, + "country": 999, + "value": 158, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 159, + "fields": { + "banding": 125, + "country": 998, + "value": 205, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 126, + "fields": { + "package": 102, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 160, + "fields": { + "banding": 126, + "country": 999, + "value": 236, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 161, + "fields": { + "banding": 126, + "country": 998, + "value": 307, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 127, + "fields": { + "package": 102, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 162, + "fields": { + "banding": 127, + "country": 999, + "value": 315, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 163, + "fields": { + "banding": 127, + "country": 998, + "value": 410, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 128, + "fields": { + "package": 102, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 164, + "fields": { + "banding": 128, + "country": 999, + "value": 394, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 165, + "fields": { + "banding": 128, + "country": 998, + "value": 512, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 129, + "fields": { + "package": 102, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 166, + "fields": { + "banding": 129, + "country": 999, + "value": 512, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 167, + "fields": { + "banding": 129, + "country": 998, + "value": 666, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 130, + "fields": { + "package": 102, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 168, + "fields": { + "banding": 130, + "country": 999, + "value": 670, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 169, + "fields": { + "banding": 130, + "country": 998, + "value": 871, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 131, + "fields": { + "package": 102, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 170, + "fields": { + "banding": 131, + "country": 999, + "value": 867, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 171, + "fields": { + "banding": 131, + "country": 998, + "value": 1127, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 103, + "fields": { + "name": "Package 103 - Standard (Choice/GBP)", + "description": "Test package 103", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 103, + "fields": { + "basepackage_ptr": 103, + "initiative": 2, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 104, + "fields": { + "package": 103, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 109, + "fields": { + "package": 103, + "banding_type_entry": 104, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 132, + "fields": { + "package": 103, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 172, + "fields": { + "banding": 132, + "country": 999, + "value": 74, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 133, + "fields": { + "package": 103, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 173, + "fields": { + "banding": 133, + "country": 999, + "value": 123, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 134, + "fields": { + "package": 103, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 174, + "fields": { + "banding": 134, + "country": 999, + "value": 196, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 135, + "fields": { + "package": 103, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 175, + "fields": { + "banding": 135, + "country": 999, + "value": 270, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 136, + "fields": { + "package": 103, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 176, + "fields": { + "banding": 136, + "country": 999, + "value": 344, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 137, + "fields": { + "package": 103, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 177, + "fields": { + "banding": 137, + "country": 999, + "value": 417, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 138, + "fields": { + "package": 103, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 178, + "fields": { + "banding": 138, + "country": 999, + "value": 491, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 139, + "fields": { + "package": 103, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 179, + "fields": { + "banding": 139, + "country": 999, + "value": 687, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 104, + "fields": { + "name": "Package 104 - Basic (FTE+Choice/GBP+USD)", + "description": "Test package 104", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 104, + "fields": { + "basepackage_ptr": 104, + "initiative": 2, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 105, + "fields": { + "package": 104, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 110, + "fields": { + "package": 104, + "banding_type_entry": 105, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 111, + "fields": { + "package": 104, + "banding_type_entry": 105, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 140, + "fields": { + "package": 104, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 180, + "fields": { + "banding": 140, + "country": 999, + "value": 147, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 181, + "fields": { + "banding": 140, + "country": 998, + "value": 191, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 141, + "fields": { + "package": 104, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 182, + "fields": { + "banding": 141, + "country": 999, + "value": 235, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 183, + "fields": { + "banding": 141, + "country": 998, + "value": 306, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 142, + "fields": { + "package": 104, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 184, + "fields": { + "banding": 142, + "country": 999, + "value": 353, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 185, + "fields": { + "banding": 142, + "country": 998, + "value": 459, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 143, + "fields": { + "package": 104, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 186, + "fields": { + "banding": 143, + "country": 999, + "value": 470, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 187, + "fields": { + "banding": 143, + "country": 998, + "value": 612, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 144, + "fields": { + "package": 104, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 188, + "fields": { + "banding": 144, + "country": 999, + "value": 588, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 189, + "fields": { + "banding": 144, + "country": 998, + "value": 764, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 145, + "fields": { + "package": 104, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 190, + "fields": { + "banding": 145, + "country": 999, + "value": 764, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 191, + "fields": { + "banding": 145, + "country": 998, + "value": 994, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 146, + "fields": { + "package": 104, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 192, + "fields": { + "banding": 146, + "country": 999, + "value": 1000, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 193, + "fields": { + "banding": 146, + "country": 998, + "value": 1299, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 147, + "fields": { + "package": 104, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 194, + "fields": { + "banding": 147, + "country": 999, + "value": 1294, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 195, + "fields": { + "banding": 147, + "country": 998, + "value": 1682, + "default_currency": "USD" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 106, + "fields": { + "package": 104, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 112, + "fields": { + "package": 104, + "banding_type_entry": 106, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 113, + "fields": { + "package": 104, + "banding_type_entry": 106, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 148, + "fields": { + "package": 104, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 196, + "fields": { + "banding": 148, + "country": 999, + "value": 88, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 197, + "fields": { + "banding": 148, + "country": 998, + "value": 115, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 149, + "fields": { + "package": 104, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 198, + "fields": { + "banding": 149, + "country": 999, + "value": 147, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 199, + "fields": { + "banding": 149, + "country": 998, + "value": 191, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 150, + "fields": { + "package": 104, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 200, + "fields": { + "banding": 150, + "country": 999, + "value": 235, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 201, + "fields": { + "banding": 150, + "country": 998, + "value": 306, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 151, + "fields": { + "package": 104, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 202, + "fields": { + "banding": 151, + "country": 999, + "value": 323, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 203, + "fields": { + "banding": 151, + "country": 998, + "value": 420, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 152, + "fields": { + "package": 104, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 204, + "fields": { + "banding": 152, + "country": 999, + "value": 412, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 205, + "fields": { + "banding": 152, + "country": 998, + "value": 535, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 153, + "fields": { + "package": 104, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 206, + "fields": { + "banding": 153, + "country": 999, + "value": 500, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 207, + "fields": { + "banding": 153, + "country": 998, + "value": 650, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 154, + "fields": { + "package": 104, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 208, + "fields": { + "banding": 154, + "country": 999, + "value": 588, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 209, + "fields": { + "banding": 154, + "country": 998, + "value": 764, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 155, + "fields": { + "package": 104, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 210, + "fields": { + "banding": 155, + "country": 999, + "value": 823, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 211, + "fields": { + "banding": 155, + "country": 998, + "value": 1070, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 105, + "fields": { + "name": "Package 105 - Advanced (FTE/GBP+EUR)", + "description": "Test package 105", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 105, + "fields": { + "basepackage_ptr": 105, + "initiative": 2, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 107, + "fields": { + "package": 105, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 114, + "fields": { + "package": 105, + "banding_type_entry": 107, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 115, + "fields": { + "package": 105, + "banding_type_entry": 107, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 156, + "fields": { + "package": 105, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 212, + "fields": { + "banding": 156, + "country": 999, + "value": 171, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 213, + "fields": { + "banding": 156, + "country": 997, + "value": 197, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 157, + "fields": { + "package": 105, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 214, + "fields": { + "banding": 157, + "country": 999, + "value": 274, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 215, + "fields": { + "banding": 157, + "country": 997, + "value": 315, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 158, + "fields": { + "package": 105, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 216, + "fields": { + "banding": 158, + "country": 999, + "value": 411, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 217, + "fields": { + "banding": 158, + "country": 997, + "value": 473, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 159, + "fields": { + "package": 105, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 218, + "fields": { + "banding": 159, + "country": 999, + "value": 548, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 219, + "fields": { + "banding": 159, + "country": 997, + "value": 630, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 160, + "fields": { + "package": 105, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 220, + "fields": { + "banding": 160, + "country": 999, + "value": 685, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 221, + "fields": { + "banding": 160, + "country": 997, + "value": 788, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 161, + "fields": { + "package": 105, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 222, + "fields": { + "banding": 161, + "country": 999, + "value": 890, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 223, + "fields": { + "banding": 161, + "country": 997, + "value": 1024, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 162, + "fields": { + "package": 105, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 224, + "fields": { + "banding": 162, + "country": 999, + "value": 1164, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 225, + "fields": { + "banding": 162, + "country": 997, + "value": 1339, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 163, + "fields": { + "package": 105, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 226, + "fields": { + "banding": 163, + "country": 999, + "value": 1507, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 227, + "fields": { + "banding": 163, + "country": 997, + "value": 1733, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 106, + "fields": { + "name": "Package 106 - Pro (Choice/USD)", + "description": "Test package 106", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 106, + "fields": { + "basepackage_ptr": 106, + "initiative": 3, + "default_country": 998, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 108, + "fields": { + "package": 106, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 116, + "fields": { + "package": 106, + "banding_type_entry": 108, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 164, + "fields": { + "package": 106, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 228, + "fields": { + "banding": 164, + "country": 998, + "value": 152, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 165, + "fields": { + "package": 106, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 229, + "fields": { + "banding": 165, + "country": 998, + "value": 254, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 166, + "fields": { + "package": 106, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 230, + "fields": { + "banding": 166, + "country": 998, + "value": 407, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 167, + "fields": { + "package": 106, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 231, + "fields": { + "banding": 167, + "country": 998, + "value": 559, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 168, + "fields": { + "package": 106, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 232, + "fields": { + "banding": 168, + "country": 998, + "value": 712, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 169, + "fields": { + "package": 106, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 233, + "fields": { + "banding": 169, + "country": 998, + "value": 864, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 170, + "fields": { + "package": 106, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 234, + "fields": { + "banding": 170, + "country": 998, + "value": 1017, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 171, + "fields": { + "package": 106, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 235, + "fields": { + "banding": 171, + "country": 998, + "value": 1423, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 107, + "fields": { + "name": "Package 107 - Essential (FTE+Choice/GBP)", + "description": "Test package 107", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 107, + "fields": { + "basepackage_ptr": 107, + "initiative": 3, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 109, + "fields": { + "package": 107, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 117, + "fields": { + "package": 107, + "banding_type_entry": 109, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 172, + "fields": { + "package": 107, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 236, + "fields": { + "banding": 172, + "country": 999, + "value": 220, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 173, + "fields": { + "package": 107, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 237, + "fields": { + "banding": 173, + "country": 999, + "value": 352, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 174, + "fields": { + "package": 107, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 238, + "fields": { + "banding": 174, + "country": 999, + "value": 527, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 175, + "fields": { + "package": 107, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 239, + "fields": { + "banding": 175, + "country": 999, + "value": 703, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 176, + "fields": { + "package": 107, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 240, + "fields": { + "banding": 176, + "country": 999, + "value": 879, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 177, + "fields": { + "package": 107, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 241, + "fields": { + "banding": 177, + "country": 999, + "value": 1143, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 178, + "fields": { + "package": 107, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 242, + "fields": { + "banding": 178, + "country": 999, + "value": 1494, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 179, + "fields": { + "package": 107, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 243, + "fields": { + "banding": 179, + "country": 999, + "value": 1934, + "default_currency": "GBP" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 110, + "fields": { + "package": 107, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 118, + "fields": { + "package": 107, + "banding_type_entry": 110, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 180, + "fields": { + "package": 107, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 244, + "fields": { + "banding": 180, + "country": 999, + "value": 132, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 181, + "fields": { + "package": 107, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 245, + "fields": { + "banding": 181, + "country": 999, + "value": 220, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 182, + "fields": { + "package": 107, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 246, + "fields": { + "banding": 182, + "country": 999, + "value": 352, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 183, + "fields": { + "package": 107, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 247, + "fields": { + "banding": 183, + "country": 999, + "value": 483, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 184, + "fields": { + "package": 107, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 248, + "fields": { + "banding": 184, + "country": 999, + "value": 615, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 185, + "fields": { + "package": 107, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 249, + "fields": { + "banding": 185, + "country": 999, + "value": 747, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 186, + "fields": { + "package": 107, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 250, + "fields": { + "banding": 186, + "country": 999, + "value": 879, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 187, + "fields": { + "package": 107, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 251, + "fields": { + "banding": 187, + "country": 999, + "value": 1231, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 108, + "fields": { + "name": "Package 108 - Complete (FTE/EUR)", + "description": "Test package 108", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 108, + "fields": { + "basepackage_ptr": 108, + "initiative": 3, + "default_country": 997, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 111, + "fields": { + "package": 108, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 119, + "fields": { + "package": 108, + "banding_type_entry": 111, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 188, + "fields": { + "package": 108, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 252, + "fields": { + "banding": 188, + "country": 997, + "value": 281, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 189, + "fields": { + "package": 108, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 253, + "fields": { + "banding": 189, + "country": 997, + "value": 449, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 190, + "fields": { + "package": 108, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 254, + "fields": { + "banding": 190, + "country": 997, + "value": 673, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 191, + "fields": { + "package": 108, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 255, + "fields": { + "banding": 191, + "country": 997, + "value": 898, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 192, + "fields": { + "package": 108, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 256, + "fields": { + "banding": 192, + "country": 997, + "value": 1122, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 193, + "fields": { + "package": 108, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 257, + "fields": { + "banding": 193, + "country": 997, + "value": 1459, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 194, + "fields": { + "package": 108, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 258, + "fields": { + "banding": 194, + "country": 997, + "value": 1908, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 195, + "fields": { + "package": 108, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 259, + "fields": { + "banding": 195, + "country": 997, + "value": 2469, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 109, + "fields": { + "name": "Package 109 - Select (Choice/GBP+USD)", + "description": "Test package 109", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 109, + "fields": { + "basepackage_ptr": 109, + "initiative": 4, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 112, + "fields": { + "package": 109, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 120, + "fields": { + "package": 109, + "banding_type_entry": 112, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 121, + "fields": { + "package": 109, + "banding_type_entry": 112, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 196, + "fields": { + "package": 109, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 260, + "fields": { + "banding": 196, + "country": 999, + "value": 161, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 261, + "fields": { + "banding": 196, + "country": 998, + "value": 209, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 197, + "fields": { + "package": 109, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 262, + "fields": { + "banding": 197, + "country": 999, + "value": 268, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 263, + "fields": { + "banding": 197, + "country": 998, + "value": 349, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 198, + "fields": { + "package": 109, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 264, + "fields": { + "banding": 198, + "country": 999, + "value": 429, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 265, + "fields": { + "banding": 198, + "country": 998, + "value": 558, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 199, + "fields": { + "package": 109, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 266, + "fields": { + "banding": 199, + "country": 999, + "value": 590, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 267, + "fields": { + "banding": 199, + "country": 998, + "value": 767, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 200, + "fields": { + "package": 109, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 268, + "fields": { + "banding": 200, + "country": 999, + "value": 751, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 269, + "fields": { + "banding": 200, + "country": 998, + "value": 976, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 201, + "fields": { + "package": 109, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 270, + "fields": { + "banding": 201, + "country": 999, + "value": 912, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 271, + "fields": { + "banding": 201, + "country": 998, + "value": 1186, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 202, + "fields": { + "package": 109, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 272, + "fields": { + "banding": 202, + "country": 999, + "value": 1073, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 273, + "fields": { + "banding": 202, + "country": 998, + "value": 1395, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 203, + "fields": { + "package": 109, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 274, + "fields": { + "banding": 203, + "country": 999, + "value": 1502, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 275, + "fields": { + "banding": 203, + "country": 998, + "value": 1953, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 110, + "fields": { + "name": "Package 110 - Core (FTE+Choice/all)", + "description": "Test package 110", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 110, + "fields": { + "basepackage_ptr": 110, + "initiative": 4, + "default_country": 997, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 113, + "fields": { + "package": 110, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 122, + "fields": { + "package": 110, + "banding_type_entry": 113, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 123, + "fields": { + "package": 110, + "banding_type_entry": 113, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 124, + "fields": { + "package": 110, + "banding_type_entry": 113, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 204, + "fields": { + "package": 110, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 276, + "fields": { + "banding": 204, + "country": 999, + "value": 292, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 277, + "fields": { + "banding": 204, + "country": 998, + "value": 380, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 278, + "fields": { + "banding": 204, + "country": 997, + "value": 336, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 205, + "fields": { + "package": 110, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 279, + "fields": { + "banding": 205, + "country": 999, + "value": 468, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 280, + "fields": { + "banding": 205, + "country": 998, + "value": 608, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 281, + "fields": { + "banding": 205, + "country": 997, + "value": 538, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 206, + "fields": { + "package": 110, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 282, + "fields": { + "banding": 206, + "country": 999, + "value": 702, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 283, + "fields": { + "banding": 206, + "country": 998, + "value": 913, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 284, + "fields": { + "banding": 206, + "country": 997, + "value": 807, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 207, + "fields": { + "package": 110, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 285, + "fields": { + "banding": 207, + "country": 999, + "value": 936, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 286, + "fields": { + "banding": 207, + "country": 998, + "value": 1217, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 287, + "fields": { + "banding": 207, + "country": 997, + "value": 1076, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 208, + "fields": { + "package": 110, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 288, + "fields": { + "banding": 208, + "country": 999, + "value": 1170, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 289, + "fields": { + "banding": 208, + "country": 998, + "value": 1521, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 290, + "fields": { + "banding": 208, + "country": 997, + "value": 1346, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 209, + "fields": { + "package": 110, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 291, + "fields": { + "banding": 209, + "country": 999, + "value": 1521, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 292, + "fields": { + "banding": 209, + "country": 998, + "value": 1977, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 293, + "fields": { + "banding": 209, + "country": 997, + "value": 1749, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 210, + "fields": { + "package": 110, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 294, + "fields": { + "banding": 210, + "country": 999, + "value": 1989, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 295, + "fields": { + "banding": 210, + "country": 998, + "value": 2586, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 296, + "fields": { + "banding": 210, + "country": 997, + "value": 2287, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 211, + "fields": { + "package": 110, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 297, + "fields": { + "banding": 211, + "country": 999, + "value": 2574, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 298, + "fields": { + "banding": 211, + "country": 998, + "value": 3346, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 299, + "fields": { + "banding": 211, + "country": 997, + "value": 2960, + "default_currency": "EUR" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 114, + "fields": { + "package": 110, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 125, + "fields": { + "package": 110, + "banding_type_entry": 114, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 126, + "fields": { + "package": 110, + "banding_type_entry": 114, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 127, + "fields": { + "package": 110, + "banding_type_entry": 114, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 212, + "fields": { + "package": 110, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 300, + "fields": { + "banding": 212, + "country": 999, + "value": 176, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 301, + "fields": { + "banding": 212, + "country": 998, + "value": 228, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 302, + "fields": { + "banding": 212, + "country": 997, + "value": 202, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 213, + "fields": { + "package": 110, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 303, + "fields": { + "banding": 213, + "country": 999, + "value": 292, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 304, + "fields": { + "banding": 213, + "country": 998, + "value": 380, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 305, + "fields": { + "banding": 213, + "country": 997, + "value": 336, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 214, + "fields": { + "package": 110, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 306, + "fields": { + "banding": 214, + "country": 999, + "value": 468, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 307, + "fields": { + "banding": 214, + "country": 998, + "value": 608, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 308, + "fields": { + "banding": 214, + "country": 997, + "value": 538, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 215, + "fields": { + "package": 110, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 309, + "fields": { + "banding": 215, + "country": 999, + "value": 644, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 310, + "fields": { + "banding": 215, + "country": 998, + "value": 837, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 311, + "fields": { + "banding": 215, + "country": 997, + "value": 740, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 216, + "fields": { + "package": 110, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 312, + "fields": { + "banding": 216, + "country": 999, + "value": 819, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 313, + "fields": { + "banding": 216, + "country": 998, + "value": 1065, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 314, + "fields": { + "banding": 216, + "country": 997, + "value": 942, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 217, + "fields": { + "package": 110, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 315, + "fields": { + "banding": 217, + "country": 999, + "value": 994, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 316, + "fields": { + "banding": 217, + "country": 998, + "value": 1293, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 317, + "fields": { + "banding": 217, + "country": 997, + "value": 1144, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 218, + "fields": { + "package": 110, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 318, + "fields": { + "banding": 218, + "country": 999, + "value": 1170, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 319, + "fields": { + "banding": 218, + "country": 998, + "value": 1521, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 320, + "fields": { + "banding": 218, + "country": 997, + "value": 1346, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 219, + "fields": { + "package": 110, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 321, + "fields": { + "banding": 219, + "country": 999, + "value": 1638, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 322, + "fields": { + "banding": 219, + "country": 998, + "value": 2129, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 323, + "fields": { + "banding": 219, + "country": 997, + "value": 1884, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 111, + "fields": { + "name": "Package 111 - Plus (FTE/GBP)", + "description": "Test package 111", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 111, + "fields": { + "basepackage_ptr": 111, + "initiative": 4, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 115, + "fields": { + "package": 111, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 128, + "fields": { + "package": 111, + "banding_type_entry": 115, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 220, + "fields": { + "package": 111, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 324, + "fields": { + "banding": 220, + "country": 999, + "value": 317, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 221, + "fields": { + "package": 111, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 325, + "fields": { + "banding": 221, + "country": 999, + "value": 507, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 222, + "fields": { + "package": 111, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 326, + "fields": { + "banding": 222, + "country": 999, + "value": 760, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 223, + "fields": { + "package": 111, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 327, + "fields": { + "banding": 223, + "country": 999, + "value": 1014, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 224, + "fields": { + "package": 111, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 328, + "fields": { + "banding": 224, + "country": 999, + "value": 1267, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 225, + "fields": { + "package": 111, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 329, + "fields": { + "banding": 225, + "country": 999, + "value": 1647, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 226, + "fields": { + "package": 111, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 330, + "fields": { + "banding": 226, + "country": 999, + "value": 2154, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 227, + "fields": { + "package": 111, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 331, + "fields": { + "banding": 227, + "country": 999, + "value": 2787, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 112, + "fields": { + "name": "Package 112 - Premium (FTE/GBP+USD)", + "description": "Test package 112", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 112, + "fields": { + "basepackage_ptr": 112, + "initiative": 5, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 116, + "fields": { + "package": 112, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 129, + "fields": { + "package": 112, + "banding_type_entry": 116, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 130, + "fields": { + "package": 112, + "banding_type_entry": 116, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 228, + "fields": { + "package": 112, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 332, + "fields": { + "banding": 228, + "country": 999, + "value": 341, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 333, + "fields": { + "banding": 228, + "country": 998, + "value": 443, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 229, + "fields": { + "package": 112, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 334, + "fields": { + "banding": 229, + "country": 999, + "value": 546, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 335, + "fields": { + "banding": 229, + "country": 998, + "value": 709, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 230, + "fields": { + "package": 112, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 336, + "fields": { + "banding": 230, + "country": 999, + "value": 818, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 337, + "fields": { + "banding": 230, + "country": 998, + "value": 1064, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 231, + "fields": { + "package": 112, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 338, + "fields": { + "banding": 231, + "country": 999, + "value": 1091, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 339, + "fields": { + "banding": 231, + "country": 998, + "value": 1419, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 232, + "fields": { + "package": 112, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 340, + "fields": { + "banding": 232, + "country": 999, + "value": 1364, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 341, + "fields": { + "banding": 232, + "country": 998, + "value": 1773, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 233, + "fields": { + "package": 112, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 342, + "fields": { + "banding": 233, + "country": 999, + "value": 1773, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 343, + "fields": { + "banding": 233, + "country": 998, + "value": 2305, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 234, + "fields": { + "package": 112, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 344, + "fields": { + "banding": 234, + "country": 999, + "value": 2319, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 345, + "fields": { + "banding": 234, + "country": 998, + "value": 3014, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 235, + "fields": { + "package": 112, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 346, + "fields": { + "banding": 235, + "country": 999, + "value": 3001, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 347, + "fields": { + "banding": 235, + "country": 998, + "value": 3901, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 113, + "fields": { + "name": "Package 113 - Standard (Choice/GBP)", + "description": "Test package 113", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 113, + "fields": { + "basepackage_ptr": 113, + "initiative": 5, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 117, + "fields": { + "package": 113, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 131, + "fields": { + "package": 113, + "banding_type_entry": 117, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 236, + "fields": { + "package": 113, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 348, + "fields": { + "banding": 236, + "country": 999, + "value": 219, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 237, + "fields": { + "package": 113, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 349, + "fields": { + "banding": 237, + "country": 999, + "value": 365, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 238, + "fields": { + "package": 113, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 350, + "fields": { + "banding": 238, + "country": 999, + "value": 584, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 239, + "fields": { + "package": 113, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 351, + "fields": { + "banding": 239, + "country": 999, + "value": 804, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 240, + "fields": { + "package": 113, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 352, + "fields": { + "banding": 240, + "country": 999, + "value": 1023, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 241, + "fields": { + "package": 113, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 353, + "fields": { + "banding": 241, + "country": 999, + "value": 1242, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 242, + "fields": { + "package": 113, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 354, + "fields": { + "banding": 242, + "country": 999, + "value": 1461, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 243, + "fields": { + "package": 113, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 355, + "fields": { + "banding": 243, + "country": 999, + "value": 2045, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 114, + "fields": { + "name": "Package 114 - Basic (FTE+Choice/GBP+USD)", + "description": "Test package 114", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 114, + "fields": { + "basepackage_ptr": 114, + "initiative": 5, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 118, + "fields": { + "package": 114, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 132, + "fields": { + "package": 114, + "banding_type_entry": 118, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 133, + "fields": { + "package": 114, + "banding_type_entry": 118, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 244, + "fields": { + "package": 114, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 356, + "fields": { + "banding": 244, + "country": 999, + "value": 390, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 357, + "fields": { + "banding": 244, + "country": 998, + "value": 506, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 245, + "fields": { + "package": 114, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 358, + "fields": { + "banding": 245, + "country": 999, + "value": 623, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 359, + "fields": { + "banding": 245, + "country": 998, + "value": 810, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 246, + "fields": { + "package": 114, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 360, + "fields": { + "banding": 246, + "country": 999, + "value": 935, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 361, + "fields": { + "banding": 246, + "country": 998, + "value": 1215, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 247, + "fields": { + "package": 114, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 362, + "fields": { + "banding": 247, + "country": 999, + "value": 1246, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 363, + "fields": { + "banding": 247, + "country": 998, + "value": 1620, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 248, + "fields": { + "package": 114, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 364, + "fields": { + "banding": 248, + "country": 999, + "value": 1558, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 365, + "fields": { + "banding": 248, + "country": 998, + "value": 2025, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 249, + "fields": { + "package": 114, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 366, + "fields": { + "banding": 249, + "country": 999, + "value": 2025, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 367, + "fields": { + "banding": 249, + "country": 998, + "value": 2633, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 250, + "fields": { + "package": 114, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 368, + "fields": { + "banding": 250, + "country": 999, + "value": 2649, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 369, + "fields": { + "banding": 250, + "country": 998, + "value": 3443, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 251, + "fields": { + "package": 114, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 370, + "fields": { + "banding": 251, + "country": 999, + "value": 3428, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 371, + "fields": { + "banding": 251, + "country": 998, + "value": 4456, + "default_currency": "USD" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 119, + "fields": { + "package": 114, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 134, + "fields": { + "package": 114, + "banding_type_entry": 119, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 135, + "fields": { + "package": 114, + "banding_type_entry": 119, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 252, + "fields": { + "package": 114, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 372, + "fields": { + "banding": 252, + "country": 999, + "value": 234, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 373, + "fields": { + "banding": 252, + "country": 998, + "value": 304, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 253, + "fields": { + "package": 114, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 374, + "fields": { + "banding": 253, + "country": 999, + "value": 390, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 375, + "fields": { + "banding": 253, + "country": 998, + "value": 506, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 254, + "fields": { + "package": 114, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 376, + "fields": { + "banding": 254, + "country": 999, + "value": 623, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 377, + "fields": { + "banding": 254, + "country": 998, + "value": 810, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 255, + "fields": { + "package": 114, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 378, + "fields": { + "banding": 255, + "country": 999, + "value": 857, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 379, + "fields": { + "banding": 255, + "country": 998, + "value": 1114, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 256, + "fields": { + "package": 114, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 380, + "fields": { + "banding": 256, + "country": 999, + "value": 1091, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 381, + "fields": { + "banding": 256, + "country": 998, + "value": 1418, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 257, + "fields": { + "package": 114, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 382, + "fields": { + "banding": 257, + "country": 999, + "value": 1324, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 383, + "fields": { + "banding": 257, + "country": 998, + "value": 1722, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 258, + "fields": { + "package": 114, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 384, + "fields": { + "banding": 258, + "country": 999, + "value": 1558, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 385, + "fields": { + "banding": 258, + "country": 998, + "value": 2025, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 259, + "fields": { + "package": 114, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 386, + "fields": { + "banding": 259, + "country": 999, + "value": 2181, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 387, + "fields": { + "banding": 259, + "country": 998, + "value": 2836, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 115, + "fields": { + "name": "Package 115 - Advanced (FTE/GBP+EUR)", + "description": "Test package 115", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 115, + "fields": { + "basepackage_ptr": 115, + "initiative": 6, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 120, + "fields": { + "package": 115, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 136, + "fields": { + "package": 115, + "banding_type_entry": 120, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 137, + "fields": { + "package": 115, + "banding_type_entry": 120, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 260, + "fields": { + "package": 115, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 388, + "fields": { + "banding": 260, + "country": 999, + "value": 414, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 389, + "fields": { + "banding": 260, + "country": 997, + "value": 476, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 261, + "fields": { + "package": 115, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 390, + "fields": { + "banding": 261, + "country": 999, + "value": 662, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 391, + "fields": { + "banding": 261, + "country": 997, + "value": 761, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 262, + "fields": { + "package": 115, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 392, + "fields": { + "banding": 262, + "country": 999, + "value": 993, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 393, + "fields": { + "banding": 262, + "country": 997, + "value": 1142, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 263, + "fields": { + "package": 115, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 394, + "fields": { + "banding": 263, + "country": 999, + "value": 1324, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 395, + "fields": { + "banding": 263, + "country": 997, + "value": 1523, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 264, + "fields": { + "package": 115, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 396, + "fields": { + "banding": 264, + "country": 999, + "value": 1655, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 397, + "fields": { + "banding": 264, + "country": 997, + "value": 1903, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 265, + "fields": { + "package": 115, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 398, + "fields": { + "banding": 265, + "country": 999, + "value": 2152, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 399, + "fields": { + "banding": 265, + "country": 997, + "value": 2474, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 266, + "fields": { + "package": 115, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 400, + "fields": { + "banding": 266, + "country": 999, + "value": 2814, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 401, + "fields": { + "banding": 266, + "country": 997, + "value": 3236, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 267, + "fields": { + "package": 115, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 402, + "fields": { + "banding": 267, + "country": 999, + "value": 3641, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 403, + "fields": { + "banding": 267, + "country": 997, + "value": 4187, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 116, + "fields": { + "name": "Package 116 - Pro (Choice/USD)", + "description": "Test package 116", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 116, + "fields": { + "basepackage_ptr": 116, + "initiative": 6, + "default_country": 998, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 121, + "fields": { + "package": 116, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 138, + "fields": { + "package": 116, + "banding_type_entry": 121, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 268, + "fields": { + "package": 116, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 404, + "fields": { + "banding": 268, + "country": 998, + "value": 342, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 269, + "fields": { + "package": 116, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 405, + "fields": { + "banding": 269, + "country": 998, + "value": 569, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 270, + "fields": { + "package": 116, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 406, + "fields": { + "banding": 270, + "country": 998, + "value": 911, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 271, + "fields": { + "package": 116, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 407, + "fields": { + "banding": 271, + "country": 998, + "value": 1253, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 272, + "fields": { + "package": 116, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 408, + "fields": { + "banding": 272, + "country": 998, + "value": 1594, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 273, + "fields": { + "package": 116, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 409, + "fields": { + "banding": 273, + "country": 998, + "value": 1936, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 274, + "fields": { + "package": 116, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 410, + "fields": { + "banding": 274, + "country": 998, + "value": 2278, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 275, + "fields": { + "package": 116, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 411, + "fields": { + "banding": 275, + "country": 998, + "value": 3189, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 117, + "fields": { + "name": "Package 117 - Essential (FTE+Choice/GBP)", + "description": "Test package 117", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 117, + "fields": { + "basepackage_ptr": 117, + "initiative": 6, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 122, + "fields": { + "package": 117, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 139, + "fields": { + "package": 117, + "banding_type_entry": 122, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 276, + "fields": { + "package": 117, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 412, + "fields": { + "banding": 276, + "country": 999, + "value": 462, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 277, + "fields": { + "package": 117, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 413, + "fields": { + "banding": 277, + "country": 999, + "value": 740, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 278, + "fields": { + "package": 117, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 414, + "fields": { + "banding": 278, + "country": 999, + "value": 1109, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 279, + "fields": { + "package": 117, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 415, + "fields": { + "banding": 279, + "country": 999, + "value": 1479, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 280, + "fields": { + "package": 117, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 416, + "fields": { + "banding": 280, + "country": 999, + "value": 1849, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 281, + "fields": { + "package": 117, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 417, + "fields": { + "banding": 281, + "country": 999, + "value": 2404, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 282, + "fields": { + "package": 117, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 418, + "fields": { + "banding": 282, + "country": 999, + "value": 3143, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 283, + "fields": { + "package": 117, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 419, + "fields": { + "banding": 283, + "country": 999, + "value": 4068, + "default_currency": "GBP" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 123, + "fields": { + "package": 117, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 140, + "fields": { + "package": 117, + "banding_type_entry": 123, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 284, + "fields": { + "package": 117, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 420, + "fields": { + "banding": 284, + "country": 999, + "value": 277, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 285, + "fields": { + "package": 117, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 421, + "fields": { + "banding": 285, + "country": 999, + "value": 462, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 286, + "fields": { + "package": 117, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 422, + "fields": { + "banding": 286, + "country": 999, + "value": 740, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 287, + "fields": { + "package": 117, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 423, + "fields": { + "banding": 287, + "country": 999, + "value": 1017, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 288, + "fields": { + "package": 117, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 424, + "fields": { + "banding": 288, + "country": 999, + "value": 1294, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 289, + "fields": { + "package": 117, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 425, + "fields": { + "banding": 289, + "country": 999, + "value": 1572, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 290, + "fields": { + "package": 117, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 426, + "fields": { + "banding": 290, + "country": 999, + "value": 1849, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 291, + "fields": { + "package": 117, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 427, + "fields": { + "banding": 291, + "country": 999, + "value": 2589, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 118, + "fields": { + "name": "Package 118 - Complete (FTE/EUR)", + "description": "Test package 118", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 118, + "fields": { + "basepackage_ptr": 118, + "initiative": 7, + "default_country": 997, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 124, + "fields": { + "package": 118, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 141, + "fields": { + "package": 118, + "banding_type_entry": 124, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 292, + "fields": { + "package": 118, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 428, + "fields": { + "banding": 292, + "country": 997, + "value": 559, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 293, + "fields": { + "package": 118, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 429, + "fields": { + "banding": 293, + "country": 997, + "value": 895, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 294, + "fields": { + "package": 118, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 430, + "fields": { + "banding": 294, + "country": 997, + "value": 1343, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 295, + "fields": { + "package": 118, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 431, + "fields": { + "banding": 295, + "country": 997, + "value": 1790, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 296, + "fields": { + "package": 118, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 432, + "fields": { + "banding": 296, + "country": 997, + "value": 2238, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 297, + "fields": { + "package": 118, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 433, + "fields": { + "banding": 297, + "country": 997, + "value": 2909, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 298, + "fields": { + "package": 118, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 434, + "fields": { + "banding": 298, + "country": 997, + "value": 3804, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 299, + "fields": { + "package": 118, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 435, + "fields": { + "banding": 299, + "country": 997, + "value": 4923, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 119, + "fields": { + "name": "Package 119 - Select (Choice/GBP+USD)", + "description": "Test package 119", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 119, + "fields": { + "basepackage_ptr": 119, + "initiative": 7, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 125, + "fields": { + "package": 119, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 142, + "fields": { + "package": 119, + "banding_type_entry": 125, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 143, + "fields": { + "package": 119, + "banding_type_entry": 125, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 300, + "fields": { + "package": 119, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 436, + "fields": { + "banding": 300, + "country": 999, + "value": 306, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 437, + "fields": { + "banding": 300, + "country": 998, + "value": 398, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 301, + "fields": { + "package": 119, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 438, + "fields": { + "banding": 301, + "country": 999, + "value": 511, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 439, + "fields": { + "banding": 301, + "country": 998, + "value": 664, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 302, + "fields": { + "package": 119, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 440, + "fields": { + "banding": 302, + "country": 999, + "value": 817, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 441, + "fields": { + "banding": 302, + "country": 998, + "value": 1062, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 303, + "fields": { + "package": 119, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 442, + "fields": { + "banding": 303, + "country": 999, + "value": 1124, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 443, + "fields": { + "banding": 303, + "country": 998, + "value": 1461, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 304, + "fields": { + "package": 119, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 444, + "fields": { + "banding": 304, + "country": 999, + "value": 1430, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 445, + "fields": { + "banding": 304, + "country": 998, + "value": 1859, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 305, + "fields": { + "package": 119, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 446, + "fields": { + "banding": 305, + "country": 999, + "value": 1737, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 447, + "fields": { + "banding": 305, + "country": 998, + "value": 2258, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 306, + "fields": { + "package": 119, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 448, + "fields": { + "banding": 306, + "country": 999, + "value": 2043, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 449, + "fields": { + "banding": 306, + "country": 998, + "value": 2656, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 307, + "fields": { + "package": 119, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 450, + "fields": { + "banding": 307, + "country": 999, + "value": 2860, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 451, + "fields": { + "banding": 307, + "country": 998, + "value": 3718, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 120, + "fields": { + "name": "Package 120 - Core (FTE+Choice/all)", + "description": "Test package 120", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 120, + "fields": { + "basepackage_ptr": 120, + "initiative": 7, + "default_country": 997, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 126, + "fields": { + "package": 120, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 144, + "fields": { + "package": 120, + "banding_type_entry": 126, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 145, + "fields": { + "package": 120, + "banding_type_entry": 126, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 146, + "fields": { + "package": 120, + "banding_type_entry": 126, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 308, + "fields": { + "package": 120, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 452, + "fields": { + "banding": 308, + "country": 999, + "value": 535, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 453, + "fields": { + "banding": 308, + "country": 998, + "value": 696, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 454, + "fields": { + "banding": 308, + "country": 997, + "value": 615, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 309, + "fields": { + "package": 120, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 455, + "fields": { + "banding": 309, + "country": 999, + "value": 856, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 456, + "fields": { + "banding": 309, + "country": 998, + "value": 1113, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 457, + "fields": { + "banding": 309, + "country": 997, + "value": 984, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 310, + "fields": { + "package": 120, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 458, + "fields": { + "banding": 310, + "country": 999, + "value": 1284, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 459, + "fields": { + "banding": 310, + "country": 998, + "value": 1669, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 460, + "fields": { + "banding": 310, + "country": 997, + "value": 1477, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 311, + "fields": { + "package": 120, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 461, + "fields": { + "banding": 311, + "country": 999, + "value": 1712, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 462, + "fields": { + "banding": 311, + "country": 998, + "value": 2226, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 463, + "fields": { + "banding": 311, + "country": 997, + "value": 1969, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 312, + "fields": { + "package": 120, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 464, + "fields": { + "banding": 312, + "country": 999, + "value": 2140, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 465, + "fields": { + "banding": 312, + "country": 998, + "value": 2782, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 466, + "fields": { + "banding": 312, + "country": 997, + "value": 2461, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 313, + "fields": { + "package": 120, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 467, + "fields": { + "banding": 313, + "country": 999, + "value": 2782, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 468, + "fields": { + "banding": 313, + "country": 998, + "value": 3617, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 469, + "fields": { + "banding": 313, + "country": 997, + "value": 3199, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 314, + "fields": { + "package": 120, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 470, + "fields": { + "banding": 314, + "country": 999, + "value": 3638, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 471, + "fields": { + "banding": 314, + "country": 998, + "value": 4729, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 472, + "fields": { + "banding": 314, + "country": 997, + "value": 4184, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 315, + "fields": { + "package": 120, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 473, + "fields": { + "banding": 315, + "country": 999, + "value": 4708, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 474, + "fields": { + "banding": 315, + "country": 998, + "value": 6120, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 475, + "fields": { + "banding": 315, + "country": 997, + "value": 5414, + "default_currency": "EUR" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 127, + "fields": { + "package": 120, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 147, + "fields": { + "package": 120, + "banding_type_entry": 127, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 148, + "fields": { + "package": 120, + "banding_type_entry": 127, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 149, + "fields": { + "package": 120, + "banding_type_entry": 127, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 316, + "fields": { + "package": 120, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 476, + "fields": { + "banding": 316, + "country": 999, + "value": 321, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 477, + "fields": { + "banding": 316, + "country": 998, + "value": 417, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 478, + "fields": { + "banding": 316, + "country": 997, + "value": 369, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 317, + "fields": { + "package": 120, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 479, + "fields": { + "banding": 317, + "country": 999, + "value": 535, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 480, + "fields": { + "banding": 317, + "country": 998, + "value": 696, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 481, + "fields": { + "banding": 317, + "country": 997, + "value": 615, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 318, + "fields": { + "package": 120, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 482, + "fields": { + "banding": 318, + "country": 999, + "value": 856, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 483, + "fields": { + "banding": 318, + "country": 998, + "value": 1113, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 484, + "fields": { + "banding": 318, + "country": 997, + "value": 984, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 319, + "fields": { + "package": 120, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 485, + "fields": { + "banding": 319, + "country": 999, + "value": 1177, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 486, + "fields": { + "banding": 319, + "country": 998, + "value": 1530, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 487, + "fields": { + "banding": 319, + "country": 997, + "value": 1354, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 320, + "fields": { + "package": 120, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 488, + "fields": { + "banding": 320, + "country": 999, + "value": 1498, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 489, + "fields": { + "banding": 320, + "country": 998, + "value": 1947, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 490, + "fields": { + "banding": 320, + "country": 997, + "value": 1723, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 321, + "fields": { + "package": 120, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 491, + "fields": { + "banding": 321, + "country": 999, + "value": 1819, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 492, + "fields": { + "banding": 321, + "country": 998, + "value": 2365, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 493, + "fields": { + "banding": 321, + "country": 997, + "value": 2092, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 322, + "fields": { + "package": 120, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 494, + "fields": { + "banding": 322, + "country": 999, + "value": 2140, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 495, + "fields": { + "banding": 322, + "country": 998, + "value": 2782, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 496, + "fields": { + "banding": 322, + "country": 997, + "value": 2461, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 323, + "fields": { + "package": 120, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 497, + "fields": { + "banding": 323, + "country": 999, + "value": 2996, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 498, + "fields": { + "banding": 323, + "country": 998, + "value": 3895, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 499, + "fields": { + "banding": 323, + "country": 997, + "value": 3445, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 121, + "fields": { + "name": "Package 121 - Plus (FTE/GBP)", + "description": "Test package 121", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 121, + "fields": { + "basepackage_ptr": 121, + "initiative": 8, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 128, + "fields": { + "package": 121, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 150, + "fields": { + "package": 121, + "banding_type_entry": 128, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 324, + "fields": { + "package": 121, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 500, + "fields": { + "banding": 324, + "country": 999, + "value": 559, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 325, + "fields": { + "package": 121, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 501, + "fields": { + "banding": 325, + "country": 999, + "value": 895, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 326, + "fields": { + "package": 121, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 502, + "fields": { + "banding": 326, + "country": 999, + "value": 1342, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 327, + "fields": { + "package": 121, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 503, + "fields": { + "banding": 327, + "country": 999, + "value": 1790, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 328, + "fields": { + "package": 121, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 504, + "fields": { + "banding": 328, + "country": 999, + "value": 2237, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 329, + "fields": { + "package": 121, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 505, + "fields": { + "banding": 329, + "country": 999, + "value": 2908, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 330, + "fields": { + "package": 121, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 506, + "fields": { + "banding": 330, + "country": 999, + "value": 3803, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 331, + "fields": { + "package": 121, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 507, + "fields": { + "banding": 331, + "country": 999, + "value": 4921, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 122, + "fields": { + "name": "Package 122 - Premium (FTE/GBP+USD)", + "description": "Test package 122", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 122, + "fields": { + "basepackage_ptr": 122, + "initiative": 8, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 129, + "fields": { + "package": 122, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 151, + "fields": { + "package": 122, + "banding_type_entry": 129, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 152, + "fields": { + "package": 122, + "banding_type_entry": 129, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 332, + "fields": { + "package": 122, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 508, + "fields": { + "banding": 332, + "country": 999, + "value": 584, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 509, + "fields": { + "banding": 332, + "country": 998, + "value": 759, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 333, + "fields": { + "package": 122, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 510, + "fields": { + "banding": 333, + "country": 999, + "value": 934, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 511, + "fields": { + "banding": 333, + "country": 998, + "value": 1214, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 334, + "fields": { + "package": 122, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 512, + "fields": { + "banding": 334, + "country": 999, + "value": 1400, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 513, + "fields": { + "banding": 334, + "country": 998, + "value": 1821, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 335, + "fields": { + "package": 122, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 514, + "fields": { + "banding": 335, + "country": 999, + "value": 1867, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 515, + "fields": { + "banding": 335, + "country": 998, + "value": 2427, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 336, + "fields": { + "package": 122, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 516, + "fields": { + "banding": 336, + "country": 999, + "value": 2334, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 517, + "fields": { + "banding": 336, + "country": 998, + "value": 3034, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 337, + "fields": { + "package": 122, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 518, + "fields": { + "banding": 337, + "country": 999, + "value": 3034, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 519, + "fields": { + "banding": 337, + "country": 998, + "value": 3944, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 338, + "fields": { + "package": 122, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 520, + "fields": { + "banding": 338, + "country": 999, + "value": 3968, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 521, + "fields": { + "banding": 338, + "country": 998, + "value": 5158, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 339, + "fields": { + "package": 122, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 522, + "fields": { + "banding": 339, + "country": 999, + "value": 5135, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 523, + "fields": { + "banding": 339, + "country": 998, + "value": 6675, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 123, + "fields": { + "name": "Package 123 - Standard (Choice/GBP)", + "description": "Test package 123", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 123, + "fields": { + "basepackage_ptr": 123, + "initiative": 8, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 130, + "fields": { + "package": 123, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 153, + "fields": { + "package": 123, + "banding_type_entry": 130, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 340, + "fields": { + "package": 123, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 524, + "fields": { + "banding": 340, + "country": 999, + "value": 365, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 341, + "fields": { + "package": 123, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 525, + "fields": { + "banding": 341, + "country": 999, + "value": 608, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 342, + "fields": { + "package": 123, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 526, + "fields": { + "banding": 342, + "country": 999, + "value": 972, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 343, + "fields": { + "package": 123, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 527, + "fields": { + "banding": 343, + "country": 999, + "value": 1337, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 344, + "fields": { + "package": 123, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 528, + "fields": { + "banding": 344, + "country": 999, + "value": 1702, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 345, + "fields": { + "package": 123, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 529, + "fields": { + "banding": 345, + "country": 999, + "value": 2066, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 346, + "fields": { + "package": 123, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 530, + "fields": { + "banding": 346, + "country": 999, + "value": 2431, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 347, + "fields": { + "package": 123, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 531, + "fields": { + "banding": 347, + "country": 999, + "value": 3403, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 124, + "fields": { + "name": "Package 124 - Basic (FTE+Choice/GBP+USD)", + "description": "Test package 124", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 124, + "fields": { + "basepackage_ptr": 124, + "initiative": 9, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 131, + "fields": { + "package": 124, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 154, + "fields": { + "package": 124, + "banding_type_entry": 131, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 155, + "fields": { + "package": 124, + "banding_type_entry": 131, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 348, + "fields": { + "package": 124, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 532, + "fields": { + "banding": 348, + "country": 999, + "value": 632, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 533, + "fields": { + "banding": 348, + "country": 998, + "value": 822, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 349, + "fields": { + "package": 124, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 534, + "fields": { + "banding": 349, + "country": 999, + "value": 1011, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 535, + "fields": { + "banding": 349, + "country": 998, + "value": 1315, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 350, + "fields": { + "package": 124, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 536, + "fields": { + "banding": 350, + "country": 999, + "value": 1517, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 537, + "fields": { + "banding": 350, + "country": 998, + "value": 1972, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 351, + "fields": { + "package": 124, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 538, + "fields": { + "banding": 351, + "country": 999, + "value": 2022, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 539, + "fields": { + "banding": 351, + "country": 998, + "value": 2629, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 352, + "fields": { + "package": 124, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 540, + "fields": { + "banding": 352, + "country": 999, + "value": 2528, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 541, + "fields": { + "banding": 352, + "country": 998, + "value": 3286, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 353, + "fields": { + "package": 124, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 542, + "fields": { + "banding": 353, + "country": 999, + "value": 3286, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 543, + "fields": { + "banding": 353, + "country": 998, + "value": 4272, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 354, + "fields": { + "package": 124, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 544, + "fields": { + "banding": 354, + "country": 999, + "value": 4298, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 545, + "fields": { + "banding": 354, + "country": 998, + "value": 5587, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 355, + "fields": { + "package": 124, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 546, + "fields": { + "banding": 355, + "country": 999, + "value": 5562, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 547, + "fields": { + "banding": 355, + "country": 998, + "value": 7230, + "default_currency": "USD" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 132, + "fields": { + "package": 124, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 156, + "fields": { + "package": 124, + "banding_type_entry": 132, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 157, + "fields": { + "package": 124, + "banding_type_entry": 132, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 356, + "fields": { + "package": 124, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 548, + "fields": { + "banding": 356, + "country": 999, + "value": 379, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 549, + "fields": { + "banding": 356, + "country": 998, + "value": 493, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 357, + "fields": { + "package": 124, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 550, + "fields": { + "banding": 357, + "country": 999, + "value": 632, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 551, + "fields": { + "banding": 357, + "country": 998, + "value": 822, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 358, + "fields": { + "package": 124, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 552, + "fields": { + "banding": 358, + "country": 999, + "value": 1011, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 553, + "fields": { + "banding": 358, + "country": 998, + "value": 1315, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 359, + "fields": { + "package": 124, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 554, + "fields": { + "banding": 359, + "country": 999, + "value": 1390, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 555, + "fields": { + "banding": 359, + "country": 998, + "value": 1808, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 360, + "fields": { + "package": 124, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 556, + "fields": { + "banding": 360, + "country": 999, + "value": 1770, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 557, + "fields": { + "banding": 360, + "country": 998, + "value": 2300, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 361, + "fields": { + "package": 124, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 558, + "fields": { + "banding": 361, + "country": 999, + "value": 2149, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 559, + "fields": { + "banding": 361, + "country": 998, + "value": 2793, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 362, + "fields": { + "package": 124, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 560, + "fields": { + "banding": 362, + "country": 999, + "value": 2528, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 561, + "fields": { + "banding": 362, + "country": 998, + "value": 3286, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 363, + "fields": { + "package": 124, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 562, + "fields": { + "banding": 363, + "country": 999, + "value": 3539, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 563, + "fields": { + "banding": 363, + "country": 998, + "value": 4601, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 125, + "fields": { + "name": "Package 125 - Advanced (FTE/GBP+EUR)", + "description": "Test package 125", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 125, + "fields": { + "basepackage_ptr": 125, + "initiative": 9, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 133, + "fields": { + "package": 125, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 158, + "fields": { + "package": 125, + "banding_type_entry": 133, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 159, + "fields": { + "package": 125, + "banding_type_entry": 133, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 364, + "fields": { + "package": 125, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 564, + "fields": { + "banding": 364, + "country": 999, + "value": 656, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 565, + "fields": { + "banding": 364, + "country": 997, + "value": 755, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 365, + "fields": { + "package": 125, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 566, + "fields": { + "banding": 365, + "country": 999, + "value": 1050, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 567, + "fields": { + "banding": 365, + "country": 997, + "value": 1208, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 366, + "fields": { + "package": 125, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 568, + "fields": { + "banding": 366, + "country": 999, + "value": 1575, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 569, + "fields": { + "banding": 366, + "country": 997, + "value": 1811, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 367, + "fields": { + "package": 125, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 570, + "fields": { + "banding": 367, + "country": 999, + "value": 2100, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 571, + "fields": { + "banding": 367, + "country": 997, + "value": 2415, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 368, + "fields": { + "package": 125, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 572, + "fields": { + "banding": 368, + "country": 999, + "value": 2625, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 573, + "fields": { + "banding": 368, + "country": 997, + "value": 3019, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 369, + "fields": { + "package": 125, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 574, + "fields": { + "banding": 369, + "country": 999, + "value": 3412, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 575, + "fields": { + "banding": 369, + "country": 997, + "value": 3924, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 370, + "fields": { + "package": 125, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 576, + "fields": { + "banding": 370, + "country": 999, + "value": 4462, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 577, + "fields": { + "banding": 370, + "country": 997, + "value": 5132, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 371, + "fields": { + "package": 125, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 578, + "fields": { + "banding": 371, + "country": 999, + "value": 5775, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 579, + "fields": { + "banding": 371, + "country": 997, + "value": 6641, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 126, + "fields": { + "name": "Package 126 - Pro (Choice/USD)", + "description": "Test package 126", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 126, + "fields": { + "basepackage_ptr": 126, + "initiative": 9, + "default_country": 998, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 134, + "fields": { + "package": 126, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 160, + "fields": { + "package": 126, + "banding_type_entry": 134, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 372, + "fields": { + "package": 126, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 580, + "fields": { + "banding": 372, + "country": 998, + "value": 531, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 373, + "fields": { + "package": 126, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 581, + "fields": { + "banding": 373, + "country": 998, + "value": 885, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 374, + "fields": { + "package": 126, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 582, + "fields": { + "banding": 374, + "country": 998, + "value": 1415, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 375, + "fields": { + "package": 126, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 583, + "fields": { + "banding": 375, + "country": 998, + "value": 1946, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 376, + "fields": { + "package": 126, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 584, + "fields": { + "banding": 376, + "country": 998, + "value": 2477, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 377, + "fields": { + "package": 126, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 585, + "fields": { + "banding": 377, + "country": 998, + "value": 3008, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 378, + "fields": { + "package": 126, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 586, + "fields": { + "banding": 378, + "country": 998, + "value": 3539, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 379, + "fields": { + "package": 126, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 587, + "fields": { + "banding": 379, + "country": 998, + "value": 4954, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 127, + "fields": { + "name": "Package 127 - Essential (FTE+Choice/GBP)", + "description": "Test package 127", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 127, + "fields": { + "basepackage_ptr": 127, + "initiative": 10, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 135, + "fields": { + "package": 127, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 161, + "fields": { + "package": 127, + "banding_type_entry": 135, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 380, + "fields": { + "package": 127, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 588, + "fields": { + "banding": 380, + "country": 999, + "value": 705, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 381, + "fields": { + "package": 127, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 589, + "fields": { + "banding": 381, + "country": 999, + "value": 1128, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 382, + "fields": { + "package": 127, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 590, + "fields": { + "banding": 382, + "country": 999, + "value": 1691, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 383, + "fields": { + "package": 127, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 591, + "fields": { + "banding": 383, + "country": 999, + "value": 2255, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 384, + "fields": { + "package": 127, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 592, + "fields": { + "banding": 384, + "country": 999, + "value": 2819, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 385, + "fields": { + "package": 127, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 593, + "fields": { + "banding": 385, + "country": 999, + "value": 3665, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 386, + "fields": { + "package": 127, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 594, + "fields": { + "banding": 386, + "country": 999, + "value": 4792, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 387, + "fields": { + "package": 127, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 595, + "fields": { + "banding": 387, + "country": 999, + "value": 6202, + "default_currency": "GBP" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 136, + "fields": { + "package": 127, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 162, + "fields": { + "package": 127, + "banding_type_entry": 136, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 388, + "fields": { + "package": 127, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 596, + "fields": { + "banding": 388, + "country": 999, + "value": 423, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 389, + "fields": { + "package": 127, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 597, + "fields": { + "banding": 389, + "country": 999, + "value": 705, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 390, + "fields": { + "package": 127, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 598, + "fields": { + "banding": 390, + "country": 999, + "value": 1128, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 391, + "fields": { + "package": 127, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 599, + "fields": { + "banding": 391, + "country": 999, + "value": 1550, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 392, + "fields": { + "package": 127, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 600, + "fields": { + "banding": 392, + "country": 999, + "value": 1973, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 393, + "fields": { + "package": 127, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 601, + "fields": { + "banding": 393, + "country": 999, + "value": 2396, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 394, + "fields": { + "package": 127, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 602, + "fields": { + "banding": 394, + "country": 999, + "value": 2819, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 395, + "fields": { + "package": 127, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 603, + "fields": { + "banding": 395, + "country": 999, + "value": 3947, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 128, + "fields": { + "name": "Package 128 - Complete (FTE/EUR)", + "description": "Test package 128", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 128, + "fields": { + "basepackage_ptr": 128, + "initiative": 10, + "default_country": 997, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 137, + "fields": { + "package": 128, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 163, + "fields": { + "package": 128, + "banding_type_entry": 137, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 396, + "fields": { + "package": 128, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 604, + "fields": { + "banding": 396, + "country": 997, + "value": 838, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 397, + "fields": { + "package": 128, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 605, + "fields": { + "banding": 397, + "country": 997, + "value": 1341, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 398, + "fields": { + "package": 128, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 606, + "fields": { + "banding": 398, + "country": 997, + "value": 2012, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 399, + "fields": { + "package": 128, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 607, + "fields": { + "banding": 399, + "country": 997, + "value": 2683, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 400, + "fields": { + "package": 128, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 608, + "fields": { + "banding": 400, + "country": 997, + "value": 3353, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 401, + "fields": { + "package": 128, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 609, + "fields": { + "banding": 401, + "country": 997, + "value": 4359, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 402, + "fields": { + "package": 128, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 610, + "fields": { + "banding": 402, + "country": 997, + "value": 5701, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 403, + "fields": { + "package": 128, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 611, + "fields": { + "banding": 403, + "country": 997, + "value": 7377, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 129, + "fields": { + "name": "Package 129 - Select (Choice/GBP+USD)", + "description": "Test package 129", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 129, + "fields": { + "basepackage_ptr": 129, + "initiative": 10, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 138, + "fields": { + "package": 129, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 164, + "fields": { + "package": 129, + "banding_type_entry": 138, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 165, + "fields": { + "package": 129, + "banding_type_entry": 138, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 404, + "fields": { + "package": 129, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 612, + "fields": { + "banding": 404, + "country": 999, + "value": 452, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 613, + "fields": { + "banding": 404, + "country": 998, + "value": 588, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 405, + "fields": { + "package": 129, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 614, + "fields": { + "banding": 405, + "country": 999, + "value": 753, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 615, + "fields": { + "banding": 405, + "country": 998, + "value": 979, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 406, + "fields": { + "package": 129, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 616, + "fields": { + "banding": 406, + "country": 999, + "value": 1205, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 617, + "fields": { + "banding": 406, + "country": 998, + "value": 1567, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 407, + "fields": { + "package": 129, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 618, + "fields": { + "banding": 407, + "country": 999, + "value": 1657, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 619, + "fields": { + "banding": 407, + "country": 998, + "value": 2154, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 408, + "fields": { + "package": 129, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 620, + "fields": { + "banding": 408, + "country": 999, + "value": 2109, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 621, + "fields": { + "banding": 408, + "country": 998, + "value": 2742, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 409, + "fields": { + "package": 129, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 622, + "fields": { + "banding": 409, + "country": 999, + "value": 2561, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 623, + "fields": { + "banding": 409, + "country": 998, + "value": 3329, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 410, + "fields": { + "package": 129, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 624, + "fields": { + "banding": 410, + "country": 999, + "value": 3013, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 625, + "fields": { + "banding": 410, + "country": 998, + "value": 3917, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 411, + "fields": { + "package": 129, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 626, + "fields": { + "banding": 411, + "country": 999, + "value": 4218, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 627, + "fields": { + "banding": 411, + "country": 998, + "value": 5484, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 130, + "fields": { + "name": "Package 130 - Core (FTE+Choice/all)", + "description": "Test package 130", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 130, + "fields": { + "basepackage_ptr": 130, + "initiative": 11, + "default_country": 997, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 139, + "fields": { + "package": 130, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 166, + "fields": { + "package": 130, + "banding_type_entry": 139, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 167, + "fields": { + "package": 130, + "banding_type_entry": 139, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 168, + "fields": { + "package": 130, + "banding_type_entry": 139, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 412, + "fields": { + "package": 130, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 628, + "fields": { + "banding": 412, + "country": 999, + "value": 778, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 629, + "fields": { + "banding": 412, + "country": 998, + "value": 1011, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 630, + "fields": { + "banding": 412, + "country": 997, + "value": 894, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 413, + "fields": { + "package": 130, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 631, + "fields": { + "banding": 413, + "country": 999, + "value": 1244, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 632, + "fields": { + "banding": 413, + "country": 998, + "value": 1617, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 633, + "fields": { + "banding": 413, + "country": 997, + "value": 1431, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 414, + "fields": { + "package": 130, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 634, + "fields": { + "banding": 414, + "country": 999, + "value": 1866, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 635, + "fields": { + "banding": 414, + "country": 998, + "value": 2426, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 636, + "fields": { + "banding": 414, + "country": 997, + "value": 2146, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 415, + "fields": { + "package": 130, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 637, + "fields": { + "banding": 415, + "country": 999, + "value": 2488, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 638, + "fields": { + "banding": 415, + "country": 998, + "value": 3234, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 639, + "fields": { + "banding": 415, + "country": 997, + "value": 2861, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 416, + "fields": { + "package": 130, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 640, + "fields": { + "banding": 416, + "country": 999, + "value": 3110, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 641, + "fields": { + "banding": 416, + "country": 998, + "value": 4043, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 642, + "fields": { + "banding": 416, + "country": 997, + "value": 3576, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 417, + "fields": { + "package": 130, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 643, + "fields": { + "banding": 417, + "country": 999, + "value": 4043, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 644, + "fields": { + "banding": 417, + "country": 998, + "value": 5256, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 645, + "fields": { + "banding": 417, + "country": 997, + "value": 4649, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 418, + "fields": { + "package": 130, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 646, + "fields": { + "banding": 418, + "country": 999, + "value": 5287, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 647, + "fields": { + "banding": 418, + "country": 998, + "value": 6873, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 648, + "fields": { + "banding": 418, + "country": 997, + "value": 6080, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 419, + "fields": { + "package": 130, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 649, + "fields": { + "banding": 419, + "country": 999, + "value": 6842, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 650, + "fields": { + "banding": 419, + "country": 998, + "value": 8895, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 651, + "fields": { + "banding": 419, + "country": 997, + "value": 7868, + "default_currency": "EUR" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 140, + "fields": { + "package": 130, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 169, + "fields": { + "package": 130, + "banding_type_entry": 140, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 170, + "fields": { + "package": 130, + "banding_type_entry": 140, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 171, + "fields": { + "package": 130, + "banding_type_entry": 140, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 420, + "fields": { + "package": 130, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 652, + "fields": { + "banding": 420, + "country": 999, + "value": 466, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 653, + "fields": { + "banding": 420, + "country": 998, + "value": 606, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 654, + "fields": { + "banding": 420, + "country": 997, + "value": 536, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 421, + "fields": { + "package": 130, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 655, + "fields": { + "banding": 421, + "country": 999, + "value": 778, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 656, + "fields": { + "banding": 421, + "country": 998, + "value": 1011, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 657, + "fields": { + "banding": 421, + "country": 997, + "value": 894, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 422, + "fields": { + "package": 130, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 658, + "fields": { + "banding": 422, + "country": 999, + "value": 1244, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 659, + "fields": { + "banding": 422, + "country": 998, + "value": 1617, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 660, + "fields": { + "banding": 422, + "country": 997, + "value": 1431, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 423, + "fields": { + "package": 130, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 661, + "fields": { + "banding": 423, + "country": 999, + "value": 1711, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 662, + "fields": { + "banding": 423, + "country": 998, + "value": 2224, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 663, + "fields": { + "banding": 423, + "country": 997, + "value": 1967, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 424, + "fields": { + "package": 130, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 664, + "fields": { + "banding": 424, + "country": 999, + "value": 2177, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 665, + "fields": { + "banding": 424, + "country": 998, + "value": 2830, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 666, + "fields": { + "banding": 424, + "country": 997, + "value": 2504, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 425, + "fields": { + "package": 130, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 667, + "fields": { + "banding": 425, + "country": 999, + "value": 2644, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 668, + "fields": { + "banding": 425, + "country": 998, + "value": 3437, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 669, + "fields": { + "banding": 425, + "country": 997, + "value": 3040, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 426, + "fields": { + "package": 130, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 670, + "fields": { + "banding": 426, + "country": 999, + "value": 3110, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 671, + "fields": { + "banding": 426, + "country": 998, + "value": 4043, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 672, + "fields": { + "banding": 426, + "country": 997, + "value": 3576, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 427, + "fields": { + "package": 130, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 673, + "fields": { + "banding": 427, + "country": 999, + "value": 4354, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 674, + "fields": { + "banding": 427, + "country": 998, + "value": 5660, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 675, + "fields": { + "banding": 427, + "country": 997, + "value": 5007, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 131, + "fields": { + "name": "Package 131 - Plus (FTE/GBP)", + "description": "Test package 131", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 131, + "fields": { + "basepackage_ptr": 131, + "initiative": 11, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 141, + "fields": { + "package": 131, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 172, + "fields": { + "package": 131, + "banding_type_entry": 141, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 428, + "fields": { + "package": 131, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 676, + "fields": { + "banding": 428, + "country": 999, + "value": 802, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 429, + "fields": { + "package": 131, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 677, + "fields": { + "banding": 429, + "country": 999, + "value": 1283, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 430, + "fields": { + "package": 131, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 678, + "fields": { + "banding": 430, + "country": 999, + "value": 1924, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 431, + "fields": { + "package": 131, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 679, + "fields": { + "banding": 431, + "country": 999, + "value": 2566, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 432, + "fields": { + "package": 131, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 680, + "fields": { + "banding": 432, + "country": 999, + "value": 3207, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 433, + "fields": { + "package": 131, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 681, + "fields": { + "banding": 433, + "country": 999, + "value": 4169, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 434, + "fields": { + "package": 131, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 682, + "fields": { + "banding": 434, + "country": 999, + "value": 5452, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 435, + "fields": { + "package": 131, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 683, + "fields": { + "banding": 435, + "country": 999, + "value": 7055, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 132, + "fields": { + "name": "Package 132 - Premium (FTE/GBP+USD)", + "description": "Test package 132", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 132, + "fields": { + "basepackage_ptr": 132, + "initiative": 12, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 142, + "fields": { + "package": 132, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 173, + "fields": { + "package": 132, + "banding_type_entry": 142, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 174, + "fields": { + "package": 132, + "banding_type_entry": 142, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 436, + "fields": { + "package": 132, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 684, + "fields": { + "banding": 436, + "country": 999, + "value": 826, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 685, + "fields": { + "banding": 436, + "country": 998, + "value": 1074, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 437, + "fields": { + "package": 132, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 686, + "fields": { + "banding": 437, + "country": 999, + "value": 1322, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 687, + "fields": { + "banding": 437, + "country": 998, + "value": 1718, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 438, + "fields": { + "package": 132, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 688, + "fields": { + "banding": 438, + "country": 999, + "value": 1982, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 689, + "fields": { + "banding": 438, + "country": 998, + "value": 2577, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 439, + "fields": { + "package": 132, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 690, + "fields": { + "banding": 439, + "country": 999, + "value": 2643, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 691, + "fields": { + "banding": 439, + "country": 998, + "value": 3436, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 440, + "fields": { + "package": 132, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 692, + "fields": { + "banding": 440, + "country": 999, + "value": 3304, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 693, + "fields": { + "banding": 440, + "country": 998, + "value": 4295, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 441, + "fields": { + "package": 132, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 694, + "fields": { + "banding": 441, + "country": 999, + "value": 4295, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 695, + "fields": { + "banding": 441, + "country": 998, + "value": 5584, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 442, + "fields": { + "package": 132, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 696, + "fields": { + "banding": 442, + "country": 999, + "value": 5617, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 697, + "fields": { + "banding": 442, + "country": 998, + "value": 7302, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 443, + "fields": { + "package": 132, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 698, + "fields": { + "banding": 443, + "country": 999, + "value": 7269, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 699, + "fields": { + "banding": 443, + "country": 998, + "value": 9449, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 133, + "fields": { + "name": "Package 133 - Standard (Choice/GBP)", + "description": "Test package 133", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 133, + "fields": { + "basepackage_ptr": 133, + "initiative": 12, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 143, + "fields": { + "package": 133, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 175, + "fields": { + "package": 133, + "banding_type_entry": 143, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 444, + "fields": { + "package": 133, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 700, + "fields": { + "banding": 444, + "country": 999, + "value": 510, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 445, + "fields": { + "package": 133, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 701, + "fields": { + "banding": 445, + "country": 999, + "value": 850, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 446, + "fields": { + "package": 133, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 702, + "fields": { + "banding": 446, + "country": 999, + "value": 1360, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 447, + "fields": { + "package": 133, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 703, + "fields": { + "banding": 447, + "country": 999, + "value": 1871, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 448, + "fields": { + "package": 133, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 704, + "fields": { + "banding": 448, + "country": 999, + "value": 2381, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 449, + "fields": { + "package": 133, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 705, + "fields": { + "banding": 449, + "country": 999, + "value": 2891, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 450, + "fields": { + "package": 133, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 706, + "fields": { + "banding": 450, + "country": 999, + "value": 3401, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 451, + "fields": { + "package": 133, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 707, + "fields": { + "banding": 451, + "country": 999, + "value": 4761, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 134, + "fields": { + "name": "Package 134 - Basic (FTE+Choice/GBP+USD)", + "description": "Test package 134", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 134, + "fields": { + "basepackage_ptr": 134, + "initiative": 13, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 144, + "fields": { + "package": 134, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 176, + "fields": { + "package": 134, + "banding_type_entry": 144, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 177, + "fields": { + "package": 134, + "banding_type_entry": 144, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 452, + "fields": { + "package": 134, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 708, + "fields": { + "banding": 452, + "country": 999, + "value": 874, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 709, + "fields": { + "banding": 452, + "country": 998, + "value": 1137, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 453, + "fields": { + "package": 134, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 710, + "fields": { + "banding": 453, + "country": 999, + "value": 1399, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 711, + "fields": { + "banding": 453, + "country": 998, + "value": 1819, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 454, + "fields": { + "package": 134, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 712, + "fields": { + "banding": 454, + "country": 999, + "value": 2099, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 713, + "fields": { + "banding": 454, + "country": 998, + "value": 2728, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 455, + "fields": { + "package": 134, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 714, + "fields": { + "banding": 455, + "country": 999, + "value": 2798, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 715, + "fields": { + "banding": 455, + "country": 998, + "value": 3638, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 456, + "fields": { + "package": 134, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 716, + "fields": { + "banding": 456, + "country": 999, + "value": 3498, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 717, + "fields": { + "banding": 456, + "country": 998, + "value": 4547, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 457, + "fields": { + "package": 134, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 718, + "fields": { + "banding": 457, + "country": 999, + "value": 4547, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 719, + "fields": { + "banding": 457, + "country": 998, + "value": 5912, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 458, + "fields": { + "package": 134, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 720, + "fields": { + "banding": 458, + "country": 999, + "value": 5947, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 721, + "fields": { + "banding": 458, + "country": 998, + "value": 7731, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 459, + "fields": { + "package": 134, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 722, + "fields": { + "banding": 459, + "country": 999, + "value": 7696, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 723, + "fields": { + "banding": 459, + "country": 998, + "value": 10004, + "default_currency": "USD" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 145, + "fields": { + "package": 134, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 178, + "fields": { + "package": 134, + "banding_type_entry": 145, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 179, + "fields": { + "package": 134, + "banding_type_entry": 145, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 460, + "fields": { + "package": 134, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 724, + "fields": { + "banding": 460, + "country": 999, + "value": 525, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 725, + "fields": { + "banding": 460, + "country": 998, + "value": 682, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 461, + "fields": { + "package": 134, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 726, + "fields": { + "banding": 461, + "country": 999, + "value": 874, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 727, + "fields": { + "banding": 461, + "country": 998, + "value": 1137, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 462, + "fields": { + "package": 134, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 728, + "fields": { + "banding": 462, + "country": 999, + "value": 1399, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 729, + "fields": { + "banding": 462, + "country": 998, + "value": 1819, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 463, + "fields": { + "package": 134, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 730, + "fields": { + "banding": 463, + "country": 999, + "value": 1924, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 731, + "fields": { + "banding": 463, + "country": 998, + "value": 2501, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 464, + "fields": { + "package": 134, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 732, + "fields": { + "banding": 464, + "country": 999, + "value": 2449, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 733, + "fields": { + "banding": 464, + "country": 998, + "value": 3183, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 465, + "fields": { + "package": 134, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 734, + "fields": { + "banding": 465, + "country": 999, + "value": 2973, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 735, + "fields": { + "banding": 465, + "country": 998, + "value": 3865, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 466, + "fields": { + "package": 134, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 736, + "fields": { + "banding": 466, + "country": 999, + "value": 3498, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 737, + "fields": { + "banding": 466, + "country": 998, + "value": 4547, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 467, + "fields": { + "package": 134, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 738, + "fields": { + "banding": 467, + "country": 999, + "value": 4897, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 739, + "fields": { + "banding": 467, + "country": 998, + "value": 6366, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 135, + "fields": { + "name": "Package 135 - Advanced (FTE/GBP+EUR)", + "description": "Test package 135", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 135, + "fields": { + "basepackage_ptr": 135, + "initiative": 13, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 146, + "fields": { + "package": 135, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 180, + "fields": { + "package": 135, + "banding_type_entry": 146, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 181, + "fields": { + "package": 135, + "banding_type_entry": 146, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 468, + "fields": { + "package": 135, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 740, + "fields": { + "banding": 468, + "country": 999, + "value": 899, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 741, + "fields": { + "banding": 468, + "country": 997, + "value": 1034, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 469, + "fields": { + "package": 135, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 742, + "fields": { + "banding": 469, + "country": 999, + "value": 1438, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 743, + "fields": { + "banding": 469, + "country": 997, + "value": 1654, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 470, + "fields": { + "package": 135, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 744, + "fields": { + "banding": 470, + "country": 999, + "value": 2157, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 745, + "fields": { + "banding": 470, + "country": 997, + "value": 2481, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 471, + "fields": { + "package": 135, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 746, + "fields": { + "banding": 471, + "country": 999, + "value": 2876, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 747, + "fields": { + "banding": 471, + "country": 997, + "value": 3307, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 472, + "fields": { + "package": 135, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 748, + "fields": { + "banding": 472, + "country": 999, + "value": 3595, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 749, + "fields": { + "banding": 472, + "country": 997, + "value": 4134, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 473, + "fields": { + "package": 135, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 750, + "fields": { + "banding": 473, + "country": 999, + "value": 4674, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 751, + "fields": { + "banding": 473, + "country": 997, + "value": 5375, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 474, + "fields": { + "package": 135, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 752, + "fields": { + "banding": 474, + "country": 999, + "value": 6112, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 753, + "fields": { + "banding": 474, + "country": 997, + "value": 7028, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 475, + "fields": { + "package": 135, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 754, + "fields": { + "banding": 475, + "country": 999, + "value": 7909, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 755, + "fields": { + "banding": 475, + "country": 997, + "value": 9095, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 136, + "fields": { + "name": "Package 136 - Pro (Choice/USD)", + "description": "Test package 136", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 136, + "fields": { + "basepackage_ptr": 136, + "initiative": 14, + "default_country": 998, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 147, + "fields": { + "package": 136, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 182, + "fields": { + "package": 136, + "banding_type_entry": 147, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 476, + "fields": { + "package": 136, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 756, + "fields": { + "banding": 476, + "country": 998, + "value": 720, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 477, + "fields": { + "package": 136, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 757, + "fields": { + "banding": 477, + "country": 998, + "value": 1200, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 478, + "fields": { + "package": 136, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 758, + "fields": { + "banding": 478, + "country": 998, + "value": 1920, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 479, + "fields": { + "package": 136, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 759, + "fields": { + "banding": 479, + "country": 998, + "value": 2640, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 480, + "fields": { + "package": 136, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 760, + "fields": { + "banding": 480, + "country": 998, + "value": 3360, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 481, + "fields": { + "package": 136, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 761, + "fields": { + "banding": 481, + "country": 998, + "value": 4080, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 482, + "fields": { + "package": 136, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 762, + "fields": { + "banding": 482, + "country": 998, + "value": 4800, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 483, + "fields": { + "package": 136, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 763, + "fields": { + "banding": 483, + "country": 998, + "value": 6719, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 137, + "fields": { + "name": "Package 137 - Essential (FTE+Choice/GBP)", + "description": "Test package 137", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 137, + "fields": { + "basepackage_ptr": 137, + "initiative": 14, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 148, + "fields": { + "package": 137, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 183, + "fields": { + "package": 137, + "banding_type_entry": 148, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 484, + "fields": { + "package": 137, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 764, + "fields": { + "banding": 484, + "country": 999, + "value": 947, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 485, + "fields": { + "package": 137, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 765, + "fields": { + "banding": 485, + "country": 999, + "value": 1516, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 486, + "fields": { + "package": 137, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 766, + "fields": { + "banding": 486, + "country": 999, + "value": 2273, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 487, + "fields": { + "package": 137, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 767, + "fields": { + "banding": 487, + "country": 999, + "value": 3031, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 488, + "fields": { + "package": 137, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 768, + "fields": { + "banding": 488, + "country": 999, + "value": 3789, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 489, + "fields": { + "package": 137, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 769, + "fields": { + "banding": 489, + "country": 999, + "value": 4926, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 490, + "fields": { + "package": 137, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 770, + "fields": { + "banding": 490, + "country": 999, + "value": 6441, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 491, + "fields": { + "package": 137, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 771, + "fields": { + "banding": 491, + "country": 999, + "value": 8336, + "default_currency": "GBP" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 149, + "fields": { + "package": 137, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 184, + "fields": { + "package": 137, + "banding_type_entry": 149, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 492, + "fields": { + "package": 137, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 772, + "fields": { + "banding": 492, + "country": 999, + "value": 568, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 493, + "fields": { + "package": 137, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 773, + "fields": { + "banding": 493, + "country": 999, + "value": 947, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 494, + "fields": { + "package": 137, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 774, + "fields": { + "banding": 494, + "country": 999, + "value": 1516, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 495, + "fields": { + "package": 137, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 775, + "fields": { + "banding": 495, + "country": 999, + "value": 2084, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 496, + "fields": { + "package": 137, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 776, + "fields": { + "banding": 496, + "country": 999, + "value": 2652, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 497, + "fields": { + "package": 137, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 777, + "fields": { + "banding": 497, + "country": 999, + "value": 3221, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 498, + "fields": { + "package": 137, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 778, + "fields": { + "banding": 498, + "country": 999, + "value": 3789, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 499, + "fields": { + "package": 137, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 779, + "fields": { + "banding": 499, + "country": 999, + "value": 5305, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 138, + "fields": { + "name": "Package 138 - Complete (FTE/EUR)", + "description": "Test package 138", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 138, + "fields": { + "basepackage_ptr": 138, + "initiative": 15, + "default_country": 997, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 150, + "fields": { + "package": 138, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 185, + "fields": { + "package": 138, + "banding_type_entry": 150, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 500, + "fields": { + "package": 138, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 780, + "fields": { + "banding": 500, + "country": 997, + "value": 1117, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 501, + "fields": { + "package": 138, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 781, + "fields": { + "banding": 501, + "country": 997, + "value": 1788, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 502, + "fields": { + "package": 138, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 782, + "fields": { + "banding": 502, + "country": 997, + "value": 2681, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 503, + "fields": { + "package": 138, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 783, + "fields": { + "banding": 503, + "country": 997, + "value": 3575, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 504, + "fields": { + "package": 138, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 784, + "fields": { + "banding": 504, + "country": 997, + "value": 4469, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 505, + "fields": { + "package": 138, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 785, + "fields": { + "banding": 505, + "country": 997, + "value": 5810, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 506, + "fields": { + "package": 138, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 786, + "fields": { + "banding": 506, + "country": 997, + "value": 7597, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 507, + "fields": { + "package": 138, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 787, + "fields": { + "banding": 507, + "country": 997, + "value": 9832, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 139, + "fields": { + "name": "Package 139 - Select (Choice/GBP+USD)", + "description": "Test package 139", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 139, + "fields": { + "basepackage_ptr": 139, + "initiative": 15, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 151, + "fields": { + "package": 139, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 186, + "fields": { + "package": 139, + "banding_type_entry": 151, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 187, + "fields": { + "package": 139, + "banding_type_entry": 151, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 508, + "fields": { + "package": 139, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 788, + "fields": { + "banding": 508, + "country": 999, + "value": 597, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 789, + "fields": { + "banding": 508, + "country": 998, + "value": 777, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 509, + "fields": { + "package": 139, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 790, + "fields": { + "banding": 509, + "country": 999, + "value": 996, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 791, + "fields": { + "banding": 509, + "country": 998, + "value": 1294, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 510, + "fields": { + "package": 139, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 792, + "fields": { + "banding": 510, + "country": 999, + "value": 1593, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 793, + "fields": { + "banding": 510, + "country": 998, + "value": 2071, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 511, + "fields": { + "package": 139, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 794, + "fields": { + "banding": 511, + "country": 999, + "value": 2191, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 795, + "fields": { + "banding": 511, + "country": 998, + "value": 2848, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 512, + "fields": { + "package": 139, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 796, + "fields": { + "banding": 512, + "country": 999, + "value": 2788, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 797, + "fields": { + "banding": 512, + "country": 998, + "value": 3625, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 513, + "fields": { + "package": 139, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 798, + "fields": { + "banding": 513, + "country": 999, + "value": 3386, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 799, + "fields": { + "banding": 513, + "country": 998, + "value": 4401, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 514, + "fields": { + "package": 139, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 800, + "fields": { + "banding": 514, + "country": 999, + "value": 3983, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 801, + "fields": { + "banding": 514, + "country": 998, + "value": 5178, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 515, + "fields": { + "package": 139, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 802, + "fields": { + "banding": 515, + "country": 999, + "value": 5576, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 803, + "fields": { + "banding": 515, + "country": 998, + "value": 7249, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 140, + "fields": { + "name": "Package 140 - Core (FTE+Choice/all)", + "description": "Test package 140", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 140, + "fields": { + "basepackage_ptr": 140, + "initiative": 16, + "default_country": 997, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 152, + "fields": { + "package": 140, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 188, + "fields": { + "package": 140, + "banding_type_entry": 152, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 189, + "fields": { + "package": 140, + "banding_type_entry": 152, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 190, + "fields": { + "package": 140, + "banding_type_entry": 152, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 516, + "fields": { + "package": 140, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 804, + "fields": { + "banding": 516, + "country": 999, + "value": 1020, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 805, + "fields": { + "banding": 516, + "country": 998, + "value": 1326, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 806, + "fields": { + "banding": 516, + "country": 997, + "value": 1173, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 517, + "fields": { + "package": 140, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 807, + "fields": { + "banding": 517, + "country": 999, + "value": 1632, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 808, + "fields": { + "banding": 517, + "country": 998, + "value": 2122, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 809, + "fields": { + "banding": 517, + "country": 997, + "value": 1877, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 518, + "fields": { + "package": 140, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 810, + "fields": { + "banding": 518, + "country": 999, + "value": 2448, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 811, + "fields": { + "banding": 518, + "country": 998, + "value": 3182, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 812, + "fields": { + "banding": 518, + "country": 997, + "value": 2815, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 519, + "fields": { + "package": 140, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 813, + "fields": { + "banding": 519, + "country": 999, + "value": 3264, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 814, + "fields": { + "banding": 519, + "country": 998, + "value": 4243, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 815, + "fields": { + "banding": 519, + "country": 997, + "value": 3754, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 520, + "fields": { + "package": 140, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 816, + "fields": { + "banding": 520, + "country": 999, + "value": 4080, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 817, + "fields": { + "banding": 520, + "country": 998, + "value": 5304, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 818, + "fields": { + "banding": 520, + "country": 997, + "value": 4692, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 521, + "fields": { + "package": 140, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 819, + "fields": { + "banding": 521, + "country": 999, + "value": 5304, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 820, + "fields": { + "banding": 521, + "country": 998, + "value": 6895, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 821, + "fields": { + "banding": 521, + "country": 997, + "value": 6100, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 522, + "fields": { + "package": 140, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 822, + "fields": { + "banding": 522, + "country": 999, + "value": 6936, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 823, + "fields": { + "banding": 522, + "country": 998, + "value": 9017, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 824, + "fields": { + "banding": 522, + "country": 997, + "value": 7976, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 523, + "fields": { + "package": 140, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 825, + "fields": { + "banding": 523, + "country": 999, + "value": 8976, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 826, + "fields": { + "banding": 523, + "country": 998, + "value": 11669, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 827, + "fields": { + "banding": 523, + "country": 997, + "value": 10322, + "default_currency": "EUR" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 153, + "fields": { + "package": 140, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 191, + "fields": { + "package": 140, + "banding_type_entry": 153, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 192, + "fields": { + "package": 140, + "banding_type_entry": 153, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 193, + "fields": { + "package": 140, + "banding_type_entry": 153, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 524, + "fields": { + "package": 140, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 828, + "fields": { + "banding": 524, + "country": 999, + "value": 612, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 829, + "fields": { + "banding": 524, + "country": 998, + "value": 796, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 830, + "fields": { + "banding": 524, + "country": 997, + "value": 704, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 525, + "fields": { + "package": 140, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 831, + "fields": { + "banding": 525, + "country": 999, + "value": 1020, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 832, + "fields": { + "banding": 525, + "country": 998, + "value": 1326, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 833, + "fields": { + "banding": 525, + "country": 997, + "value": 1173, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 526, + "fields": { + "package": 140, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 834, + "fields": { + "banding": 526, + "country": 999, + "value": 1632, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 835, + "fields": { + "banding": 526, + "country": 998, + "value": 2122, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 836, + "fields": { + "banding": 526, + "country": 997, + "value": 1877, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 527, + "fields": { + "package": 140, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 837, + "fields": { + "banding": 527, + "country": 999, + "value": 2244, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 838, + "fields": { + "banding": 527, + "country": 998, + "value": 2917, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 839, + "fields": { + "banding": 527, + "country": 997, + "value": 2581, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 528, + "fields": { + "package": 140, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 840, + "fields": { + "banding": 528, + "country": 999, + "value": 2856, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 841, + "fields": { + "banding": 528, + "country": 998, + "value": 3713, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 842, + "fields": { + "banding": 528, + "country": 997, + "value": 3284, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 529, + "fields": { + "package": 140, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 843, + "fields": { + "banding": 529, + "country": 999, + "value": 3468, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 844, + "fields": { + "banding": 529, + "country": 998, + "value": 4508, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 845, + "fields": { + "banding": 529, + "country": 997, + "value": 3988, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 530, + "fields": { + "package": 140, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 846, + "fields": { + "banding": 530, + "country": 999, + "value": 4080, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 847, + "fields": { + "banding": 530, + "country": 998, + "value": 5304, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 848, + "fields": { + "banding": 530, + "country": 997, + "value": 4692, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 531, + "fields": { + "package": 140, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 849, + "fields": { + "banding": 531, + "country": 999, + "value": 5712, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 850, + "fields": { + "banding": 531, + "country": 998, + "value": 7426, + "default_currency": "USD" + } + }, + { + "model": "package.price", + "pk": 851, + "fields": { + "banding": 531, + "country": 997, + "value": 6569, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 141, + "fields": { + "name": "Package 141 - Plus (FTE/GBP)", + "description": "Test package 141", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 141, + "fields": { + "basepackage_ptr": 141, + "initiative": 16, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 154, + "fields": { + "package": 141, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 194, + "fields": { + "package": 141, + "banding_type_entry": 154, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 532, + "fields": { + "package": 141, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 852, + "fields": { + "banding": 532, + "country": 999, + "value": 1044, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 533, + "fields": { + "package": 141, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 853, + "fields": { + "banding": 533, + "country": 999, + "value": 1671, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 534, + "fields": { + "package": 141, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 854, + "fields": { + "banding": 534, + "country": 999, + "value": 2506, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 535, + "fields": { + "package": 141, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 855, + "fields": { + "banding": 535, + "country": 999, + "value": 3342, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 536, + "fields": { + "package": 141, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 856, + "fields": { + "banding": 536, + "country": 999, + "value": 4177, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 537, + "fields": { + "package": 141, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 857, + "fields": { + "banding": 537, + "country": 999, + "value": 5430, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 538, + "fields": { + "package": 141, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 858, + "fields": { + "banding": 538, + "country": 999, + "value": 7101, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 539, + "fields": { + "package": 141, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 859, + "fields": { + "banding": 539, + "country": 999, + "value": 9189, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 142, + "fields": { + "name": "Package 142 - Premium (FTE/GBP+USD)", + "description": "Test package 142", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 142, + "fields": { + "basepackage_ptr": 142, + "initiative": 17, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 155, + "fields": { + "package": 142, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 195, + "fields": { + "package": 142, + "banding_type_entry": 155, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 196, + "fields": { + "package": 142, + "banding_type_entry": 155, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 540, + "fields": { + "package": 142, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 860, + "fields": { + "banding": 540, + "country": 999, + "value": 1068, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 861, + "fields": { + "banding": 540, + "country": 998, + "value": 1389, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 541, + "fields": { + "package": 142, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 862, + "fields": { + "banding": 541, + "country": 999, + "value": 1710, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 863, + "fields": { + "banding": 541, + "country": 998, + "value": 2222, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 542, + "fields": { + "package": 142, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 864, + "fields": { + "banding": 542, + "country": 999, + "value": 2564, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 865, + "fields": { + "banding": 542, + "country": 998, + "value": 3334, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 543, + "fields": { + "package": 142, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 866, + "fields": { + "banding": 543, + "country": 999, + "value": 3419, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 867, + "fields": { + "banding": 543, + "country": 998, + "value": 4445, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 544, + "fields": { + "package": 142, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 868, + "fields": { + "banding": 544, + "country": 999, + "value": 4274, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 869, + "fields": { + "banding": 544, + "country": 998, + "value": 5556, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 545, + "fields": { + "package": 142, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 870, + "fields": { + "banding": 545, + "country": 999, + "value": 5556, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 871, + "fields": { + "banding": 545, + "country": 998, + "value": 7223, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 546, + "fields": { + "package": 142, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 872, + "fields": { + "banding": 546, + "country": 999, + "value": 7266, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 873, + "fields": { + "banding": 546, + "country": 998, + "value": 9446, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 547, + "fields": { + "package": 142, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 874, + "fields": { + "banding": 547, + "country": 999, + "value": 9403, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 875, + "fields": { + "banding": 547, + "country": 998, + "value": 12224, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 143, + "fields": { + "name": "Package 143 - Standard (Choice/GBP)", + "description": "Test package 143", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 143, + "fields": { + "basepackage_ptr": 143, + "initiative": 17, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 156, + "fields": { + "package": 143, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 197, + "fields": { + "package": 143, + "banding_type_entry": 156, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 548, + "fields": { + "package": 143, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 876, + "fields": { + "banding": 548, + "country": 999, + "value": 656, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 549, + "fields": { + "package": 143, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 877, + "fields": { + "banding": 549, + "country": 999, + "value": 1093, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 550, + "fields": { + "package": 143, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 878, + "fields": { + "banding": 550, + "country": 999, + "value": 1748, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 551, + "fields": { + "package": 143, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 879, + "fields": { + "banding": 551, + "country": 999, + "value": 2404, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 552, + "fields": { + "package": 143, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 880, + "fields": { + "banding": 552, + "country": 999, + "value": 3060, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 553, + "fields": { + "package": 143, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 881, + "fields": { + "banding": 553, + "country": 999, + "value": 3715, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 554, + "fields": { + "package": 143, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 882, + "fields": { + "banding": 554, + "country": 999, + "value": 4371, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 555, + "fields": { + "package": 143, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 883, + "fields": { + "banding": 555, + "country": 999, + "value": 6119, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 144, + "fields": { + "name": "Package 144 - Basic (FTE+Choice/GBP+USD)", + "description": "Test package 144", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 144, + "fields": { + "basepackage_ptr": 144, + "initiative": 18, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 157, + "fields": { + "package": 144, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 198, + "fields": { + "package": 144, + "banding_type_entry": 157, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 199, + "fields": { + "package": 144, + "banding_type_entry": 157, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 556, + "fields": { + "package": 144, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 884, + "fields": { + "banding": 556, + "country": 999, + "value": 1117, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 885, + "fields": { + "banding": 556, + "country": 998, + "value": 1452, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 557, + "fields": { + "package": 144, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 886, + "fields": { + "banding": 557, + "country": 999, + "value": 1787, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 887, + "fields": { + "banding": 557, + "country": 998, + "value": 2323, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 558, + "fields": { + "package": 144, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 888, + "fields": { + "banding": 558, + "country": 999, + "value": 2681, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 889, + "fields": { + "banding": 558, + "country": 998, + "value": 3485, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 559, + "fields": { + "package": 144, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 890, + "fields": { + "banding": 559, + "country": 999, + "value": 3574, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 891, + "fields": { + "banding": 559, + "country": 998, + "value": 4647, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 560, + "fields": { + "package": 144, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 892, + "fields": { + "banding": 560, + "country": 999, + "value": 4468, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 893, + "fields": { + "banding": 560, + "country": 998, + "value": 5808, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 561, + "fields": { + "package": 144, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 894, + "fields": { + "banding": 561, + "country": 999, + "value": 5808, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 895, + "fields": { + "banding": 561, + "country": 998, + "value": 7551, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 562, + "fields": { + "package": 144, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 896, + "fields": { + "banding": 562, + "country": 999, + "value": 7596, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 897, + "fields": { + "banding": 562, + "country": 998, + "value": 9874, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 563, + "fields": { + "package": 144, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 898, + "fields": { + "banding": 563, + "country": 999, + "value": 9830, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 899, + "fields": { + "banding": 563, + "country": 998, + "value": 12778, + "default_currency": "USD" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 158, + "fields": { + "package": 144, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 200, + "fields": { + "package": 144, + "banding_type_entry": 158, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 201, + "fields": { + "package": 144, + "banding_type_entry": 158, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 564, + "fields": { + "package": 144, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 900, + "fields": { + "banding": 564, + "country": 999, + "value": 670, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 901, + "fields": { + "banding": 564, + "country": 998, + "value": 871, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 565, + "fields": { + "package": 144, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 902, + "fields": { + "banding": 565, + "country": 999, + "value": 1117, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 903, + "fields": { + "banding": 565, + "country": 998, + "value": 1452, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 566, + "fields": { + "package": 144, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 904, + "fields": { + "banding": 566, + "country": 999, + "value": 1787, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 905, + "fields": { + "banding": 566, + "country": 998, + "value": 2323, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 567, + "fields": { + "package": 144, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 906, + "fields": { + "banding": 567, + "country": 999, + "value": 2457, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 907, + "fields": { + "banding": 567, + "country": 998, + "value": 3195, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 568, + "fields": { + "package": 144, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 908, + "fields": { + "banding": 568, + "country": 999, + "value": 3128, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 909, + "fields": { + "banding": 568, + "country": 998, + "value": 4066, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 569, + "fields": { + "package": 144, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 910, + "fields": { + "banding": 569, + "country": 999, + "value": 3798, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 911, + "fields": { + "banding": 569, + "country": 998, + "value": 4937, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 570, + "fields": { + "package": 144, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 912, + "fields": { + "banding": 570, + "country": 999, + "value": 4468, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 913, + "fields": { + "banding": 570, + "country": 998, + "value": 5808, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 571, + "fields": { + "package": 144, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 914, + "fields": { + "banding": 571, + "country": 999, + "value": 6255, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 915, + "fields": { + "banding": 571, + "country": 998, + "value": 8132, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 145, + "fields": { + "name": "Package 145 - Advanced (FTE/GBP+EUR)", + "description": "Test package 145", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 145, + "fields": { + "basepackage_ptr": 145, + "initiative": 18, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 159, + "fields": { + "package": 145, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 202, + "fields": { + "package": 145, + "banding_type_entry": 159, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 203, + "fields": { + "package": 145, + "banding_type_entry": 159, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 572, + "fields": { + "package": 145, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 916, + "fields": { + "banding": 572, + "country": 999, + "value": 1141, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 917, + "fields": { + "banding": 572, + "country": 997, + "value": 1312, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 573, + "fields": { + "package": 145, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 918, + "fields": { + "banding": 573, + "country": 999, + "value": 1826, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 919, + "fields": { + "banding": 573, + "country": 997, + "value": 2100, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 574, + "fields": { + "package": 145, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 920, + "fields": { + "banding": 574, + "country": 999, + "value": 2739, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 921, + "fields": { + "banding": 574, + "country": 997, + "value": 3150, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 575, + "fields": { + "package": 145, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 922, + "fields": { + "banding": 575, + "country": 999, + "value": 3652, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 923, + "fields": { + "banding": 575, + "country": 997, + "value": 4200, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 576, + "fields": { + "package": 145, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 924, + "fields": { + "banding": 576, + "country": 999, + "value": 4565, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 925, + "fields": { + "banding": 576, + "country": 997, + "value": 5250, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 577, + "fields": { + "package": 145, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 926, + "fields": { + "banding": 577, + "country": 999, + "value": 5934, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 927, + "fields": { + "banding": 577, + "country": 997, + "value": 6825, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 578, + "fields": { + "package": 145, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 928, + "fields": { + "banding": 578, + "country": 999, + "value": 7760, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 929, + "fields": { + "banding": 578, + "country": 997, + "value": 8925, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 579, + "fields": { + "package": 145, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 930, + "fields": { + "banding": 579, + "country": 999, + "value": 10043, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 931, + "fields": { + "banding": 579, + "country": 997, + "value": 11549, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 146, + "fields": { + "name": "Package 146 - Pro (Choice/USD)", + "description": "Test package 146", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 146, + "fields": { + "basepackage_ptr": 146, + "initiative": 19, + "default_country": 998, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 160, + "fields": { + "package": 146, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 204, + "fields": { + "package": 146, + "banding_type_entry": 160, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 580, + "fields": { + "package": 146, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 932, + "fields": { + "banding": 580, + "country": 998, + "value": 909, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 581, + "fields": { + "package": 146, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 933, + "fields": { + "banding": 581, + "country": 998, + "value": 1515, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 582, + "fields": { + "package": 146, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 934, + "fields": { + "banding": 582, + "country": 998, + "value": 2424, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 583, + "fields": { + "package": 146, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 935, + "fields": { + "banding": 583, + "country": 998, + "value": 3333, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 584, + "fields": { + "package": 146, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 936, + "fields": { + "banding": 584, + "country": 998, + "value": 4242, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 585, + "fields": { + "package": 146, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 937, + "fields": { + "banding": 585, + "country": 998, + "value": 5152, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 586, + "fields": { + "package": 146, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 938, + "fields": { + "banding": 586, + "country": 998, + "value": 6061, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 587, + "fields": { + "package": 146, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 939, + "fields": { + "banding": 587, + "country": 998, + "value": 8485, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 147, + "fields": { + "name": "Package 147 - Essential (FTE+Choice/GBP)", + "description": "Test package 147", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 147, + "fields": { + "basepackage_ptr": 147, + "initiative": 19, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 161, + "fields": { + "package": 147, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 205, + "fields": { + "package": 147, + "banding_type_entry": 161, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 588, + "fields": { + "package": 147, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 940, + "fields": { + "banding": 588, + "country": 999, + "value": 1190, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 589, + "fields": { + "package": 147, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 941, + "fields": { + "banding": 589, + "country": 999, + "value": 1904, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 590, + "fields": { + "package": 147, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 942, + "fields": { + "banding": 590, + "country": 999, + "value": 2855, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 591, + "fields": { + "package": 147, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 943, + "fields": { + "banding": 591, + "country": 999, + "value": 3807, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 592, + "fields": { + "package": 147, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 944, + "fields": { + "banding": 592, + "country": 999, + "value": 4759, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 593, + "fields": { + "package": 147, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 945, + "fields": { + "banding": 593, + "country": 999, + "value": 6187, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 594, + "fields": { + "package": 147, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 946, + "fields": { + "banding": 594, + "country": 999, + "value": 8090, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 595, + "fields": { + "package": 147, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 947, + "fields": { + "banding": 595, + "country": 999, + "value": 10470, + "default_currency": "GBP" + } + }, + { + "model": "package.bandingtypeentry", + "pk": 162, + "fields": { + "package": 147, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 206, + "fields": { + "package": 147, + "banding_type_entry": 162, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 596, + "fields": { + "package": 147, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 948, + "fields": { + "banding": 596, + "country": 999, + "value": 714, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 597, + "fields": { + "package": 147, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 949, + "fields": { + "banding": 597, + "country": 999, + "value": 1190, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 598, + "fields": { + "package": 147, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 950, + "fields": { + "banding": 598, + "country": 999, + "value": 1904, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 599, + "fields": { + "package": 147, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 951, + "fields": { + "banding": 599, + "country": 999, + "value": 2617, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 600, + "fields": { + "package": 147, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 952, + "fields": { + "banding": 600, + "country": 999, + "value": 3331, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 601, + "fields": { + "package": 147, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 953, + "fields": { + "banding": 601, + "country": 999, + "value": 4045, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 602, + "fields": { + "package": 147, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 954, + "fields": { + "banding": 602, + "country": 999, + "value": 4759, + "default_currency": "GBP" + } + }, + { + "model": "package.banding", + "pk": 603, + "fields": { + "package": 147, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 955, + "fields": { + "banding": 603, + "country": 999, + "value": 6663, + "default_currency": "GBP" + } + }, + { + "model": "package.basepackage", + "pk": 148, + "fields": { + "name": "Package 148 - Complete (FTE/EUR)", + "description": "Test package 148", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 148, + "fields": { + "basepackage_ptr": 148, + "initiative": 20, + "default_country": 997, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 163, + "fields": { + "package": 148, + "banding_type": 100, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 207, + "fields": { + "package": 148, + "banding_type_entry": 163, + "country": 997, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 604, + "fields": { + "package": 148, + "banding_type": 100, + "vocab": 100, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 956, + "fields": { + "banding": 604, + "country": 997, + "value": 1396, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 605, + "fields": { + "package": 148, + "banding_type": 100, + "vocab": 101, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 957, + "fields": { + "banding": 605, + "country": 997, + "value": 2234, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 606, + "fields": { + "package": 148, + "banding_type": 100, + "vocab": 102, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 958, + "fields": { + "banding": 606, + "country": 997, + "value": 3351, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 607, + "fields": { + "package": 148, + "banding_type": 100, + "vocab": 103, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 959, + "fields": { + "banding": 607, + "country": 997, + "value": 4468, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 608, + "fields": { + "package": 148, + "banding_type": 100, + "vocab": 104, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 960, + "fields": { + "banding": 608, + "country": 997, + "value": 5584, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 609, + "fields": { + "package": 148, + "banding_type": 100, + "vocab": 105, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 961, + "fields": { + "banding": 609, + "country": 997, + "value": 7260, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 610, + "fields": { + "package": 148, + "banding_type": 100, + "vocab": 106, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 962, + "fields": { + "banding": 610, + "country": 997, + "value": 9493, + "default_currency": "EUR" + } + }, + { + "model": "package.banding", + "pk": 611, + "fields": { + "package": 148, + "banding_type": 100, + "vocab": 107, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 963, + "fields": { + "banding": 611, + "country": 997, + "value": 12286, + "default_currency": "EUR" + } + }, + { + "model": "package.basepackage", + "pk": 149, + "fields": { + "name": "Package 149 - Select (Choice/GBP+USD)", + "description": "Test package 149", + "active": true, + "banding_type": 101, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.package", + "pk": 149, + "fields": { + "basepackage_ptr": 149, + "initiative": 20, + "default_country": 999, + "meta_only": false + } + }, + { + "model": "package.bandingtypeentry", + "pk": 164, + "fields": { + "package": 149, + "banding_type": 101, + "redirect_display": "", + "redirect": "" + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 208, + "fields": { + "package": 149, + "banding_type_entry": 164, + "country": 999, + "payment_processor": null + } + }, + { + "model": "package.bandingtypecurrencyentry", + "pk": 209, + "fields": { + "package": 149, + "banding_type_entry": 164, + "country": 998, + "payment_processor": null + } + }, + { + "model": "package.banding", + "pk": 612, + "fields": { + "package": 149, + "banding_type": 101, + "vocab": 108, + "active": true, + "order": 1 + } + }, + { + "model": "package.price", + "pk": 964, + "fields": { + "banding": 612, + "country": 999, + "value": 743, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 965, + "fields": { + "banding": 612, + "country": 998, + "value": 966, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 613, + "fields": { + "package": 149, + "banding_type": 101, + "vocab": 109, + "active": true, + "order": 2 + } + }, + { + "model": "package.price", + "pk": 966, + "fields": { + "banding": 613, + "country": 999, + "value": 1238, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 967, + "fields": { + "banding": 613, + "country": 998, + "value": 1610, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 614, + "fields": { + "package": 149, + "banding_type": 101, + "vocab": 110, + "active": true, + "order": 3 + } + }, + { + "model": "package.price", + "pk": 968, + "fields": { + "banding": 614, + "country": 999, + "value": 1981, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 969, + "fields": { + "banding": 614, + "country": 998, + "value": 2576, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 615, + "fields": { + "package": 149, + "banding_type": 101, + "vocab": 111, + "active": true, + "order": 4 + } + }, + { + "model": "package.price", + "pk": 970, + "fields": { + "banding": 615, + "country": 999, + "value": 2724, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 971, + "fields": { + "banding": 615, + "country": 998, + "value": 3541, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 616, + "fields": { + "package": 149, + "banding_type": 101, + "vocab": 112, + "active": true, + "order": 5 + } + }, + { + "model": "package.price", + "pk": 972, + "fields": { + "banding": 616, + "country": 999, + "value": 3467, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 973, + "fields": { + "banding": 616, + "country": 998, + "value": 4507, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 617, + "fields": { + "package": 149, + "banding_type": 101, + "vocab": 113, + "active": true, + "order": 6 + } + }, + { + "model": "package.price", + "pk": 974, + "fields": { + "banding": 617, + "country": 999, + "value": 4210, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 975, + "fields": { + "banding": 617, + "country": 998, + "value": 5473, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 618, + "fields": { + "package": 149, + "banding_type": 101, + "vocab": 114, + "active": true, + "order": 7 + } + }, + { + "model": "package.price", + "pk": 976, + "fields": { + "banding": 618, + "country": 999, + "value": 4953, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 977, + "fields": { + "banding": 618, + "country": 998, + "value": 6439, + "default_currency": "USD" + } + }, + { + "model": "package.banding", + "pk": 619, + "fields": { + "package": 149, + "banding_type": 101, + "vocab": 115, + "active": true, + "order": 8 + } + }, + { + "model": "package.price", + "pk": 978, + "fields": { + "banding": 619, + "country": 999, + "value": 6934, + "default_currency": "GBP" + } + }, + { + "model": "package.price", + "pk": 979, + "fields": { + "banding": 619, + "country": 998, + "value": 9014, + "default_currency": "USD" + } + }, + { + "model": "package.basepackage", + "pk": 300, + "fields": { + "name": "Complete Academic Suite", + "description": "Meta package: Complete Academic Suite", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.metapackage", + "pk": 300, + "fields": { + "basepackage_ptr": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 200, + "fields": { + "package": 100, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 201, + "fields": { + "package": 101, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 202, + "fields": { + "package": 102, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 203, + "fields": { + "package": 103, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 204, + "fields": { + "package": 104, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 205, + "fields": { + "package": 105, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 206, + "fields": { + "package": 110, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 207, + "fields": { + "package": 111, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 208, + "fields": { + "package": 112, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 209, + "fields": { + "package": 115, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 210, + "fields": { + "package": 116, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 211, + "fields": { + "package": 120, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 212, + "fields": { + "package": 121, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 213, + "fields": { + "package": 130, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 214, + "fields": { + "package": 131, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 215, + "fields": { + "package": 140, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 216, + "fields": { + "package": 141, + "meta_package": 300 + } + }, + { + "model": "package.metapackagelink", + "pk": 217, + "fields": { + "package": 142, + "meta_package": 300 + } + }, + { + "model": "package.basepackage", + "pk": 301, + "fields": { + "name": "Research Excellence Bundle", + "description": "Meta package: Research Excellence Bundle", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.metapackage", + "pk": 301, + "fields": { + "basepackage_ptr": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 218, + "fields": { + "package": 103, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 219, + "fields": { + "package": 106, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 220, + "fields": { + "package": 107, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 221, + "fields": { + "package": 108, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 222, + "fields": { + "package": 113, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 223, + "fields": { + "package": 114, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 224, + "fields": { + "package": 117, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 225, + "fields": { + "package": 122, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 226, + "fields": { + "package": 123, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 227, + "fields": { + "package": 127, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 228, + "fields": { + "package": 132, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 229, + "fields": { + "package": 133, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 230, + "fields": { + "package": 143, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 231, + "fields": { + "package": 144, + "meta_package": 301 + } + }, + { + "model": "package.metapackagelink", + "pk": 232, + "fields": { + "package": 145, + "meta_package": 301 + } + }, + { + "model": "package.basepackage", + "pk": 302, + "fields": { + "name": "Digital Futures Pack", + "description": "Meta package: Digital Futures Pack", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.metapackage", + "pk": 302, + "fields": { + "basepackage_ptr": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 233, + "fields": { + "package": 105, + "meta_package": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 234, + "fields": { + "package": 109, + "meta_package": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 235, + "fields": { + "package": 110, + "meta_package": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 236, + "fields": { + "package": 118, + "meta_package": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 237, + "fields": { + "package": 119, + "meta_package": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 238, + "fields": { + "package": 124, + "meta_package": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 239, + "fields": { + "package": 125, + "meta_package": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 240, + "fields": { + "package": 128, + "meta_package": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 241, + "fields": { + "package": 134, + "meta_package": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 242, + "fields": { + "package": 135, + "meta_package": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 243, + "fields": { + "package": 146, + "meta_package": 302 + } + }, + { + "model": "package.metapackagelink", + "pk": 244, + "fields": { + "package": 147, + "meta_package": 302 + } + }, + { + "model": "package.basepackage", + "pk": 303, + "fields": { + "name": "Global Access Collection", + "description": "Meta package: Global Access Collection", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.metapackage", + "pk": 303, + "fields": { + "basepackage_ptr": 303 + } + }, + { + "model": "package.metapackagelink", + "pk": 245, + "fields": { + "package": 100, + "meta_package": 303 + } + }, + { + "model": "package.metapackagelink", + "pk": 246, + "fields": { + "package": 111, + "meta_package": 303 + } + }, + { + "model": "package.metapackagelink", + "pk": 247, + "fields": { + "package": 115, + "meta_package": 303 + } + }, + { + "model": "package.metapackagelink", + "pk": 248, + "fields": { + "package": 120, + "meta_package": 303 + } + }, + { + "model": "package.metapackagelink", + "pk": 249, + "fields": { + "package": 126, + "meta_package": 303 + } + }, + { + "model": "package.metapackagelink", + "pk": 250, + "fields": { + "package": 129, + "meta_package": 303 + } + }, + { + "model": "package.metapackagelink", + "pk": 251, + "fields": { + "package": 136, + "meta_package": 303 + } + }, + { + "model": "package.metapackagelink", + "pk": 252, + "fields": { + "package": 137, + "meta_package": 303 + } + }, + { + "model": "package.metapackagelink", + "pk": 253, + "fields": { + "package": 148, + "meta_package": 303 + } + }, + { + "model": "package.metapackagelink", + "pk": 254, + "fields": { + "package": 149, + "meta_package": 303 + } + }, + { + "model": "package.basepackage", + "pk": 304, + "fields": { + "name": "Innovation Portfolio", + "description": "Meta package: Innovation Portfolio", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.metapackage", + "pk": 304, + "fields": { + "basepackage_ptr": 304 + } + }, + { + "model": "package.metapackagelink", + "pk": 255, + "fields": { + "package": 101, + "meta_package": 304 + } + }, + { + "model": "package.metapackagelink", + "pk": 256, + "fields": { + "package": 106, + "meta_package": 304 + } + }, + { + "model": "package.metapackagelink", + "pk": 257, + "fields": { + "package": 112, + "meta_package": 304 + } + }, + { + "model": "package.metapackagelink", + "pk": 258, + "fields": { + "package": 121, + "meta_package": 304 + } + }, + { + "model": "package.metapackagelink", + "pk": 259, + "fields": { + "package": 130, + "meta_package": 304 + } + }, + { + "model": "package.metapackagelink", + "pk": 260, + "fields": { + "package": 138, + "meta_package": 304 + } + }, + { + "model": "package.metapackagelink", + "pk": 261, + "fields": { + "package": 139, + "meta_package": 304 + } + }, + { + "model": "package.metapackagelink", + "pk": 262, + "fields": { + "package": 142, + "meta_package": 304 + } + }, + { + "model": "package.metapackagelink", + "pk": 263, + "fields": { + "package": 145, + "meta_package": 304 + } + }, + { + "model": "package.basepackage", + "pk": 305, + "fields": { + "name": "Core Scholarship Set", + "description": "Meta package: Core Scholarship Set", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.metapackage", + "pk": 305, + "fields": { + "basepackage_ptr": 305 + } + }, + { + "model": "package.metapackagelink", + "pk": 264, + "fields": { + "package": 102, + "meta_package": 305 + } + }, + { + "model": "package.metapackagelink", + "pk": 265, + "fields": { + "package": 107, + "meta_package": 305 + } + }, + { + "model": "package.metapackagelink", + "pk": 266, + "fields": { + "package": 113, + "meta_package": 305 + } + }, + { + "model": "package.metapackagelink", + "pk": 267, + "fields": { + "package": 122, + "meta_package": 305 + } + }, + { + "model": "package.metapackagelink", + "pk": 268, + "fields": { + "package": 131, + "meta_package": 305 + } + }, + { + "model": "package.metapackagelink", + "pk": 269, + "fields": { + "package": 143, + "meta_package": 305 + } + }, + { + "model": "package.metapackagelink", + "pk": 270, + "fields": { + "package": 146, + "meta_package": 305 + } + }, + { + "model": "package.basepackage", + "pk": 306, + "fields": { + "name": "Discovery Bundle", + "description": "Meta package: Discovery Bundle", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.metapackage", + "pk": 306, + "fields": { + "basepackage_ptr": 306 + } + }, + { + "model": "package.metapackagelink", + "pk": 271, + "fields": { + "package": 104, + "meta_package": 306 + } + }, + { + "model": "package.metapackagelink", + "pk": 272, + "fields": { + "package": 108, + "meta_package": 306 + } + }, + { + "model": "package.metapackagelink", + "pk": 273, + "fields": { + "package": 116, + "meta_package": 306 + } + }, + { + "model": "package.metapackagelink", + "pk": 274, + "fields": { + "package": 123, + "meta_package": 306 + } + }, + { + "model": "package.metapackagelink", + "pk": 275, + "fields": { + "package": 132, + "meta_package": 306 + } + }, + { + "model": "package.metapackagelink", + "pk": 276, + "fields": { + "package": 147, + "meta_package": 306 + } + }, + { + "model": "package.basepackage", + "pk": 307, + "fields": { + "name": "Analytics Pack", + "description": "Meta package: Analytics Pack", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.metapackage", + "pk": 307, + "fields": { + "basepackage_ptr": 307 + } + }, + { + "model": "package.metapackagelink", + "pk": 277, + "fields": { + "package": 109, + "meta_package": 307 + } + }, + { + "model": "package.metapackagelink", + "pk": 278, + "fields": { + "package": 114, + "meta_package": 307 + } + }, + { + "model": "package.metapackagelink", + "pk": 279, + "fields": { + "package": 124, + "meta_package": 307 + } + }, + { + "model": "package.metapackagelink", + "pk": 280, + "fields": { + "package": 133, + "meta_package": 307 + } + }, + { + "model": "package.metapackagelink", + "pk": 281, + "fields": { + "package": 140, + "meta_package": 307 + } + }, + { + "model": "package.basepackage", + "pk": 308, + "fields": { + "name": "Essentials Collection", + "description": "Meta package: Essentials Collection", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.metapackage", + "pk": 308, + "fields": { + "basepackage_ptr": 308 + } + }, + { + "model": "package.metapackagelink", + "pk": 282, + "fields": { + "package": 117, + "meta_package": 308 + } + }, + { + "model": "package.metapackagelink", + "pk": 283, + "fields": { + "package": 125, + "meta_package": 308 + } + }, + { + "model": "package.metapackagelink", + "pk": 284, + "fields": { + "package": 134, + "meta_package": 308 + } + }, + { + "model": "package.metapackagelink", + "pk": 285, + "fields": { + "package": 141, + "meta_package": 308 + } + }, + { + "model": "package.basepackage", + "pk": 309, + "fields": { + "name": "Starter Kit", + "description": "Meta package: Starter Kit", + "active": true, + "banding_type": 100, + "pricing_display": "", + "display_contract_length_months": 12, + "image": null, + "recommended": false + } + }, + { + "model": "package.metapackage", + "pk": 309, + "fields": { + "basepackage_ptr": 309 + } + }, + { + "model": "package.metapackagelink", + "pk": 286, + "fields": { + "package": 118, + "meta_package": 309 + } + }, + { + "model": "package.metapackagelink", + "pk": 287, + "fields": { + "package": 126, + "meta_package": 309 + } + }, + { + "model": "package.metapackagelink", + "pk": 288, + "fields": { + "package": 144, + "meta_package": 309 + } + } +] diff --git a/infrastructure/ansible/cron/install_cron_tasks.yml b/infrastructure/ansible/cron/install_cron_tasks.yml index 4e3f950..ae693c1 100644 --- a/infrastructure/ansible/cron/install_cron_tasks.yml +++ b/infrastructure/ansible/cron/install_cron_tasks.yml @@ -16,3 +16,19 @@ hour: "0" minute: "15" job: "/usr/bin/bash -c \"/home/obc/intrepid/.backup.sh\"" + - name: Install Thoth updater + become: true + become_user: obc + ansible.builtin.cron: + name: "Update Thoth" + hour: "0" + minute: "0" + job: "/home/obc/intrepid-venv/bin/python /home/obc/intrepid/src/manage.py sync_thoth" + - name: Install price updater + become: true + become_user: obc + ansible.builtin.cron: + name: "Update Prices" + hour: "3" + minute: "0" + job: "/home/obc/intrepid-venv/bin/python /home/obc/intrepid/src/manage.py pregenerate_prices" \ No newline at end of file diff --git a/infrastructure/main.tf b/infrastructure/main.tf index 9c433cb..a1c7838 100644 --- a/infrastructure/main.tf +++ b/infrastructure/main.tf @@ -30,7 +30,8 @@ module "key" { module "route53" { source = "./modules/route53" - primary_ip = module.ec2.ips[0] + # primary_ip = module.ec2.ips[0] + primary_ip = "18.170.47.219" domain_name = var.domain_name } diff --git a/infrastructure/modules/route53/main.tf b/infrastructure/modules/route53/main.tf index 201d2db..f17b320 100644 --- a/infrastructure/modules/route53/main.tf +++ b/infrastructure/modules/route53/main.tf @@ -27,7 +27,8 @@ resource "aws_route53_record" "bookstack" { type = "A" ttl = "300" - records = ["3.223.136.50"] + #records = ["3.223.136.50"] + records = ["3.226.254.16"] } resource "aws_route53_record" "bookstack_toolkit" { @@ -36,7 +37,8 @@ resource "aws_route53_record" "bookstack_toolkit" { type = "A" ttl = "300" - records = ["3.223.136.50"] + #records = ["3.223.136.50"] + records = ["3.226.254.16"] } resource "aws_route53_record" "cert_one_javi" { diff --git a/requirements.txt b/requirements.txt index 3fe4276..d8a1c1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,40 +1,71 @@ +asgiref==3.7.2 +Babel==2.13.1 +beautifulsoup4==4.12.2 +bleach==5.0.1 +boto3==1.28.84 +botocore==1.31.84 +certifi==2023.7.22 +charset-normalizer==3.3.2 +crispy-bootstrap4==2022.1 +CurrencyConverter==0.17.5 Django==3.2.8 -Pillow==8.4.0 -python-dateutil==2.8.1 -pytz==2020.1 -psycopg2-binary -django-summernote==0.8.11.6 django-anymail==8.1 -django-bootstrap5 -django-bootstrap4 -django-debug-toolbar==3.2.1 -django-two-factor-auth -phonenumbers -git+https://github.com/dqprogramming/django-fluid-permissions@master#django-fluid-permissions -django-bleach -django-ckeditor --e git+https://github.com/dqprogramming/thoth-client.git#egg=thothlibrary -python-crontab==2.2.3 django-basicauth==0.5.3 -django-request -django-crispy-forms -tqdm -pycountry -babel -djangorestframework -python-magic==0.4.25 -resize-image -python-magic -django-extensions -babel -gunicorn +django-bleach==3.1.0 +django-bootstrap4==23.2 +django-bootstrap5==23.3 +django-ckeditor==6.7.0 +django-crispy-forms==2.0 +django-debug-toolbar==3.2.1 +django-extensions==3.2.3 +django-formtools==2.4.1 +django-js-asset==2.1.0 django-middleware-global-request==0.2.0 -boto3 -rich -humanize -sentry-sdk +django-otp==1.3.0 +django-phonenumber-field==6.4.0 +django-request==1.6.3 +django-summernote==0.8.11.6 +django-two-factor-auth==1.13.2 +djangorestframework==3.14.0 +django-modeltranslation +docx==0.2.4 +FluidPermissions @ git+https://github.com/dqprogramming/django-fluid-permissions@c5e039e49956af4f6a5a08122dc7d966f2af5724 +graphqlclient==0.2.4 +gunicorn==21.2.0 +humanize==4.8.0 +idna==3.4 +jmespath==1.0.1 +lxml==4.9.3 +markdown-it-py==3.0.0 +mdurl==0.1.2 +munch==4.0.0 +packaging==23.2 +pdfkit==1.0.0 +phonenumbers==8.13.24 +Pillow==8.4.0 +psycopg2-binary==2.9.9 +pycountry==22.3.5 +Pygments==2.16.1 +pypng==0.20220715.0 +python-crontab==2.2.3 +python-dateutil==2.8.1 python-docx==0.8.11 -CurrencyConverter==0.17.5 -requests -pdfkit -docx \ No newline at end of file +python-magic==0.4.25 +python-resize-image==1.1.20 +pytz==2020.1 +qrcode==7.4.2 +requests==2.31.0 +resize-image==0.4.0 +rich==13.6.0 +s3transfer==0.7.0 +sentry-sdk==1.34.0 +six==1.16.0 +soupsieve==2.5 +sqlparse==0.4.4 +-e git+https://github.com/dqprogramming/thoth-client.git@5a7396dd1030d01858e4e2478b6328209e35328d#egg=thothlibrary +tinycss2==1.1.1 +tqdm==4.66.1 +typing_extensions==4.8.0 +urllib3==2.0.7 +webencodings==0.5.1 +geoip2 \ No newline at end of file diff --git a/src/api/serializers.py b/src/api/serializers.py index 3554866..c96cd29 100644 --- a/src/api/serializers.py +++ b/src/api/serializers.py @@ -5,6 +5,21 @@ from vocab import models as vocab_models +class BasketSummarySerializer(serializers.Serializer): + # Internal Python name: + session_id = serializers.CharField() + basket = serializers.URLField() + items = serializers.IntegerField() + + def to_representation(self, instance): + """ + Rename `session_id` → `session-id` in the JSON output. + """ + rep = super().to_representation(instance) + rep["session-id"] = rep.pop("session_id") + return rep + + class PackageSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = package_models.Package diff --git a/src/api/urls.py b/src/api/urls.py index 0385f77..f8cf3ce 100644 --- a/src/api/urls.py +++ b/src/api/urls.py @@ -28,6 +28,7 @@ class DocumentedRouter(routers.DefaultRouter): router.register(r"standards", views.StandardViewSet) + # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. urlpatterns = [ @@ -35,4 +36,5 @@ class DocumentedRouter(routers.DefaultRouter): path( "api-auth/", include("rest_framework.urls", namespace="rest_framework") ), + path("session//", views.BasketSessionAPIView.as_view(), name="basket_session"), ] diff --git a/src/api/views.py b/src/api/views.py index fd576af..799c7a6 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -1,10 +1,103 @@ +from django.conf import settings +from django.contrib.auth import get_user_model +from django.contrib.sessions.models import Session +from django.urls import reverse +from rest_framework import generics from rest_framework import viewsets +from rest_framework.response import Response from api import serializers from initiatives import models as initiative_models from package import models as package_models +from package.models import Basket # adjust import to your app from vocab import models as vocab_models +User = get_user_model() + +class BasketSessionAPIView(generics.GenericAPIView): + """ + Shows basket status for a session cookie ID + """ + serializer_class = serializers.BasketSummarySerializer + + def get_basket_url(self, request): + basket_path = reverse("basket_list") + + # Prefer explicit DOMAIN_NAME setting if present, + # else use current request + domain = getattr(settings, "DOMAIN_NAME", "openbookcollective.org") + if domain: + # Ensure no trailing slash duplication issues + domain = domain.rstrip("/") + return f"{domain}{basket_path}" + return request.build_absolute_uri(basket_path) + + def get_basket_count(self, session_id, user): + + if not user: + baskets = Basket.objects.filter(session_id=session_id, active=True) + else: + baskets = Basket.objects.filter(account=user, active=True) + + orders = 0 + + for basket in baskets: + orders_basket = basket.packages.count() + orders += orders_basket + + meta_packages_basket = basket.meta_packages.count() + orders += meta_packages_basket + + # set the basket + if baskets.exists(): + basket = baskets.first() + else: + basket = None + + return orders, basket + + def get_user_from_session_key(self, session_key: str): + """ + Given a Django session_key (sessionid cookie value), + return a tuple: (session, user_or_None) + """ + + try: + session = Session.objects.get(session_key=session_key) + except Session.DoesNotExist: + return None, None + + # The session is valid; decode it + session_data = session.get_decoded() + + # Django stores the authenticated user's ID under this key + uid = session_data.get('_auth_user_id') + backend_path = session_data.get('_auth_user_backend') + + if not uid or not backend_path: + return session, None + + try: + user = User.objects.get(pk=uid) + except User.DoesNotExist: + return session, None + + return session, user + + def get(self, request, session_id, *args, **kwargs): + session, user = self.get_user_from_session_key(session_id) + + orders, basket = self.get_basket_count(session_id, user) + + data = { + "session_id": session_id, + "basket": self.get_basket_url(request), + "items": orders, + } + + serializer = self.get_serializer(data) + return Response(serializer.data) + class PackageViewSet(viewsets.ReadOnlyModelViewSet): """ diff --git a/src/cms/forms.py b/src/cms/forms.py index 01d6c43..bda3f42 100644 --- a/src/cms/forms.py +++ b/src/cms/forms.py @@ -1,5 +1,9 @@ from django import forms from django_summernote.widgets import SummernoteWidget +from django.conf import settings + +from modeltranslation.utils import get_translation_fields +from django.utils.translation import get_language from cms import models, utils from thoth import models as thoth_models @@ -14,6 +18,7 @@ class Meta: model = models.PageUpdate fields = ( "title", + "url_expression", "abstract_paragraph", "display", "sequence", @@ -79,16 +84,37 @@ class VersionForm(forms.ModelForm): class Meta: model = models.Version - exclude = ("created", "created_by") + fields = ( + 'first_paragraph', + 'pre_break_content', + 'pull_quote', + 'show_quote_icons', + 'body', + ) - def __init__(self, *args, **kwargs): + def __init__( + self, + *args, + **kwargs, + ): self.user = kwargs.pop("user") - super(VersionForm, self).__init__(*args, **kwargs) + self.source_version = kwargs.pop("source_version", None) + super().__init__(*args, **kwargs) def save(self, commit=True): - version = super(VersionForm, self).save(commit=False) + version = super().save(commit=False) version.created_by = self.user + if self.source_version: + current_lang = get_language() + for field in self.Meta.fields: + for lang_code in [lang[0] for lang in settings.LANGUAGES]: + trans_field = f"{field}_{lang_code}" + + if lang_code != current_lang: + value = getattr(self.source_version, trans_field, None) + setattr(version, trans_field, value) + if commit: version.save() @@ -102,7 +128,12 @@ class WhoWeAreEntryForm(forms.ModelForm): class Meta: model = models.WhoWeAreProfileItem - + fields = ( + "display_name", + "affiliation_line", + "bio", + "police_mugshot", + ) exclude = ("section", "order") def clean_police_mugshot(self): @@ -154,6 +185,12 @@ class HomePageQuoteForm(forms.ModelForm): class Meta: model = models.HomePageQuote + fields = ( + 'pill_name', + 'quotation', + 'organization_attribution', + 'organization_logo', + ) exclude = ("order",) def clean_organization_logo(self): diff --git a/src/cms/migrations/0038_pageupdate_url_expression.py b/src/cms/migrations/0038_pageupdate_url_expression.py new file mode 100644 index 0000000..4674618 --- /dev/null +++ b/src/cms/migrations/0038_pageupdate_url_expression.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.8 on 2024-08-24 09:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0037_primaryinitiative_supporters_page'), + ] + + operations = [ + migrations.AddField( + model_name='pageupdate', + name='url_expression', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='URL expression'), + ), + ] diff --git a/src/cms/migrations/0039_auto_20240914_0823.py b/src/cms/migrations/0039_auto_20240914_0823.py new file mode 100644 index 0000000..148457b --- /dev/null +++ b/src/cms/migrations/0039_auto_20240914_0823.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.8 on 2024-09-14 08:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0038_pageupdate_url_expression'), + ] + + operations = [ + migrations.AddField( + model_name='sitetext', + name='body_de', + field=models.TextField(null=True), + ), + migrations.AddField( + model_name='sitetext', + name='body_en', + field=models.TextField(null=True), + ), + ] diff --git a/src/cms/migrations/0040_alter_sitetext_key.py b/src/cms/migrations/0040_alter_sitetext_key.py new file mode 100644 index 0000000..18dcd5d --- /dev/null +++ b/src/cms/migrations/0040_alter_sitetext_key.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.8 on 2024-09-30 12:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0039_auto_20240914_0823'), + ] + + operations = [ + migrations.AlterField( + model_name='sitetext', + name='key', + field=models.SlugField(max_length=255, unique=True), + ), + ] diff --git a/src/cms/migrations/0041_auto_20241125_1147.py b/src/cms/migrations/0041_auto_20241125_1147.py new file mode 100644 index 0000000..17c8e2e --- /dev/null +++ b/src/cms/migrations/0041_auto_20241125_1147.py @@ -0,0 +1,124 @@ +# Generated by Django 3.2.8 on 2024-11-25 11:47 + +from django.db import migrations, models +import django_bleach.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0040_alter_sitetext_key'), + ] + + operations = [ + migrations.AddField( + model_name='homepagequote', + name='organization_attribution_de', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='homepagequote', + name='organization_attribution_en', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='homepagequote', + name='person_attribution_de', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='homepagequote', + name='person_attribution_en', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='homepagequote', + name='pill_name_de', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='homepagequote', + name='pill_name_en', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='homepagequote', + name='quotation_de', + field=models.TextField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='homepagequote', + name='quotation_en', + field=models.TextField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='pageupdate', + name='abstract_paragraph_de', + field=models.TextField(blank=True, null=True, verbose_name='Page abstract paragraph (a brief summary)'), + ), + migrations.AddField( + model_name='pageupdate', + name='abstract_paragraph_en', + field=models.TextField(blank=True, null=True, verbose_name='Page abstract paragraph (a brief summary)'), + ), + migrations.AddField( + model_name='pageupdate', + name='title_de', + field=models.CharField(help_text='Text displayed in h1 for this page.', max_length=255, null=True), + ), + migrations.AddField( + model_name='pageupdate', + name='title_en', + field=models.CharField(help_text='Text displayed in h1 for this page.', max_length=255, null=True), + ), + migrations.AddField( + model_name='version', + name='body_de', + field=django_bleach.models.BleachField(default='', help_text='The rest of the page or news update, HTML. We recommend beginning this with a heading tag.', null=True), + ), + migrations.AddField( + model_name='version', + name='body_en', + field=django_bleach.models.BleachField(default='', help_text='The rest of the page or news update, HTML. We recommend beginning this with a heading tag.', null=True), + ), + migrations.AddField( + model_name='version', + name='first_paragraph_de', + field=django_bleach.models.BleachField(default='', help_text='The first paragraph of the page, HTML. Do NOT start this paragraph with a link.', null=True), + ), + migrations.AddField( + model_name='version', + name='first_paragraph_en', + field=django_bleach.models.BleachField(default='', help_text='The first paragraph of the page, HTML. Do NOT start this paragraph with a link.', null=True), + ), + migrations.AddField( + model_name='version', + name='pre_break_content_de', + field=django_bleach.models.BleachField(blank=True, default='', help_text='A second paragraph that will be displayed before the image break. We recommend beginning this with a heading tag.', null=True), + ), + migrations.AddField( + model_name='version', + name='pre_break_content_en', + field=django_bleach.models.BleachField(blank=True, default='', help_text='A second paragraph that will be displayed before the image break. We recommend beginning this with a heading tag.', null=True), + ), + migrations.AddField( + model_name='version', + name='pull_quote_de', + field=models.TextField(blank=True, default='', help_text='The text to display in the pull-quote', null=True), + ), + migrations.AddField( + model_name='version', + name='pull_quote_en', + field=models.TextField(blank=True, default='', help_text='The text to display in the pull-quote', null=True), + ), + migrations.AddField( + model_name='whoweareprofileitem', + name='bio_de', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='whoweareprofileitem', + name='bio_en', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/src/cms/migrations/0042_auto_20250414_1249.py b/src/cms/migrations/0042_auto_20250414_1249.py new file mode 100644 index 0000000..7e79d61 --- /dev/null +++ b/src/cms/migrations/0042_auto_20250414_1249.py @@ -0,0 +1,226 @@ +from django.db import migrations, models + + +def mark_frontend_site_texts(apps, schema_editor): + SiteText = apps.get_model('cms', 'SiteText') + + frontend_keys = [ + '2fa_backup', + '2fa_backup_phone', + '2fa_calling', + '2fa_last_resort', + '2fa_last_resort_button', + '2fa_text', + '2fa_token', + 'account_email', + 'activate_account', + 'activate_new_account', + 'add', + 'add_search', + 'add_to_quote', + 'add_to_search', + 'advanced_search', + 'agree_terms_of_signup', + 'all_initiatives', + 'all_subjects', + 'approval', + 'approval_description', + 'approve_this_signup', + 'approved_by_initiative', + 'approved_by_provider', + 'auth_step_info', + 'author', + 'author_search_note', + 'baskets_active_quote', + 'baskets_anon', + 'baskets_header', + 'baskets_sub_header', + 'board_of_stewards', + 'books', + 'books_header', + 'books_sub_header', + 'build_advanced_search', + 'build_your_quote', + 'change', + 'change_country', + 'change_my_password', + 'close', + 'close_quote', + 'collection_collectiv', + 'collections_and_collectives', + 'collective_catalogue', + 'collective_catalogue_description', + 'completed_order', + 'confirm_password', + 'contact_details', + 'contact_details_description', + 'contact_email', + 'contact_us', + 'cost', + 'create_account', + 'create_add_packages', + 'date_newest', + 'date_oldest', + 'delete', + 'delete_all_terms', + 'doi', + 'download_formats', + 'download_order_documents', + 'download_quotation', + 'email', + 'email_us', + 'enter_new_password_twice', + 'external', + 'filters', + 'finance_committee', + 'find_out_more', + 'first', + 'forgot_your_password', + 'free_text', + 'included_in', + 'included_in_packages', + 'included_packages', + 'initiative', + 'initiative_packages', + 'initiatives', + 'initiatives_subhead', + 'invoice_status', + 'keyword', + 'keywords', + 'last', + 'latest_titles', + 'log_in', + 'login', + 'login_error', + 'login_info', + 'login_to_see_page', + 'manage_your_searches', + 'management_team', + 'membership_committee', + 'membership_offer', + 'metadata_formats', + 'months_contract', + 'more_info', + 'name', + 'new_password', + 'news', + 'next', + 'no_abstract', + 'no_access_info', + 'no_active_quotes', + 'no_packages_display', + 'no_packages_standards', + 'no_results', + 'no_standards', + 'not_approved', + 'number_of_packages', + 'oa_inits_header', + 'oa_inits_sub_header', + 'of', + 'only_in_collectives', + 'open_book_collective', + 'open_quote', + 'or_try_the', + 'order_approval', + 'order_com_contact', + 'order_complete_body', + 'order_complete_body_partial', + 'order_complete_contact', + 'order_complete_message', + 'order_complete_number', + 'order_docs', + 'order_docs_info', + 'order_documents', + 'order_documents_button', + 'order_documents_description', + 'order_documents_info', + 'order_form', + 'order_form_sub_h', + 'order_number', + 'order_text_1', + 'order_text_2', + 'order_text_3', + 'orders_and_documents', + 'package', + 'package_media_files', + 'package_name', + 'packages', + 'page', + 'page_of', + 'password', + 'password_reset_form_instructions', + 'password_reset_instructions', + 'password_reset_no_email', + 'password_set_info', + 'payment_processor', + 'per_year', + 'post_login_info', + 'previous', + 'previous_board_members', + 'pricing', + 'pricing_country', + 'publisher_landing_page', + 'publisher_links', + 'publishers_and_service_providers', + 'quotation_details', + 'quotation_generated', + 'quotation_name', + 'quotation_reference', + 'quotations', + 'recommended', + 'register', + 'register_a_new_account', + 'register_for_the_open_book_collective', + 'remove', + 'repeat_email', + 'repeat_password', + 'reset_password', + 'reset_search', + 'reset_your_password', + 'save_this_search', + 'search', + 'search_configuration', + 'search_the_catalogue', + 'secretariat', + 'select_an_initiative', + 'sign_in_to_account', + 'sign_up', + 'sort', + 'standards', + 'standards_attestation_history', + 'standards_info', + 'summary', + 'thanks_for_your_interest', + 'topics', + 'type', + 'updates_from_the_open_book_collective', + 'username', + 'username_email_info', + 'value', + 'view', + 'view_attestation_history', + 'view_biography', + 'who_we_are', + 'who_we_are_subhead', + 'your_order_post', + 'your_order_pre', + 'your_quotation', + ] + + SiteText.objects.filter(key__in=frontend_keys).update(frontend=True) + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0041_auto_20241125_1147'), + ] + + operations = [ + migrations.AddField( + model_name='sitetext', + name='frontend', + field=models.BooleanField(default=False), + ), + migrations.RunPython(mark_frontend_site_texts), + ] diff --git a/src/cms/migrations/0043_auto_20251202_1250.py b/src/cms/migrations/0043_auto_20251202_1250.py new file mode 100644 index 0000000..4e3ee84 --- /dev/null +++ b/src/cms/migrations/0043_auto_20251202_1250.py @@ -0,0 +1,33 @@ +from django.db import migrations, models + + +def unmark_frontend_site_texts(apps, schema_editor): + SiteText = apps.get_model('cms', 'SiteText') + + frontend_keys = [ + 'select_currency', + 'institutions_fte', + 'fte_student_count_prompt', + 'jisc_banding', + 'jisc_banding_info', + 'classification_by_size_income', + 'org_size_country_classification_note', + 'save_button', + 'email_address', + 'contact_name', + 'institution', + 'update' + ] + + SiteText.objects.filter(key__in=frontend_keys).update(frontend=True) + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0042_auto_20250414_1249'), + ] + + operations = [ + migrations.RunPython(unmark_frontend_site_texts), + ] diff --git a/src/cms/models.py b/src/cms/models.py index c377667..f641f04 100644 --- a/src/cms/models.py +++ b/src/cms/models.py @@ -184,6 +184,10 @@ class PageUpdate(models.Model): null=True, ) + url_expression = models.CharField( + max_length=255, verbose_name="URL expression", blank=True, null=True + ) + def __str__(self): return self.title @@ -402,7 +406,9 @@ class HomePageQuote(models.Model): pill_name = models.CharField(max_length=255, blank=True, null=True) quotation = models.TextField(max_length=255, blank=True, null=True) - person_attribution = models.CharField(max_length=255, blank=True, null=True) + person_attribution = models.CharField( + max_length=255, blank=True, null=True + ) organization_attribution = models.CharField( max_length=255, blank=True, null=True ) @@ -436,10 +442,11 @@ class SiteText(models.Model): A site text object """ - key = models.SlugField(max_length=20, unique=True) + key = models.SlugField(max_length=255, unique=True) body = models.TextField() help_text = models.TextField() rich_text = models.BooleanField(default=False) + frontend = models.BooleanField(default=False) def __str__(self): return self.key @@ -450,16 +457,4 @@ def display(self, context) -> str: :param context: the request context :return: the text, with an edit link if the user is staff """ - request = context.get("request") - if not request.user or not request.user.is_staff: - return self.body - elif request.user and request.user.is_staff: - url = reverse( - "edit_site_text", - kwargs={"key": self.key}, - ) - body = ( - '' - "Edit Text
{body}".format(url=url, body=self.body) - ) - return body + return self.body \ No newline at end of file diff --git a/src/cms/templatetags/site_text.py b/src/cms/templatetags/site_text.py index 11a554c..82859ca 100644 --- a/src/cms/templatetags/site_text.py +++ b/src/cms/templatetags/site_text.py @@ -7,34 +7,48 @@ @register.simple_tag(takes_context=True) -def get_site_text(context, site_text_key): +def get_site_text(context, site_text_key, cms_prefetched=None): """ Get the site text for the given key and render it with the given context. :param context: The context to render the site text with. :param site_text_key: The key of the site text to render. + :param cms_prefetched: A dictionary of prefetched site text objects. :return: The rendered site text. """ - try: - site_text = models.SiteText.objects.get( - key=site_text_key, - ) - return mark_safe(site_text.display(context)) - except models.SiteText.DoesNotExist: - return "" + if cms_prefetched: + context["request"].cms_prefetched = cms_prefetched + if site_text_key in cms_prefetched: + return mark_safe(cms_prefetched[site_text_key].display(context)) + else: + return "!!{}".format(site_text_key) + else: + if hasattr(context["request"], "cms_prefetched"): + return get_site_text( + context, + site_text_key, + cms_prefetched=context["request"].cms_prefetched, + ) -@register.simple_tag() -def get_site_text_no_edit(site_text_key): + else: + return get_site_text( + context, + site_text_key, + cms_prefetched={ + o.key: o for o in models.SiteText.objects.all() + }, + ) + + +@register.simple_tag(takes_context=True) +def get_site_text_no_edit(context, site_text_key, cms_prefetched=None): """ Get the site text for the given key and render it with the given context without the edit text. + :param context: The context to render the site text with. :param site_text_key: The key of the site text to render. + :param cms_prefetched: A dictionary of prefetched site text objects. :return: The rendered site text. """ - try: - site_text = models.SiteText.objects.get( - key=site_text_key, - ) - return mark_safe(site_text.body) - except models.SiteText.DoesNotExist: - return "" + + return get_site_text(context, site_text_key, cms_prefetched) diff --git a/src/cms/translation.py b/src/cms/translation.py new file mode 100644 index 0000000..6dd7915 --- /dev/null +++ b/src/cms/translation.py @@ -0,0 +1,47 @@ +from modeltranslation.translator import translator, TranslationOptions + +from cms import models + + +class SiteTextTranslation(TranslationOptions): + fields = ( + 'body', + ) + + +class VersionTranslation(TranslationOptions): + fields = ( + 'first_paragraph', + 'pre_break_content', + 'pull_quote', + 'body', + ) + + +class PageUpdateTranslation(TranslationOptions): + fields = ( + 'title', + 'abstract_paragraph', + ) + + +class WhoWeAreProfileItemTranslation(TranslationOptions): + fields = ( + 'bio', + ) + + +class HomePageQuoteTranslation(TranslationOptions): + fields = ( + 'pill_name', + 'quotation', + 'person_attribution', + 'organization_attribution', + ) + + +translator.register(models.SiteText, SiteTextTranslation) +translator.register(models.Version, VersionTranslation) +translator.register(models.PageUpdate, PageUpdateTranslation) +translator.register(models.WhoWeAreProfileItem, WhoWeAreProfileItemTranslation) +translator.register(models.HomePageQuote, HomePageQuoteTranslation) diff --git a/src/cms/urls.py b/src/cms/urls.py index 1c6c685..3578b50 100644 --- a/src/cms/urls.py +++ b/src/cms/urls.py @@ -113,6 +113,16 @@ views.fixed_page, name="fixed_page", ), + path( + "all_pages/", + views.all_pages, + name="all_pages", + ), + path( + "page_render//", + views.page_render, + name="page_render", + ), path( "homepage_quotes/", views.homepage_quotes, @@ -167,4 +177,19 @@ views.edit_site_text, name="edit_site_text", ), + path( + "site-texts/", + views.list_site_text, + name="list_site_text", + ), + path( + "site-texts///", + views.edit_site_text, + name="edit_site_text", + ), + path( + "site-text-csv/", + views.site_text_csv, + name="site_text_csv", + ), ] diff --git a/src/cms/views.py b/src/cms/views.py index 9a37a82..b296bd2 100644 --- a/src/cms/views.py +++ b/src/cms/views.py @@ -1,5 +1,9 @@ +import csv +from io import StringIO + from django.contrib import messages from django.contrib.admin.views.decorators import staff_member_required +from django.db import transaction from django.http import HttpResponse from django.shortcuts import ( render, @@ -9,8 +13,13 @@ reverse, ) from django.views.decorators.http import require_POST +from django.conf import settings +from django.utils import translation +from django.utils import timezone + from fluid_permissions import decorators +import intrepid.views from cms import models, forms, utils from intrepid import diff_match_patch as dmp_module from intrepid.security import user_is_initiative_manager @@ -125,20 +134,21 @@ def page_detail(request, initiative, page_or_update, page_id) -> HttpResponse: :param page_id: the page ID :return: HttpResponse object """ - if page_or_update == "page": - page_or_update_object = get_object_or_404( - models.PageUpdate, - pk=page_id, - is_update=False, - initiative=initiative, - ) - else: - page_or_update_object = get_object_or_404( - models.PageUpdate, + try: + page_or_update_object = models.PageUpdate.objects.get( pk=page_id, - is_update=True, + is_update=(page_or_update != "page"), initiative=initiative, ) + except models.PageUpdate.DoesNotExist: + try: + page_or_update_object = models.PageUpdate.objects.get( + pk=page_id, + is_update=(page_or_update == "page"), + initiative=initiative, + ) + except models.PageUpdate.DoesNotExist: + raise Http404("No matching page or update found.") if request.POST and "set_current" in request.POST: version = get_object_or_404( @@ -214,20 +224,17 @@ def featured_book_create(request, initiative) -> HttpResponse: @user_is_initiative_manager def page_edit_or_create( - request, initiative, page_or_update, page_id=None + request, + initiative, + page_or_update, + page_id=None, ) -> HttpResponse: """ View for editing or creating a page - :param request: the request object - :param initiative: the initiative object - :param page_or_update: either "page" or "update" - :param page_id: the page ID - :return: HttpResponse object """ - # TODO: consider breaking this function up into smaller functions - page, current_version = None, None + if page_id: page = get_object_or_404( models.PageUpdate, @@ -235,122 +242,122 @@ def page_edit_or_create( initiative=initiative, ) current_version = page.current_version - page_form = forms.PageUpdateForm( - instance=page, - initiative=initiative, - user=request.user, - page_or_update=page_or_update, - ror="", - ) - version_form = forms.VersionForm( - initial={ - "body": current_version.body if current_version else "", - "first_paragraph": current_version.first_paragraph - if current_version - else "", - "pre_break_content": current_version.pre_break_content - if current_version - else "", - "pull_quote": current_version.pull_quote if current_version else "", - "show_quote_icons": current_version.show_quote_icons - if current_version - else True, - }, - user=request.user, - ) - if request.POST: - if request.POST["inst_lookup"] == "": - # the user has blanked the lookup box - ror = "" - elif request.POST["institution_ROR"] == "": - # the user has left the lookup box as it was - try: - ror = page.target_institution.ror_id - except AttributeError: - ror = "" - else: - # a new ROR has been submitted - ror = request.POST["institution_ROR"] + with translation.override(request.LANGUAGE_CODE): page_form = forms.PageUpdateForm( - request.POST, - request.FILES, instance=page, initiative=initiative, user=request.user, page_or_update=page_or_update, - ror=ror, + ror="", ) + version_form = forms.VersionForm( - request.POST, + initial={ + "body": current_version.body if current_version else "", + "first_paragraph": ( + current_version.first_paragraph if current_version else "" + ), + "pre_break_content": ( + current_version.pre_break_content if current_version else "" + ), + "pull_quote": ( + current_version.pull_quote if current_version else "" + ), + "show_quote_icons": ( + current_version.show_quote_icons if current_version else True + ), + }, user=request.user, + source_version=current_version, # ✅ important for copying other langs ) - page_form_valid = page_form.is_valid() - version_form_valid = version_form.is_valid() + if request.POST: + if request.POST["inst_lookup"] == "": + ror = "" + elif request.POST["institution_ROR"] == "": + try: + ror = page.target_institution.ror_id + except AttributeError: + ror = "" + else: + ror = request.POST["institution_ROR"] + + page_form = forms.PageUpdateForm( + request.POST, + request.FILES, + instance=page, + initiative=initiative, + user=request.user, + page_or_update=page_or_update, + ror=ror, + ) - if page_form_valid and version_form_valid: - page = page_form.save() + version_form = forms.VersionForm( + request.POST, + user=request.user, + source_version=current_version, # ✅ critical for copying + ) - if not current_version: - version = version_form.save() - page.current_version = version - page.other_versions.add(version) - page.save() + page_form_valid = page_form.is_valid() + version_form_valid = version_form.is_valid() + + if page_form_valid and version_form_valid: + page = page_form.save() - else: - # now we have the structured version, we just always save a new - # version rather than attempting to detect changes version = version_form.save() - if "set_current" in request.POST: + + if not current_version or "set_current" in request.POST: page.current_version = version + page.other_versions.add(version) page.save() - messages.add_message( - request, - messages.SUCCESS, - "Details of {} saved".format(page_or_update), - ) - utils.send_cms_change_notification( - request, - initiative, - page_or_update, - page, - ) - if ( - page_or_update == "update" - and page.target_institution - and page.display - and not page.notification_sent - ): - # this update has just been created, send a notification to - # users with the target_institution - utils.send_target_institution_notification( + messages.add_message( + request, + messages.SUCCESS, + "Details of {} saved".format(page_or_update), + ) + + utils.send_cms_change_notification( request, + initiative, + page_or_update, page, ) - return redirect( - reverse( - "page_detail", - kwargs={ - "initiative": initiative.pk, - "page_or_update": page_or_update, - "page_id": page.pk, - }, + + if ( + page_or_update == "update" + and page.target_institution + and page.display + and not page.notification_sent + ): + utils.send_target_institution_notification( + request, + page, + ) + + return redirect( + reverse( + "page_detail", + kwargs={ + "initiative": initiative.pk, + "page_or_update": page_or_update, + "page_id": page.pk, + }, + ) ) - ) - inst_name = "" - ror = "" + inst_name = "" + ror = "" - if page and page.target_institution: - try: - inst_name = page.target_institution.institution_name - ror = page.target_institution.ror_id - except AttributeError: - pass + if page and page.target_institution: + try: + inst_name = page.target_institution.institution_name + ror = page.target_institution.ror_id + except AttributeError: + pass template = "cms/edit_or_create.html" context = { @@ -368,7 +375,6 @@ def page_edit_or_create( context, ) - @user_is_initiative_manager def featured_book_delete(request, initiative, fb_id) -> HttpResponse: """ @@ -380,7 +386,9 @@ def featured_book_delete(request, initiative, fb_id) -> HttpResponse: """ # put the initiative in here to constrain via security # (i.e. the decorator validates that the user can modify the initiative) - fb = get_object_or_404(models.FeaturedBook, initiative=initiative, pk=fb_id) + fb = get_object_or_404( + models.FeaturedBook, initiative=initiative, pk=fb_id + ) fb.delete() @@ -502,7 +510,12 @@ def view_current_version( @user_is_initiative_manager def diff_versions( - request, initiative, page_or_update, page_id, version_one_id, version_two_id + request, + initiative, + page_or_update, + page_id, + version_one_id, + version_two_id, ) -> HttpResponse: """ View the diff between two versions of a page or update @@ -700,6 +713,9 @@ def who_we_are_config(request) -> HttpResponse: secretariat = models.WhoWeAreProfileItem.objects.filter( section="secretariat" ).order_by("order") + finance = models.WhoWeAreProfileItem.objects.filter( + section="finance" + ).order_by("order") previous = models.WhoWeAreProfileItem.objects.filter( section="previous" ).order_by("order") @@ -710,6 +726,7 @@ def who_we_are_config(request) -> HttpResponse: "members": members, "managers": managers, "secretariat": secretariat, + "finance": finance, "previous": previous, "sort_url": reverse("who_we_are_order"), } @@ -891,6 +908,58 @@ def fixed_page(request, page_string) -> HttpResponse: if page_object.initiative != instance.initiative: raise Http404 + # if there's a nav entry, redirect + if ( + page_object.url_expression is not None + and page_object.url_expression != "" + ): + return redirect( + intrepid.views.nav, + page_name=page_object.url_expression, + ) + + template = "base/frontend/cms_page.html" + context = { + "page": page_object, + } + + return render( + request, + template, + context, + ) + + except IndexError: + raise Http404 + + +@staff_member_required +def all_pages(request) -> HttpResponse: + """ + View all pages + :param request: the request object + :return: HttpResponse object + """ + pages = models.PageUpdate.objects.all() + template = "cms/all_pages.html" + context = { + "objects": pages, + } + return render( + request, + template, + context, + ) + + +@staff_member_required +def page_render(request, page_id: int) -> HttpResponse: + """ + Render a page + """ + try: + page_object = get_object_or_404(models.PageUpdate, pk=page_id) + template = "base/frontend/cms_page.html" context = { "page": page_object, @@ -1073,3 +1142,84 @@ def edit_site_text(request, key) -> HttpResponse: template, context, ) + + +@staff_member_required +def list_site_text(request): + """ + View to list all site texts with a language selection dropdown. + """ + site_texts = models.SiteText.objects.all() + languages = settings.LANGUAGES # Dynamically get languages from settings + + return render( + request, + "cms/site_text_list.html", + { + "site_texts": site_texts, + "languages": languages, + }, + ) + + +@staff_member_required +def site_text_csv(request): + """ + View to download a CSV of all site texts + """ + site_texts = models.SiteText.objects.all() + languages = settings.LANGUAGES # Dynamically get languages from settings + + def data(site_texts_objects): + csvfile = StringIO() + csvwriter = csv.writer(csvfile) + + csvwriter.writerow(["ID", "Key", "Is Frontend?", "English", "German"]) + + for site_text in site_texts_objects: + english = site_text.body_en + german = site_text.body_de + + csvwriter.writerow([site_text.pk, site_text.key, site_text.frontend, english, german]) + + yield csvfile.getvalue() + + response = HttpResponse(data(site_texts), content_type="text/csv") + response["Content-Disposition"] = "attachment; filename=languages.csv" + return response + + +@staff_member_required +def edit_site_text(request, key, lang_code): + """ + View to handle both displaying the modal for editing site text + and saving the updated text in the selected language. + """ + site_text = get_object_or_404(models.SiteText, key=key) + + # Use `with override(lang_code)` to temporarily set the language + with translation.override(lang_code): + if request.method == "POST": + body = request.POST.get("body") + + # Save the updated site text + site_text.body = body + site_text.save() + + # Get the current date and time using Django's timezone + current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S") + + # Return an HTML success message with the timestamp + return HttpResponse( + f'
Saved successfully at {current_time}.
' + ) + + # If GET request, render the full modal for editing + return render( + request, + "cms/htmx/site_text_modal.html", + { + "site_text": site_text, + "lang_code": lang_code, + }, + ) diff --git a/src/dashboard/views.py b/src/dashboard/views.py index b89f47e..f985843 100644 --- a/src/dashboard/views.py +++ b/src/dashboard/views.py @@ -34,8 +34,11 @@ def visitor_dict(stats) -> dict: for stat in stats: # convert from ISO 3166-1 alpha-3 to ISO 3166-1 alpha-2 - country = pycountry.countries.get(alpha_3=stat.country_code) - ret[country.alpha_2.lower()] = stat.country_count + if len(stat.country_code) == 3: + country = pycountry.countries.get(alpha_3=stat.country_code) + ret[country.alpha_2.lower()] = stat.country_count + else: + ret[stat.country_code.lower()] = stat.country_count return ret @@ -49,13 +52,24 @@ def index(request) -> HttpResponse: """ template = "dashboard/dashboard_index.html" + try: + book_count = thoth_models.OverallStats.objects.first() + book_count = book_count.book_count + except AttributeError: + book_count = thoth_models.OverallStats() + count = thoth_models.Work.objects.all().count() + book_count.book_count = count + book_count.save() + + book_count = book_count.book_count + books = thoth_models.Work.objects.all() - book_count = books.count() # this catches instances where a user doesn't have a profile try: profile = request.user.profile - profile.save() + if not profile: + raise acc_models.Profile.DoesNotExist except acc_models.Profile.DoesNotExist: request.user.profile = acc_models.Profile() @@ -198,13 +212,22 @@ def generate_graph(books): """ # a list of colours to loop through for the books published colours = [ - "rgba(210, 0, 0, 1)", + "rgba(68, 119, 179, 1)", + "rgba(102, 204, 238, 1)", + "rgba(34, 136, 51, 1)", + "rgba(204, 187, 68, 1)", + "rgba(238, 102, 119, 1)", + "rgba(170, 51, 119, 1)", + "rgba(255, 255, 255, 1)", "rgba(0, 0, 0, 1)", - "rgba(210, 214, 222, 1)", ] # see if we have any stats in the DB - stats = thoth_models.Stats.objects.all().order_by("year") + stats = ( + thoth_models.Stats.objects.all() + .select_related("publisher") + .order_by("year") + ) # variable initialization publishers = {} @@ -215,16 +238,19 @@ def generate_graph(books): final_publishers = {} for stat in stats: + if int(stat.year) < 2000: + continue + # create the years list if stat.year not in years: years.append(stat.year) # add the publisher to the publishers list if stat.publisher not in publishers: - publishers[stat.publisher] = [] + publishers[stat.publisher] = {} # add the data to a list - publishers[stat.publisher].append(stat.book_count) + publishers[stat.publisher][stat.year] = stat.book_count # count publishers publisher_count = len(publishers) @@ -248,6 +274,7 @@ def generate_graph(books): # return labels and values to the dashboard labels = sorted(years)[:-1] + return labels, publisher_count, publisher_templates @@ -268,6 +295,6 @@ def create_js_template(color, publisher_name, publisher_data): "pointStrokeColor": "#c1c7d1", "pointHighlightFill": "#fff", "pointHighlightStroke": "rgba(220,220,220,1)", - "data": publisher_data, + "data": list(publisher_data.values()), } return pub_template diff --git a/src/initiatives/migrations/0021_alter_initiative_more_info.py b/src/initiatives/migrations/0021_alter_initiative_more_info.py new file mode 100644 index 0000000..d4a0f88 --- /dev/null +++ b/src/initiatives/migrations/0021_alter_initiative_more_info.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.8 on 2023-10-08 16:59 + +from django.db import migrations +import django_bleach.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('initiatives', '0020_alter_highlights_options'), + ] + + operations = [ + migrations.AlterField( + model_name='initiative', + name='more_info', + field=django_bleach.models.BleachField(blank=True, help_text='Appears on the Initiative/Package more info page.', null=True), + ), + ] diff --git a/src/initiatives/migrations/0022_initiative_exclude_from_thoth_filter.py b/src/initiatives/migrations/0022_initiative_exclude_from_thoth_filter.py new file mode 100644 index 0000000..ab6e5a2 --- /dev/null +++ b/src/initiatives/migrations/0022_initiative_exclude_from_thoth_filter.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.8 on 2024-02-12 11:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('initiatives', '0021_alter_initiative_more_info'), + ] + + operations = [ + migrations.AddField( + model_name='initiative', + name='exclude_from_thoth_filter', + field=models.BooleanField(default=False, help_text='Check this to exclude this initiative from the Thoth search drop down.'), + ), + ] diff --git a/src/initiatives/models.py b/src/initiatives/models.py index 8be2d32..124308e 100644 --- a/src/initiatives/models.py +++ b/src/initiatives/models.py @@ -107,6 +107,12 @@ class Initiative(models.Model): default=0, ) + exclude_from_thoth_filter = models.BooleanField( + default=False, + help_text='Check this to exclude this initiative from the Thoth ' + 'search drop down.', + ) + class Meta: ordering = ("name",) diff --git a/src/initiatives/views.py b/src/initiatives/views.py index 6690099..764ad4b 100644 --- a/src/initiatives/views.py +++ b/src/initiatives/views.py @@ -188,11 +188,11 @@ def user_initiatives(request) -> HttpResponse: :return: an HTTP response """ if request.user.is_staff: - users_initiatives = models.Initiative.objects.all() + users_initiatives = models.Initiative.objects.all().prefetch_related("users").prefetch_related("users__profile") else: users_initiatives = models.Initiative.objects.filter( users=request.user, - ) + ).prefetch_related("users").prefetch_related("users__profile") template = "initiatives/user_initiatives.html" context = { "users_initiatives": users_initiatives, diff --git a/src/intrepid/forms.py b/src/intrepid/forms.py index 39831d6..ba16b5d 100644 --- a/src/intrepid/forms.py +++ b/src/intrepid/forms.py @@ -26,6 +26,7 @@ class Meta: "management_team_description", "membership_committee_description", "secretariat_description", + "finance_committee_description", "fee_amount", ) diff --git a/src/intrepid/middleware.py b/src/intrepid/middleware.py index f950fd2..0eef9eb 100644 --- a/src/intrepid/middleware.py +++ b/src/intrepid/middleware.py @@ -5,6 +5,7 @@ from invoicing import models as invoice_models from package import models as package_models from thoth import models as thoth_models +from utils import country def debug_middleware(get_response): @@ -41,6 +42,25 @@ def middleware(request): return middleware +def country_middleware(get_response): + """ + Middleware that ensures that a session key is always present + :param get_response: a callable + :return: a request object + """ + + def middleware(request): + if not request.session.get("country"): + request.session["country"] = country.get_iso_country_from_request( + request, + ) + request.session["country"] = "GB" + + return get_response(request) + + return middleware + + def variables_middleware(get_response): """ Middleware that injects commonly used variables into the request @@ -50,7 +70,7 @@ def variables_middleware(get_response): def middleware(request): try: - setup = intrepid_models.SiteSetup.objects.all().order_by("pk")[0] + setup = intrepid_models.SiteSetup.objects.first() except IndexError: setup = intrepid_models.SiteSetup() setup.save() @@ -64,6 +84,31 @@ def middleware(request): request.matomo_site_url = settings.MATOMO_SITE_URL request.matomo_site_id = settings.MATOMO_SITE_ID + try: + if request.user.is_authenticated: + baskets = package_models.Basket.objects.filter( + account=request.user, + active=True, + ) + else: + baskets = package_models.Basket.objects.filter( + session_id=request.session.session_key, + active=True, + ) + + request.basket = baskets.first() + if request.basket: + request.basket_count = ( + request.basket.packages.count() + + request.basket.meta_packages.count() + ) + else: + request.basket_count = 0 + except package_models.Basket.DoesNotExist: + request.basket = None + except AttributeError: + request.basket_count = 0 + if hasattr(request, "user") and request.user.is_authenticated: # inject a document count into the request for the count on the # library dashboard @@ -103,7 +148,9 @@ def middleware(request): ) # active baskets - request.active_baskets = request.user.basket_set.filter(active=True) + request.active_baskets = request.user.basket_set.filter( + active=True + ) # saved searches request.saved_searches = thoth_models.ThothSearch.objects.filter( diff --git a/src/intrepid/migrations/0029_sitesetup_finance_committee_description.py b/src/intrepid/migrations/0029_sitesetup_finance_committee_description.py new file mode 100644 index 0000000..2f5a8b5 --- /dev/null +++ b/src/intrepid/migrations/0029_sitesetup_finance_committee_description.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.8 on 2024-07-06 07:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('intrepid', '0028_alter_privateimage_image'), + ] + + operations = [ + migrations.AddField( + model_name='sitesetup', + name='finance_committee_description', + field=models.TextField(blank=True, default='', null=True, verbose_name='Description of the Finance Committee'), + ), + ] diff --git a/src/intrepid/migrations/0030_alter_privateimage_image.py b/src/intrepid/migrations/0030_alter_privateimage_image.py new file mode 100644 index 0000000..408ef7d --- /dev/null +++ b/src/intrepid/migrations/0030_alter_privateimage_image.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.8 on 2024-09-14 08:22 + +import django.core.files.storage +from django.db import migrations, models +import intrepid.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('intrepid', '0029_sitesetup_finance_committee_description'), + ] + + operations = [ + migrations.AlterField( + model_name='privateimage', + name='image', + field=models.ImageField(storage=django.core.files.storage.FileSystemStorage(location='/Users/ajrbyers/Code/copim-intrepid/src/files/private_images'), upload_to=intrepid.models.private_images_upload_path), + ), + ] diff --git a/src/intrepid/migrations/0031_alter_privateimage_image.py b/src/intrepid/migrations/0031_alter_privateimage_image.py new file mode 100644 index 0000000..543c2d5 --- /dev/null +++ b/src/intrepid/migrations/0031_alter_privateimage_image.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.8 on 2024-11-12 15:18 + +import django.core.files.storage +from django.db import migrations, models +import intrepid.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('intrepid', '0030_alter_privateimage_image'), + ] + + operations = [ + migrations.AlterField( + model_name='privateimage', + name='image', + field=models.ImageField(storage=django.core.files.storage.FileSystemStorage(location='/home/martin/Documents/Programming/intrepid/src/files/private_images'), upload_to=intrepid.models.private_images_upload_path), + ), + ] diff --git a/src/intrepid/migrations/0032_alter_privateimage_image.py b/src/intrepid/migrations/0032_alter_privateimage_image.py new file mode 100644 index 0000000..15886ab --- /dev/null +++ b/src/intrepid/migrations/0032_alter_privateimage_image.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.8 on 2025-10-20 10:05 + +import django.core.files.storage +from django.db import migrations, models +import intrepid.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('intrepid', '0031_alter_privateimage_image'), + ] + + operations = [ + migrations.AlterField( + model_name='privateimage', + name='image', + field=models.ImageField(storage=django.core.files.storage.FileSystemStorage(location='/home/ajrbyers/Code/intrepid/src/files/private_images'), upload_to=intrepid.models.private_images_upload_path), + ), + ] diff --git a/src/intrepid/models.py b/src/intrepid/models.py index 8f0ce16..55c4274 100644 --- a/src/intrepid/models.py +++ b/src/intrepid/models.py @@ -81,6 +81,12 @@ class SiteSetup(models.Model): null=True, verbose_name="Description of the Membership Committee", ) + finance_committee_description = models.TextField( + default="", + blank=True, + null=True, + verbose_name="Description of the Finance Committee", + ) management_team_description = models.TextField( default="", blank=True, diff --git a/src/intrepid/templatetags/cost_calculator.py b/src/intrepid/templatetags/cost_calculator.py index e676209..df47f10 100644 --- a/src/intrepid/templatetags/cost_calculator.py +++ b/src/intrepid/templatetags/cost_calculator.py @@ -74,6 +74,38 @@ def meta_package_cost(costs, meta_package, converted_currency): return currency_totals +@register.simple_tag() +def package_cost_multiplier(costs, package, order, converted_currency=None): + """ + Get the cost for a package. + :param costs: the costs + :param package: the package to get the cost for + :param converted_currency: the currency to convert to + :return: the cost + """ + for cost in costs: + if cost.get("package") == package: + if ( + converted_currency + and converted_currency != cost.get("cost").country.currency + ): + convert_value = currency_convert.convert( + currency_from=cost.get("cost").country.currency, + currency_to=converted_currency, + value=cost.get("cost").value, + ) + + convert_value = convert_value * order.term + + return mark_safe( + "{}*".format( + format_price(convert_value, converted_currency) + ) + ) + else: + return cost.get("cost") * order.term + + @register.simple_tag() def package_cost(costs, package, converted_currency=None): """ @@ -114,6 +146,14 @@ def format_price(cost, currency): return utils.format_price(cost, currency) +@register.simple_tag() +def format_price_multiplier(cost, currency, order): + """ + Format a price. + """ + return utils.format_price(int(cost) * int(order.term), currency) + + @register.simple_tag() def add_total_and_site_fee(currency_total, site_percentage): """ diff --git a/src/intrepid/test_settings.py b/src/intrepid/test_settings.py new file mode 100644 index 0000000..205d441 --- /dev/null +++ b/src/intrepid/test_settings.py @@ -0,0 +1,14 @@ +import os + +from intrepid.settings import * # noqa: F401, F403 + +MIGRATION_MODULES = { + 'fluid_permissions': 'test_migrations.fluid_permissions', +} + +FIXTURE_DIRS = [ + os.path.join(BASE_DIR, '..', 'fixtures'), +] + +# Disable HTTP Basic Auth for test client requests +BASICAUTH_DISABLE = True diff --git a/src/intrepid/urls.py b/src/intrepid/urls.py index cadb0ad..83fa810 100644 --- a/src/intrepid/urls.py +++ b/src/intrepid/urls.py @@ -120,6 +120,8 @@ path("api/", include("api.urls")), path("books/", include("thoth.urls")), path("manage/vocabs/", include("vocab.urls")), + path("nav/", views.nav, name="nav"), + path('i18n/', include('django.conf.urls.i18n')), ] if settings.DEBUG: diff --git a/src/intrepid/views.py b/src/intrepid/views.py index b6e6764..0877584 100644 --- a/src/intrepid/views.py +++ b/src/intrepid/views.py @@ -2,8 +2,11 @@ from django.http import Http404, HttpResponse from django.shortcuts import render +from django.views.decorators.cache import cache_page from cms import models as cms_models + +from package import models as packagemodels from initiatives import models @@ -14,8 +17,14 @@ def index(request): :return: a response """ + # prefetch all CMS text objects to avoid template hits + final_prefetched = {o.key: o for o in cms_models.SiteText.objects.all()} + # Generate an initiative if one does not exist. - if models.Initiative.objects.all().count() == 0: + try: + first = models.Initiative.objects.first() + + except models.Initiative.DoesNotExist: initiative, c = models.Initiative.objects.get_or_create( name="Test Initiative", defaults={ @@ -24,8 +33,6 @@ def index(request): "active": True, }, ) - else: - initiative = None news_items = cms_models.SiteWideNewsItem.objects.all().order_by("order")[ 0:4 @@ -33,10 +40,13 @@ def index(request): quotes = cms_models.HomePageQuote.objects.all().order_by("order")[0:4] + news_items_exists = cms_models.SiteWideNewsItem.objects.exists() + context = { - "initiative": initiative, + "news_items_exists": news_items_exists, "news_items": news_items, "quotes": quotes, + "prefetched": final_prefetched, } template = "base/frontend/index.html" return render(request, template, context) @@ -80,6 +90,9 @@ def who_we_are(request): secretariat = cms_models.WhoWeAreProfileItem.objects.filter( section="secretariat" ).order_by("order") + finance = cms_models.WhoWeAreProfileItem.objects.filter( + section="finance" + ).order_by("order") previous = cms_models.WhoWeAreProfileItem.objects.filter( section="previous" ).order_by("order") @@ -90,6 +103,7 @@ def who_we_are(request): "members": members, "managers": managers, "secretariat": secretariat, + "finance": finance, "previous": previous, } return render( @@ -172,3 +186,26 @@ def export(request, target): ) else: raise Http404 + + +def nav(request, page_name: str): + """ + Render a site page + """ + + # get the page using page_expression + try: + page = cms_models.PageUpdate.objects.get(url_expression=page_name) + except cms_models.PageUpdate.DoesNotExist: + raise Http404 + + template = "base/frontend/cms_page.html" + context = { + "page": page, + } + + return render( + request, + template, + context, + ) diff --git a/src/mail/models.py b/src/mail/models.py index fed952e..6c90243 100644 --- a/src/mail/models.py +++ b/src/mail/models.py @@ -135,7 +135,11 @@ def _send_email( }, ) - json_response = response.json() + try: + json_response = response.json() + except requests.exceptions.JSONDecodeError: + return "" + if settings.DEBUG: print(json_response) @@ -171,7 +175,7 @@ def send( :param attachments: the email attachments :return: the email response """ - if attachments is not None: + if attachments is None: attachments = [] html = self.render_email(context) diff --git a/src/package/admin.py b/src/package/admin.py index 4222ed4..9387f23 100644 --- a/src/package/admin.py +++ b/src/package/admin.py @@ -175,6 +175,10 @@ class PackageSignupAdmin(admin.ModelAdmin): inlines = [SignupInvoiceInline] +class PreCalcMinMaxAdmin(admin.ModelAdmin): + list_display = ('pk', 'package', 'meta_package', 'country', 'min_amount', 'max_amount') + + admin_list = [ (models.BandingType, BandingTypeAdmin), (models.Country, CountryAdmin), @@ -192,6 +196,7 @@ class PackageSignupAdmin(admin.ModelAdmin): (models.CustomPackageDocument,), (models.PackageDocumentHistorical, PackageDocumentHistoricalAdmin), (models.BandingTypeCurrencyEntry, BandingTypeCurrencyEntryAdmin), + (models.PreCalcMinMax, PreCalcMinMaxAdmin), ] [admin.site.register(*t) for t in admin_list] diff --git a/src/package/forms.py b/src/package/forms.py index b000be4..cbcb901 100644 --- a/src/package/forms.py +++ b/src/package/forms.py @@ -5,11 +5,13 @@ from django.db.models import Max from django.forms import widgets from django_summernote.widgets import SummernoteWidget +from django.db.utils import ProgrammingError from accounts import models as accm from intrepid.models import PrivateImage from package import models from vocab import models as vm +from cms.models import SiteText class NewBasketForm(forms.ModelForm): @@ -49,29 +51,38 @@ class FTEForm(forms.Form): Form for entering FTE and currency """ - # TODO: need to abstract these strings to the database for multilingualism # TODO: break up this form into smaller functions currency = forms.ModelChoiceField( queryset=models.Country.objects.exclude( name__in=["EUROZONE", "Eurozone", "eurozone"] ), - help_text="Select your preferred currency. Note: a package may not " - "have a price set for your chosen currency.", + help_text="", # Will be set in __init__ label="", ) fte = forms.IntegerField( - help_text="Please enter your institution's full time equivalent " - "student count.", - label="Institution FTE", + help_text="", # Will be set in __init__ + label="", # Will be set in __init__ ) def __init__(self, *args, **kwargs): self.user_session = kwargs.pop("user_session") self.user = kwargs.pop("user") self.banding_types = kwargs.pop("banding_types") + super(FTEForm, self).__init__(*args, **kwargs) + # Load translatable strings from database + self.fields["currency"].help_text = SiteText.objects.get( + key="select_currency_note" + ).body + self.fields["fte"].help_text = SiteText.objects.get( + key="fte_student_count_prompt" + ).body + self.fields["fte"].label = SiteText.objects.get( + key="institutions_fte" + ).body + if self.user: self.fields["fte"].initial = self.user.profile.fte else: @@ -80,11 +91,18 @@ def __init__(self, *args, **kwargs): self.helper = FormHelper() layout = list() layout.append("currency") + + # get the CMS text for "Your Currency" + your_currency = SiteText.objects.get( + key="your_currency" + ).body + self.helper.layout = Layout( - HTML('

Your Currency

'), + HTML('

' + your_currency + '

'), HTML( - "

After you enter your currency you will be asked " - "to supple information on bandings.

" + "

{}

".format( + SiteText.objects.get(key="after_currency_bandings_info").body + ) ), "currency", ) @@ -105,45 +123,52 @@ def __init__(self, *args, **kwargs): else: self.helper.layout.append( Layout( - HTML("

Your Institution Details

"), + HTML("

{}

".format( + SiteText.objects.get(key="institution_details").body + )), "fte", ) ) if self.fields["currency"].initial: for banding_type in self.banding_types: - session_string = "banding_type_{}".format(banding_type.pk) - choices = [["", "-----"]] - for vocab in banding_type.vocabs.all(): - choices.append([vocab.pk, vocab.text]) - self.fields[session_string] = forms.ChoiceField( - choices=choices, - label=banding_type.name, - ) - if self.user: - account_banding_choice = ( - accm.AccountBandingChoices.objects.filter( - account=self.user, - banding_type=banding_type, - ).first() - ) - self.fields[session_string].initial = ( - account_banding_choice.banding_type_vocab.pk - if account_banding_choice - else None + + if banding_type.active: + + session_string = "banding_type_{}".format(banding_type.pk) + choices = [["", "-----"]] + for vocab in banding_type.vocabs.all(): + choices.append([vocab.pk, vocab.text]) + self.fields[session_string] = forms.ChoiceField( + choices=choices, + label=banding_type.name, ) - else: - self.fields[session_string].initial = self.user_session.get( - session_string, None + if self.user: + account_banding_choice = ( + accm.AccountBandingChoices.objects.filter( + account=self.user, + banding_type=banding_type, + ).first() + ) + self.fields[session_string].initial = ( + account_banding_choice.banding_type_vocab.pk + if account_banding_choice + else None + ) + else: + self.fields[session_string].initial = ( + self.user_session.get(session_string, None) + ) + + self.fields[session_string].help_text = ( + banding_type.description ) - self.fields[session_string].help_text = banding_type.description - - self.helper.layout.append( - Layout(session_string), - ) - if "currency" in self.changed_data: - self.fields[session_string].required = False + self.helper.layout.append( + Layout(session_string), + ) + if "currency" in self.changed_data: + self.fields[session_string].required = False if "currency" in self.changed_data and self.fields.get("fte"): self.fields["fte"].required = False @@ -189,6 +214,180 @@ def save(self) -> None: self.user_session[key] = value +class FastFTEForm(forms.Form): + """ + Optimized version of FTEForm. + Accepts prefetched site_texts dict and batch-fetches + AccountBandingChoices instead of per-type queries. + """ + + currency = forms.ModelChoiceField( + queryset=models.Country.objects.exclude( + name__in=["EUROZONE", "Eurozone", "eurozone"] + ), + help_text="", + label="", + ) + fte = forms.IntegerField( + help_text="", + label="", + ) + + def __init__(self, *args, **kwargs): + self.user_session = kwargs.pop("user_session") + self.user = kwargs.pop("user") + self.banding_types = kwargs.pop("banding_types") + self.site_texts = kwargs.pop("site_texts", None) + + super(FastFTEForm, self).__init__(*args, **kwargs) + + def get_site_text(key): + if self.site_texts and key in self.site_texts: + return self.site_texts[key].body + return SiteText.objects.get(key=key).body + + self.fields["currency"].help_text = get_site_text( + "select_currency_note" + ) + self.fields["fte"].help_text = get_site_text( + "fte_student_count_prompt" + ) + self.fields["fte"].label = get_site_text("institutions_fte") + + if self.user: + self.fields["fte"].initial = self.user.profile.fte + else: + self.fields["fte"].initial = self.user_session.get("fte", 0) + + self.helper = FormHelper() + + your_currency = get_site_text("your_currency") + + self.helper.layout = Layout( + HTML( + '

' + your_currency + "

" + ), + HTML( + "

{}

".format( + get_site_text("after_currency_bandings_info") + ) + ), + "currency", + ) + + if self.user: + self.fields["currency"].initial = ( + self.user.profile.default_currency + if self.user.profile.default_currency + else None + ) + else: + self.fields["currency"].initial = self.user_session.get( + "currency", 0 + ) + + if not self.fields["currency"].initial: + self.fields.pop("fte") + else: + self.helper.layout.append( + Layout( + HTML( + "

{}

".format( + get_site_text("institution_details") + ) + ), + "fte", + ) + ) + + if self.fields["currency"].initial: + # Batch-fetch all AccountBandingChoices at once + if self.user: + account_choices = { + abc.banding_type_id: abc + for abc in accm.AccountBandingChoices.objects.filter( + account=self.user, + ).select_related("banding_type_vocab") + } + else: + account_choices = {} + + for banding_type in self.banding_types: + if banding_type.active: + session_string = "banding_type_{}".format( + banding_type.pk + ) + choices = [["", "-----"]] + for vocab in banding_type.vocabs.all(): + choices.append([vocab.pk, vocab.text]) + self.fields[session_string] = forms.ChoiceField( + choices=choices, + label=banding_type.name, + ) + if self.user: + account_banding_choice = account_choices.get( + banding_type.pk + ) + self.fields[session_string].initial = ( + account_banding_choice.banding_type_vocab.pk + if account_banding_choice + else None + ) + else: + self.fields[session_string].initial = ( + self.user_session.get(session_string, None) + ) + + self.fields[session_string].help_text = ( + banding_type.description + ) + + self.helper.layout.append( + Layout(session_string), + ) + if "currency" in self.changed_data: + self.fields[session_string].required = False + + if "currency" in self.changed_data and self.fields.get("fte"): + self.fields["fte"].required = False + + self.helper.layout.append( + Submit( + "fte_form", + "Update", + css_class="btn btn-primary btn-obc-blue", + ), + ) + + def save(self) -> None: + for key, value in self.cleaned_data.items(): + if self.user: + if key == "fte" and value: + self.user.profile.fte = value + elif key == "currency" and value: + self.user.profile.default_currency = value + elif value: + banding_type_pk = key.replace("banding_type_", "") + banding_type_vocab = vm.BandingVocab.objects.get( + pk=value, + ) + accm.AccountBandingChoices.objects.update_or_create( + account=self.user, + banding_type_id=banding_type_pk, + defaults={ + "banding_type_vocab": banding_type_vocab, + }, + ) + self.user.profile.save() + else: + if key == "fte": + self.user_session[key] = value + elif key == "currency": + self.user_session[key] = value.pk + else: + self.user_session[key] = value + + class ManagePackageForm(forms.ModelForm): """ Form for managing packages @@ -208,10 +407,10 @@ def __init__(self, *args, **kwargs): self.fields.pop("signup_contacts") self.fields.pop("access_contacts") - def save( - self, commit=True - ) -> tuple[ - models.Package, models.BandingTypeEntry, models.BandingTypeCurrencyEntry + def save(self, commit=True) -> tuple[ + models.Package, + models.BandingTypeEntry, + models.BandingTypeCurrencyEntry, ]: """ Save the form @@ -260,12 +459,13 @@ def __init__(self, *args, **kwargs): self.helper.form_method = "post" self.helper.add_input(Submit("submit", "Save")) - self.fields[ - "contact" - ].label_from_instance = lambda obj: "%s %s (%s)" % ( - obj.first_name, - obj.last_name, - obj.email, + self.fields["contact"].label_from_instance = ( + lambda obj: "%s %s (%s)" + % ( + obj.first_name, + obj.last_name, + obj.email, + ) ) def save(self, commit=True) -> models.MetaPackage: @@ -444,7 +644,9 @@ class CurrencyField(forms.Form): """ currencies = forms.ModelChoiceField( - queryset=models.Country.objects.all(), empty_label=None, label="Country" + queryset=models.Country.objects.all(), + empty_label=None, + label="Country", ) @@ -561,16 +763,54 @@ class GeneratedForm(forms.Form): """ email_address = forms.EmailField(required=True) + term_length = forms.ChoiceField(choices=[]) # Will be set in __init__ def __init__(self, *args, **kwargs): order = kwargs.pop("order", None) fields_required = kwargs.pop("fields_required", True) email = kwargs.pop("email_address", None) + term_length = kwargs.pop("term_length", 3) super(GeneratedForm, self).__init__(*args, **kwargs) + + # Load translatable strings from database + try: + months_text = SiteText.objects.get(key="months").body + except SiteText.DoesNotExist: + months_text = "months" + + # Set term length choices with translated text + TERM_CHOICES = { + "1": f"12 {months_text}", + "3": f"36 {months_text}", + } + self.fields["term_length"].choices = TERM_CHOICES.items() + + # Set labels from SiteText + try: + self.fields["email_address"].label = SiteText.objects.get( + key="email_address" + ).body + except SiteText.DoesNotExist: + self.fields["email_address"].label = "Email address*" + + try: + self.fields["term_length"].label = SiteText.objects.get( + key="term_length" + ).body + except SiteText.DoesNotExist: + self.fields["term_length"].label = "Term length*" + self.helper = FormHelper() self.helper.form_method = "post" + + # Load Save button text from SiteText + try: + save_text = SiteText.objects.get(key="save_button").body + except SiteText.DoesNotExist: + save_text = "Save" + self.helper.add_input( - Submit("submit", "Save", css_class="btn btn-primary btn-obc-blue") + Submit("save", save_text, css_class="btn btn-primary btn-obc-blue") ) elements = order.data_to_collect @@ -626,9 +866,12 @@ def __init__(self, *args, **kwargs): ) for answer in answers: if self.fields[str(answer.form_element.pk)]: - self.fields[str(answer.form_element.pk)].initial = answer.answer + self.fields[str(answer.form_element.pk)].initial = ( + answer.answer + ) self.fields["email_address"].initial = email + self.fields["term_length"].initial = term_length class CustomDocumentForm(forms.ModelForm): @@ -750,3 +993,23 @@ def save(self, commit=True) -> models.MediaFile: file.save() return file + + +class CountryForm(forms.Form): + country = forms.ModelChoiceField( + queryset=models.Country.objects.exclude( + name__in=["EUROZONE", "Eurozone", "eurozone"] + ), + help_text="Select your preferred country.", + label="Countries", + ) + + def __init__(self, *args, **kwargs): + session_country_code = kwargs.pop("session_country_code") + super(CountryForm, self).__init__(*args, **kwargs) + if session_country_code: + country = models.Country.objects.filter( + code=session_country_code, + ).first() + if country: + self.fields["country"].initial = country.pk diff --git a/src/package/management/__init__.py b/src/package/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/package/management/commands/__init__.py b/src/package/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/package/management/commands/pregenerate_prices.py b/src/package/management/commands/pregenerate_prices.py new file mode 100644 index 0000000..3a28fde --- /dev/null +++ b/src/package/management/commands/pregenerate_prices.py @@ -0,0 +1,149 @@ +import sys +from pprint import pprint + +from django.core.management.base import BaseCommand + +from package.models import Package, MetaPackage, PreCalcMinMax, Country, Price +from package.utils import get_price_for_package +from package.currency import convert + + +class Command(BaseCommand): + """ + A management command that pregenerates package prices + """ + + help = "Pre-generates package prices" + + def add_arguments(self, parser): + parser.add_argument( + "--convert-currencies", + action="store_true", + help="Enable currency conversion using default package prices when missing", + ) + + def handle(self, *args, **options): + convert_currencies = options["convert_currencies"] + + package_list = {} + meta_package_list = {} + + PreCalcMinMax.objects.all().delete() + + for package in Package.objects.all(): + package_list[package] = {} + + for key, val in package.price_bandings.items(): + for country, prices in val.items(): + found_min = sys.maxsize + found_max = 0 + + min_price = min(prices, key=lambda x: x.value) + max_price = max(prices, key=lambda x: x.value) + + found_min = ( + min_price.value + if min_price.value < found_min + else found_min + ) + found_max = ( + max_price.value + if max_price.value > found_max + else found_max + ) + + package_list[package][country] = { + "min": found_min, + "max": found_max, + } + + for mp in MetaPackage.objects.all(): + meta_package_list[mp] = {} + packages = list(mp.packages.all()) + + all_countries = set() + for package in packages: + all_countries.update(package_list.get(package, {}).keys()) + + for country in all_countries: + min_total = 0 + max_total = 0 + all_found = True + + for package in packages: + package_data = package_list.get(package, {}).get(country) + + if package_data: + min_total += package_data["min"] + max_total += package_data["max"] + elif convert_currencies: + fallback = self.get_converted_default_price(package, country.currency) + if fallback: + converted_min, converted_max = fallback + min_total += converted_min + max_total += converted_max + else: + all_found = False + break + else: + all_found = False + break + + if all_found: + meta_package_list[mp][country] = { + "min": min_total, + "max": max_total, + } + + for package, country_data in package_list.items(): + for country, values in country_data.items(): + PreCalcMinMax.objects.get_or_create( + country=country, + package=package, + min_amount=values["min"], + max_amount=values["max"], + ) + + for meta_package, country_data in meta_package_list.items(): + for country, values in country_data.items(): + PreCalcMinMax.objects.get_or_create( + meta_package=meta_package, + country=country, + min_amount=values["min"], + max_amount=values["max"], + ) + + for country in Country.objects.filter(catch_all=True): + PreCalcMinMax.objects.filter( + country__currency=country.currency, + ).exclude( + country=country, + ).delete() + + for precalc in PreCalcMinMax.objects.all().order_by("package", "meta_package", "country"): + print( + f"{precalc.get_package()} {precalc}" + ) + + def get_converted_default_price(self, package, currency_code): + default_country = package.default_country + if not default_country: + return None + + prices = Price.objects.filter( + banding__package=package, + country=default_country, + ) + + if not prices.exists(): + return None + + min_value = min(p.value for p in prices) + max_value = max(p.value for p in prices) + + try: + converted_min = round(convert(default_country.currency, currency_code, min_value)) + converted_max = round(convert(default_country.currency, currency_code, max_value)) + return converted_min, converted_max + except Exception: + return None diff --git a/src/package/migrations/0066_auto_20231008_1659.py b/src/package/migrations/0066_auto_20231008_1659.py new file mode 100644 index 0000000..976702b --- /dev/null +++ b/src/package/migrations/0066_auto_20231008_1659.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.8 on 2023-10-08 16:59 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('initiatives', '0021_alter_initiative_more_info'), + ('package', '0065_auto_20230621_1932'), + ] + + operations = [ + migrations.AlterField( + model_name='basepackage', + name='pricing_display', + field=models.CharField(help_text='Display version of pricing eg. £1000-£2000', max_length=255, null=True), + ), + migrations.AlterField( + model_name='order', + name='initiative', + field=models.ForeignKey(blank=True, help_text='If signup inserted by initiative store here.', null=True, on_delete=django.db.models.deletion.CASCADE, to='initiatives.initiative'), + ), + ] diff --git a/src/package/migrations/0067_auto_20240212_1346.py b/src/package/migrations/0067_auto_20240212_1346.py new file mode 100644 index 0000000..9b18d17 --- /dev/null +++ b/src/package/migrations/0067_auto_20240212_1346.py @@ -0,0 +1,34 @@ +# Generated by Django 3.2.8 on 2024-02-12 13:46 + +import django.core.files.storage +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('package', '0066_auto_20231008_1659'), + ] + + operations = [ + migrations.AlterField( + model_name='frozenpackagedocument', + name='final_zip', + field=models.FileField(blank=True, storage=django.core.files.storage.FileSystemStorage(base_url='/files', location='/Users/ajrbyers/Code/copim-intrepid/src/files'), upload_to='private_documents'), + ), + migrations.AlterField( + model_name='mediafile', + name='file', + field=models.FileField(blank=True, storage=django.core.files.storage.FileSystemStorage(base_url='/files', location='/Users/ajrbyers/Code/copim-intrepid/src/files'), upload_to='private_documents'), + ), + migrations.CreateModel( + name='PreCalcMinMax', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('min_amount', models.IntegerField()), + ('max_amount', models.IntegerField()), + ('country', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='package.country')), + ], + ), + ] diff --git a/src/package/migrations/0068_precalcminmax_package.py b/src/package/migrations/0068_precalcminmax_package.py new file mode 100644 index 0000000..c6c31d8 --- /dev/null +++ b/src/package/migrations/0068_precalcminmax_package.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.8 on 2024-02-12 13:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('package', '0067_auto_20240212_1346'), + ] + + operations = [ + migrations.AddField( + model_name='precalcminmax', + name='package', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='package.package'), + preserve_default=False, + ), + ] diff --git a/src/package/migrations/0069_auto_20240212_1424.py b/src/package/migrations/0069_auto_20240212_1424.py new file mode 100644 index 0000000..f389f32 --- /dev/null +++ b/src/package/migrations/0069_auto_20240212_1424.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.8 on 2024-02-12 14:24 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('package', '0068_precalcminmax_package'), + ] + + operations = [ + migrations.AddField( + model_name='precalcminmax', + name='meta_package', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='package.metapackage'), + ), + migrations.AlterField( + model_name='precalcminmax', + name='package', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='package.package'), + ), + ] diff --git a/src/package/migrations/0070_auto_20240701_1017.py b/src/package/migrations/0070_auto_20240701_1017.py new file mode 100644 index 0000000..48b9b4a --- /dev/null +++ b/src/package/migrations/0070_auto_20240701_1017.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.8 on 2024-07-01 10:17 + +import django.core.files.storage +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('package', '0069_auto_20240212_1424'), + ] + + operations = [ + migrations.AlterField( + model_name='frozenpackagedocument', + name='final_zip', + field=models.FileField(blank=True, storage=django.core.files.storage.FileSystemStorage(base_url='/files', location='/home/martin/Documents/Programming/intrepid/src/files'), upload_to='private_documents'), + ), + migrations.AlterField( + model_name='mediafile', + name='file', + field=models.FileField(blank=True, storage=django.core.files.storage.FileSystemStorage(base_url='/files', location='/home/martin/Documents/Programming/intrepid/src/files'), upload_to='private_documents'), + ), + ] diff --git a/src/package/migrations/0071_auto_20240914_0822.py b/src/package/migrations/0071_auto_20240914_0822.py new file mode 100644 index 0000000..6c2686a --- /dev/null +++ b/src/package/migrations/0071_auto_20240914_0822.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.8 on 2024-09-14 08:22 + +import django.core.files.storage +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('package', '0070_auto_20240701_1017'), + ] + + operations = [ + migrations.AlterField( + model_name='frozenpackagedocument', + name='final_zip', + field=models.FileField(blank=True, storage=django.core.files.storage.FileSystemStorage(base_url='/files', location='/Users/ajrbyers/Code/copim-intrepid/src/files'), upload_to='private_documents'), + ), + migrations.AlterField( + model_name='mediafile', + name='file', + field=models.FileField(blank=True, storage=django.core.files.storage.FileSystemStorage(base_url='/files', location='/Users/ajrbyers/Code/copim-intrepid/src/files'), upload_to='private_documents'), + ), + ] diff --git a/src/package/migrations/0072_auto_20241112_1518.py b/src/package/migrations/0072_auto_20241112_1518.py new file mode 100644 index 0000000..62c462d --- /dev/null +++ b/src/package/migrations/0072_auto_20241112_1518.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.8 on 2024-11-12 15:18 + +import django.core.files.storage +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('package', '0071_auto_20240914_0822'), + ] + + operations = [ + migrations.AddField( + model_name='basket', + name='term', + field=models.IntegerField(default=1), + ), + migrations.AlterField( + model_name='frozenpackagedocument', + name='final_zip', + field=models.FileField(blank=True, storage=django.core.files.storage.FileSystemStorage(base_url='/files', location='/home/martin/Documents/Programming/intrepid/src/files'), upload_to='private_documents'), + ), + migrations.AlterField( + model_name='mediafile', + name='file', + field=models.FileField(blank=True, storage=django.core.files.storage.FileSystemStorage(base_url='/files', location='/home/martin/Documents/Programming/intrepid/src/files'), upload_to='private_documents'), + ), + ] diff --git a/src/package/migrations/0073_order_term.py b/src/package/migrations/0073_order_term.py new file mode 100644 index 0000000..31604ee --- /dev/null +++ b/src/package/migrations/0073_order_term.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.8 on 2024-11-12 15:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('package', '0072_auto_20241112_1518'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='term', + field=models.IntegerField(default=1), + ), + ] diff --git a/src/package/migrations/0074_auto_20251020_0946.py b/src/package/migrations/0074_auto_20251020_0946.py new file mode 100644 index 0000000..664d3a8 --- /dev/null +++ b/src/package/migrations/0074_auto_20251020_0946.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.8 on 2025-10-20 09:46 + +import django.core.files.storage +from django.db import migrations, models +import django_bleach.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('package', '0073_order_term'), + ] + + operations = [ + migrations.AddField( + model_name='bandingtype', + name='description_de', + field=django_bleach.models.BleachField(help_text='Description that will appear on the banding selection section.', null=True), + ), + migrations.AddField( + model_name='bandingtype', + name='description_en', + field=django_bleach.models.BleachField(help_text='Description that will appear on the banding selection section.', null=True), + ), + ] diff --git a/src/package/migrations/0075_auto_20251020_0950.py b/src/package/migrations/0075_auto_20251020_0950.py new file mode 100644 index 0000000..4c71d75 --- /dev/null +++ b/src/package/migrations/0075_auto_20251020_0950.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.8 on 2025-10-20 09:50 + +import django.core.files.storage +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('package', '0074_auto_20251020_0946'), + ] + + operations = [ + migrations.AddField( + model_name='bandingtype', + name='name_de', + field=models.CharField(help_text='Public name of the Banding Type.#,', max_length=255, null=True), + ), + migrations.AddField( + model_name='bandingtype', + name='name_en', + field=models.CharField(help_text='Public name of the Banding Type.#,', max_length=255, null=True), + ), + ] diff --git a/src/package/migrations/0076_auto_20251020_1005.py b/src/package/migrations/0076_auto_20251020_1005.py new file mode 100644 index 0000000..45463a3 --- /dev/null +++ b/src/package/migrations/0076_auto_20251020_1005.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.8 on 2025-10-20 10:05 + +import django.core.files.storage +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('package', '0075_auto_20251020_0950'), + ] + + operations = [ + migrations.AlterField( + model_name='frozenpackagedocument', + name='final_zip', + field=models.FileField(blank=True, storage=django.core.files.storage.FileSystemStorage(base_url='/files', location='/home/ajrbyers/Code/intrepid/src/files'), upload_to='private_documents'), + ), + migrations.AlterField( + model_name='mediafile', + name='file', + field=models.FileField(blank=True, storage=django.core.files.storage.FileSystemStorage(base_url='/files', location='/home/ajrbyers/Code/intrepid/src/files'), upload_to='private_documents'), + ), + ] diff --git a/src/package/migrations/0077_auto_20260208_1931.py b/src/package/migrations/0077_auto_20260208_1931.py new file mode 100644 index 0000000..266feea --- /dev/null +++ b/src/package/migrations/0077_auto_20260208_1931.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.8 on 2026-02-08 19:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('package', '0076_auto_20251020_1005'), + ] + + operations = [ + migrations.AddIndex( + model_name='banding', + index=models.Index(fields=['package', 'banding_type'], name='banding_pkg_type_idx'), + ), + migrations.AddIndex( + model_name='bandingtypecurrencyentry', + index=models.Index(fields=['package', 'banding_type_entry'], name='btce_pkg_bte_idx'), + ), + migrations.AddIndex( + model_name='bandingtypeentry', + index=models.Index(fields=['package', 'banding_type'], name='bte_pkg_type_idx'), + ), + migrations.AddIndex( + model_name='country', + index=models.Index(fields=['currency', 'catch_all'], name='country_currency_catchall_idx'), + ), + migrations.AddIndex( + model_name='price', + index=models.Index(fields=['banding', 'country'], name='price_banding_country_idx'), + ), + ] diff --git a/src/package/models.py b/src/package/models.py index 4550962..f8a81ac 100644 --- a/src/package/models.py +++ b/src/package/models.py @@ -1,4 +1,5 @@ import datetime +import functools import os import sys import uuid @@ -19,6 +20,7 @@ from access import models as access_models from accounts import models as account_models +from cms import models as cms_models from initiatives import models as im from package import utils from vocab import models as vm @@ -68,6 +70,12 @@ class Country(models.Model): class Meta: verbose_name_plural = "Countries" ordering = ("name",) + indexes = [ + models.Index( + fields=["currency", "catch_all"], + name="country_currency_catchall_idx", + ), + ] def __str__(self): return "{} ({})".format(self.name, self.currency) @@ -135,7 +143,9 @@ def set_vocabs(self, ids, request) -> list[vm.BandingVocab]: self.vocabs.remove(vocab) if new_vocabs: - vocab_utils.notify_initiatives_new_vocabs(self, new_vocabs, request) + vocab_utils.notify_initiatives_new_vocabs( + self, new_vocabs, request + ) return new_vocabs @@ -183,6 +193,14 @@ class Banding(models.Model): ) objects = BandingManager() + class Meta: + indexes = [ + models.Index( + fields=["package", "banding_type"], + name="banding_pkg_type_idx", + ), + ] + def name(self) -> str: """ Returns the name of the banding @@ -269,6 +287,14 @@ class Price(models.Model): ) objects = PriceManager() + class Meta: + indexes = [ + models.Index( + fields=["banding", "country"], + name="price_banding_country_idx", + ), + ] + @staticmethod def collect() -> None: """ @@ -317,6 +343,14 @@ class BandingTypeCurrencyEntry(models.Model): on_delete=models.SET_NULL, ) + class Meta: + indexes = [ + models.Index( + fields=["package", "banding_type_entry"], + name="btce_pkg_bte_idx", + ), + ] + def __str__(self): return "{} ({}) in {}".format( self.country, self.banding_type_entry, self.package @@ -354,6 +388,14 @@ class BandingTypeEntry(models.Model): blank=True, null=True, default="", verbose_name="Redirect URL" ) + class Meta: + indexes = [ + models.Index( + fields=["package", "banding_type"], + name="bte_pkg_type_idx", + ), + ] + def __str__(self): return "{} in {}".format(self.banding_type, self.package) @@ -540,11 +582,17 @@ def price_bandings(self) -> dict: E.g. price_bandings[BandingTypeCurrencyEntry][Country] = [Prices] :return: a dictionary of BandingTypeCurrencyEntries that contains """ - banding_entries = self.bandingtypecurrencyentry_set.all() - price_objects = Price.objects.all() + # Fetch all related objects in one go + banding_entries = ( + self.bandingtypecurrencyentry_set.all() + .select_related("banding_type_entry__banding_type", "country") + .prefetch_related("banding_type_entry__banding_type__vocabs") + ) + price_objects = Price.objects.all().select_related("banding") prices = {} - # all available BandingTypeCurrencyEntries for this package + bandings = Banding.objects.all().select_related("banding_type") + for banding_entry in banding_entries: prices[banding_entry] = {} @@ -552,27 +600,94 @@ def price_bandings(self) -> dict: for ( v_banding ) in banding_entry.banding_type_entry.banding_type.vocabs.all(): - # get a banding object - banding = Banding.objects.get( - package=self, - vocab=v_banding, - banding_type=banding_entry.banding_type_entry.banding_type, - ) + for banding in bandings: + if ( + banding.package == self + and banding.vocab == v_banding + and banding.banding_type + == banding_entry.banding_type_entry.banding_type + ): + try: + price = None + for price_obj in price_objects: + if ( + price_obj.banding == banding + and price_obj.country + == banding_entry.country + ): + price = price_obj + + """ + # see if there are prices set + price = price_objects.get( + banding=banding, + country=banding_entry.country, + ) + """ - try: - # see if there are prices set - price = price_objects.get( - banding=banding, - country=banding_entry.country, - ) + # if so, append to the list for each country + if price.country not in prices[banding_entry]: + prices[banding_entry][price.country] = [] + + prices[banding_entry][price.country].append(price) + except Price.DoesNotExist: + pass + except AttributeError: + pass + + return prices - # if so, append to the list for each country - if price.country not in prices[banding_entry]: - prices[banding_entry][price.country] = [] + @functools.cached_property + def fast_price_bandings(self) -> dict: + """ + Optimized version of price_bandings. + Scopes queries to this package only and uses dict lookups + instead of triple-nested loops over global querysets. + """ + banding_entries = ( + self.bandingtypecurrencyentry_set.all() + .select_related("banding_type_entry__banding_type", "country") + .prefetch_related("banding_type_entry__banding_type__vocabs") + ) + + # Scope to THIS package only (not all bandings/prices globally) + package_bandings = Banding.objects.filter( + package=self + ).select_related("banding_type", "vocab") + + # Build lookup: (banding_type_id, vocab_id) -> banding + banding_lookup = {} + for b in package_bandings: + banding_lookup[(b.banding_type_id, b.vocab_id)] = b + + package_prices = Price.objects.filter( + banding__package=self + ).select_related("banding", "country") - prices[banding_entry][price.country].append(price) - except Price.DoesNotExist: - pass + # Build lookup: (banding_id, country_id) -> price + price_lookup = {} + for p in package_prices: + price_lookup[(p.banding_id, p.country_id)] = p + + prices = {} + for banding_entry in banding_entries: + prices[banding_entry] = {} + bt = banding_entry.banding_type_entry.banding_type + + for v_banding in bt.vocabs.all(): + banding = banding_lookup.get((bt.id, v_banding.id)) + if banding is None: + continue + + price = price_lookup.get( + (banding.id, banding_entry.country_id) + ) + if price is None: + continue + + if price.country not in prices[banding_entry]: + prices[banding_entry][price.country] = [] + prices[banding_entry][price.country].append(price) return prices @@ -613,17 +728,17 @@ def display_price(self) -> str: return ( "From {0} to {1} (in {2})".format( babel.numbers.format_currency( - low, self.default_country.currency + low, self.default_country.currency, locale="en_US" ), babel.numbers.format_currency( - high, self.default_country.currency + high, self.default_country.currency, locale="en_US" ), self.default_country.currency, ) if low != high else "Around {0}".format( babel.numbers.format_currency( - high, self.default_country.currency + high, self.default_country.currency, locale="en_US" ) ) ) @@ -761,17 +876,25 @@ class Basket(models.Model): help_text="If False this basket will not show in active baskets.", ) + term = models.IntegerField(default=1) + def package_set(self) -> set[Package]: """ Returns a set of packages that are in this basket :return: a set of packages """ - package_list = list() - for package in self.packages.all(): - package_list.append(package) - for meta_package in self.meta_packages.all(): - for package in meta_package.packages.all(): + # package_list = cache.get("package_list") + package_list = None + + if not package_list: + package_list = list() + for package in self.packages.all(): package_list.append(package) + for meta_package in self.meta_packages.all(): + for package in meta_package.packages.all(): + package_list.append(package) + + # cache.set("package_list", package_list, 5) # 5 seconds return set(package_list) @@ -797,21 +920,33 @@ def list_of_conflicting_packages(self) -> set[Package]: Returns a set of packages that conflict with one another :return: a set of packages """ - conflicting_packages = list() + # conflicting_packages = cache.get("conflicting_packages") + conflicting_packages = None - for package in self.packages.all(): - for meta_package in self.meta_packages.all(): - if package in meta_package.packages.all(): - conflicting_packages.append(package) + if not conflicting_packages: + conflicting_packages = list() + + for package in self.packages.all(): + for meta_package in self.meta_packages.all(): + if package in meta_package.packages.all(): + conflicting_packages.append(package) - for meta_package in self.meta_packages.all(): - other_meta_packages = self.meta_packages.exclude(pk=meta_package.pk) - for omp in other_meta_packages: - conflicts = set(meta_package.packages.all()).intersection( - set(omp.packages.all()) + for meta_package in self.meta_packages.all(): + other_meta_packages = self.meta_packages.exclude( + pk=meta_package.pk ) - for conflict in conflicts: - conflicting_packages.append(conflict) + for omp in other_meta_packages: + conflicts = set(meta_package.packages.all()).intersection( + set(omp.packages.all()) + ) + for conflict in conflicts: + conflicting_packages.append(conflict) + + """ + cache.set( + "conflicting_packages", conflicting_packages, 5 + ) # 5 seconds + """ return set(conflicting_packages) @@ -924,7 +1059,8 @@ def get_best_bandings_for_form( suitable_prices = Price.objects.filter( country__currency=catch_all_matching_currency.currency, country__catch_all=True, - ) + ).select_related("banding__banding_type") + package_bandings_return = package_bandings_return + [ price.banding for price in suitable_prices ] @@ -935,12 +1071,13 @@ def get_best_bandings_for_form( package_bandings = bandings.filter( package=package, ) + prices = Price.objects.filter( banding__in=package_bandings, - ).select_related("banding") + ).select_related("banding__banding_type") suitable_prices = prices.filter( country=currency, - ) + ).select_related("banding__banding_type") if suitable_prices: package_bandings_return = package_bandings_return + [ price.banding for price in suitable_prices @@ -948,7 +1085,8 @@ def get_best_bandings_for_form( else: suitable_prices = prices.filter( country=package.default_country, - ) + ).select_related("banding__banding_type") + if suitable_prices: package_bandings_return = package_bandings_return + [ price.banding for price in suitable_prices @@ -961,10 +1099,10 @@ def get_best_bandings_for_form( ) prices = Price.objects.filter( banding__in=package_bandings, - ) + ).select_related("banding__banding_type") suitable_prices = prices.filter( country=currency, - ) + ).select_related("banding__banding_type") if suitable_prices: package_bandings_return = package_bandings_return + [ price.banding for price in suitable_prices @@ -973,7 +1111,8 @@ def get_best_bandings_for_form( if not catch_all_matching_currency: suitable_prices = prices.filter( country=package.default_country, - ) + ).select_related("banding__banding_type") + if suitable_prices: package_bandings_return = ( package_bandings_return @@ -991,6 +1130,235 @@ def get_best_bandings_for_form( ] ) + def fast_list_of_conflicting_packages(self) -> set[Package]: + """ + Optimized version of list_of_conflicting_packages. + Uses prefetched M2M data and in-memory set operations + instead of nested DB queries. + """ + basket_packages = set(self.packages.all()) + basket_meta_packages = list(self.meta_packages.all()) + + # Build {meta_package: set(its packages)} once + mp_package_sets = { + mp: set(mp.packages.all()) for mp in basket_meta_packages + } + + conflicting_packages = [] + + # Packages that appear both individually and in a meta_package + for package in basket_packages: + for mp_pkgs in mp_package_sets.values(): + if package in mp_pkgs: + conflicting_packages.append(package) + break + + # Packages shared between different meta_packages + mp_list = list(mp_package_sets.items()) + for i, (mp, mp_pkgs) in enumerate(mp_list): + for j in range(i + 1, len(mp_list)): + omp, omp_pkgs = mp_list[j] + conflicting_packages.extend(mp_pkgs & omp_pkgs) + + return set(conflicting_packages) + + def fast_cost( + self, identifier, identifier_type="user", country=None + ) -> tuple[list, dict]: + """ + Optimized version of cost. + Accepts pre-resolved country to avoid redundant user lookups. + Uses prefetched M2M relations. + Batch-prefetches all bandings and prices in 2 queries. + """ + package_costs = [] + + if country is None: + if identifier_type == "user": + user = account_models.User.objects.get(username=identifier) + country = user.profile.default_currency + else: + country_pk = identifier.get("currency", None) + country = Country.objects.filter(pk=country_pk).first() + + # Pre-fetch all AccountBandingChoices for the user once + account_banding_choices = None + if identifier_type == "user": + account_banding_choices = { + abc.banding_type_id: abc + for abc in account_models.AccountBandingChoices.objects.filter( + account=identifier, + ).select_related("banding_type_vocab") + } + + # --- Batch-prefetch all bandings and prices for all basket packages --- + all_pkg_ids = [p.pk for p in self.packages.all()] + for mp in self.meta_packages.all(): + all_pkg_ids.extend(p.pk for p in mp.packages.all()) + + all_bandings = Banding.objects.filter( + package_id__in=all_pkg_ids + ).select_related("banding_type", "vocab") + + all_prices = Price.objects.filter( + banding__package_id__in=all_pkg_ids + ).select_related("banding", "country") + + # Build lookups: (package_id, banding_type_id) -> [bandings] + bandings_cache = {} + for b in all_bandings: + bandings_cache.setdefault( + (b.package_id, b.banding_type_id), [] + ).append(b) + + # Build lookups: (banding_id, country_id) -> price + price_cache = {} + for p in all_prices: + price_cache[(p.banding_id, p.country_id)] = p + + for package in self.packages.all(): + price, banding = utils.fast_get_price_for_package( + package=package, + identifier=identifier, + identifier_type=identifier_type, + country=country if country else None, + account_banding_choices=account_banding_choices, + bandings_cache=bandings_cache, + price_cache=price_cache, + ) + package_costs.append( + {"package": package, "cost": price, "banding": banding} + ) + + for mp in self.meta_packages.all(): + for package in mp.packages.all(): + price, banding = utils.fast_get_price_for_package( + package=package, + identifier=identifier, + identifier_type=identifier_type, + country=country if country else None, + account_banding_choices=account_banding_choices, + bandings_cache=bandings_cache, + price_cache=price_cache, + ) + package_costs.append( + {"package": package, "cost": price, "banding": banding} + ) + + currency_totals = dict() + completed_packages = [] + for pc in package_costs: + if (pc.get("cost") and pc.get("banding")) and pc.get( + "package" + ) not in completed_packages: + if pc.get("cost").country: + currency = pc.get("cost").country.currency + else: + currency = pc.get("cost").default_currency + + if currency_totals.get(currency): + currency_totals[currency] = ( + currency_totals[currency] + pc.get("cost").value + ) + else: + currency_totals[currency] = pc.get("cost").value + + completed_packages.append(pc.get("package")) + + return package_costs, currency_totals + + def fast_get_best_bandings_for_form( + self, identifier, identifier_type="user", country=None + ) -> set: + """ + Optimized version of get_best_bandings_for_form. + Accepts pre-resolved country and batch-fetches all prices + in 2 queries instead of N+1. + """ + package_bandings_return = [] + + if country is None: + if identifier_type == "user": + user = account_models.User.objects.get(username=identifier) + currency = user.profile.default_currency + else: + currency_pk = identifier.get("currency") + currency = Country.objects.filter(pk=currency_pk).first() + else: + currency = country + + if not currency: + return set() + + # Collect ALL packages from basket (direct + meta_packages) + all_packages = list(self.packages.all()) + for mp in self.meta_packages.all(): + all_packages.extend(mp.packages.all()) + all_package_ids = [p.pk for p in all_packages] + + # Find catch-all country for currency + catch_all_matching_currency = Country.objects.filter( + currency=currency.currency, catch_all=True + ).first() + + if catch_all_matching_currency: + suitable_prices = Price.objects.filter( + country__currency=catch_all_matching_currency.currency, + country__catch_all=True, + ).select_related("banding__banding_type") + package_bandings_return.extend( + price.banding for price in suitable_prices + ) + + # Batch fetch ALL bandings for all basket packages at once + bandings = Banding.objects.filter( + package_id__in=all_package_ids + ).select_related("banding_type") + + # Batch fetch ALL prices for those bandings at once + all_prices = Price.objects.filter( + banding__in=bandings + ).select_related("banding__banding_type", "country") + + # Build lookup: package_id -> list of prices + price_by_package = {} + for price in all_prices: + pkg_id = price.banding.package_id + price_by_package.setdefault(pkg_id, []).append(price) + + default_countries = { + p.pk: p.default_country_id for p in all_packages + } + + for package in all_packages: + pkg_prices = price_by_package.get(package.pk, []) + + # First try: prices matching user currency + suitable = [ + p for p in pkg_prices if p.country_id == currency.pk + ] + if suitable: + package_bandings_return.extend( + p.banding for p in suitable + ) + else: + # Fallback: default country + default_country_id = default_countries.get(package.pk) + suitable = [ + p for p in pkg_prices + if p.country_id == default_country_id + ] + if suitable: + package_bandings_return.extend( + p.banding for p in suitable + ) + + return set( + banding.banding_type + for banding in package_bandings_return + if not banding.banding_type.is_fte + ) + class AggregateForm(models.Model): """ @@ -1347,6 +1715,9 @@ class Order(models.Model): blank=True, null=True, default=datetime.date.today ) + # the term length in years + term = models.IntegerField(default=1) + # the associated basket for this order basket = models.ForeignKey( Basket, @@ -1516,8 +1887,10 @@ def save_order_form(self, form) -> None: :return: None """ self.email_address = form.cleaned_data.get("email_address") + self.term = form.cleaned_data.get("term_length") + for k, v in form.cleaned_data.items(): - if not k == "email_address": + if not k == "email_address" and not k == "term_length": form_element = AggregateFormElement.objects.get(pk=k) answer, _ = OrderFormAnswer.objects.update_or_create( order=self, @@ -1698,6 +2071,16 @@ def formatted_price(self) -> str: """ return utils.format_price(self.price, self.currency.currency) + @property + def formatted_price_multiplier(self) -> str: + """ + Returns a formatted price + :return: str + """ + return utils.format_price( + self.price * self.associated_order.term, self.currency.currency + ) + class MediaFile(models.Model): """ @@ -1750,3 +2133,45 @@ def serve_file(self) -> HttpResponse: return serve_file( path=self.file.path, filename=self.file.name, mime=self.mime ) + + +class PreCalcMinMax(models.Model): + package = models.ForeignKey( + "Package", + on_delete=models.CASCADE, + null=True, + blank=True, + ) + meta_package = models.ForeignKey( + "MetaPackage", + on_delete=models.CASCADE, + null=True, + blank=True, + ) + country = models.ForeignKey( + "Country", + on_delete=models.CASCADE, + ) + min_amount = models.IntegerField() + max_amount = models.IntegerField() + + def __str__(self): + # fetch the "to" translation field + to_translation = cms_models.SiteText.objects.get(key="to") + + return "{0} {2} {1}".format( + babel.numbers.format_currency( + self.min_amount, self.country.currency, locale="en_US" + ), + babel.numbers.format_currency( + self.max_amount, self.country.currency, locale="en_US" + ), + to_translation + ) + + def get_package(self): + if self.package: + return self.package.name + if self.meta_package: + return self.meta_package + return "No package associated with pre calc price" diff --git a/src/package/templatetags/conflicts.py b/src/package/templatetags/conflicts.py index ec06cec..2fdda04 100644 --- a/src/package/templatetags/conflicts.py +++ b/src/package/templatetags/conflicts.py @@ -1,5 +1,7 @@ from django import template +from cms import models + register = template.Library() @@ -13,12 +15,16 @@ def conflict_statement(package, basket): :return: a string describing the conflict between the package and the basket """ package_meta_packages = package.meta_packages() + text = models.SiteText.objects.get( + key='included_within_offers', + ) conflicting_metapackage_names = [] for meta_package in package_meta_packages: if meta_package in basket.meta_packages.all(): conflicting_metapackage_names.append(meta_package.name) - return "Included within these offers: {}".format( + return "{}: {}".format( + text, ", ".join(conflicting_metapackage_names) ) diff --git a/src/package/templatetags/country.py b/src/package/templatetags/country.py new file mode 100644 index 0000000..f00dec1 --- /dev/null +++ b/src/package/templatetags/country.py @@ -0,0 +1,18 @@ +from django.template.defaulttags import register + +from package import models + + +@register.simple_tag(takes_context=True) +def country_code_to_name(context): + request = context.get("request") + try: + country = models.Country.objects.filter( + code=request.session.get('country') + ).first() + if country: + return country.name + else: + return '' + except models.Country.DoesNotExist: + return request.session.get('country') diff --git a/src/package/tests.py b/src/package/tests.py new file mode 100644 index 0000000..afaaffd --- /dev/null +++ b/src/package/tests.py @@ -0,0 +1,1181 @@ +""" +Comparison tests for view_basket (original) vs fast_view_basket (optimized). + +These tests verify that for multiple package and meta-package basket +configurations, both views produce identical template context and rendered +HTML output. + +Uses fixtures/test_package.json which provides: + - 20 Initiatives (pk 1-20) + - 3 Countries (GBP pk=999, USD pk=998, EUR pk=997) + - 2 BandingTypes: FTE pk=100 (8 vocabs 100-107), + Choice pk=101 (8 vocabs 108-115) + - 50 Packages (pk 100-149) across 20 initiatives, with 10 different + banding-type / currency configurations + - 10 MetaPackages (pk 300-309) with 3-18 packages each, overlapping + membership creating 39 conflict points + - 880 prices across 520 bandings + +Additional objects (SiteSetup, SiteText, User, Profile, +AccountBandingChoices) are created programmatically in setUpTestData. +""" + +import re +import time +from decimal import Decimal +from unittest.mock import patch + +from django.contrib.auth.models import User +from django.contrib.sessions.backends.db import SessionStore +from django.core.management import call_command +from django.test import TestCase, RequestFactory + +from accounts.models import AccountBandingChoices, Profile +from cms.models import SiteText +from intrepid.models import SiteSetup +from package import forms, models, utils, views +from vocab.models import BandingVocab + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +class _AnonymousUser: + """Minimal stand-in for an anonymous user.""" + + is_authenticated = False + pk = None + + def __str__(self): + return "AnonymousUser" + + +# --------------------------------------------------------------------------- +# Shared fixture mixin +# --------------------------------------------------------------------------- + + +class BasketFixtureMixin: + """ + Loads ``fixtures/test_package.json`` (20 initiatives, 50 packages, + 10 metas, 1825 objects) and creates the supporting objects needed by + views and forms. + + Package config cycle (repeats every 10 PKs starting at 100): + 0: FTE+Choice / GBP+USD+EUR (pk 100, 110, 120, 130, 140) + 1: FTE / GBP (pk 101, 111, 121, 131, 141) + 2: FTE / GBP+USD (pk 102, 112, 122, 132, 142) + 3: Choice / GBP (pk 103, 113, 123, 133, 143) + 4: FTE+Choice / GBP+USD (pk 104, 114, 124, 134, 144) + 5: FTE / GBP+EUR (pk 105, 115, 125, 135, 145) + 6: Choice / USD (pk 106, 116, 126, 136, 146) + 7: FTE+Choice / GBP (pk 107, 117, 127, 137, 147) + 8: FTE / EUR (pk 108, 118, 128, 138, 148) + 9: Choice / GBP+USD (pk 109, 119, 129, 139, 149) + """ + + @classmethod + def setUpTestData(cls): + # ---- Load fixture (includes 20 initiatives, 50 packages, etc.) ---- + call_command("loaddata", "test_package", verbosity=0) + + # ---- Countries ---- + cls.country_gbp = models.Country.objects.get(pk=999) + cls.country_usd = models.Country.objects.get(pk=998) + cls.country_eur = models.Country.objects.get(pk=997) + + # ---- BandingTypes ---- + cls.fte_banding_type = models.BandingType.objects.get(pk=100) + cls.choice_banding_type = models.BandingType.objects.get(pk=101) + + # ---- All 50 packages (ordered by pk) ---- + cls.all_packages = list( + models.Package.objects.filter( + pk__gte=100, pk__lte=149, + ).order_by("pk") + ) + + # ---- Named references for targeted tests ---- + cls.pkg_all_combos = models.Package.objects.get(pk=100) + cls.pkg_simple_fte = models.Package.objects.get(pk=101) + cls.pkg_fte_multi_curr = models.Package.objects.get(pk=102) + cls.pkg_simple_choice = models.Package.objects.get(pk=103) + cls.pkg_dual_multi = models.Package.objects.get(pk=104) + cls.pkg_fte_gbp_eur = models.Package.objects.get(pk=105) + cls.pkg_choice_usd = models.Package.objects.get(pk=106) + cls.pkg_dual_gbp = models.Package.objects.get(pk=107) + cls.pkg_fte_eur = models.Package.objects.get(pk=108) + cls.pkg_choice_multi = models.Package.objects.get(pk=109) + + # ---- Convenience subsets ---- + # GBP-only packages (no currency conversion needed) + cls.gbp_only_packages = [ + models.Package.objects.get(pk=pk) + for pk in [101, 103, 107, 111, 113, 117, 121, 123, 127] + ] + + # ---- All 10 meta packages (ordered by pk) ---- + cls.all_metas = list( + models.MetaPackage.objects.filter( + pk__gte=300, pk__lte=309, + ).order_by("pk") + ) + cls.meta_18 = models.MetaPackage.objects.get(pk=300) + cls.meta_15 = models.MetaPackage.objects.get(pk=301) + cls.meta_3 = models.MetaPackage.objects.get(pk=309) + + # ---- SiteSetup ---- + cls.site_setup = SiteSetup.objects.create( + site_name="Test Site", + enable_signup=True, + enable_meta_package_signup=True, + enable_individual_package_signup=True, + fee_amount=5, + fallback_currency="GBP", + ) + + # ---- SiteText entries required by FTEForm / FastFTEForm ---- + for key in ( + "select_currency_note", + "fte_student_count_prompt", + "institutions_fte", + "your_currency", + "after_currency_bandings_info", + "institution_details", + "included_within_offers", + ): + SiteText.objects.get_or_create( + key=key, + defaults={ + "body": key.replace("_", " ").title(), + "help_text": "", + "rich_text": False, + "frontend": False, + }, + ) + + # ---- User + Profile ---- + cls.user = User.objects.create_user( + username="testuser", password="testpass123", + email="test@example.com", + ) + profile, _ = Profile.objects.get_or_create(account=cls.user) + profile.fte = 12000 # falls in "Large" tier (10,001-20,000) + profile.default_currency = cls.country_gbp + profile.save() + cls.profile = profile + + # ---- AccountBandingChoices (for choice-based banding type) ---- + AccountBandingChoices.objects.create( + account=cls.user, + banding_type=cls.choice_banding_type, + banding_type_vocab=BandingVocab.objects.get(pk=113), + ) + + +# --------------------------------------------------------------------------- +# Model-level: price_bandings +# --------------------------------------------------------------------------- + + +class TestFastPriceBandings(BasketFixtureMixin, TestCase): + """Verify fast_price_bandings produces identical output to price_bandings.""" + + def _compare(self, package): + original = package.price_bandings + + if "fast_price_bandings" in package.__dict__: + del package.__dict__["fast_price_bandings"] + fast = package.fast_price_bandings + + self.assertEqual( + set(original.keys()), set(fast.keys()), + "Banding entry keys differ", + ) + for entry_key in original: + orig_inner = original[entry_key] + fast_inner = fast[entry_key] + self.assertEqual( + set(orig_inner.keys()), set(fast_inner.keys()), + "Country keys differ for {}".format(entry_key), + ) + for country_key in orig_inner: + orig_pks = sorted(p.pk for p in orig_inner[country_key]) + fast_pks = sorted(p.pk for p in fast_inner[country_key]) + self.assertEqual( + orig_pks, fast_pks, + "Price PKs differ for {} / {}".format( + entry_key, country_key, + ), + ) + + def test_all_combos(self): + """FTE+Choice / GBP+USD+EUR — maximum complexity.""" + self._compare(self.pkg_all_combos) + + def test_simple_fte(self): + """FTE / GBP — simplest case.""" + self._compare(self.pkg_simple_fte) + + def test_fte_multi_currency(self): + """FTE / GBP+USD.""" + self._compare(self.pkg_fte_multi_curr) + + def test_simple_choice(self): + """Choice / GBP.""" + self._compare(self.pkg_simple_choice) + + def test_dual_multi_currency(self): + """FTE+Choice / GBP+USD.""" + self._compare(self.pkg_dual_multi) + + def test_fte_gbp_eur(self): + """FTE / GBP+EUR.""" + self._compare(self.pkg_fte_gbp_eur) + + def test_choice_usd_only(self): + """Choice / USD — foreign currency only.""" + self._compare(self.pkg_choice_usd) + + def test_dual_gbp_only(self): + """FTE+Choice / GBP.""" + self._compare(self.pkg_dual_gbp) + + def test_fte_eur_only(self): + """FTE / EUR.""" + self._compare(self.pkg_fte_eur) + + def test_choice_multi_currency(self): + """Choice / GBP+USD.""" + self._compare(self.pkg_choice_multi) + + +# --------------------------------------------------------------------------- +# Model-level: list_of_conflicting_packages +# --------------------------------------------------------------------------- + + +class TestFastListOfConflictingPackages(BasketFixtureMixin, TestCase): + """ + Verify fast_list_of_conflicting_packages matches the original for + various basket configurations. + """ + + def _compare(self, basket): + original = basket.list_of_conflicting_packages() + fast = basket.fast_list_of_conflicting_packages() + self.assertEqual(original, fast) + + def test_empty_basket(self): + basket = models.Basket.objects.create( + name="Empty", account=self.user, active=True, + ) + self._compare(basket) + + def test_packages_only_no_conflicts(self): + basket = models.Basket.objects.create( + name="Pkgs only", account=self.user, active=True, + ) + basket.packages.add(*self.gbp_only_packages[:5]) + self._compare(basket) + + def test_two_metas_with_overlap(self): + """meta_18 and meta_15 share packages -> conflicts.""" + basket = models.Basket.objects.create( + name="Meta overlap", account=self.user, active=True, + ) + basket.meta_packages.add(self.meta_18, self.meta_15) + self._compare(basket) + + def test_direct_and_meta_overlap(self): + """pkg 100 is both direct and inside meta_18 -> conflict.""" + basket = models.Basket.objects.create( + name="Direct+meta", account=self.user, active=True, + ) + basket.packages.add(self.pkg_all_combos) + basket.meta_packages.add(self.meta_18) + self._compare(basket) + + def test_all_metas(self): + """All 10 meta packages — lots of overlaps.""" + basket = models.Basket.objects.create( + name="All metas", account=self.user, active=True, + ) + basket.meta_packages.add(*self.all_metas) + self._compare(basket) + + def test_single_meta(self): + basket = models.Basket.objects.create( + name="Single meta", account=self.user, active=True, + ) + basket.meta_packages.add(self.meta_3) + self._compare(basket) + + def test_full_basket(self): + """All 50 packages + all 10 metas.""" + basket = models.Basket.objects.create( + name="Full", account=self.user, active=True, + ) + basket.packages.add(*self.all_packages) + basket.meta_packages.add(*self.all_metas) + self._compare(basket) + + +# --------------------------------------------------------------------------- +# Model-level: cost +# --------------------------------------------------------------------------- + + +class TestFastCost(BasketFixtureMixin, TestCase): + """ + Verify fast_cost returns the same package_costs and currency_totals + as the original cost method. + """ + + def _compare(self, basket): + identifier = self.user + identifier_type = "user" + country = self.user.profile.default_currency + + orig_costs, orig_totals = basket.cost( + identifier=identifier, identifier_type=identifier_type, + ) + fast_costs, fast_totals = basket.fast_cost( + identifier=identifier, identifier_type=identifier_type, + country=country, + ) + + self.assertEqual( + len(orig_costs), len(fast_costs), + "package_costs length differs", + ) + for i, (orig, fast) in enumerate(zip(orig_costs, fast_costs)): + self.assertEqual( + orig["package"].pk, fast["package"].pk, + "package_costs[{}].package differs".format(i), + ) + if orig["cost"] == 0: + self.assertEqual( + fast["cost"], 0, + "package_costs[{}].cost: expected 0".format(i), + ) + else: + self.assertEqual( + orig["cost"].pk, fast["cost"].pk, + "package_costs[{}].cost PK differs".format(i), + ) + self.assertEqual( + orig["cost"].value, fast["cost"].value, + "package_costs[{}].cost value differs".format(i), + ) + if orig["banding"] is None: + self.assertIsNone( + fast["banding"], + "package_costs[{}].banding: expected None".format(i), + ) + else: + self.assertEqual( + orig["banding"].pk, fast["banding"].pk, + "package_costs[{}].banding PK differs".format(i), + ) + + self.assertEqual(orig_totals, fast_totals, "currency_totals differ") + + def test_single_package(self): + basket = models.Basket.objects.create( + name="Single", account=self.user, active=True, + ) + basket.packages.add(self.pkg_all_combos) + self._compare(basket) + + def test_five_gbp_packages(self): + basket = models.Basket.objects.create( + name="5 GBP", account=self.user, active=True, + ) + basket.packages.add(*self.gbp_only_packages[:5]) + self._compare(basket) + + def test_packages_mixed_currency(self): + basket = models.Basket.objects.create( + name="Mixed curr", account=self.user, active=True, + ) + basket.packages.add( + self.pkg_all_combos, self.pkg_choice_usd, self.pkg_fte_eur, + ) + self._compare(basket) + + def test_meta_packages(self): + basket = models.Basket.objects.create( + name="Meta", account=self.user, active=True, + ) + basket.meta_packages.add(self.meta_18, self.meta_15) + self._compare(basket) + + def test_twenty_packages(self): + basket = models.Basket.objects.create( + name="Twenty", account=self.user, active=True, + ) + basket.packages.add(*self.all_packages[:20]) + self._compare(basket) + + def test_mixed_packages_and_meta(self): + basket = models.Basket.objects.create( + name="Mixed", account=self.user, active=True, + ) + basket.packages.add(self.pkg_simple_choice, self.pkg_fte_eur) + basket.meta_packages.add(self.meta_18, self.meta_15) + self._compare(basket) + + def test_full_basket(self): + basket = models.Basket.objects.create( + name="Full", account=self.user, active=True, + ) + basket.packages.add(*self.all_packages) + basket.meta_packages.add(*self.all_metas) + self._compare(basket) + + +# --------------------------------------------------------------------------- +# Model-level: get_best_bandings_for_form +# --------------------------------------------------------------------------- + + +class TestFastGetBestBandingsForForm(BasketFixtureMixin, TestCase): + """ + Verify fast_get_best_bandings_for_form returns the same set of + BandingType objects as the original. + """ + + def _compare(self, basket): + identifier = self.user + identifier_type = "user" + country = self.user.profile.default_currency + + original = basket.get_best_bandings_for_form( + identifier, identifier_type, + ) + fast = basket.fast_get_best_bandings_for_form( + identifier, identifier_type, country=country, + ) + self.assertEqual(original, fast) + + def test_single_package(self): + basket = models.Basket.objects.create( + name="Single", account=self.user, active=True, + ) + basket.packages.add(self.pkg_simple_choice) + self._compare(basket) + + def test_multiple_packages(self): + basket = models.Basket.objects.create( + name="Multi", account=self.user, active=True, + ) + basket.packages.add( + self.pkg_all_combos, self.pkg_simple_fte, self.pkg_simple_choice, + ) + self._compare(basket) + + def test_meta_packages(self): + basket = models.Basket.objects.create( + name="Meta", account=self.user, active=True, + ) + basket.meta_packages.add(self.meta_18, self.meta_15) + self._compare(basket) + + def test_full_basket(self): + basket = models.Basket.objects.create( + name="Full", account=self.user, active=True, + ) + basket.packages.add(*self.all_packages) + basket.meta_packages.add(*self.all_metas) + self._compare(basket) + + def test_cross_currency_package(self): + """pkg_choice_usd is USD-priced; user prefers GBP -> fallback.""" + basket = models.Basket.objects.create( + name="Cross", account=self.user, active=True, + ) + basket.packages.add(self.pkg_choice_usd) + self._compare(basket) + + def test_multi_banding_basket(self): + basket = models.Basket.objects.create( + name="Multi-band", account=self.user, active=True, + ) + basket.packages.add( + self.pkg_all_combos, self.pkg_dual_multi, self.pkg_dual_gbp, + ) + self._compare(basket) + + +# --------------------------------------------------------------------------- +# Util-level: get_price_for_package +# --------------------------------------------------------------------------- + + +class TestFastGetPriceForPackage(BasketFixtureMixin, TestCase): + """ + Verify fast_get_price_for_package returns the same price/banding + as the original for each package. + """ + + def _compare(self, package, country=None): + identifier = self.user + identifier_type = "user" + if country is None: + country = self.user.profile.default_currency + + orig_price, orig_banding = utils.get_price_for_package( + package=package, identifier=identifier, + identifier_type=identifier_type, country=country, + ) + + if "fast_price_bandings" in package.__dict__: + del package.__dict__["fast_price_bandings"] + + account_banding_choices = { + abc.banding_type_id: abc + for abc in AccountBandingChoices.objects.filter( + account=identifier, + ).select_related("banding_type_vocab") + } + + fast_price, fast_banding = utils.fast_get_price_for_package( + package=package, identifier=identifier, + identifier_type=identifier_type, country=country, + account_banding_choices=account_banding_choices, + ) + + if orig_price == 0: + self.assertEqual(fast_price, 0) + else: + self.assertEqual(orig_price.pk, fast_price.pk) + self.assertEqual(orig_price.value, fast_price.value) + + if orig_banding is None: + self.assertIsNone(fast_banding) + else: + self.assertEqual(orig_banding.pk, fast_banding.pk) + + def test_fte_matching_currency(self): + """FTE/GBP, user currency is GBP.""" + self._compare(self.pkg_simple_fte) + + def test_fte_different_currency(self): + """FTE/EUR, user currency is GBP -> fallback.""" + self._compare(self.pkg_fte_eur) + + def test_choice_package(self): + """Choice/GBP.""" + self._compare(self.pkg_simple_choice) + + def test_all_combos_package(self): + """FTE+Choice / GBP+USD+EUR — most complex.""" + self._compare(self.pkg_all_combos) + + def test_dual_multi_currency(self): + """FTE+Choice / GBP+USD.""" + self._compare(self.pkg_dual_multi) + + def test_choice_usd_with_gbp_user(self): + """Choice/USD, user is GBP -> fallback.""" + self._compare(self.pkg_choice_usd) + + def test_eur_country_override(self): + """Force EUR country for a GBP+EUR package.""" + self._compare(self.pkg_fte_gbp_eur, country=self.country_eur) + + def test_usd_country_override(self): + """Force USD country for an all-currency package.""" + self._compare(self.pkg_all_combos, country=self.country_usd) + + +# --------------------------------------------------------------------------- +# Util-level: get_user_currency +# --------------------------------------------------------------------------- + + +class TestFastGetUserCurrency(BasketFixtureMixin, TestCase): + """Verify fast_get_user_currency matches get_user_currency.""" + + def test_authenticated_user(self): + orig = utils.get_user_currency(self.user, "user") + fast = utils.fast_get_user_currency( + self.user, "user", country=self.user.profile.default_currency, + ) + self.assertEqual(orig, fast) + + def test_session_user(self): + session = {"currency": self.country_usd.pk} + orig = utils.get_user_currency(session, "session") + fast = utils.fast_get_user_currency(session, "session") + self.assertEqual(orig, fast) + + def test_pre_resolved_bypasses_lookup(self): + """When country is passed, it should be returned as-is.""" + result = utils.fast_get_user_currency( + None, "user", country=self.country_gbp, + ) + self.assertEqual(result, self.country_gbp) + + +# --------------------------------------------------------------------------- +# View-level: compare full template contexts +# --------------------------------------------------------------------------- + + +class _ViewComparisonMixin: + """ + Shared logic for comparing view_basket vs fast_view_basket contexts. + Patches ``render`` to capture the template context dict. + """ + + def _extract_context(self, view_func, request, basket_id): + captured = {} + + import package.views as pv + original_render = pv.render + + def mock_render(req, template, context): + captured.update(context) + return original_render(req, template, context) + + pv.render = mock_render + try: + view_func(request, basket_id=basket_id) + finally: + pv.render = original_render + + return captured + + def _compare_contexts(self, orig_ctx, fast_ctx): + # ---- package_costs ---- + orig_costs = orig_ctx["package_costs"] + fast_costs = fast_ctx["package_costs"] + self.assertEqual( + len(orig_costs), len(fast_costs), + "package_costs length differs", + ) + for i, (o, f) in enumerate(zip(orig_costs, fast_costs)): + self.assertEqual( + o["package"].pk, f["package"].pk, + "package_costs[{}].package differs".format(i), + ) + if o["cost"] == 0: + self.assertEqual(f["cost"], 0) + else: + self.assertEqual( + o["cost"].pk, f["cost"].pk, + "package_costs[{}].cost PK differs".format(i), + ) + if o["banding"] is None: + self.assertIsNone(f["banding"]) + else: + self.assertEqual( + o["banding"].pk, f["banding"].pk, + "package_costs[{}].banding PK differs".format(i), + ) + + # ---- currency_totals ---- + self.assertEqual( + orig_ctx["currency_totals"], fast_ctx["currency_totals"], + ) + + # ---- conflicting_packages ---- + orig_pks = {p.pk for p in orig_ctx["conflicting_packages"]} + fast_pks = {p.pk for p in fast_ctx["conflicting_packages"]} + self.assertEqual(orig_pks, fast_pks) + + # ---- scalar context values ---- + for key in ( + "has_all_prices", + "message", + "site_percentage", + "currency_converted", + "converted_total", + "converted_currency", + ): + self.assertEqual( + orig_ctx.get(key), fast_ctx.get(key), + "Context key '{}' differs".format(key), + ) + + # ---- user_currency ---- + self.assertEqual( + orig_ctx["user_currency"], fast_ctx["user_currency"], + ) + + # ---- bandings list ---- + orig_banding_pks = [b.pk for b in orig_ctx["bandings"]] + fast_banding_pks = [b.pk for b in fast_ctx["bandings"]] + self.assertEqual(orig_banding_pks, fast_banding_pks) + + +class TestViewBasketComparison( + BasketFixtureMixin, _ViewComparisonMixin, TestCase +): + """ + End-to-end comparison for an authenticated user: call both + view_basket and fast_view_basket and verify contexts match. + """ + + def setUp(self): + self.factory = RequestFactory() + + def _make_request(self): + request = self.factory.get("/fake/") + request.user = self.user + request.session = {"country": self.country_gbp.code} + request.site = self.site_setup + request.signups = [] + return request + + def _run_comparison(self, basket): + orig_ctx = self._extract_context( + views.view_basket, self._make_request(), basket.pk, + ) + fast_ctx = self._extract_context( + views.fast_view_basket, self._make_request(), basket.pk, + ) + self._compare_contexts(orig_ctx, fast_ctx) + + def test_single_package(self): + basket = models.Basket.objects.create( + name="Single", account=self.user, active=True, + ) + basket.packages.add(self.pkg_all_combos) + self._run_comparison(basket) + + def test_five_packages(self): + basket = models.Basket.objects.create( + name="Five", account=self.user, active=True, + ) + basket.packages.add(*self.gbp_only_packages[:5]) + self._run_comparison(basket) + + @patch("package.currency.convert", return_value=1.25) + def test_meta_packages(self, mock_convert): + basket = models.Basket.objects.create( + name="Meta", account=self.user, active=True, + ) + basket.meta_packages.add(self.meta_18, self.meta_15) + self._run_comparison(basket) + + @patch("package.currency.convert", return_value=1.25) + def test_mixed_packages_and_meta(self, mock_convert): + basket = models.Basket.objects.create( + name="Mixed", account=self.user, active=True, + ) + basket.packages.add(self.pkg_simple_choice) + basket.meta_packages.add(self.meta_18, self.meta_15) + self._run_comparison(basket) + + @patch("package.currency.convert", return_value=1.25) + def test_twenty_packages(self, mock_convert): + basket = models.Basket.objects.create( + name="Twenty", account=self.user, active=True, + ) + basket.packages.add(*self.all_packages[:20]) + basket.meta_packages.add(*self.all_metas[:5]) + self._run_comparison(basket) + + @patch("package.currency.convert", return_value=1.25) + def test_full_basket(self, mock_convert): + """All 50 packages + all 10 metas.""" + basket = models.Basket.objects.create( + name="Full", account=self.user, active=True, + ) + basket.packages.add(*self.all_packages) + basket.meta_packages.add(*self.all_metas) + self._run_comparison(basket) + + @patch("package.currency.convert", return_value=1.25) + def test_cross_currency(self, mock_convert): + basket = models.Basket.objects.create( + name="Cross", account=self.user, active=True, + ) + basket.packages.add( + self.pkg_all_combos, self.pkg_choice_usd, self.pkg_fte_eur, + ) + self._run_comparison(basket) + + +# --------------------------------------------------------------------------- +# Session-based (anonymous) view comparison tests +# --------------------------------------------------------------------------- + + +class TestViewBasketSessionComparison( + BasketFixtureMixin, _ViewComparisonMixin, TestCase +): + """Same comparisons but for anonymous/session-based users.""" + + def setUp(self): + self.factory = RequestFactory() + self.session = SessionStore() + self.session["currency"] = self.country_gbp.pk + self.session["fte"] = 12000 + self.session.create() + + def _make_request(self): + request = self.factory.get("/fake/") + request.user = _AnonymousUser() + request.session = self.session + request.site = self.site_setup + request.signups = [] + return request + + def _run_comparison(self, basket): + orig_ctx = self._extract_context( + views.view_basket, self._make_request(), basket.pk, + ) + fast_ctx = self._extract_context( + views.fast_view_basket, self._make_request(), basket.pk, + ) + self._compare_contexts(orig_ctx, fast_ctx) + + def test_session_single(self): + basket = models.Basket.objects.create( + name="Sess single", session_id=self.session.session_key, + active=True, + ) + basket.packages.add(self.pkg_all_combos) + self._run_comparison(basket) + + def test_session_five(self): + basket = models.Basket.objects.create( + name="Sess five", session_id=self.session.session_key, + active=True, + ) + basket.packages.add(*self.gbp_only_packages[:5]) + self._run_comparison(basket) + + def test_session_meta(self): + basket = models.Basket.objects.create( + name="Sess meta", session_id=self.session.session_key, + active=True, + ) + basket.meta_packages.add(self.meta_18, self.meta_15) + self._run_comparison(basket) + + @patch("package.currency.convert", return_value=1.25) + def test_session_twenty(self, mock_convert): + basket = models.Basket.objects.create( + name="Sess twenty", session_id=self.session.session_key, + active=True, + ) + basket.packages.add(*self.all_packages[:20]) + basket.meta_packages.add(*self.all_metas[:5]) + self._run_comparison(basket) + + @patch("package.currency.convert", return_value=1.25) + def test_session_full(self, mock_convert): + basket = models.Basket.objects.create( + name="Sess full", session_id=self.session.session_key, + active=True, + ) + basket.packages.add(*self.all_packages) + basket.meta_packages.add(*self.all_metas) + self._run_comparison(basket) + + +# --------------------------------------------------------------------------- +# Rendered output comparison: actual HTTP responses from both views +# --------------------------------------------------------------------------- + + +class _TimingTableMixin: + """ + Collects per-test timing results and prints a summary table in + ``tearDownClass``. + """ + + _timing_results = None + _timing_title = "Timing Results" + + @classmethod + def _init_timing(cls): + cls._timing_results = [] + + @classmethod + def _print_timing_table(cls): + if not cls._timing_results: + return + + label_w = max(len(r[0]) for r in cls._timing_results) + label_w = max(label_w, len("Scenario")) + + rows = [] + for label, orig, fast in cls._timing_results: + speedup = orig / fast if fast > 0 else float("inf") + saved = orig - fast + rows.append((label, orig, fast, speedup, saved)) + + print("\n") + print(" {}".format(cls._timing_title)) + print(" {}".format("=" * (label_w + 52))) + print(" {:<{w}} {:>10} {:>10} {:>9} {:>9}".format( + "Scenario", "Original", "Fast", "Speedup", "Saved", + w=label_w, + )) + print(" {:<{w}} {:>10} {:>10} {:>9} {:>9}".format( + "-" * label_w, "-" * 10, "-" * 10, "-" * 9, "-" * 9, + w=label_w, + )) + for label, orig, fast, speedup, saved in rows: + print(" {:<{w}} {:>9.3f}s {:>9.3f}s {:>8.1f}x {:>8.3f}s".format( + label, orig, fast, speedup, saved, + w=label_w, + )) + print(" {:<{w}} {:>10} {:>10} {:>9} {:>9}".format( + "-" * label_w, "-" * 10, "-" * 10, "-" * 9, "-" * 9, + w=label_w, + )) + + total_orig = sum(r[1] for r in rows) + total_fast = sum(r[2] for r in rows) + total_speedup = total_orig / total_fast if total_fast > 0 else float("inf") + total_saved = total_orig - total_fast + print(" {:<{w}} {:>9.3f}s {:>9.3f}s {:>8.1f}x {:>8.3f}s".format( + "TOTAL", total_orig, total_fast, total_speedup, total_saved, + w=label_w, + )) + print() + + _csrf_re = re.compile( + r'(name="csrfmiddlewaretoken"\s+value=")[^"]*(")' + ) + + @classmethod + def _normalise(cls, html): + return cls._csrf_re.sub(r'\1CSRF_TOKEN\2', html) + + def _get_responses(self, basket): + orig_url = "/packages/baskets/{}/".format(basket.pk) + fast_url = "/packages/baskets/{}/fast/".format(basket.pk) + + t0 = time.perf_counter() + orig_resp = self.client.get(orig_url) + orig_time = time.perf_counter() - t0 + + t0 = time.perf_counter() + fast_resp = self.client.get(fast_url) + fast_time = time.perf_counter() - t0 + + self.assertEqual( + orig_resp.status_code, 200, + "Original view returned {}".format(orig_resp.status_code), + ) + self.assertEqual( + fast_resp.status_code, 200, + "Fast view returned {}".format(fast_resp.status_code), + ) + return orig_resp, fast_resp, orig_time, fast_time + + def _compare_html(self, basket, label=""): + orig_resp, fast_resp, orig_time, fast_time = self._get_responses( + basket, + ) + orig_html = self._normalise(orig_resp.content.decode("utf-8")) + fast_html = self._normalise(fast_resp.content.decode("utf-8")) + + display_label = label or basket.name + self.__class__._timing_results.append( + (display_label, orig_time, fast_time), + ) + + self.assertEqual( + orig_html, fast_html, + "Rendered HTML differs between view_basket and fast_view_basket", + ) + + +class TestRenderedOutputComparison( + BasketFixtureMixin, _TimingTableMixin, TestCase, +): + """ + End-to-end test that hits both /packages/baskets// and + /packages/baskets//fast/ via Django's test Client and compares + the rendered HTML output byte-for-byte (after normalising CSRF tokens). + """ + + _timing_title = "Authenticated User Timing" + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls._init_timing() + + @classmethod + def tearDownClass(cls): + cls._print_timing_table() + super().tearDownClass() + + def setUp(self): + self.client.login(username="testuser", password="testpass123") + session = self.client.session + session["country"] = self.country_gbp.code + session.save() + + def test_single_package(self): + basket = models.Basket.objects.create( + name="1 pkg", account=self.user, active=True, + ) + basket.packages.add(self.pkg_all_combos) + self._compare_html(basket, label="1 pkg (auth)") + + def test_five_packages(self): + basket = models.Basket.objects.create( + name="5 pkg", account=self.user, active=True, + ) + basket.packages.add(*self.gbp_only_packages[:5]) + self._compare_html(basket, label="5 pkgs GBP-only (auth)") + + @patch("package.currency.convert", return_value=1.25) + def test_ten_packages(self, mock_convert): + basket = models.Basket.objects.create( + name="10 pkg", account=self.user, active=True, + ) + basket.packages.add(*self.all_packages[:10]) + self._compare_html(basket, label="10 pkgs (auth)") + + @patch("package.currency.convert", return_value=1.25) + def test_twenty_packages(self, mock_convert): + basket = models.Basket.objects.create( + name="20 pkg", account=self.user, active=True, + ) + basket.packages.add(*self.all_packages[:20]) + basket.meta_packages.add(*self.all_metas[:5]) + self._compare_html(basket, label="20 pkgs + 5 metas (auth)") + + @patch("package.currency.convert", return_value=1.25) + def test_thirty_packages_with_metas(self, mock_convert): + basket = models.Basket.objects.create( + name="30 pkg", account=self.user, active=True, + ) + basket.packages.add(*self.all_packages[:30]) + basket.meta_packages.add(*self.all_metas[:7]) + self._compare_html(basket, label="30 pkgs + 7 metas (auth)") + + @patch("package.currency.convert", return_value=1.25) + def test_full_basket(self, mock_convert): + """All 50 packages + all 10 metas.""" + basket = models.Basket.objects.create( + name="Full", account=self.user, active=True, + ) + basket.packages.add(*self.all_packages) + basket.meta_packages.add(*self.all_metas) + self._compare_html(basket, label="50 pkgs + 10 metas (auth)") + + @patch("package.currency.convert", return_value=1.25) + def test_meta_only(self, mock_convert): + basket = models.Basket.objects.create( + name="Meta only", account=self.user, active=True, + ) + basket.meta_packages.add(self.meta_18, self.meta_15) + self._compare_html(basket, label="2 metas / 33 pkgs (auth)") + + @patch("package.currency.convert", return_value=1.25) + def test_cross_currency(self, mock_convert): + basket = models.Basket.objects.create( + name="Cross", account=self.user, active=True, + ) + basket.packages.add( + self.pkg_all_combos, self.pkg_choice_usd, self.pkg_fte_eur, + ) + self._compare_html(basket, label="3 pkgs cross-currency (auth)") + + def test_choice_only(self): + basket = models.Basket.objects.create( + name="Choice", account=self.user, active=True, + ) + basket.packages.add(self.pkg_simple_choice) + self._compare_html(basket, label="1 choice pkg (auth)") + + @patch("package.currency.convert", return_value=1.25) + def test_all_metas_no_direct(self, mock_convert): + """All 10 meta packages, no direct packages.""" + basket = models.Basket.objects.create( + name="All metas", account=self.user, active=True, + ) + basket.meta_packages.add(*self.all_metas) + self._compare_html(basket, label="10 metas / no direct (auth)") + + +class TestRenderedOutputSessionComparison( + BasketFixtureMixin, _TimingTableMixin, TestCase, +): + """Same rendered-output comparison for anonymous/session-based users.""" + + _timing_title = "Anonymous/Session User Timing" + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls._init_timing() + + @classmethod + def tearDownClass(cls): + cls._print_timing_table() + super().tearDownClass() + + def setUp(self): + session = self.client.session + session["country"] = self.country_gbp.code + session["currency"] = self.country_gbp.pk + session["fte"] = 12000 + session.save() + + def _get_session_key(self): + return self.client.session.session_key + + def test_session_single(self): + basket = models.Basket.objects.create( + name="Sess 1", session_id=self._get_session_key(), active=True, + ) + basket.packages.add(self.pkg_all_combos) + self._compare_html(basket, label="1 pkg (anon)") + + def test_session_five(self): + basket = models.Basket.objects.create( + name="Sess 5", session_id=self._get_session_key(), active=True, + ) + basket.packages.add(*self.gbp_only_packages[:5]) + self._compare_html(basket, label="5 pkgs (anon)") + + @patch("package.currency.convert", return_value=1.25) + def test_session_twenty(self, mock_convert): + basket = models.Basket.objects.create( + name="Sess 20", session_id=self._get_session_key(), active=True, + ) + basket.packages.add(*self.all_packages[:20]) + basket.meta_packages.add(*self.all_metas[:5]) + self._compare_html(basket, label="20 pkgs + 5 metas (anon)") + + @patch("package.currency.convert", return_value=1.25) + def test_session_thirty(self, mock_convert): + basket = models.Basket.objects.create( + name="Sess 30", session_id=self._get_session_key(), active=True, + ) + basket.packages.add(*self.all_packages[:30]) + basket.meta_packages.add(*self.all_metas[:7]) + self._compare_html(basket, label="30 pkgs + 7 metas (anon)") + + @patch("package.currency.convert", return_value=1.25) + def test_session_full(self, mock_convert): + basket = models.Basket.objects.create( + name="Sess full", session_id=self._get_session_key(), active=True, + ) + basket.packages.add(*self.all_packages) + basket.meta_packages.add(*self.all_metas) + self._compare_html(basket, label="50 pkgs + 10 metas (anon)") + + @patch("package.currency.convert", return_value=1.25) + def test_session_all_metas(self, mock_convert): + basket = models.Basket.objects.create( + name="Sess metas", session_id=self._get_session_key(), active=True, + ) + basket.meta_packages.add(*self.all_metas) + self._compare_html(basket, label="10 metas / no direct (anon)") diff --git a/src/package/translation.py b/src/package/translation.py new file mode 100644 index 0000000..654e22f --- /dev/null +++ b/src/package/translation.py @@ -0,0 +1,12 @@ +from modeltranslation.translator import translator, TranslationOptions + +from package import models + + +class BandingTypeTranslation(TranslationOptions): + fields = ( + 'name', + 'description', + ) + +translator.register(models.BandingType, BandingTypeTranslation) diff --git a/src/package/urls.py b/src/package/urls.py index ff9dbc5..2d307b4 100644 --- a/src/package/urls.py +++ b/src/package/urls.py @@ -8,6 +8,7 @@ path('/info/', views.package_info, name='package_info'), path('baskets/', views.view_baskets, name='basket_list'), path('baskets//', views.view_basket, name='basket_detail'), + path('baskets//fast/', views.fast_view_basket, name='fast_basket_detail'), path('baskets//remove//', views.remove_from_basket, name='basket_remove_from_basket', @@ -158,4 +159,5 @@ media_views.list_media_files, name='media_files'), path('/initiative//media_files//', media_views.download_media_file, name='download_media_file'), + path('change_country/', views.change_session_country, name='change_session_country'), ] diff --git a/src/package/utils.py b/src/package/utils.py index edc1f5e..8ac40c8 100644 --- a/src/package/utils.py +++ b/src/package/utils.py @@ -186,41 +186,63 @@ def get_price_for_package( return 0, None -def fetch_price(banding, country, explanation) -> tuple[str, "models.Price"]: +def fetch_price( + banding, country, explanation, price_cache=None, +) -> tuple[str, "models.Price"]: """ This function is used to fetch a price :param banding: the banding to fetch the price for :param country: the country to fetch the price for :param explanation: the explanation of the price calculation + :param price_cache: optional pre-fetched price lookup :return: a tuple of the explanation and the price """ explanation += "Trying to fetch {} for {}.\n".format(banding, country) - price = models.Price.objects.get( - banding=banding, - country=country, - ) + if price_cache is not None: + country_id = country.pk if country else None + price = price_cache.get((banding.pk, country_id)) + if price is None: + raise models.Price.DoesNotExist + else: + price = models.Price.objects.get( + banding=banding, + country=country, + ) return explanation, price -def use_default_pricing(banding, explanation) -> tuple[str, "models.Price"]: +def use_default_pricing( + banding, explanation, price_cache=None, +) -> tuple[str, "models.Price"]: """ This function is used to use the default pricing :param banding: the banding to use the default pricing for :param explanation: the explanation of the price calculation + :param price_cache: optional pre-fetched price lookup :return: a tuple of the explanation and the price """ explanation += "Trying to fetch {} for {} (default).\n".format( banding, banding.package.default_country ) - price = models.Price.objects.get( - banding=banding, - country=banding.package.default_country, - ) + if price_cache is not None: + default_country_id = ( + banding.package.default_country_id + if banding.package.default_country_id + else None + ) + price = price_cache.get((banding.pk, default_country_id)) + if price is None: + raise models.Price.DoesNotExist + else: + price = models.Price.objects.get( + banding=banding, + country=banding.package.default_country, + ) return explanation, price def fallback_to_default( - banding, bandings, explanation, fte, package + banding, bandings, explanation, fte, package, use_list=False, ) -> tuple["models.Banding", str]: """ This function is used to fall back to the default pricing @@ -229,6 +251,7 @@ def fallback_to_default( :param explanation: the explanation of the price calculation :param fte: the FTE to fall back to the default pricing for :param package: the package to fall back to the default pricing for + :param use_list: if True, bandings is a Python list, filter in-memory :return: a tuple of the banding and the explanation """ explanation += ( @@ -236,12 +259,17 @@ def fallback_to_default( "package because we couldn't find one in the user's " "preferred currency.\n" ) - banding = bandings.filter( - Q(vocab__upper_limit=None) | Q(vocab__upper_limit__gte=fte), - package=package, - banding_type=package.banding_type, - vocab__lower_limit__lte=fte, - ).first() + if use_list: + banding = _filter_bandings_by_fte( + bandings, package.pk, package.banding_type_id, fte + ) + else: + banding = bandings.filter( + Q(vocab__upper_limit=None) | Q(vocab__upper_limit__gte=fte), + package=package, + banding_type=package.banding_type, + vocab__lower_limit__lte=fte, + ).first() return banding, explanation @@ -277,7 +305,7 @@ def get_user( def get_band_filter( - band_filter, country, explanation, package + band_filter, country, explanation, package, bandings_cache=None, ) -> tuple[ "models.Banding", "models.QuerySet[models.Banding]", "models.Country", str ]: @@ -287,6 +315,7 @@ def get_band_filter( :param country: the country :param explanation: the explanation of the price calculation :param package: the package + :param bandings_cache: optional pre-fetched bandings lookup :return: a tuple of the band filter, country and explanation """ band_filter, explanation = test_country( @@ -294,18 +323,21 @@ def get_band_filter( ) if band_filter: bandings, explanation = handle_band_filter( - band_filter, explanation, package + band_filter, explanation, package, + bandings_cache=bandings_cache, ) else: # If we get here, we need to check for a catch-all price band_filter, bandings, country, explanation = check_for_catch_all( - band_filter, country, explanation, package + band_filter, country, explanation, package, + bandings_cache=bandings_cache, ) return band_filter, bandings, country, explanation def user_without_banding( - band_filter, banding, banding_choice, bandings, explanation, package + band_filter, banding, banding_choice, bandings, explanation, package, + use_list=False, ) -> tuple["models.Banding", str]: """ This function is used to handle a user without a banding @@ -315,14 +347,21 @@ def user_without_banding( :param bandings: the bandings :param explanation: the explanation of the price calculation :param package: the package + :param use_list: if True, bandings is a Python list, filter in-memory :return: a tuple of the banding and the explanation """ if banding_choice: - banding = bandings.get( - banding_type=band_filter.banding_type, - package=package, - vocab=banding_choice, - ) + if use_list: + banding = _find_banding_by_vocab( + bandings, band_filter.banding_type_id, + package.pk, banding_choice, + ) + else: + banding = bandings.get( + banding_type=band_filter.banding_type, + package=package, + vocab=banding_choice, + ) else: explanation += ( "The user has not set their banding for this " @@ -333,7 +372,7 @@ def user_without_banding( def check_default_currency_and_fte( - band_filter, bandings, explanation, fte, package + band_filter, bandings, explanation, fte, package, use_list=False, ) -> tuple["models.Banding", str]: """ This function is used to check the default currency and FTE @@ -342,23 +381,29 @@ def check_default_currency_and_fte( :param explanation: the explanation of the price calculation :param fte: the FTE :param package: the package + :param use_list: if True, bandings is a Python list, filter in-memory :return: a tuple of the banding and the explanation """ explanation += ( "We have found an FTE pricing in the customer's " "preferred currency.\n" ) - banding = bandings.filter( - Q(vocab__upper_limit=None) | Q(vocab__upper_limit__gte=fte), - package=package, - banding_type=band_filter.banding_type, - vocab__lower_limit__lte=fte, - ).first() + if use_list: + banding = _filter_bandings_by_fte( + bandings, package.pk, band_filter.banding_type_id, fte + ) + else: + banding = bandings.filter( + Q(vocab__upper_limit=None) | Q(vocab__upper_limit__gte=fte), + package=package, + banding_type=band_filter.banding_type, + vocab__lower_limit__lte=fte, + ).first() return banding, explanation def check_for_catch_all( - band_filter, country, explanation, package + band_filter, country, explanation, package, bandings_cache=None, ) -> tuple[ "models.Banding", "models.QuerySet[models.Banding]", "models.Country", str ]: @@ -368,6 +413,7 @@ def check_for_catch_all( :param country: the country :param explanation: the explanation of the price calculation :param package: the package + :param bandings_cache: optional pre-fetched bandings lookup :return: a tuple of the band filter, bandings, country and explanation """ if not band_filter: @@ -379,18 +425,29 @@ def check_for_catch_all( if band_filter: bandings, explanation = explain_catch_all_band_filter( - band_filter, explanation, package + band_filter, explanation, package, + bandings_cache=bandings_cache, + ) + else: + if bandings_cache is not None: + bandings = bandings_cache.get( + (package.pk, package.banding_type_id), [] + ) + else: + bandings = models.Banding.objects.filter( + package=package, + banding_type=package.banding_type, + ) + else: + if bandings_cache is not None: + bandings = bandings_cache.get( + (package.pk, package.banding_type_id), [] ) else: bandings = models.Banding.objects.filter( package=package, banding_type=package.banding_type, ) - else: - bandings = models.Banding.objects.filter( - package=package, - banding_type=package.banding_type, - ) return band_filter, bandings, country, explanation @@ -445,13 +502,14 @@ def find_user_in_database( def explain_catch_all_band_filter( - band_filter, explanation, package + band_filter, explanation, package, bandings_cache=None, ) -> tuple["models.Banding", str]: """ This function is used to explain a catch-all band filter :param band_filter: the band filter :param explanation: the explanation of the price calculation :param package: the package + :param bandings_cache: optional pre-fetched bandings lookup :return: a tuple of the banding and the explanation """ explanation += ( @@ -459,10 +517,15 @@ def explain_catch_all_band_filter( "can handle this: " "{}\n".format(band_filter) ) - bandings = models.Banding.objects.filter( - package=package, - banding_type=band_filter.banding_type, - ) + if bandings_cache is not None: + bandings = bandings_cache.get( + (package.pk, band_filter.banding_type_id), [] + ) + else: + bandings = models.Banding.objects.filter( + package=package, + banding_type=band_filter.banding_type, + ) return bandings, explanation @@ -493,23 +556,29 @@ def match_price_to_code( def handle_band_filter( - band_filter, explanation, package + band_filter, explanation, package, bandings_cache=None, ) -> tuple["models.Banding", str]: """ This function is used to handle a band filter :param band_filter: the band filter :param explanation: the explanation of the price calculation :param package: the package + :param bandings_cache: optional pre-fetched bandings lookup :return: a tuple of the banding and the explanation """ explanation += ( "We found a band filter that can handle this: " "{}\n".format(band_filter) ) - bandings = models.Banding.objects.filter( - package=package, - banding_type=band_filter.banding_type, - ) + if bandings_cache is not None: + bandings = bandings_cache.get( + (package.pk, band_filter.banding_type_id), [] + ) + else: + bandings = models.Banding.objects.filter( + package=package, + banding_type=band_filter.banding_type, + ) return bandings, explanation @@ -575,7 +644,9 @@ def unlink_frozen_doc(order) -> None: os.unlink(file_path) -def generate_package_docs_zip(order, request) -> "models.FrozenPackageDocument": +def generate_package_docs_zip( + order, request +) -> "models.FrozenPackageDocument": """ This function is used to generate a zip file of the documents for a given :param order: the order to generate the zip file for @@ -755,9 +826,11 @@ def send_new_order_notification( "order": order, } email_template.send( - to=order.associated_user.email - if order.associated_user - else order.email_address, + to=( + order.associated_user.email + if order.associated_user + else order.email_address + ), context=context, attachments=[quote_path, accept_path], ) @@ -884,7 +957,7 @@ def format_price(cost, currency) -> str: :return: the formatted price """ try: - return babel.numbers.format_currency(cost, currency) + return babel.numbers.format_currency(cost, currency, locale="en_US") except ( babel.numbers.UnknownCurrencyError, babel.numbers.UnknownCurrencyFormatError, @@ -1023,20 +1096,35 @@ def generate_doc( ) if order.converted_currency and order.converted_value: + processing_fee_calc = order.platform_fee * order.term + processing_fee = format_price( - order.platform_fee, order.converted_currency + processing_fee_calc, order.converted_currency ) total = "{}".format( format_price( - (order.converted_value + order.platform_fee), + ( + order.converted_value * order.term + + order.platform_fee * order.term + ), order.converted_currency, ) ) else: costs, currency_totals = order.basket.cost(identifier, identifier_type) for k, v in currency_totals.items(): - processing_fee = format_price(order.platform_fee, k) - total = "{}".format(format_price((v + order.platform_fee), k)) + processing_fee = format_price( + int(order.platform_fee) * int(order.term), k + ) + total = "{}".format( + format_price( + ( + int(v) * int(order.term) + + int(order.platform_fee) * int(order.term) + ), + k, + ) + ) css = [ os.path.join( settings.BASE_DIR, "static", "frontend", "css", "bootstrap.min.css" @@ -1101,6 +1189,10 @@ def generate_acceptance_doc( ) order_doc.add_paragraph(f"Contact Email: {order.email_address}") + order_doc.add_paragraph( + "Please note, we may pass your name and email address to the Provider(s) you have chosen to support. If you do not want us to pass you on your details please make a note next to your contact name." + ) + order_doc.add_paragraph( "You have selected the following offers to potentially support: " ) @@ -1114,7 +1206,7 @@ def generate_acceptance_doc( if order.converted_currency and order.converted_value: order_doc.add_paragraph( - "Offers: {} {}".format( + "Annual Subscription Cost: {} {}".format( order.converted_currency, order.converted_value ) ) @@ -1203,6 +1295,10 @@ def generate_acceptance_doc( "In order to recognise the support it receives, the OBC would, where possible, like to list Supporting Institutions on its website and publicise this support via other channels (e.g. in newsletters, on social media). Do you give permission for the OBC to acknowledge your Institution’s support in this way?", ) order_doc.add_paragraph("Yes / No") + add_bold_paragraph( + order_doc, + "Do you give permission for the OBC and/or supported initiatives to use your institution's logo to acknowledge your institution’s support on their channels -- including on respective websites and social media channels, as well as in presentations? ", + ) add_bold_paragraph( order_doc, "Once we have reviewed this form, would you like the OBC to invoice you immediately, or should we contact you to confirm further invoicing details (e.g. obtain a purchase order)? (Delete as appropriate)", @@ -1266,3 +1362,429 @@ def get_user_currency(identifier, identifier_type) -> "models.Country": country = models.Country.objects.filter(pk=country_pk).first() return country + + +def add_pre_calc_to_objects(country_code, packages): + country = models.Country.objects.filter(code=country_code).first() + + for package in packages: + try: + package.pre_calc = models.PreCalcMinMax.objects.get( + country=country, + package=package, + ) + except models.PreCalcMinMax.DoesNotExist: + try: + catch_all_country = models.Country.objects.get( + catch_all=True, + currency=country.currency, + ) + package.pre_calc = models.PreCalcMinMax.objects.get( + country=catch_all_country, + package=package, + ) + except ( + models.Country.DoesNotExist, + models.PreCalcMinMax.DoesNotExist, + ): + package.pre_calc = models.PreCalcMinMax.objects.get( + country=package.default_country, + package=package, + ) + return packages + + +def add_pre_calc_to_meta_objects(country_code, packages): + country = models.Country.objects.filter(code=country_code).first() + + for package in packages: + try: + package.pre_calc = models.PreCalcMinMax.objects.get( + country=country, + meta_package=package, + ) + except models.PreCalcMinMax.DoesNotExist: + try: + catch_all_country = models.Country.objects.get( + catch_all=True, + currency=country.currency, + ) + package.pre_calc = models.PreCalcMinMax.objects.get( + country=catch_all_country, + meta_package=package, + ) + except ( + models.Country.DoesNotExist, + models.PreCalcMinMax.DoesNotExist, + ): + if hasattr(package, "default_country"): + package.pre_calc = models.PreCalcMinMax.objects.get( + country=package.default_country, + meta_package=package, + ) + return packages + + +# --------------------------------------------------------------------------- +# In-memory filtering helpers for pre-fetched banding lists. +# --------------------------------------------------------------------------- + + +def _filter_bandings_by_fte(bandings, package_id, banding_type_id, fte): + """Filter a pre-fetched bandings list by FTE range (mirrors queryset logic).""" + for b in bandings: + if b.package_id != package_id or b.banding_type_id != banding_type_id: + continue + if not b.vocab: + continue + if b.vocab.lower_limit is not None and b.vocab.lower_limit > fte: + continue + if ( + b.vocab.upper_limit is not None + and b.vocab.upper_limit < fte + ): + continue + return b + return None + + +def _find_banding_by_vocab(bandings, banding_type_id, package_id, vocab_pk): + """Find a single banding matching banding_type, package and vocab pk.""" + for b in bandings: + if ( + b.banding_type_id == banding_type_id + and b.package_id == package_id + and b.vocab_id == vocab_pk + ): + return b + raise models.Banding.DoesNotExist + + +# --------------------------------------------------------------------------- +# fast_ prefixed optimized versions of pricing / basket utility functions. +# These eliminate redundant DB lookups by accepting pre-resolved values. +# --------------------------------------------------------------------------- + + +def fast_get_user_currency( + identifier, identifier_type, country=None +) -> "models.Country": + """ + Optimized version of get_user_currency. + Returns pre-resolved country when provided; falls back to DB lookup. + """ + if country is not None: + return country + if identifier_type == "user": + user = account_models.User.objects.get(username=identifier) + return user.profile.default_currency + else: + country_pk = identifier.get("currency", None) + return models.Country.objects.filter(pk=country_pk).first() + + +def fast_convert_currency_totals( + request, identifier_type, identifier, totals, country=None +) -> ( + tuple[None, dict, None, None, None] + | tuple[str, dict, str, Decimal, Decimal] +): + """ + Optimized version of convert_currency_totals. + Accepts pre-resolved country to avoid redundant user lookup. + """ + if country is None: + if identifier_type == "user": + user = account_models.User.objects.get(username=identifier) + country = user.profile.default_currency + else: + country_pk = identifier.get("currency", None) + country = models.Country.objects.filter(pk=country_pk).first() + + from package import currency as convert_currency + + if country: + amounts = [] + for currency, value in totals.items(): + if currency == country.currency: + amounts.append(value) + else: + try: + converted_currency = convert_currency.convert( + currency_from=currency, + currency_to=country.currency, + value=value, + ) + amounts.append(converted_currency) + except ValueError: + country = models.Country.objects.get( + currency=request.site.fallback_currency, + ) + converted_currency = convert_currency.convert( + currency_from=currency, + currency_to=country.currency, + value=value, + ) + amounts.append(converted_currency) + + currency_conversion_total = Decimal(round(sum(amounts), 2)) + site_percentage, site_percentage_value = calculate_site_percentage( + request, {country.currency: currency_conversion_total} + ) + + return ( + site_percentage, + {country.currency: currency_conversion_total}, + country.currency, + currency_conversion_total, + site_percentage_value, + ) + return None, {}, None, None, None + + +def fast_find_user_in_database( + band_filter, explanation, identifier, package, + account_banding_choices=None, +) -> tuple["models.Banding", str, Decimal]: + """ + Optimized version of find_user_in_database. + Accepts pre-fetched account_banding_choices dict to avoid + per-package DB queries. + """ + explanation += "We are looking up a user from the database\n" + fte = identifier.profile.fte + + if account_banding_choices is not None: + bt_id = ( + band_filter.banding_type_id if band_filter + else package.banding_type_id + ) + banding_choice_obj = account_banding_choices.get(bt_id) + banding_choice = ( + banding_choice_obj.banding_type_vocab.pk + if banding_choice_obj else None + ) + else: + if band_filter: + banding_choice = account_models.AccountBandingChoices.objects.filter( + account=identifier, + banding_type=band_filter.banding_type, + ).first() + else: + banding_choice = account_models.AccountBandingChoices.objects.filter( + account=identifier, + banding_type=package.banding_type, + ).first() + if banding_choice: + banding_choice = banding_choice.banding_type_vocab.pk + + return banding_choice, explanation, fte + + +def fast_get_user( + band_filter, + banding_choice, + explanation, + fte, + identifier, + identifier_type, + package, + account_banding_choices=None, +) -> tuple[str, str, Decimal]: + """ + Optimized version of get_user. + Passes account_banding_choices through to avoid per-package queries. + """ + if identifier_type == "user": + banding_choice, explanation, fte = fast_find_user_in_database( + band_filter, explanation, identifier, package, + account_banding_choices=account_banding_choices, + ) + elif identifier_type == "session": + banding_choice, explanation, fte = find_user_from_session( + band_filter, explanation, identifier, package + ) + return banding_choice, explanation, fte + + +def fast_test_country( + band_filter, country, explanation, package +) -> tuple["models.Banding", str]: + """ + Optimized version of test_country. + Uses fast_price_bandings (cached_property, scoped queries). + """ + if country: + explanation += ( + "The user in question prefers the currency " + "{}\n".format(country) + ) + country_prices = package.fast_price_bandings + + for key, val in country_prices.items(): + for inner_key, inner_val in val.items(): + if inner_key == country: + band_filter = key.banding_type_entry + return band_filter, explanation + + +def fast_match_price_to_code( + band_filter, country, explanation, package +) -> tuple["models.Banding", "models.Country", str]: + """ + Optimized version of match_price_to_code. + Uses fast_price_bandings and fixes duplicate nested loop. + """ + explanation += "Attempting to find a catch-all price for {}\n".format( + country + ) + country_prices = package.fast_price_bandings + for key, val in country_prices.items(): + for inner_key, inner_val in val.items(): + if inner_key.catch_all: + if inner_key.currency == country.currency: + explanation += "Using {}\n".format(inner_key) + band_filter = key.banding_type_entry + country = inner_key + return band_filter, country, explanation + + +def fast_get_band_filter( + band_filter, country, explanation, package, + bandings_cache=None, +) -> tuple[ + "models.Banding", "models.QuerySet[models.Banding]", "models.Country", str +]: + """ + Optimized version of get_band_filter. + Uses fast_ variants that use fast_price_bandings. + """ + band_filter, explanation = fast_test_country( + band_filter, country, explanation, package + ) + if band_filter: + bandings, explanation = handle_band_filter( + band_filter, explanation, package, + bandings_cache=bandings_cache, + ) + else: + # Check for a catch-all price + if not band_filter: + if country: + band_filter, country, explanation = fast_match_price_to_code( + band_filter, country, explanation, package + ) + if band_filter: + bandings, explanation = explain_catch_all_band_filter( + band_filter, explanation, package, + bandings_cache=bandings_cache, + ) + else: + if bandings_cache is not None: + bandings = bandings_cache.get( + (package.pk, package.banding_type_id), [] + ) + else: + bandings = models.Banding.objects.filter( + package=package, + banding_type=package.banding_type, + ) + else: + if bandings_cache is not None: + bandings = bandings_cache.get( + (package.pk, package.banding_type_id), [] + ) + else: + bandings = models.Banding.objects.filter( + package=package, + banding_type=package.banding_type, + ) + return band_filter, bandings, country, explanation + + +def fast_get_price_for_package( + package, identifier, identifier_type, country=None, + account_banding_choices=None, + bandings_cache=None, price_cache=None, +) -> tuple[int, None] | tuple["models.Price", "models.Banding"]: + """ + Optimized version of get_price_for_package. + Uses fast_price_bandings (cached_property, scoped to package). + Accepts pre-fetched account_banding_choices dict. + Accepts pre-fetched bandings_cache and price_cache dicts. + Does not support explain mode (use original for debugging). + """ + band_filter = None + use_list = bandings_cache is not None + + band_filter, bandings, country, _ = fast_get_band_filter( + band_filter, country, "", package, + bandings_cache=bandings_cache, + ) + + banding, banding_choice = None, None + fte = None + + banding_choice, _, fte = fast_get_user( + band_filter, + banding_choice, + "", + fte, + identifier, + identifier_type, + package, + account_banding_choices=account_banding_choices, + ) + + if not fte: + return 0, None + + if band_filter and band_filter.banding_type.is_fte: + banding, _ = check_default_currency_and_fte( + band_filter, bandings, "", fte, package, + use_list=use_list, + ) + elif band_filter and not band_filter.banding_type.is_fte: + try: + banding, _ = user_without_banding( + band_filter, banding, banding_choice, bandings, "", package, + use_list=use_list, + ) + except (ValueError, models.Banding.DoesNotExist): + return 0, None + elif package.banding_type.is_fte: + banding, _ = fallback_to_default( + banding, bandings, "", fte, package, + use_list=use_list, + ) + elif banding_choice: + try: + if use_list: + banding = _find_banding_by_vocab( + bandings, package.banding_type_id, + package.pk, banding_choice, + ) + else: + banding = bandings.get( + banding_type=package.banding_type, + package=package, + vocab=banding_choice, + ) + except (ValueError, models.Banding.DoesNotExist): + return 0, None + + if banding: + try: + _, price = fetch_price( + banding, country, "", price_cache=price_cache, + ) + except models.Price.DoesNotExist: + try: + _, price = use_default_pricing( + banding, "", price_cache=price_cache, + ) + except models.Price.DoesNotExist: + return 0, None + return price, banding + else: + return 0, None diff --git a/src/package/views.py b/src/package/views.py index ebc2702..dcefc5a 100644 --- a/src/package/views.py +++ b/src/package/views.py @@ -26,6 +26,7 @@ from invoicing import models as invoicing_models from package import models, forms, utils from vocab import models as vocab_models +from cms import models as cms_models def initiative_list(request) -> HttpResponse: @@ -44,11 +45,19 @@ def initiative_list(request) -> HttpResponse: for subject in package.subjects.all(): subject_pks.append(subject.pk) - subjects = vocab_models.SubjectVocab.objects.filter(pk__in=set(subject_pks)) + subjects = vocab_models.SubjectVocab.objects.filter( + pk__in=set(subject_pks) + ) initiatives = initiative_models.Initiative.objects.filter( active=True, ) + if request.session.get("country", None): + packages = utils.add_pre_calc_to_objects( + request.session.get("country"), + packages, + ) + initiative = request.GET.get("initiative") subject = request.GET.get("subject") sort = request.GET.get("sort") @@ -85,6 +94,9 @@ def initiative_list(request) -> HttpResponse: "standards": vocab_models.StandardVocab.objects.all(), "get_standards": standards, "initiatives": initiatives, + "country_form": forms.CountryForm( + session_country_code=request.session["country"] + ), } return render( request, @@ -102,11 +114,21 @@ def collective_list(request) -> HttpResponse: meta_packages = models.MetaPackage.objects.filter( active=True, ).order_by("-recommended", "name") + + if request.session.get("country", None): + utils.add_pre_calc_to_meta_objects( + request.session.get("country"), + meta_packages, + ) + template = "base/frontend/quote/collective_list.html" context = { "meta_packages": meta_packages, "search_on": False, "collectives_only": True, + "country_form": forms.CountryForm( + session_country_code=request.session.get("country") + ), } return render( request, @@ -127,7 +149,9 @@ def package_info(request, package_id) -> HttpResponse: package_type = "package" except models.Package.DoesNotExist: try: - package = models.MetaPackage.objects.get(pk=package_id, active=True) + package = models.MetaPackage.objects.get( + pk=package_id, active=True + ) package_type = "meta" except models.MetaPackage.DoesNotExist: raise Http404 @@ -605,6 +629,9 @@ def view_basket(request, basket_id) -> HttpResponse: ) identifier_type = "user" if request.user.is_authenticated else "session" + # prefetch all CMS text objects to avoid template hits + final_prefetched = {o.key: o for o in cms_models.SiteText.objects.all()} + ( site_percentage, currency_converted, @@ -719,9 +746,9 @@ def view_basket(request, basket_id) -> HttpResponse: # Create the order here. order = models.Order.objects.create( - associated_user=request.user - if request.user.is_authenticated - else None, + associated_user=( + request.user if request.user.is_authenticated else None + ), session_id=request.session.session_key, basket=basket, valid_period="{0} to {1}".format( @@ -754,6 +781,212 @@ def view_basket(request, basket_id) -> HttpResponse: "converted_total": converted_total, "converted_currency": converted_currency, "user_currency": user_currency, + "prefetched": final_prefetched, + } + return render( + request, + template, + context, + ) + + +def fast_view_basket(request, basket_id) -> HttpResponse: + """ + Optimized version of view_basket. + - Resolves user/country once up front + - Prefetches M2M relations on the basket + - Uses fast_ model methods and utility functions + - Passes prefetched SiteText to FastFTEForm + """ + identifier = ( + request.user if request.user.is_authenticated else request.session + ) + identifier_type = "user" if request.user.is_authenticated else "session" + + # --- OPTIMIZATION: resolve country once --- + if identifier_type == "user": + country = request.user.profile.default_currency + else: + country_pk = request.session.get("currency", None) + country = ( + models.Country.objects.filter(pk=country_pk).first() + if country_pk + else None + ) + + # --- OPTIMIZATION: prefetch all CMS text objects once --- + final_prefetched = {o.key: o for o in cms_models.SiteText.objects.all()} + + ( + site_percentage, + currency_converted, + converted_total, + converted_currency, + currency_conversion_total, + site_percentage_value, + ) = (None, None, None, None, None, None) + + # --- OPTIMIZATION: prefetch M2M relations --- + basket_qs = models.Basket.objects.prefetch_related( + "packages", + "packages__bandingtypeentry_set__banding_type", + "meta_packages", + "meta_packages__packages", + ) + + if request.user.is_authenticated: + basket = get_object_or_404( + basket_qs, + pk=basket_id, + account=request.user, + active=True, + ) + else: + basket = get_object_or_404( + basket_qs, + pk=basket_id, + session_id=request.session.session_key, + active=True, + ) + + # --- OPTIMIZATION: fast_ method, pass pre-resolved country --- + banding_types = basket.fast_get_best_bandings_for_form( + identifier, identifier_type, country=country + ) + + # --- OPTIMIZATION: FastFTEForm with prefetched site_texts --- + fte_form = forms.FastFTEForm( + user_session=request.session, + user=request.user if request.user.is_authenticated else None, + banding_types=banding_types, + site_texts=final_prefetched, + ) + + message = utils.check_basket_for_disabled_signups(request, basket) + + if request.POST: + fte_form = forms.FastFTEForm( + request.POST, + user_session=request.session, + user=request.user if request.user.is_authenticated else None, + banding_types=banding_types, + site_texts=final_prefetched, + ) + if fte_form.is_valid(): + fte_form.save() + url = "{}#form_start".format( + reverse( + "fast_basket_detail", + kwargs={"basket_id": basket_id}, + ) + ) + return redirect(url) + + # --- OPTIMIZATION: fast_cost, pass pre-resolved country --- + package_costs, currency_totals = basket.fast_cost( + identifier=identifier, + identifier_type=identifier_type, + country=country, + ) + + # --- OPTIMIZATION: fast_get_user_currency, no DB hit --- + user_currency = utils.fast_get_user_currency( + identifier, identifier_type, country=country + ) + + banding_list = [] + for package in basket.packages.all(): + for obj in package.bandingtypeentry_set.all().order_by( + "banding_type__name" + ): + banding_list.append(obj) + + all_prices_found = True + for price in package_costs: + if not price["banding"] or price["cost"] == 0: + all_prices_found = False + break + + if all_prices_found: + if len(currency_totals) > 1: + # --- OPTIMIZATION: fast_convert, pass pre-resolved country --- + ( + site_percentage, + converted_total, + converted_currency, + currency_conversion_total, + site_percentage_value, + ) = utils.fast_convert_currency_totals( + request=request, + identifier_type=identifier_type, + identifier=identifier, + totals=currency_totals, + country=country, + ) + currency_converted = True + else: + ( + site_percentage, + site_percentage_value, + ) = utils.calculate_site_percentage( + request, + currency_totals, + ) + + # User clicks the proceed button + if "complete" in request.POST: + if ( + not request.site.enable_signup + or not request.site.enable_meta_package_signup + or not request.site.enable_individual_package_signup + ): + if message: + messages.add_message(request, messages.ERROR, message) + return redirect( + reverse( + "fast_basket_detail", + kwargs={"basket_id": basket.pk}, + ) + ) + + order = models.Order.objects.create( + associated_user=( + request.user if request.user.is_authenticated else None + ), + session_id=request.session.session_key, + basket=basket, + valid_period="{0} to {1}".format( + timezone.now().year, timezone.now().year + 1 + ), + order_date=timezone.now(), + converted_currency=converted_currency, + converted_value=currency_conversion_total, + platform_fee=site_percentage_value, + ) + return redirect( + reverse( + "start_checkout", + kwargs={"order_id": order.pk}, + ) + ) + + template = "package/view_basket.html" + context = { + "basket": basket, + "fte_form": fte_form, + "package_costs": package_costs, + "currency_totals": currency_totals, + # --- OPTIMIZATION: fast_ version uses prefetched M2M --- + "conflicting_packages": basket.fast_list_of_conflicting_packages(), + "bandings": banding_list, + "has_all_prices": all_prices_found, + "message": message, + "site_percentage": site_percentage, + "currency_converted": currency_converted, + "converted_total": converted_total, + "converted_currency": converted_currency, + "user_currency": user_currency, + "prefetched": final_prefetched, } return render( request, @@ -918,9 +1151,9 @@ def create_checkout_documents(request, order_id=None) -> HttpResponse: # with the order models.PackageSignup.objects.create( - associated_user=request.user - if request.user.is_authenticated - else None, + associated_user=( + request.user if request.user.is_authenticated else None + ), associated_session=request.session.session_key, associated_order=order, associated_package=package, @@ -964,6 +1197,7 @@ def order_form(request, order_id) -> HttpResponse: order_amount = "" platform_fee = "" total = "" + multiplier = 3 if request.user.is_authenticated: order = get_object_or_404( @@ -983,36 +1217,51 @@ def order_form(request, order_id) -> HttpResponse: identifier = request.session if order.converted_currency and order.converted_value: - order_amount = "Offers: {} {}".format( + if request.POST: + multiplier = request.POST.get("term_length", 3) + else: + multiplier = 3 + + order.converted_value = order.converted_value * int(multiplier) + + # don't set order.platform fee here as we will recalculate at the end + platform_fee_calc = order.platform_fee * int(multiplier) + + order_amount = "{} {}".format( order.converted_currency, order.converted_value ) - platform_fee = "OBC Processing Fee: {} {}".format( - order.converted_currency, order.platform_fee + platform_fee = "{} {}".format( + order.converted_currency, platform_fee_calc ) - total = "Total: {}".format( + total = "{}".format( utils.format_price( - (order.converted_value + order.platform_fee), + (order.converted_value + platform_fee_calc), order.converted_currency, ) ) else: costs, currency_totals = order.basket.cost(identifier, identifier_type) for k, v in currency_totals.items(): - order_amount = "Offers: {}".format(utils.format_price(v, k)) - platform_fee = "OBC Processing Fee: {}".format( - utils.format_price(order.platform_fee, k) - ) - total = "Total: {}".format( - utils.format_price((v + order.platform_fee), k) - ) + if request.POST: + multiplier = request.POST.get("term_length", 3) + else: + multiplier = 3 + + v = v * int(multiplier) + platform_fee_calc = order.platform_fee * int(multiplier) + + order_amount = utils.format_price(v, k) + platform_fee = utils.format_price(platform_fee_calc, k) + + total = utils.format_price((v + platform_fee_calc), k) signups = order.packagesignup_set.all() form = forms.GeneratedForm( order=order, fields_required=True, - email_address=request.user.email - if request.user.is_authenticated - else "", + email_address=( + request.user.email if request.user.is_authenticated else "" + ), ) if request.POST: @@ -1020,11 +1269,15 @@ def order_form(request, order_id) -> HttpResponse: request.POST, order=order, fields_required=True, - email_address=request.user.email - if request.user.is_authenticated - else "", + email_address=( + request.user.email if request.user.is_authenticated else "" + ), ) - if form.is_valid(): + + # check if it's a state change + is_changed = request.GET.get("changed", "") + + if form.is_valid() and is_changed == "": order.save_order_form(form) order.status = "provisional" order.save() @@ -1055,6 +1308,7 @@ def order_form(request, order_id) -> HttpResponse: "order_amount": order_amount, "platform_fee": platform_fee, "total": total, + "multiplier": multiplier, } return render( request, @@ -1425,7 +1679,8 @@ def package_data(request, initiative_id, package_id) -> HttpResponse: # the user has submitted data if "add_new" in request.POST: form_element = get_object_or_404( - models.AggregateFormElement, pk=int(request.POST["form_fields"]) + models.AggregateFormElement, + pk=int(request.POST["form_fields"]), ) obj, created = models.PackageFormElement.objects.get_or_create( @@ -1878,7 +2133,9 @@ def delete_package_banding_currency( @user_is_initiative_manager -def manage_package_bandings(request, package_id, initiative_id) -> HttpResponse: +def manage_package_bandings( + request, package_id, initiative_id +) -> HttpResponse: """ Manage the bandings for a package. :param request: the request @@ -2202,3 +2459,14 @@ def new_order_complete(request, order_id) -> HttpResponse: template, context, ) + + +def change_session_country(request): + if request.POST and "country" in request.POST: + country_id = request.POST.get("country") + try: + country = models.Country.objects.get(pk=country_id) + request.session["country"] = country.code + except models.Country.DoesNotExist: + pass + return redirect(request.POST.get("next")) diff --git a/src/static-assets/css/bootstrap.min.css b/src/static-assets/css/bootstrap.min.css new file mode 100644 index 0000000..b666f88 --- /dev/null +++ b/src/static-assets/css/bootstrap.min.css @@ -0,0 +1,620 @@ +/* Global variables. */ +:root { + /* Set sans-serif & mono fonts */ + --sans-font: -apple-system, BlinkMacSystemFont, "Avenir Next", Avenir, + "Nimbus Sans L", Roboto, "Noto Sans", "Segoe UI", Arial, Helvetica, + "Helvetica Neue", sans-serif; + --mono-font: Consolas, Menlo, Monaco, "Andale Mono", "Ubuntu Mono", monospace; + + /* Default (light) theme */ + --bg: #fff; + --accent-bg: #f5f7ff; + --text: #212121; + --text-light: #585858; + --border: #898EA4; + --accent: #0d47a1; + --code: #d81b60; + --preformatted: #444; + --marked: #ffdd33; + --disabled: #efefef; +} + +/* Dark theme */ +@media (prefers-color-scheme: dark) { + :root { + color-scheme: dark; + --bg: #212121; + --accent-bg: #2b2b2b; + --text: #dcdcdc; + --text-light: #ababab; + --accent: #ffb300; + --code: #f06292; + --preformatted: #ccc; + --disabled: #111; + } + /* Add a bit of transparency so light media isn't so glaring in dark mode */ + img, + video { + opacity: 0.8; + } +} + +/* Reset box-sizing */ +*, *::before, *::after { + box-sizing: border-box; +} + +/* Reset default appearance */ +textarea, +select, +input, +progress { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; +} + +html { + /* Set the font globally */ + font-family: var(--sans-font); + scroll-behavior: smooth; +} + +/* Make the body a nice central block */ +body { + color: var(--text); + background-color: var(--bg); + font-size: 1.15rem; + line-height: 1.5; + display: grid; + grid-template-columns: 1fr min(45rem, 90%) 1fr; + margin: 0; +} +body > * { + grid-column: 2; +} + +/* Make the header bg full width, but the content inline with body */ +body > header { + background-color: var(--accent-bg); + border-bottom: 1px solid var(--border); + text-align: center; + padding: 0 0.5rem 2rem 0.5rem; + grid-column: 1 / -1; +} + +body > header h1 { + max-width: 1200px; + margin: 1rem auto; +} + +body > header p { + max-width: 40rem; + margin: 1rem auto; +} + +/* Add a little padding to ensure spacing is correct between content and header > nav */ +main { + padding-top: 1.5rem; +} + +body > footer { + margin-top: 4rem; + padding: 2rem 1rem 1.5rem 1rem; + color: var(--text-light); + font-size: 0.9rem; + text-align: center; + border-top: 1px solid var(--border); +} + +/* Format headers */ +h1 { + font-size: 3rem; +} + +h2 { + font-size: 2.6rem; + margin-top: 3rem; +} + +h3 { + font-size: 2rem; + margin-top: 3rem; +} + +h4 { + font-size: 1.44rem; +} + +h5 { + font-size: 1.15rem; +} + +h6 { + font-size: 0.96rem; +} + +/* Prevent long strings from overflowing container */ +p, h1, h2, h3, h4, h5, h6 { + overflow-wrap: break-word; +} + +/* Fix line height when title wraps */ +h1, +h2, +h3 { + line-height: 1.1; +} + +/* Reduce header size on mobile */ +@media only screen and (max-width: 720px) { + h1 { + font-size: 2.5rem; + } + + h2 { + font-size: 2.1rem; + } + + h3 { + font-size: 1.75rem; + } + + h4 { + font-size: 1.25rem; + } +} + +/* Format links & buttons */ +a, +a:visited { + color: var(--accent); +} + +a:hover { + text-decoration: none; +} + +button, +[role="button"], +input[type="submit"], +input[type="reset"], +input[type="button"], +label[type="button"] { + border: none; + border-radius: 5px; + background-color: var(--accent); + font-size: 1rem; + color: var(--bg); + padding: 0.7rem 0.9rem; + margin: 0.5rem 0; +} + +button[disabled], +[role="button"][aria-disabled="true"], +input[type="submit"][disabled], +input[type="reset"][disabled], +input[type="button"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][disabled], +select[disabled] { + cursor: not-allowed; +} + +input:disabled, +textarea:disabled, +select:disabled, +button[disabled] { + cursor: not-allowed; + background-color: var(--disabled); + color: var(--text-light) +} + +input[type="range"] { + padding: 0; +} + +/* Set the cursor to '?' on an abbreviation and style the abbreviation to show that there is more information underneath */ +abbr[title] { + cursor: help; + text-decoration-line: underline; + text-decoration-style: dotted; +} + +button:enabled:hover, +[role="button"]:not([aria-disabled="true"]):hover, +input[type="submit"]:enabled:hover, +input[type="reset"]:enabled:hover, +input[type="button"]:enabled:hover, +label[type="button"]:hover { + filter: brightness(1.4); + cursor: pointer; +} + +button:focus-visible:where(:enabled, [role="button"]:not([aria-disabled="true"])), +input:enabled:focus-visible:where( + [type="submit"], + [type="reset"], + [type="button"] +) { + outline: 2px solid var(--accent); + outline-offset: 1px; +} + +/* Format navigation */ +header > nav { + font-size: 1rem; + line-height: 2; + padding: 1rem 0 0 0; +} + +/* Use flexbox to allow items to wrap, as needed */ +header > nav ul, +header > nav ol { + align-content: space-around; + align-items: center; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + list-style-type: none; + margin: 0; + padding: 0; +} + +/* List items are inline elements, make them behave more like blocks */ +header > nav ul li, +header > nav ol li { + display: inline-block; +} + +header > nav a, +header > nav a:visited { + margin: 0 0.5rem 1rem 0.5rem; + border: 1px solid var(--border); + border-radius: 5px; + color: var(--text); + display: inline-block; + padding: 0.1rem 1rem; + text-decoration: none; +} + +header > nav a:hover { + border-color: var(--accent); + color: var(--accent); + cursor: pointer; +} + +/* Reduce nav side on mobile */ +@media only screen and (max-width: 720px) { + header > nav a { + border: none; + padding: 0; + text-decoration: underline; + line-height: 1; + } +} + +/* Consolidate box styling */ +aside, details, pre, progress { + background-color: var(--accent-bg); + border: 1px solid var(--border); + border-radius: 5px; + margin-bottom: 1rem; +} + +aside { + font-size: 1rem; + width: 30%; + padding: 0 15px; + margin-left: 15px; + float: right; +} + +/* Make aside full-width on mobile */ +@media only screen and (max-width: 720px) { + aside { + width: 100%; + float: none; + margin-left: 0; + } +} + +article, fieldset { + border: 1px solid var(--border); + padding: 1rem; + border-radius: 5px; + margin-bottom: 1rem; +} + +article h2:first-child, +section h2:first-child { + margin-top: 1rem; +} + +section { + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); + padding: 2rem 1rem; + margin: 3rem 0; +} + +/* Don't double separators when chaining sections */ +section + section, +section:first-child { + border-top: 0; + padding-top: 0; +} + +section:last-child { + border-bottom: 0; + padding-bottom: 0; +} + +details { + padding: 0.7rem 1rem; +} + +summary { + cursor: pointer; + font-weight: bold; + padding: 0.7rem 1rem; + margin: -0.7rem -1rem; + word-break: break-all; +} + +details[open] > summary + * { + margin-top: 0; +} + +details[open] > summary { + margin-bottom: 0.5rem; +} + +details[open] > :last-child { + margin-bottom: 0; +} + +/* Format tables */ +table { + border-collapse: collapse; + display: block; + margin: 1.5rem 0; + overflow: auto; + width: 100%; +} + +td, +th { + border: 1px solid var(--border); + text-align: left; + padding: 0.5rem; +} + +th { + background-color: var(--accent-bg); + font-weight: bold; +} + +table caption { + font-weight: bold; + margin-bottom: 0.5rem; +} + +/* Format forms */ +textarea, +select, +input { + font-size: inherit; + font-family: inherit; + padding: 0.5rem; + margin-bottom: 0.5rem; + color: var(--text); + background-color: var(--bg); + border: 1px solid var(--border); + border-radius: 5px; + box-shadow: none; + max-width: 100%; + display: inline-block; +} +label { + display: block; +} +textarea:not([cols]) { + width: 100%; +} + +/* Add arrow to drop-down */ +select:not([multiple]) { + background-image: linear-gradient(45deg, transparent 49%, var(--text) 51%), + linear-gradient(135deg, var(--text) 51%, transparent 49%); + background-position: calc(100% - 15px), calc(100% - 10px); + background-size: 5px 5px, 5px 5px; + background-repeat: no-repeat; + padding-right: 25px; +} + +/* checkbox and radio button style */ +input[type="checkbox"], +input[type="radio"] { + vertical-align: middle; + position: relative; + width: min-content; +} + +input[type="checkbox"] + label, +input[type="radio"] + label { + display: inline-block; +} + +input[type="radio"] { + border-radius: 100%; +} + +input[type="checkbox"]:checked, +input[type="radio"]:checked { + background-color: var(--accent); +} + +input[type="checkbox"]:checked::after { + /* Creates a rectangle with colored right and bottom borders which is rotated to look like a check mark */ + content: " "; + width: 0.18em; + height: 0.32em; + border-radius: 0; + position: absolute; + top: 0.05em; + left: 0.17em; + background-color: transparent; + border-right: solid var(--bg) 0.08em; + border-bottom: solid var(--bg) 0.08em; + font-size: 1.8em; + transform: rotate(45deg); +} +input[type="radio"]:checked::after { + /* creates a colored circle for the checked radio button */ + content: " "; + width: 0.25em; + height: 0.25em; + border-radius: 100%; + position: absolute; + top: 0.125em; + background-color: var(--bg); + left: 0.125em; + font-size: 32px; +} + +/* Makes input fields wider on smaller screens */ +@media only screen and (max-width: 720px) { + textarea, + select, + input { + width: 100%; + } +} + +/* Set a height for color input */ +input[type="color"] { + height: 2.5rem; + padding: 0.2rem; +} + +/* do not show border around file selector button */ +input[type="file"] { + border: 0; +} + +/* Misc body elements */ +hr { + border: none; + height: 1px; + background: var(--border); + margin: 1rem auto; +} + +mark { + padding: 2px 5px; + border-radius: 4px; + background-color: var(--marked); +} + +img, +video { + max-width: 100%; + height: auto; + border-radius: 5px; +} + +figure { + margin: 0; + text-align: center; +} + +figcaption { + font-size: 0.9rem; + color: var(--text-light); + margin-bottom: 1rem; +} + +blockquote { + margin: 2rem 0 2rem 2rem; + padding: 0.4rem 0.8rem; + border-left: 0.35rem solid var(--accent); + color: var(--text-light); + font-style: italic; +} + +cite { + font-size: 0.9rem; + color: var(--text-light); + font-style: normal; +} + +dt { + color: var(--text-light); +} + +/* Use mono font for code elements */ +code, +pre, +pre span, +kbd, +samp { + font-family: var(--mono-font); + color: var(--code); +} + +kbd { + color: var(--preformatted); + border: 1px solid var(--preformatted); + border-bottom: 3px solid var(--preformatted); + border-radius: 5px; + padding: 0.1rem 0.4rem; +} + +pre { + padding: 1rem 1.4rem; + max-width: 100%; + overflow: auto; + color: var(--preformatted); +} + +/* Fix embedded code within pre */ +pre code { + color: var(--preformatted); + background: none; + margin: 0; + padding: 0; +} + +/* Progress bars */ +/* Declarations are repeated because you */ +/* cannot combine vendor-specific selectors */ +progress { + width: 100%; +} + +progress:indeterminate { + background-color: var(--accent-bg); +} + +progress::-webkit-progress-bar { + border-radius: 5px; + background-color: var(--accent-bg); +} + +progress::-webkit-progress-value { + border-radius: 5px; + background-color: var(--accent); +} + +progress::-moz-progress-bar { + border-radius: 5px; + background-color: var(--accent); + transition-property: width; + transition-duration: 0.3s; +} + +progress:indeterminate::-moz-progress-bar { + background-color: var(--accent-bg); +} diff --git a/src/static-assets/frontend/css/style.css b/src/static-assets/frontend/css/style.css index bc8d475..d5e2fe1 100644 --- a/src/static-assets/frontend/css/style.css +++ b/src/static-assets/frontend/css/style.css @@ -33,6 +33,11 @@ display: flex; } +.btn-sm +{ + margin-top: 0.5rem; +} + #header { top: 0px; left: 0px; @@ -65,7 +70,7 @@ .navbar-collapse { background: transparent linear-gradient(175deg, var(--obc-dark-blue) 0%, var(--obc-blue) 100%) 0% 0% no-repeat padding-box; color: white; - font: normal normal bold 18px/60px rubrik-edge-new; + font: normal normal bold 18px/60px Rubrik Edge New; letter-spacing: 0px; } @@ -73,13 +78,13 @@ { background-color: transparent; color: white; - font: normal normal bold 18px/25px rubrik-edge-new; + font: normal normal bold 18px/60px Rubrik Edge New; letter-spacing: 0px; } .nav-link { color: white !important; - font: normal normal bold 18px/60px rubrik-edge-new !important; + font: normal normal bold 18px/60px Rubrik Edge New !important; letter-spacing: 0px; margin-left: 5%; padding: 0px; @@ -87,13 +92,13 @@ .nav-link:hover { color: white !important; - font: normal normal bold 18px/60px rubrik-edge-new !important; + font: normal normal bold 18px/60px Rubrik Edge New !important; letter-spacing: 0px; } .dropdown-item, .dropdown-item:hover { color: white !important; - font: normal normal 16px/60px rubrik-edge-new !important; + font: normal normal 16px/60px Rubrik Edge New !important; letter-spacing: 0px; margin-left: 5%; padding: 0px; @@ -114,7 +119,7 @@ } a.nav-small, a.nav-small:hover { - font: normal normal 16px/60px rubrik-edge-new !important; + font: normal normal 16px/60px Rubrik Edge New !important; letter-spacing: 0px; } @@ -171,16 +176,6 @@ a.nav-small, a.nav-small:hover { margin-right: auto; } -.magenta-bg { - margin-top: 0px; - min-height: 600px; - padding-top: 50px; - background-color: var(--obc-magenta); - width: 75%; - margin-left: auto; - margin-right: auto; -} - .home-text { margin-top: 50px; } @@ -263,16 +258,6 @@ button.close, button.close:hover { letter-spacing: 0px; } -a { - color: var(--obc-magenta); - font-weight: bold; -} - -a:hover { - color: var(--obc-blue); - text-decoration: underline; -} - .footer-logo { width: 300%; } @@ -472,7 +457,7 @@ a:hover { .working-together-info { margin-top: 100px; - font: normal normal normal 22px/31px rubrik-edge-new; + font: normal normal normal 32px/41px rubrik-edge-new; letter-spacing: -0.8px; text-align: left; color: white; @@ -592,7 +577,7 @@ a:hover { } .mattering { - width: 20% !important; + width: 18% !important; } .ohp { @@ -600,11 +585,11 @@ a:hover { } .meson { - width: 20% !important; + width: 16% !important; } .punctum { - width: 20% !important; + width: 18% !important; } .publisher-pill, .publisher-pill button:hover { @@ -630,16 +615,6 @@ a:hover { color: white !important; } -.inactive-blue-pill { - background-color: var(--obc-blue) !important; - color: white !important; -} - -.inactive-blue-pill:hover { - background-color: white !important; - color: var(--obc-magenta) !important; -} - .mid-pill:hover { border-style: solid; width: 30%; @@ -839,6 +814,27 @@ a:hover { margin-top: 20px; } +.pill-button { + border-style: solid; + width: 100%; + border-width: 1px; + border-color: #FFFFFF; + color: #FFFFFF; + background-color: transparent; + padding: 10px 20px; + text-align: center; + text-decoration: none; + display: inline-block; + margin: 4px 2px; + cursor: pointer; + border-radius: 16px; + font: normal normal 800 16px/21px rubrik-edge-new; + letter-spacing: 0.16px; + margin-bottom: 50px; + margin-top: 50px; + text-align: center; +} + .pill-button-library { border-style: solid; width: 100%; @@ -1042,28 +1038,13 @@ a:hover { } } - -@media screen and (max-width: 1148px) { - .navbar-lower { - padding-top: 30px; - } - - .lower-nav-item a { - font-size: 18px; - } -} - -@media screen and (max-width: 976px) { +@media screen and (min-width: 300px) { .navbar-lower { - padding-top: 30px; + padding-top: 50px; } - .lower-nav-item a { - font-size: 14px; - } - - .main h1 { - font-size: 2.5rem; + .build-your-quotation { + display: none; } } @@ -1107,7 +1088,7 @@ i { nav a, nav a:hover { color: var(--obc-blue); - font: normal normal bold 20px/24px rubrik-edge-new; + font: normal normal bold 24px/28px rubrik-edge-new; letter-spacing: -0.24px; opacity: 1; text-decoration: none; @@ -1177,33 +1158,13 @@ nav a, nav a:hover { margin-bottom: 30px; } -.container.grey-background { - max-width: 73%; -} - -.grey-border { - max-width: 73%; -} - -.grey-border-sides { - max-width: 73%; -} - .blue-header h1 { margin-top: 45px; text-align: center; - font: normal normal bold 28px/32px rubrik-edge-new; + font: normal normal bold 48px/50px rubrik-edge-new; letter-spacing: 0px; } -@media only screen and (max-width: 1200px) { - .blue-header h1 { - font: normal normal bold 22px/28px rubrik-edge-new; - } -} - - - .offset-content { margin-top: -80px; background-color: white; @@ -1362,10 +1323,6 @@ nav a, nav a:hover { margin-bottom: 0px; } -.catalogue-main h1 { - color: var(--obc-blue); -} - .book-item { border: 1px solid var(--obc-book-border); border-radius: 8px; @@ -1496,6 +1453,7 @@ nav a, nav a:hover { margin-top: 10px; margin-bottom: 10px; border: 1px solid var(--obc-book-border); + max-height: 75%; } .book-item:hover, .book-item-link:hover div, .book-item-link:hover p, .book-item-link:hover h3 { @@ -1578,7 +1536,7 @@ nav a, nav a:hover { } .book-info-row { - margin-top: 15px; + margin-top: 20px; } .formats-box-upper { @@ -1662,6 +1620,13 @@ nav a, nav a:hover { margin-bottom: 30px; } +.blue-header h1 { + margin-top: 45px; + text-align: center; + font: normal normal bold 48px/50px rubrik-edge-new; + letter-spacing: 0px; +} + .offset-content { margin-top: -100px; background-color: white; @@ -1990,7 +1955,7 @@ nav a, nav a:hover { .pagination, .pagination a, .pagination a:hover { color: var(--obc-blue); - font: normal normal 13px/22px rubrik-edge-new; + font: normal normal normal 24px/34px rubrik-edge-new; letter-spacing: -0.24px; text-align: center; margin: 0px; @@ -2140,6 +2105,8 @@ nav a, nav a:hover { .navbar.navbar-lower .navbar-nav { margin-left: 100px; } + + } .navbar-logo { @@ -2381,7 +2348,7 @@ nav a, nav a:hover { border-radius: 4px; height: 48px; color: var(--obc-input-pale-blue); - font: normal normal normal 18px/23px rubrik-edge-new; + font: normal normal normal 18px/23px Rubrik Edge New; letter-spacing: 0px; margin-bottom: 20px; } @@ -2556,13 +2523,14 @@ nav a, nav a:hover { width: 25%; color: var(--obc-blue); background-color: transparent; + text-align: center; text-decoration: none; margin: 0px; cursor: pointer; border-radius: 28px 0px 0px 28px; height: 50px; - font: normal normal bold 12px/14px rubrik-edge-new; + font: normal normal bold 16px/19px rubrik-edge-new; letter-spacing: 0.16px; display: inline-flex; @@ -2621,21 +2589,6 @@ nav a, nav a:hover { justify-content: center; } -.right-pill-initiative { - text-align: center; - text-decoration: none; - margin: 0px; - cursor: pointer; - border-radius: 0px 28px 28px 0px; - height: 50px; - font: normal normal bold 16px/19px rubrik-edge-new; - letter-spacing: 0.16px; - - display: inline-flex; - align-items: center; - justify-content: center; -} - .right-pill-blue { border-style: solid; border-color: var(--obc-blue); @@ -2649,7 +2602,7 @@ nav a, nav a:hover { cursor: pointer; border-radius: 0px 28px 28px 0px; height: 50px; - font: normal normal bold 12px/14px rubrik-edge-new; + font: normal normal bold 16px/19px rubrik-edge-new; letter-spacing: 0.16px; display: inline-flex; @@ -2676,7 +2629,7 @@ nav a, nav a:hover { border-style: solid; border-color: var(--obc-blue); border-width: 1px; - width: 25%; + width: 33%; color: white; background-color: var(--obc-blue); text-align: center; @@ -2684,7 +2637,7 @@ nav a, nav a:hover { margin: 0px; cursor: pointer; text-align: center; - font: normal normal bold 12px/14px rubrik-edge-new; + font: normal normal bold 16px/19px rubrik-edge-new; letter-spacing: 0.16px; height: 50px; display: inline-flex; @@ -2725,7 +2678,7 @@ nav a, nav a:hover { } .mid-pill-blue:hover { - width: 25%; + width: 33%; color: var(--obc-magenta); background-color: white; text-align: center; @@ -2733,7 +2686,7 @@ nav a, nav a:hover { margin: 0px; cursor: pointer; text-align: center; - font: normal normal bold 12px/14px rubrik-edge-new; + font: normal normal bold 16px/19px rubrik-edge-new; letter-spacing: 0.16px; height: 50px; @@ -2768,16 +2721,20 @@ nav a, nav a:hover { .circle-image { border-radius: 50%; - max-width: 85%; + max-width: 75%; text-align: center; - max-height: 85%; + max-height: 75%; } .circle-image-mobile { width: 65% !important; } +.circle-row { + height: 150px; +} + .person { font: normal normal bold 24px/28px rubrik-edge-new; letter-spacing: -0.24px; @@ -2928,18 +2885,12 @@ nav a, nav a:hover { color: var(--obc-blue); } -.cms-col { - text-align: left; - font: normal normal normal 18px/28px rubrik-edge-new; - letter-spacing: 0px; - color: var(--obc-blue); -} - .cms-col p { text-align: left; font: normal normal normal 18px/28px rubrik-edge-new; letter-spacing: 0px; color: var(--obc-blue); + margin-top: 30px; } .cms-para { @@ -2947,6 +2898,7 @@ nav a, nav a:hover { font: normal normal normal 18px/28px rubrik-edge-new; letter-spacing: 0px; color: var(--obc-blue); + margin-top: 30px; } .pull-quote-block { @@ -3034,10 +2986,6 @@ nav a, nav a:hover { color: var(--obc-blue) !important; } -.blue-h2 h2 { - color: var(--obc-blue); -} - .message-alert { margin-top: 10px; } @@ -3045,121 +2993,3 @@ nav a, nav a:hover { .cms-body { margin-top: 0px !important; } - -.edit-button { - font-size: small; - margin: 0; - padding: 0; -} - -.edit-button:hover { - color: var(--obc-magenta); -} - -.take-action-mobile h1 { - color: black; - font: normal normal bold 54px/60px rubrik-edge-new; -} - -.no-margin-top { - margin-top: 0; -} - -.highlights-title { - margin-bottom: 40px; -} - -.highlights { - width: 90%; - margin-left: auto; - margin-right: auto; - font: normal normal bold 16px/19px rubrik-edge-new; - letter-spacing: 0.16px; - min-height: 300px; -} - -.highlights h3 { - color: white; - text-align: center; - font: normal normal bold 42px/46px rubrik-edge-new; - letter-spacing: 0px; -} - -.highlights-inner { - max-width: 85%; - margin-left: auto; - margin-right: auto; - color: white; -} - -.highlight-img { - height: 100px; - width: 100px; - text-align: center; -} - -.highlight-title { - color:white; - margin-top: 20px; -} - -.highlight-title { - color:white; - margin-top: 20px; -} - -.highlight-text-block { - font: normal normal 13px/23px rubrik-edge-new; -} - -.megenta-pill-button-more { - width: 250px; - background: var(--obc-magenta) 0% 0% no-repeat padding-box; - border: 2px solid #FFFFFF; - padding: 10px 20px; - text-align: center; - text-decoration: none; - display: inline-block; - margin: 4px 2px; - cursor: pointer; - border-radius: 35px; - font: normal normal 800 16px/21px rubrik-edge-new; - letter-spacing: 0.16px; - margin-bottom: 50px; - margin-top: 50px; - color: white; -} - -.megenta-pill-button-more:hover { - width: 250px; - background: #FFFFFF 0% 0% no-repeat padding-box; - border: 2px solid #FFFFFF; - padding: 10px 20px; - text-align: center; - text-decoration: none; - display: inline-block; - margin: 4px 2px; - cursor: pointer; - border-radius: 37px; - font: normal normal 800 16px/21px rubrik-edge-new; - letter-spacing: 0.16px; - margin-bottom: 50px; - margin-top: 50px; - color: var(--obc-magenta); -} - -.colinfo a { - font-weight: normal; -} - -.package-collection-pill-row { - margin-bottom: 0px; -} - -.package-collection-text p { - margin-top: 15px; - margin-bottom: 15px; - font: normal normal normal 14px/24px rubrik-edge-new; - color: var(--obc-blue); - text-align: left; -} \ No newline at end of file diff --git a/src/summary/views.py b/src/summary/views.py index 23e134c..21954c6 100644 --- a/src/summary/views.py +++ b/src/summary/views.py @@ -4,8 +4,9 @@ from django.shortcuts import render, get_object_or_404, redirect, reverse from initiatives import models as im -from package import models as pm +from package import models as pm, utils as pu from thoth import models +from cms import models as cms_models def get_initiative(package_or_initiative, identifier) -> im.Initiative: @@ -49,17 +50,50 @@ def summary_package_initiative( if not initiative: raise Http404("No package or initiative found.") - works = models.Work.objects.filter( - publisher__thoth_id=initiative.thoth_id - ).order_by("-published_date")[:6] + works = ( + models.Work.objects.filter(publisher__thoth_id=initiative.thoth_id) + .exclude(published_date="n.d.") + .order_by("-published_date") + ) + + work_list = [] + to_shunt = [] + + for work in works: + if work.published_date == "n.d.": + to_shunt.append(work) + elif ( + datetime.strptime(work.published_date, "%Y-%m-%d") > datetime.now() + ): + to_shunt.append(work) + work_list.append(work) + + # push all n.d. results to the back of the list + for work_to_shunt in to_shunt: + work_list.remove(work_to_shunt) + # work_list.append(work_to_shunt) + + pages = cms_models.PageUpdate.objects.filter( + is_update=False, + initiative=initiative, + ).order_by("sequence") + + package = initiative.packages.filter(active=True).first() + request.session.get('country') + if request.session.get('country', None): + pu.add_pre_calc_to_objects( + request.session.get('country'), + [package], + ) template = "summary/summary.html" context = { "initiative": initiative, - "package": initiative.packages.filter(active=True).first(), + "package": package, "package_or_initiative": package_or_initiative, "identifier": identifier, - "works": works, + "works": work_list[:6], + "additional_nav": pages, } return render( request, @@ -79,6 +113,10 @@ def summary_more_info( :return: the response """ initiative = get_initiative(package_or_initiative, identifier) + pages = cms_models.PageUpdate.objects.filter( + is_update=False, + initiative=initiative, + ).order_by("sequence") template = "summary/more_info.html" context = { @@ -86,6 +124,7 @@ def summary_more_info( "package": initiative.packages.filter(active=True).first(), "package_or_initiative": package_or_initiative, "identifier": identifier, + "additional_nav": pages, } return render( request, @@ -103,6 +142,10 @@ def summary_pricing(request, package_or_initiative, identifier) -> HttpResponse: :return: the response """ initiative = get_initiative(package_or_initiative, identifier) + pages = cms_models.PageUpdate.objects.filter( + is_update=False, + initiative=initiative, + ).order_by("sequence") template = "summary/pricing.html" context = { @@ -110,6 +153,7 @@ def summary_pricing(request, package_or_initiative, identifier) -> HttpResponse: "package": initiative.packages.filter(active=True).first(), "package_or_initiative": package_or_initiative, "identifier": identifier, + "additional_nav": pages, } return render( request, @@ -132,7 +176,7 @@ def summary_meta_package(request, package_id) -> HttpResponse: reverse( "summary_package_initiative", kwargs={ - "package_or_initiative": package, + "package_or_initiative": "package", "identifier": package.pk, }, ) @@ -141,6 +185,11 @@ def summary_meta_package(request, package_id) -> HttpResponse: try: package = pm.MetaPackage.objects.get(pk=package_id, active=True) package_type = "meta" + if request.session.get('country', None): + pu.add_pre_calc_to_meta_objects( + request.session.get('country'), + [package], + ) except pm.MetaPackage.DoesNotExist: raise Http404 diff --git a/src/templates/403.html b/src/templates/403.html index 76ccf5d..2d89ea4 100644 --- a/src/templates/403.html +++ b/src/templates/403.html @@ -3,15 +3,15 @@ {% block breadcrumb %} - + {% endblock %} {% block content %}
-

Permission Denied

-

{{ exception }}

+

{% get_site_text 'permission_denied' %}

+

{{ exception }}

-{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/accounts/activate.html b/src/templates/accounts/activate.html index 27cc58b..f0dbb7d 100644 --- a/src/templates/accounts/activate.html +++ b/src/templates/accounts/activate.html @@ -2,7 +2,7 @@ {% load i18n two_factor static crispy_forms_tags %} {% block breadcrumb %} - + {% endblock %} {% block js %} @@ -19,9 +19,9 @@
- {% endblock %} diff --git a/src/templates/accounts/banding_setup.html b/src/templates/accounts/banding_setup.html index e644ace..b4558c3 100644 --- a/src/templates/accounts/banding_setup.html +++ b/src/templates/accounts/banding_setup.html @@ -1,10 +1,10 @@ {% extends "base/admin_base.html" %} {% load static two_factor account_tags %} -{% block title %}{% trans "Profile" %}{% endblock title %} +{% block title %}{% get_site_text 'profile' %}{% endblock title %} {% block breadcrumb %} - + {% endblock %} {% block content %} @@ -16,7 +16,7 @@
-

{% trans "Banding Setup" %}

+

{% get_site_text 'banding_setup' %}

@@ -27,7 +27,7 @@

{% trans "Banding Setup" %}

{{ banding }}

+
diff --git a/src/templates/accounts/edit_profile.html b/src/templates/accounts/edit_profile.html index adfb74a..acffd4e 100644 --- a/src/templates/accounts/edit_profile.html +++ b/src/templates/accounts/edit_profile.html @@ -1,10 +1,10 @@ {% extends "base/admin_base.html" %} {% load static two_factor crispy_forms_tags %} -{% block title %}{% trans "Profile" %}{% endblock title %} +{% block title %}{% get_site_text 'profile' %}{% endblock title %} {% block breadcrumb %} - + {% endblock %} {% block dashboard_js %} @@ -26,7 +26,7 @@
-

{% trans "Profile" %}

+

{% get_site_text 'profile' %}

@@ -39,7 +39,7 @@

{% trans "Profile" %}

{{ form.fte|as_crispy_field }} diff --git a/src/templates/accounts/manage_accounts.html b/src/templates/accounts/manage_accounts.html index 4565b69..5de0c01 100644 --- a/src/templates/accounts/manage_accounts.html +++ b/src/templates/accounts/manage_accounts.html @@ -1,22 +1,22 @@ {% extends "base/base.html" %} -{% block title %}{% trans "Manage Accounts" %}{% endblock title %} +{% block title %}{% get_site_text 'manage_accounts' %}{% endblock title %} {% block breadcrumb %} - + {% endblock breadcrumb %} {% block content %} - - - - - + + + + + diff --git a/src/templates/accounts/manage_fluid_permissions.html b/src/templates/accounts/manage_fluid_permissions.html index c8bede8..0b9abda 100644 --- a/src/templates/accounts/manage_fluid_permissions.html +++ b/src/templates/accounts/manage_fluid_permissions.html @@ -1,27 +1,27 @@ {% extends "base/base.html" %} {% block title %} -Permissions +{% get_site_text 'permissions' %} {% endblock title %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block content %} -

Permission Management

-

Manage permissions across the system by granting groups access to views.

+

{% get_site_text 'permission_management' %}

+

{% get_site_text 'permission_management_help' %}

{% csrf_token %} - +
UsernameEmail AddressFirst NameLast NameAffiliation{% get_site_text 'username' %}{% get_site_text 'email' %}{% get_site_text 'first_name' %}{% get_site_text 'last_name' %}{% get_site_text 'affiliation' %}
- + {% for group in groups %} {% endfor %} @@ -38,6 +38,6 @@

Permission Management

{% endfor %}
View Name{% get_site_text 'view_name' %}{{ group }}
- +
{% endblock content %} diff --git a/src/templates/accounts/profile.html b/src/templates/accounts/profile.html index d0659bd..8b8460a 100644 --- a/src/templates/accounts/profile.html +++ b/src/templates/accounts/profile.html @@ -1,10 +1,10 @@ {% extends "base/admin_base.html" %} {% load static two_factor %} -{% block title %}{% trans "Profile" %}{% endblock title %} +{% block title %}{% get_site_text 'profile' %}{% endblock title %} {% block breadcrumb %} - + {% endblock %} {% block content %} @@ -20,14 +20,14 @@

{% trans "Profile" %}

@@ -39,28 +39,28 @@

{{ user.profile.full_name }}

{% endif %} - + - + - + - + - - + + - - + +
{% trans 'Username' %}{% get_site_text 'username' %} {{ request.user.username }}
{% trans 'Full Name' %}{% get_site_text 'full_name' %} {{ user.profile.full_name }}
{% trans 'Email' %}{% get_site_text 'email' %} {{ request.user.email }}
{% trans 'Institution' %}{% get_site_text 'institution' %} {{ user.profile.institution.institution_name }}
{% trans 'Two Factor Authentication Enabled' %}{% if default_device %}{% trans 'Enabled' %}{% else %}{% trans '2FA is not enabled' %}.{% endif %}{% get_site_text '2fa_enabled' %}{% if default_device %}{% get_site_text 'is_enabled' %}{% else %}{% get_site_text 'disabled' %}.{% endif %}
{% trans 'Bandings' %}{% trans 'Configure bandings' %}{% get_site_text 'bandings' %}{% get_site_text 'configure_bandings' %}
diff --git a/src/templates/accounts/register.html b/src/templates/accounts/register.html index 8ba0a30..37f2ddb 100644 --- a/src/templates/accounts/register.html +++ b/src/templates/accounts/register.html @@ -2,7 +2,7 @@ {% load i18n two_factor static crispy_forms_tags %} {% block breadcrumb %} - + {% endblock %} {% block js %} @@ -20,9 +20,9 @@
@@ -34,9 +34,9 @@

{% block title %}{% trans "Register a new account" %}{% endblock %}

@@ -58,7 +58,7 @@

{% block title %}{% trans "Register a new account" %}{% endblock %}

@@ -66,9 +66,9 @@

{% trans "Register a new account" %}

> diff --git a/src/templates/backups/backup_list.html b/src/templates/backups/backup_list.html index 2ff5ba1..fb03c95 100644 --- a/src/templates/backups/backup_list.html +++ b/src/templates/backups/backup_list.html @@ -3,15 +3,15 @@ {% load crispy_forms_tags %} {% block title %} - Site Backups + {% get_site_text 'site_backups' %} {% endblock title %} {% block page-title %} - Site Backups + {% get_site_text 'site_backups' %} {% endblock page-title %} {% block breadcrumb %} - + {% endblock %} {% block dashboard_js %} @@ -31,14 +31,14 @@
-

SQL Backups

+

{% get_site_text 'sql_backups' %}

- - - - + + + + @@ -47,7 +47,7 @@

SQL Backups

- + {% endfor %} @@ -57,14 +57,14 @@

SQL Backups

-

Data File Backups

+

{% get_site_text 'data_file_backup' %}

Date ModifiedVersion IDSizeDownload{% get_site_text 'date_modified' %}{% get_site_text 'version_id' %}{% get_site_text 'size' %}{% get_site_text 'download' %}
{{ sql_entry.lastmodified }} {{ sql_entry.versionid }} {{ sql_entry.size }}Download{% get_site_text 'download' %}
- - - - + + + + @@ -73,7 +73,7 @@

Data File Backups

- + {% endfor %} @@ -84,14 +84,14 @@

Data File Backups

-

Thoth Backups

+

{% get_site_text 'thoth_backup' %}

Date ModifiedVersion IDSizeDownload{% get_site_text 'date_modified' %}{% get_site_text 'version_id' %}{% get_site_text 'size' %}{% get_site_text 'download' %}
{{ sql_entry.lastmodified }} {{ sql_entry.versionid }} {{ sql_entry.size }}Download{% get_site_text 'download' %}
- - - - + + + + @@ -100,7 +100,7 @@

Thoth Backups

- + {% endfor %} diff --git a/src/templates/base/admin_base.html b/src/templates/base/admin_base.html index b801579..6e3b174 100644 --- a/src/templates/base/admin_base.html +++ b/src/templates/base/admin_base.html @@ -49,13 +49,13 @@ @@ -108,7 +108,7 @@

{% block page-title %}{% endblock %}

@@ -177,7 +177,7 @@

{% block page-title %}{% endblock %}

{% endblock dashboard_js %} - + + + + + + +{% endblock %} diff --git a/src/templates/cms/sitewide_news_config.html b/src/templates/cms/sitewide_news_config.html index f585ed2..1f4b874 100644 --- a/src/templates/cms/sitewide_news_config.html +++ b/src/templates/cms/sitewide_news_config.html @@ -1,10 +1,10 @@ {% extends "base/admin_base.html" %} {% load static two_factor %} -{% block title %}Site-Wide News Configuration{% endblock title %} +{% block title %}{% get_site_text 'site_wide_news_config' %}{% endblock title %} {% block breadcrumb %} - + {% endblock breadcrumb %} {% block dashboard_js %} @@ -19,15 +19,15 @@
-

Configures Site-Wide News Items

+

{% get_site_text 'site_wide_news_config_items' %}

Date ModifiedVersion IDSizeDownload{% get_site_text 'date_modified' %}{% get_site_text 'version_id' %}{% get_site_text 'size' %}{% get_site_text 'download' %}
{{ sql_entry.lastmodified }} {{ sql_entry.versionid }} {{ sql_entry.size }}Download{% get_site_text 'download' %}
- - - - + + + + @@ -37,7 +37,7 @@ - + {% endfor %} @@ -46,12 +46,12 @@
PageInitiativeOrderRemove{% get_site_text 'page' %}{% get_site_text 'initiative' %}{% get_site_text 'sequence' %}{% get_site_text 'delete' %}
{{ news_item.update_item.title }} {{ news_item.update_item.initiative.name }} {{ news_item.order }}
{% csrf_token %}
{% csrf_token %}
- - - - - - + + + + + + @@ -61,8 +61,8 @@ - - + + {% endfor %} diff --git a/src/templates/cms/update_feed.html b/src/templates/cms/update_feed.html index 4204a23..62d78e2 100644 --- a/src/templates/cms/update_feed.html +++ b/src/templates/cms/update_feed.html @@ -1,10 +1,10 @@ {% extends "base/admin_base.html" %} {% load static two_factor %} -{% block title %}Update Feed{% endblock title %} +{% block title %}{% get_site_text 'update_feed' %}{% endblock title %} {% block breadcrumb %} - + {% endblock breadcrumb %} {% block dashboard_js %} @@ -19,16 +19,16 @@
-

Lists updates to pages and versions.

+

{% get_site_text 'lists_updates_to_pages' %}

Page IDTitleInitiativeDate/TimeEditAdd to Homepage{% get_site_text 'id' %}{% get_site_text 'title' %}{% get_site_text 'initiative' %}{% get_site_text 'date_time' %}{% get_site_text 'edit' %}{% get_site_text 'add_to_homepage' %}
{{ page.title }} {{ page.initiative.name }} {{ page.current_version.created }}Edit
{% csrf_token %}
{% get_site_text 'edit' %}
{% csrf_token %}
- - - - - - + + + + + + @@ -38,13 +38,13 @@ {% endfor %} diff --git a/src/templates/cms/view_page_version.html b/src/templates/cms/view_page_version.html index 735d8a4..a3d2ee3 100644 --- a/src/templates/cms/view_page_version.html +++ b/src/templates/cms/view_page_version.html @@ -11,7 +11,7 @@
-

Viewing version #{{ version.pk }}, created on {{ version.created }} by {{ version.created_by.profile.full_name }}

+

Viewing version #{{ version.pk }}, {{ version.created }}, {{ version.created_by.profile.full_name }}

{{ page.title }}

{{ version.body|safe }}
diff --git a/src/templates/cms/view_update_version.html b/src/templates/cms/view_update_version.html index d18ebb0..57c0111 100644 --- a/src/templates/cms/view_update_version.html +++ b/src/templates/cms/view_update_version.html @@ -11,7 +11,7 @@
-

Viewing version #{{ version.pk }}, created on {{ version.created }} by {{ version.created_by.profile.full_name }}

+

Viewing version #{{ version.pk }}, {{ version.created }}, {{ version.created_by.profile.full_name }}

{{ page.title }}

Written by {{ page.updated_by.profile.full_name }}, published {{ page.created|date:"Y-m-d" }}

{{ version.body|safe }} diff --git a/src/templates/cms/who_we_are_section_list.html b/src/templates/cms/who_we_are_section_list.html index a8ffb5c..b0a15ea 100644 --- a/src/templates/cms/who_we_are_section_list.html +++ b/src/templates/cms/who_we_are_section_list.html @@ -1,10 +1,10 @@ {% extends "base/admin_base.html" %} {% load static two_factor %} -{% block title %}Who We Are Configuration{% endblock title %} +{% block title %}{% get_site_text 'who_we_are_configuration' %}{% endblock title %} {% block breadcrumb %} - + {% endblock breadcrumb %} {% block dashboard_js %} @@ -22,18 +22,18 @@
-

Board of Stewards

+

{% get_site_text 'board_of_stewards' %}

- Add New Steward + {% get_site_text 'add' %}

Version IDUpdated ByDate/TimePage/UpdateEditDetail{% get_site_text 'id' %}{% get_site_text 'last_updated_by' %}{% get_site_text 'date_time' %}{% get_site_text 'page_update' %}{% get_site_text 'edit' %}{% get_site_text 'details' %}
{{ version.created_by.profile.full_name }} {{ version.created }} - {% if version.pageupdate_set.all.0.is_update %}Update {% else %}Page {% endif %}{{ version.pageupdate_set.all.0.title }} + {% if version.pageupdate_set.all.0.is_update %}{% get_site_text 'update' %} {% else %}{% get_site_text 'page' %} {% endif %}{{ version.pageupdate_set.all.0.title }} - Edit + {% get_site_text 'edit' %} - Detail + {% get_site_text 'details' %}
- - - - + + + + @@ -42,8 +42,8 @@

Board of Stewards

- - + + {% endfor %} @@ -54,18 +54,18 @@

Board of Stewards

-

Membership Committee

+

{% get_site_text 'membership_committee' %}

- Add New Member + {% get_site_text 'add' %}

NameAffiliationEditDelete{% get_site_text 'name' %}{% get_site_text 'affiliation' %}{% get_site_text 'edit' %}{% get_site_text 'delete' %}
{{ steward.display_name }} {{ steward.affiliation_line }}Edit
{% csrf_token %}
{% get_site_text 'edit' %}
{% csrf_token %}
- - - - + + + + @@ -74,8 +74,8 @@

Membership Committee

- - + + {% endfor %} @@ -85,18 +85,18 @@

Membership Committee

-

Management Team

+

{% get_site_text 'management_team' %}

- Add New Manager + {% get_site_text 'add' %}

NameAffiliationEditDelete{% get_site_text 'name' %}{% get_site_text 'affiliation' %}{% get_site_text 'edit' %}{% get_site_text 'delete' %}
{{ member.display_name }} {{ member.affiliation_line }}Edit
{% csrf_token %}
{% get_site_text 'edit' %}
{% csrf_token %}
- - - - + + + + @@ -105,8 +105,8 @@

Management Team

- - + + {% endfor %} @@ -115,18 +115,18 @@

Management Team

-

Secretariat

+

{% get_site_text 'secretariat' %}

- Add New Member + {% get_site_text 'add' %}

NameAffiliationEditDelete{% get_site_text 'name' %}{% get_site_text 'affiliation' %}{% get_site_text 'edit' %}{% get_site_text 'delete' %}
{{ manager.display_name }} {{ manager.affiliation_line }}Edit
{% csrf_token %}
{% get_site_text 'edit' %}
{% csrf_token %}
- - - - + + + + @@ -135,8 +135,8 @@

Secretariat

- - + + {% endfor %} @@ -146,18 +146,49 @@

Secretariat

-

Previous Board Members

+

{% get_site_text 'finance_committee' %}

+

+ {% get_site_text 'add' %} +

+
NameAffiliationEditDelete{% get_site_text 'name' %}{% get_site_text 'affiliation' %}{% get_site_text 'edit' %}{% get_site_text 'delete' %}
{{ member.display_name }} {{ member.affiliation_line }}Edit
{% csrf_token %}
{% get_site_text 'edit' %}
{% csrf_token %}
+ + + + + + + + + + + {% for member in finance %} + + + + + + + + {% endfor %} + +
{% get_site_text 'name' %}{% get_site_text 'affiliation' %}{% get_site_text 'edit' %}{% get_site_text 'delete' %}
{{ member.display_name }}{{ member.affiliation_line }}{% get_site_text 'edit' %}
{% csrf_token %}
+
+
+ +
+
+

{% get_site_text 'previous_board_members' %}

- Add New Member + {% get_site_text 'add' %}

- - - - + + + + @@ -166,8 +197,8 @@

Previous Board Members

- - + + {% endfor %} diff --git a/src/templates/contacts/contact_list.html b/src/templates/contacts/contact_list.html index f8d5e39..9ea2132 100644 --- a/src/templates/contacts/contact_list.html +++ b/src/templates/contacts/contact_list.html @@ -1,11 +1,11 @@ {% extends "base/admin_base.html" %} {% block title %} -Initiatives +{% get_site_text 'initiatives' %} {% endblock title %} {% block breadcrumb %} - + {% endblock breadcrumb %} {% block dashboard_js %} @@ -18,20 +18,20 @@
-

Contacts for your initiatives

-

Contacts for the initiative can be assigned to receive notifications on signups and on changes to access controls. Note that anybody listed here can use their access code to view signups and access controls for the listed initiative.

+

{% get_site_text 'contacts_for_initiatives' %}

+

{% get_site_text 'contacts_for_initiatives_explainer' %}

- Add New Contact + {% get_site_text 'add' %}

NameAffiliationEditDelete{% get_site_text 'name' %}{% get_site_text 'affiliation' %}{% get_site_text 'edit' %}{% get_site_text 'delete' %}
{{ member.display_name }} {{ member.affiliation_line }}Edit
{% csrf_token %}
{% get_site_text 'edit' %}
{% csrf_token %}
- - - - - + + + + + @@ -40,8 +40,8 @@

Contacts for your initiatives

- - + + {% endfor %} diff --git a/src/templates/contacts/create_contact.html b/src/templates/contacts/create_contact.html index 5c3a86b..231c598 100644 --- a/src/templates/contacts/create_contact.html +++ b/src/templates/contacts/create_contact.html @@ -3,9 +3,9 @@ {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block content %} @@ -14,16 +14,16 @@
-

{% if package %}Edit {{ contact.name }}{% else %}Add New Contact{% endif %}

-

Add new and edit existing contacts.

+

{% if package %}{% get_site_text 'edit' %} {{ contact.name }}{% else %}{% get_site_text 'add' %}{% endif %}

+

{% get_site_text 'contacts_add_edit' %}

{% csrf_token %}
-

Contact Details

+

{% get_site_text 'details' %}

{{ form|crispy }} - +
diff --git a/src/templates/dashboard/dashboard_index.html b/src/templates/dashboard/dashboard_index.html index 80d9c7e..f5ed69e 100644 --- a/src/templates/dashboard/dashboard_index.html +++ b/src/templates/dashboard/dashboard_index.html @@ -2,15 +2,15 @@ {% load static %} {% block title %} - Dashboard + {% get_site_text 'dashboard' %} {% endblock title %} {% block page-title %} - Dashboard + {% get_site_text 'dashboard' %} {% endblock page-title %} {% block breadcrumb %} - + {% endblock %} {% block dashboard_js %} @@ -152,9 +152,9 @@
-

{{ request.active_baskets.count }} Quotes

+

{{ request.active_baskets.count }} {% get_site_text 'quotes' %}

-

View Quotes in Progress

+

{% get_site_text 'view_quotes_in_progress' %}

@@ -169,7 +169,7 @@

{{ request.active_baskets.count }} Quotes

{{ book_count }}

-

Books in Packages at OBC

+

{% get_site_text 'books_in_packages' %}

@@ -187,7 +187,7 @@

{{ book_count }}

{{ institution_books }}

-

Authors from Your Institution

+

{% get_site_text 'authors_from_institution' %}

@@ -206,7 +206,7 @@

{{ institution_books }}

{{ pub_count }}

-

Funded Presses at OBC

+

{% get_site_text 'funded_presses' %}

@@ -223,7 +223,7 @@

{{ pub_count }}

{{ index_count }}

-

Books Indexed

+

{% get_site_text 'books_indexed' %}

@@ -244,7 +244,7 @@

{{ index_count }}

- Books Published by OBC Presses + {% get_site_text 'books_published' %}

@@ -273,12 +273,12 @@

- Book Authorship Map + {% get_site_text 'books_authorship_map' %}

@@ -318,7 +318,7 @@

-

Payment Processors

+

{% get_site_text 'payment_processors' %}

NameEmailInitiativeEditDelete{% get_site_text 'name' %}{% get_site_text 'email' %}{% get_site_text 'initiative' %}{% get_site_text 'edit' %}{% get_site_text 'delete' %}
{{ contact.name }} {{ contact.email }} {{ contact.initiative }}Edit
{% csrf_token %}
{% get_site_text 'edit' %}
{% csrf_token %}
- - - - - - + + + + + + {% for order in orders %} @@ -41,14 +41,14 @@

Orders and Documents

- + diff --git a/src/templates/docs/order_doc.html b/src/templates/docs/order_doc.html index 5d83585..39fc8d2 100644 --- a/src/templates/docs/order_doc.html +++ b/src/templates/docs/order_doc.html @@ -1,4 +1,4 @@ -{% load static conflicts currency_display cost_calculator %} +{% load static conflicts currency_display cost_calculator site_text %} @@ -57,7 +57,7 @@
Order NumberOrder DateNumber of PackagesStatusDocumentsDetails{% get_site_text 'order_number' %}{% get_site_text 'date_time' %}{% get_site_text 'number_of_packages' %}{% get_site_text 'status' %}{% get_site_text 'documents' %}{% get_site_text 'details' %}
{{ order.order_date }} {{ order.packagesignup_set.all.count }} {{ order.get_status_display }}{% if order.document %} Order Documents{% else %}Order has no documents{% endif %}{% if order.document %} {% get_site_text 'documents' %}{% else %}{% get_site_text 'no_documents' %}{% endif %} {% if order.status == 'new' %} - Complete Order Form + {% get_site_text 'complete_order_form' %} {% elif order.status == 'provisional' %} - View Order Details + {% get_site_text 'details' %} {% elif order.status == 'complete' %} - View Order Details + {% get_site_text 'details' %} {% endif %}
- + {% for meta_package in order.basket.meta_packages.all %} @@ -74,7 +74,7 @@ @@ -82,7 +82,7 @@ {% for package in order.basket.packages.all %} - + {% endfor %} diff --git a/src/templates/elements/country_change_modal.html b/src/templates/elements/country_change_modal.html new file mode 100644 index 0000000..69c158f --- /dev/null +++ b/src/templates/elements/country_change_modal.html @@ -0,0 +1,26 @@ +{% load country crispy_forms_tags %} + \ No newline at end of file diff --git a/src/templates/elements/delete_check.js.html b/src/templates/elements/delete_check.js.html index b35c6c5..84f5a69 100644 --- a/src/templates/elements/delete_check.js.html +++ b/src/templates/elements/delete_check.js.html @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/src/templates/index.html b/src/templates/index.html deleted file mode 100644 index 24264e8..0000000 --- a/src/templates/index.html +++ /dev/null @@ -1,50 +0,0 @@ -{% extends "base/base.html" %} - -{% block content %} -
-
-

Site Index

-

This is a temporary site index to help show you around Intrepid as a work in progress.
Most pages have basic styling in anticipation of design work .

- -

The following areas are accessible and fairly stable:

- -

The following areas are unstable:

- -
-
-{% endblock content %} \ No newline at end of file diff --git a/src/templates/initiatives/detail.html b/src/templates/initiatives/detail.html index 06d801f..0e3a595 100644 --- a/src/templates/initiatives/detail.html +++ b/src/templates/initiatives/detail.html @@ -1,38 +1,38 @@ {% extends "base/admin_base.html" %} {% block title %} -Initiatives +{% get_site_text 'initiatives' %} {% endblock title %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block content %}
-

Initiative Details

-

Expanded information of a given initiative including name and accounts associated with it.

+

{% get_site_text 'initiative_details' %}

+

{% get_site_text 'expanded_initiative' %}

-

Basic Information

-

Name:

+

{% get_site_text 'basic_information' %}

+

{% get_site_text 'name' %}:

{{ initiative.name }}

-

Description:

+

{% get_site_text 'description' %}:

{{ initiative.description|safe }} -

Accounts

+

{% get_site_text 'accounts' %}

- Below are the accounts linked to this Initiative. Linked accounts have permission to create and edit initiative pages and updates. + {% get_site_text 'accounts_helper' %}

OBC Offer SelectionAmountAmount (for {% widthratio order.term 1 12 %} months)
{% for currency, amount in calculated_meta_package_cost.items %} - {% format_price amount currency %}{% if not forloop.last %}, {% endif %} + {% format_price_multiplier amount currency order %}{% if not forloop.last %}, {% endif %} {% endfor %}
{{ package.name }} ({% get_banding package_costs package %}){% package_cost package_costs package converted_currency %}{% package_cost_multiplier package_costs order package converted_currency %}
- - - + + + {% for account in initiative.users.all %} @@ -40,7 +40,7 @@

Accounts

- + {% endfor %}
NameEmailUsername{% get_site_text 'name' %}{% get_site_text 'email' %}{% get_site_text 'username' %}
{{ account.profile.full_name }} {{ account.email }} {{ account.username }}
diff --git a/src/templates/initiatives/edit_initiative.html b/src/templates/initiatives/edit_initiative.html index 5b4adf4..63e8c8e 100644 --- a/src/templates/initiatives/edit_initiative.html +++ b/src/templates/initiatives/edit_initiative.html @@ -2,10 +2,10 @@ {% load static two_factor %} {% load django_bootstrap5 %} -{% block title %}Edit initiative{% endblock title %} +{% block title %}{% get_site_text 'edit_initiative' %}{% endblock title %} {% block breadcrumb %} - + {% endblock breadcrumb %} {% block content %} @@ -20,11 +20,11 @@ {% csrf_token %} {% bootstrap_form form %} {% if page %} {% endif %} diff --git a/src/templates/initiatives/initiative_packages.html b/src/templates/initiatives/initiative_packages.html index a3bb043..2239d21 100644 --- a/src/templates/initiatives/initiative_packages.html +++ b/src/templates/initiatives/initiative_packages.html @@ -1,12 +1,12 @@ {% extends "base/admin_base.html" %} {% block title %} -Initiatives +{% get_site_text 'initiatives' %} {% endblock title %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block dashboard_js %} @@ -19,10 +19,10 @@
-

Initiative Packages

-

Packages owned by {{ initiative.name }}

+

{% get_site_text 'initiative_packages' %}

+

{% get_site_text 'packages_owned_by' %} {{ initiative.name }}

- Add New Package + {% get_site_text 'add' %}

@@ -30,16 +30,16 @@

Initiative Packages

- - - - - - - - - - + + + + + + + + + + @@ -47,17 +47,17 @@

Initiative Packages

- - - + + + - - - - + + + + {% endfor %} diff --git a/src/templates/initiatives/list.html b/src/templates/initiatives/list.html index d62d716..07823d9 100644 --- a/src/templates/initiatives/list.html +++ b/src/templates/initiatives/list.html @@ -1,25 +1,25 @@ {% extends "base/admin_base.html" %} {% block title %} -Initiatives +{% get_site_text 'initiatives' %} {% endblock title %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block content %}
-

Initiative List

-

View, edit, delete and create initiative objects.

+

{% get_site_text 'initiative_list' %}

+

{% get_site_text 'initiative_list_helper' %}

NameActiveEditBandingsData FieldsDocumentsContactsStandardsAccess ControlDelete{% get_site_text 'name' %}{% get_site_text 'active' %}{% get_site_text 'edit' %}{% get_site_text 'bandings' %}{% get_site_text 'data_fields' %}{% get_site_text 'documents' %}{% get_site_text 'contacts' %}{% get_site_text 'standards' %}{% get_site_text 'access_control' %}{% get_site_text 'delete' %}
{{ package.name }} {{ package.active_display }}Edit PackageBandingsData Fields{% get_site_text 'edit' %}{% get_site_text 'bandings' %}{% get_site_text 'data_fields' %} - Documents
- Media Files + {% get_site_text 'documents' %}
+ {% get_site_text 'media_files' %}
ContactsStandardsAccess Control
{% csrf_token %}
{% get_site_text 'contacts' %}{% get_site_text 'standards' %}{% get_site_text 'access_control' %}
{% csrf_token %}
- - + + @@ -29,8 +29,8 @@

Initiative List

- - + + {% endfor %} diff --git a/src/templates/initiatives/user_initiatives.html b/src/templates/initiatives/user_initiatives.html index 83ecb35..ce4a12e 100644 --- a/src/templates/initiatives/user_initiatives.html +++ b/src/templates/initiatives/user_initiatives.html @@ -1,11 +1,11 @@ {% extends "base/admin_base.html" %} {% block title %} -Initiatives +{% get_site_text 'initiatives' %} {% endblock title %} {% block breadcrumb %} - + {% endblock breadcrumb %} {% block dashboard_js %} @@ -18,23 +18,23 @@
-

Initiative List

-

{% if request.user.is_staff %}As a staff member you will see all Initiatives listed here.{% else %}A list of Initiatives for which you are a manager.{% endif %}

+

{% get_site_text 'initiative_list' %}

+

{% if request.user.is_staff %}{% get_site_text 'initiatives_staff' %}{% else %}{% get_site_text 'initiatives_manager' %}{% endif %}

-
NameAccounts{% get_site_text 'name' %}{% get_site_text 'accounts' %}
{{ initiative.name }} {% for account in initiative.users.all %}{{ account.profile.full_name}}{% if not forloop.last %}, {% endif %}{% endfor %}View DetailDelete{% get_site_text 'view' %}{% get_site_text 'delete' %}
+
- - - - - - - - - - - + + + + + + + + + + + @@ -43,14 +43,14 @@

Initiative List

- - - - - - - - + + + + + + + + {% endfor %} diff --git a/src/templates/invoicing/detail_invoice.html b/src/templates/invoicing/detail_invoice.html index 4d2c350..e7e07ab 100644 --- a/src/templates/invoicing/detail_invoice.html +++ b/src/templates/invoicing/detail_invoice.html @@ -1,11 +1,11 @@ {% extends "base/admin_base.html" %} {% load django_bootstrap5 %} -{% block title %}Invoice Management - {{ payment_processor.name }}{% endblock %} +{% block title %}{% get_site_text 'invoice_management' %} - {{ payment_processor.name }}{% endblock %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block content %} @@ -13,18 +13,18 @@
-

Invoice Management - {{ payment_processor.name }}

-

Order {{ order.order_number }}

-

Listed here are the ordered packages that require invoicing. Note, these packages may not make up the whole order, but contain those that you are a payment processor for.

+

{% get_site_text 'invoice_management' %} - {{ payment_processor.name }}

+

{% get_site_text 'order' %} {{ order.order_number }}

+

{% get_site_text 'order_helper' %}

NameManagersActive# of PackagesAdd New PackageView PackagesFeatured BooksOrdersInvoicingPagesUpdates{% get_site_text 'name' %}{% get_site_text 'managers' %}{% get_site_text 'active' %}{% get_site_text 'add' %}{% get_site_text 'view' %}{% get_site_text 'featured_books' %}{% get_site_text 'orders' %}{% get_site_text 'invoicing' %}{% get_site_text 'pages' %}{% get_site_text 'updates' %}
{{ initiative.name }} {% for account in initiative.users.all %}{{ account.profile.full_name }}{% if not forloop.last %}, {% endif %}{% endfor %} {{ initiative.active_display }}{{ initiative.packages.count }}Add New PackageView PackagesFeatured BooksOrdersInvoicingManage PagesManage Updates{% get_site_text 'add' %}{% get_site_text 'view' %}{% get_site_text 'featured_books' %}{% get_site_text 'orders' %}{% get_site_text 'invoicing' %}{% get_site_text 'pages' %}{% get_site_text 'updates' %}
- - - - - + + + + + @@ -37,7 +37,7 @@

Order {{ order.order_number }}

{% if signup.signupinvoice %} {{ signup.signupinvoice.last_status_update.get_status_display }} - {{ signup.signupinvoice.last_status_update.date_time|date:"Y-m-d H:i" }} {% else %} - No status updates + {% get_site_text 'no_status_updates' %} {% endif %}
Package IDPackage NamePriceLatest Status ChangeChange Status{% get_site_text 'id' %}{% get_site_text 'name' %}{% get_site_text 'price' %}{% get_site_text 'latest_status_change' %}{% get_site_text 'change_status' %}
@@ -45,9 +45,9 @@

Order {{ order.order_number }}

{% csrf_token %}
@@ -56,7 +56,7 @@

Order {{ order.order_number }}

{% csrf_token %} {% endif %} diff --git a/src/templates/invoicing/detail_invoice_initiative.html b/src/templates/invoicing/detail_invoice_initiative.html index 177ed0b..3d95341 100644 --- a/src/templates/invoicing/detail_invoice_initiative.html +++ b/src/templates/invoicing/detail_invoice_initiative.html @@ -1,11 +1,11 @@ {% extends "base/admin_base.html" %} {% load django_bootstrap5 %} -{% block title %}Invoice Management - {{ initiative.name }}{% endblock %} +{% block title %}{% get_site_text 'invoice_management' %} - {{ initiative.name }}{% endblock %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block content %} @@ -13,18 +13,18 @@
-

Invoice Management - {{ initiative.name }}

-

Order {{ order.order_number }}

-

Listed here are the ordered packages that require invoicing. Note, these packages may not make up the whole order, but contain those that you are a payment processor for.

+

{% get_site_text 'invoice_management' %} - {{ initiative.name }}

+

{% get_site_text 'order' %} {{ order.order_number }}

+

{% get_site_text 'order_helper' %}

- - - - - + + + + + @@ -37,7 +37,7 @@

Order {{ order.order_number }}

{% if signup.signupinvoice %} {{ signup.signupinvoice.last_status_update.get_status_display }} - {{ signup.signupinvoice.last_status_update.date_time|date:"Y-m-d H:i" }} {% else %} - No status updates + {% get_site_text 'no_status_updates' %} {% endif %}
Package IDPackage NamePriceLatest Status ChangeChange Status{% get_site_text 'id' %}{% get_site_text 'name' %}{% get_site_text 'price' %}{% get_site_text 'latest_status_change' %}{% get_site_text 'change_status' %}
@@ -45,9 +45,9 @@

Order {{ order.order_number }}

{% csrf_token %}
@@ -56,7 +56,7 @@

Order {{ order.order_number }}

{% csrf_token %} {% endif %} diff --git a/src/templates/invoicing/edit_payment_processors.html b/src/templates/invoicing/edit_payment_processors.html index 6a24fd7..191a043 100644 --- a/src/templates/invoicing/edit_payment_processors.html +++ b/src/templates/invoicing/edit_payment_processors.html @@ -1,11 +1,11 @@ {% extends "base/admin_base.html" %} {% load crispy_forms_tags %} -{% block title %}{% if processor %}Edit Payment Processor{% else %}Add New Payment Processor{% endif %}{% endblock %} +{% block title %}{% if processor %}{% get_site_text 'edit_payment_processor' %}{% else %}{% get_site_text 'add_payment_processor' %}{% endif %}{% endblock %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block content %} @@ -13,12 +13,12 @@
-

{% if processor %}Edit Payment Processor{% else %}Add New Payment Processor{% endif %}

-

Hold control (ctrl) to select multiple managers and countries.

+

{% if processor %}{% get_site_text 'edit_payment_processor' %}{% else %}{% get_site_text 'add_payment_processor' %}{% endif %}

+

{% get_site_text 'hold_ctrl' %}

{% csrf_token %} {{ form|crispy }} - +
diff --git a/src/templates/invoicing/list_order_invoices.html b/src/templates/invoicing/list_order_invoices.html index c2a0025..0d6316e 100644 --- a/src/templates/invoicing/list_order_invoices.html +++ b/src/templates/invoicing/list_order_invoices.html @@ -1,10 +1,10 @@ {% extends "base/admin_base.html" %} {% load django_bootstrap5 invoicing %} -{% block title %}Invoice Management - {{ payment_processor.name }}{% endblock %} +{% block title %}{% get_site_text 'invoice_management' %} - {{ payment_processor.name }}{% endblock %} {% block breadcrumb %} - + {% endblock breadcrumb %} {% block content %} @@ -12,19 +12,19 @@
-

Invoice Management - {{ payment_processor.name }}

-

Listed below are the orders that contain a package for which you are listed as one of the payment processors for. You will be able to see the details of the order that relate to you including billing information and agreed pricing.

+

{% get_site_text 'invoice_management' %} - {{ payment_processor.name }}

+

{% get_site_text 'order_helper_invoices' %}

{% csrf_token %} - - - - - - + + + + + + @@ -44,9 +44,9 @@

Invoice Management - {{ payment_processor.name }}

Order NumberCustomerDateStatusInvoice StatusesView{% get_site_text 'id' %}{% get_site_text 'customer' %}{% get_site_text 'date_time' %}{% get_site_text 'status' %}{% get_site_text 'invoice_statuses' %}{% get_site_text 'view' %}
- - - + + + {% for signup in order_signups %} @@ -59,7 +59,7 @@

Invoice Management - {{ payment_processor.name }}

{% endfor %} diff --git a/src/templates/invoicing/list_order_invoices_initiative.html b/src/templates/invoicing/list_order_invoices_initiative.html index d62b35a..8837011 100644 --- a/src/templates/invoicing/list_order_invoices_initiative.html +++ b/src/templates/invoicing/list_order_invoices_initiative.html @@ -1,10 +1,10 @@ {% extends "base/admin_base.html" %} {% load django_bootstrap5 invoicing %} -{% block title %}Invoice Management - {{ initiative.name }}{% endblock %} +{% block title %}{% get_site_text 'invoice_management' %} - {{ initiative.name }}{% endblock %} {% block breadcrumb %} - + {% endblock breadcrumb %} {% block content %} @@ -12,19 +12,19 @@
-

Invoice Management - {{ initiative.name }}

-

Listed below are the orders that contain a package for which you are listed as one of the payment processors for. You will be able to see the details of the order that relate to you including billing information and agreed pricing.

+

{% get_site_text 'invoice_management' %} - {{ initiative.name }}

+

{% get_site_text 'order_helper_invoices' %}

{% csrf_token %}
PackageAmountStatus{% get_site_text 'package' %}{% get_site_text 'amount' %}{% get_site_text 'status' %}
View Details + class="btn btn-primary btn-sm">{% get_site_text 'view' %}
- - - - - - + + + + + + @@ -44,9 +44,9 @@

Invoice Management - {{ initiative.name }}

Order NumberCustomerDateStatusInvoice StatusesView{% get_site_text 'id' %}{% get_site_text 'customer' %}{% get_site_text 'date_time' %}{% get_site_text 'status' %}{% get_site_text 'invoice_statuses' %}{% get_site_text 'view' %}
- - - + + + {% for signup in order_signups %} @@ -59,7 +59,7 @@

Invoice Management - {{ initiative.name }}

{% endfor %} diff --git a/src/templates/invoicing/list_payment_processors.html b/src/templates/invoicing/list_payment_processors.html index cfdbfed..c43d67a 100644 --- a/src/templates/invoicing/list_payment_processors.html +++ b/src/templates/invoicing/list_payment_processors.html @@ -1,10 +1,10 @@ {% extends "base/admin_base.html" %} {% load django_bootstrap5 %} -{% block title %}Payment Processors{% endblock %} +{% block title %}{% get_site_text 'payment_processors' %}{% endblock %} {% block breadcrumb %} - + {% endblock breadcrumb %} {% block content %} @@ -12,17 +12,17 @@
-

Payment Processors

-

Add New Payment Processor

+

{% get_site_text 'payment_processors' %}

+

{% get_site_text 'add' %}

{% csrf_token %}
PackageAmountStatus{% get_site_text 'package' %}{% get_site_text 'amount' %}{% get_site_text 'status' %}
View Details + class="btn btn-primary btn-sm">{% get_site_text 'view' %}
- - - - + + + + @@ -34,8 +34,8 @@

Payment Processors

{{ country }}{% if not forloop.last %}, {% endif %} {% endfor %} - - + + {% endfor %} diff --git a/src/templates/mail/list_log_entries.html b/src/templates/mail/list_log_entries.html index 76bf012..3bc1111 100644 --- a/src/templates/mail/list_log_entries.html +++ b/src/templates/mail/list_log_entries.html @@ -2,8 +2,8 @@ {% load static %} {% block breadcrumb %} - - + + {% endblock %} {% block dashboard_js %} @@ -19,11 +19,11 @@
-

{% block title %}Email Log Entries{% endblock %}

-

This page allows platform managers to view email logs.

-

= Success | = Failed | = Sending

+

{% block title %}{% get_site_text 'email_log_entries' %}{% endblock %}

+

{% get_site_text 'email_logs_description' %}

+

= {% get_site_text 'success' %} | = {% get_site_text 'failed' %} | = {% get_site_text 'sending' %}

- Update Statuses

+ {% get_site_text 'update_statuses' %}

@@ -32,10 +32,10 @@

{% block title %}Email Log Entries{% endblock %}

- - - - + + + + diff --git a/src/templates/orders/order_detail.html b/src/templates/orders/order_detail.html index 330cf9e..f2c18c8 100644 --- a/src/templates/orders/order_detail.html +++ b/src/templates/orders/order_detail.html @@ -36,13 +36,15 @@

Basic InformationOrder Date -

- + + + - - + + + @@ -89,7 +91,7 @@

Packages

- + @@ -98,7 +100,7 @@

Packages

- +
NameCountriesDeleteEdit{% get_site_text 'name' %}{% get_site_text 'countries' %}{% get_site_text 'delete' %}{% get_site_text 'edit' %}
{% csrf_token %}Edit
{% csrf_token %}
{% get_site_text 'edit' %}
ToSubjectStatusDate{% get_site_text 'to' %}{% get_site_text 'subject' %}{% get_site_text 'status' %}{% get_site_text 'date' %}
Order Valid PeriodInitiativeOrder Valid PeriodInitiativeTerm Length
{{ order.order_date }}{{ order.valid_period }}{{ order.initiative }}{{ order.valid_period }}{{ order.initiative }}{% widthratio order.term 1 12 %} {% get_site_text "months" %}
Documents
Package Name Package InitiativeCostCost over Term ({% widthratio order.term 1 12 %} {% get_site_text "months" %}) Payment Processor Invoice Status Remove
{{ signup.associated_package.name }} {{ signup.associated_package.initiative.name }}{{ signup.formatted_price }}{{ signup.formatted_price_multiplier }} {% if signup.payment_processor.payment_processor %} {{ signup.payment_processor.payment_processor }} diff --git a/src/templates/package/access_control.html b/src/templates/package/access_control.html index f8883fa..d32fbaa 100644 --- a/src/templates/package/access_control.html +++ b/src/templates/package/access_control.html @@ -1,12 +1,12 @@ {% extends "base/admin_base.html" %} {% block title %} -Initiatives +{% get_site_text 'initiatives' %} {% endblock title %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block dashboard_js %} @@ -19,24 +19,24 @@
-

Signups and Access Control

-

Some types of open access deal have unique perks for specific institutions (e.g. Opening the Future). This page allows for the generation of an access control list for subscription portions of such programmes.

-

The access control list is downloadable by all contacts.

-

You can also export this data to CSV. -
(Warning: This file may contain sensitive personal data subject to GDPR or other data protection legislation.)

+

{% get_site_text 'signups_and_access_control' %}

+

{% get_site_text 'access_control_lead' %}

+

{% get_site_text 'access_control_download' %} {% get_site_text 'all_contacts' %}.

+

{% get_site_text 'export_csv' %} {% get_site_text 'export_csv_link' %}. +
{% get_site_text 'gdpr_warning' %}



- - - - - - - + + + + + + + @@ -46,9 +46,9 @@

Signups and Access Control

- + - + {% endfor %} diff --git a/src/templates/package/banding/create.html b/src/templates/package/banding/create.html index 706afba..ada634f 100644 --- a/src/templates/package/banding/create.html +++ b/src/templates/package/banding/create.html @@ -1,25 +1,23 @@ {% extends "base/admin_base.html" %} {% load crispy_forms_tags %} -{% block title %}Create Banding Type{% endblock %} -{% block page-title %}Create Banding Type{% endblock %} +{% block title %}{% get_site_text 'create_banding_type' %}{% endblock %} +{% block page-title %}{% get_site_text 'create_banding_type' %}{% endblock %} {% block breadcrumb %} - - - + + + {% endblock %} {% block content %}
-

- Create a new Banding Type by providing required metadata. Once the banding type has been created you can select the relevant banding vocabularies. -

+

{% get_site_text 'create_banding_type_description' %}

{% csrf_token %} {{ form|crispy }} - +
diff --git a/src/templates/package/banding/package_banding.html b/src/templates/package/banding/package_banding.html index 77acca8..245bb42 100644 --- a/src/templates/package/banding/package_banding.html +++ b/src/templates/package/banding/package_banding.html @@ -1,14 +1,14 @@ {% extends "base/admin_base.html" %} {% block title %} -Initiatives +{% get_site_text 'initiatives' %} {% endblock title %} {% block breadcrumb %} - - - - + + + + {% endblock breadcrumb %} {% block dashboard_js %} @@ -21,16 +21,16 @@
-

Bandings for “{{ package.name }}”

+

{% get_site_text 'bandings_for' %} “{{ package.name }}”

{% if not has_default %}
-

This package does not have pricing information set for the default currency ({{ package.default_country.currency }}).

+

{% get_site_text 'no_pricing_info' %} ({{ package.default_country.currency }}).

{% endif %} -

The following bandings are associated with this package.

+

{% get_site_text 'bandings_associated' %}

{% csrf_token %} - {{ bandings_list }} + {{ bandings_list }}

@@ -38,19 +38,19 @@

Bandings for “{{ package.name }}”

PackageInitiativePriceUserView DataSignup DateAccess{% get_site_text 'package' %}{% get_site_text 'initiative' %}{% get_site_text 'price' %}{% get_site_text 'user' %}{% get_site_text 'view_data' %}{% get_site_text 'signup_date' %}{% get_site_text 'access' %}
{{ signup.associated_package.initiative.name }} {{ signup.formatted_price }} {{ signup.associated_user.first_name }} {{ signup.associated_user.last_name }}View Data{% get_site_text 'view_data' %} {{ signup.signup_date }}{% if signup.access %}Revoke{% else %}Grant{% endif %}{% if signup.access %}{% get_site_text 'revoke' %}{% else %}{% get_site_text 'grant' %}{% endif %}
- - - - + + + + {% for banding in bandings %} - - - + + + {% endfor %} @@ -60,8 +60,8 @@

Bandings for “{{ package.name }}”

-

Basic Price Overview

-

Here you can see, at a glance, all prices associated with this package.

+

{% get_site_text 'basic_price_overview' %}

+

{% get_site_text 'price_overview_description' %}

diff --git a/src/templates/package/banding/package_banding_currency.html b/src/templates/package/banding/package_banding_currency.html index 4bcd0a9..8c841e6 100644 --- a/src/templates/package/banding/package_banding_currency.html +++ b/src/templates/package/banding/package_banding_currency.html @@ -1,14 +1,14 @@ {% extends "base/admin_base.html" %} {% block title %} -Initiatives +{% get_site_text 'initiatives' %} {% endblock title %} {% block breadcrumb %} - - + + - + {% endblock breadcrumb %} @@ -22,11 +22,11 @@
-

Currencies for {{ banding_type_entry.banding_type.name }} in “{{ package.name }}”

-

The following currencies are associated with this banding.

+

{% get_site_text 'currencies_for' %} {{ banding_type_entry.banding_type.name }} {% get_site_text 'in' %} “{{ package.name }}”

+

{% get_site_text 'currencies_associated' %}

{% csrf_token %} - {{ country_list }} + {{ country_list }}

@@ -34,19 +34,19 @@

Currencies for {{ banding_type_entry.banding_type.name }} in “{{ package.n

NameEdit CurrenciesRemove Banding and PricesEdit Redirect{% get_site_text 'name' %}{% get_site_text 'edit_currencies' %}{% get_site_text 'remove_banding_and_prices' %}{% get_site_text 'edit' %}
{{ banding.banding_type.name }}Edit Currencies
{% csrf_token %}
Edit Redirect{% get_site_text 'edit_currencies' %}
{% csrf_token %}
{% get_site_text 'edit' %}
- - - - + + + + {% for banding_currency in banding_type_currencies %} - - - + + + {% endfor %} @@ -61,7 +61,7 @@

Currencies for {{ banding_type_entry.banding_type.name }} in “{{ package.n @@ -69,23 +69,23 @@

NameEdit PricesRemove Currency and PricesPayment Processor{% get_site_text 'name' %}{% get_site_text 'edit_prices' %}{% get_site_text 'remove_currency_and_prices' %}{% get_site_text 'payment_processor' %}
{{ banding_currency.country }}Edit Prices
{% csrf_token %}
{{ banding_currency.payment_processor|default_if_none:"Select a Payment Processor" }}{% get_site_text 'edit_prices' %}
{% csrf_token %}
{{ banding_currency.payment_processor|default_if_none:"{% get_site_text 'select_payment_processor' %}" }}
- - - - + + + + @@ -49,4 +47,4 @@
NameIs FTE?Active?# Linked Vocabs{% get_site_text 'name' %}{% get_site_text 'is_fte' %}{% get_site_text 'active' %}{% get_site_text 'linked_vocabs' %}
-{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/banding/vocabs.html b/src/templates/package/banding/vocabs.html index 8f13328..b29c934 100644 --- a/src/templates/package/banding/vocabs.html +++ b/src/templates/package/banding/vocabs.html @@ -1,13 +1,13 @@ {% extends "base/admin_base.html" %} {% load booleans %} -{% block title %}Manage Banding Type Vocabs for {{ banding_type.name }}{% endblock %} -{% block page-title %}Manage Banding Type Vocabs for {{ banding_type.name }}{% endblock %} +{% block title %}{% get_site_text 'manage_banding_type_vocabs_for' %} {{ banding_type.name }}{% endblock %} +{% block page-title %}{% get_site_text 'manage_banding_type_vocabs_for' %} {{ banding_type.name }}{% endblock %} {% block breadcrumb %} - - - + + + {% endblock %} {% block dashboard_js %} @@ -23,24 +23,18 @@
-

- Select vocabs for this Banding Type. When a Initiative creates packages they will use these as the basis for their Banding/Prices.
- Warning: Affiliating new bandings will generate a default price of zero where initiatives have not yet set a price.
- Warning: Removing a vocabulary from a band where prices are set will delete those prices. -

-

- Add a new vocabulary -

+

{% get_site_text 'select_vocabs_description' %}

+

{% get_site_text 'add_new_vocabulary' %}

{% csrf_token %} - - - - + + + + @@ -56,7 +50,7 @@
TextUpper LimitLower LimitIs FTE?{% get_site_text 'text' %}{% get_site_text 'upper_limit' %}{% get_site_text 'lower_limit' %}{% get_site_text 'is_fte' %}

- +

diff --git a/src/templates/package/basket.html b/src/templates/package/basket.html index cf5fca5..faddf09 100644 --- a/src/templates/package/basket.html +++ b/src/templates/package/basket.html @@ -2,15 +2,15 @@ {% block breadcrumb %}
    - +
{% endblock %} {% block content %}
-

Quotes

-

A list of your uncompleted quotations.

+

{% get_site_text 'quotes' %}

+

{% get_site_text 'uncompleted_quotations' %}

-{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/change_access.html b/src/templates/package/change_access.html index b1d68d8..a614cc8 100644 --- a/src/templates/package/change_access.html +++ b/src/templates/package/change_access.html @@ -3,9 +3,9 @@ {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block content %} @@ -14,15 +14,15 @@
-

Add an Access Control Entry

-

Add a new access control instruction.

- Warning: These entries cannot be deleted, but they can be reversed by adding the opposite instruction (i.e. revoke). +

{% get_site_text 'add_access_control_entry' %}

+

{% get_site_text 'add_access_control_instruction' %}

+ {% get_site_text 'access_control_warning' %}
{% csrf_token %}
{{ form|crispy }} - +
@@ -30,4 +30,4 @@

Add an Access Control Entry

-{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/checkout/new_order_complete.html b/src/templates/package/checkout/new_order_complete.html index 55f470f..efec6b0 100644 --- a/src/templates/package/checkout/new_order_complete.html +++ b/src/templates/package/checkout/new_order_complete.html @@ -4,16 +4,16 @@ {% block css %} - + {% endblock %} {% block title %} - Order No. {{ order.pk }} + {% get_site_text 'order_number' %} {{ order.pk }} {% endblock title %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} @@ -22,9 +22,9 @@

{% get_site_text 'completed_order' %}

-

Thanks for your interest in supporting Open Access book publishing. Your quotation is now complete. Your reference number is {{ order.pk }}.

+

{% get_site_text 'order_complete_message' %} {{ order.pk }}.

-
Quotations logo
@@ -34,10 +34,10 @@

{% get_site_text 'completed_order' %}

- {% get_site_text 'order_complete_body' %} -

Quotation Details

+ {% get_site_text 'order_complete_number' %} {{ order.order_number }} {% get_site_text 'order_complete_body_partial' %} +

{% get_site_text 'quotation_details' %}

{% get_site_text 'order_documents' %} - Download Quotation + {% get_site_text 'download_quotation' %}
@@ -47,8 +47,8 @@

Quotation Details

- Build your quote image + {% get_site_text 'build_your_quote' %}

{% get_site_text 'completed_order' %}

@@ -57,15 +57,15 @@

{% get_site_text 'completed_order' %}

-

Thanks for your interest in supporting Open Access book publishing. Your quotation is now complete. Your reference number is {{ order.pk }}.

+

{% get_site_text 'thanks_for_your_interest' %} {{ order.pk }}.

{% endblock header-mobile %} {% block body-mobile %} - {% get_site_text 'order_complete_body' %} + {% get_site_text 'order_complete_number' %} {{ order.order_number }} {% get_site_text 'order_complete_body_partial' %}

Quotation Details

{% get_site_text 'order_documents' %} - Download Quotation + {% get_site_text 'download_quotation' %} {% endblock body-mobile %} diff --git a/src/templates/package/checkout/order_complete.html b/src/templates/package/checkout/order_complete.html index a6115ad..8ca010c 100644 --- a/src/templates/package/checkout/order_complete.html +++ b/src/templates/package/checkout/order_complete.html @@ -4,16 +4,16 @@ {% block css %} - + {% endblock %} {% block title %} - Order No. {{ order.order_number }} + {% get_site_text 'order_number' %} {{ order.order_number }} {% endblock title %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} @@ -22,11 +22,9 @@

{% get_site_text 'completed_order' %}

-

Your order with order number {{ order.order_number }} is now complete. If you - require assistance with your order - ensure you quote this order number.

+

{% get_site_text 'order_complete_body' %}

-
Quotations logo
@@ -36,21 +34,20 @@

{% get_site_text 'completed_order' %}

-

{% get_site_text 'order_docs' %}

+

{% get_site_text 'order_documents_button' %}

- {% get_site_text 'order_docs_info' %} + {% get_site_text 'order_documents_info' %}

- Download - Order Documents + {% get_site_text 'download_order_documents' %}

- {% get_site_text 'order_com_contact' %} + {% get_site_text 'order_complete_contact' %}
- - - + + + {% for k, row in contact_rows.items %} @@ -68,12 +65,12 @@

{% get_site_text 'order_approval' %}

{% csrf_token %}
InitiativePackage(s)Contact Email{% get_site_text 'initiative' %}{% get_site_text 'packages' %}{% get_site_text 'contact_email' %}
- - - - - - + + + + + + {% for signup in order.packagesignup_set.all %} @@ -99,7 +96,7 @@

{% get_site_text 'order_approval' %}

{% if signup.associated_package.mediafile_set.all %} {% for package in basket.list_of_conflicting_packages %} @@ -104,9 +104,9 @@

{% get_site_text 'quotation_packages' %}

{% endif %} @@ -128,14 +128,14 @@

{% get_site_text 'quotation_packages' %}

{% if converted_total %} {% endif %} {% if site_percentage %} {% endif %} @@ -170,21 +170,22 @@

{% get_site_text 'quotation_packages' %}

{% endif %} {% if has_all_prices and basket.package_set %} -

{% get_site_text 'has_all_prices_head' %}

- {% get_site_text 'has_all_prices_text' %} +

{% get_site_text 'has_all_prices_head' prefetched %}

+ {% get_site_text 'has_all_prices_text' prefetched %} + {% get_site_text 'about_term_length_next_page' prefetched %} {% csrf_token %} -

+

{% endif %}
-

{% get_site_text 'quotation_additional' %}

- {% get_site_text 'quotation_add_text' %} -

Add

+

{% get_site_text 'quotation_additional' prefetched %}

+ {% get_site_text 'quotation_add_text' prefetched %} +

{% get_site_text 'add' prefetched %}

{% if basket.package_set %} - {% include "package/elements/basket_form.html" %} + {% include "package/elements/basket_form.html" with prefetched=prefetched%} {% endif %} {% if mobile %} @@ -205,4 +206,4 @@

Want to discuss your quote?

{% endif %} - \ No newline at end of file + diff --git a/src/templates/package/elements/view_basket_mobile.html b/src/templates/package/elements/view_basket_mobile.html index faea3af..59fc364 100644 --- a/src/templates/package/elements/view_basket_mobile.html +++ b/src/templates/package/elements/view_basket_mobile.html @@ -8,8 +8,8 @@

{% get_site_text 'quotation_packages' %}

{% csrf_token %}
Package NameCostApproved by Initiative?ApprovalPayment ProcessorInvoice Status{% get_site_text 'package_name' %}{% get_site_text 'cost' %}{% get_site_text 'approved_by_initiative' %}{% get_site_text 'approval' %}{% get_site_text 'payment_processor' %}{% get_site_text 'invoice_status' %}
- Package Media Files: + {% get_site_text 'package_media_files' %}: {% for file in signup.associated_package.mediafile_set.all %} {{ file.name }} {% if not forloop.last %}, {% endif %} @@ -121,8 +118,8 @@

{% get_site_text 'order_approval' %}

- Build your quote image + {% get_site_text 'build_your_quote' %}

{% get_site_text 'completed_order' %}

@@ -131,9 +128,7 @@

{% get_site_text 'completed_order' %}

-

Your order with order number {{ order.order_number }} is now complete. If you require assistance with - your order - ensure you quote this order number.

+

{% get_site_text 'your_order_pre' %} {{ order.order_number }} {% get_site_text 'your_order_post' %}

@@ -144,15 +139,14 @@

{% get_site_text 'completed_order' %}

{% get_site_text 'order_docs' %}

{% get_site_text 'order_docs_info' %}

- Download Order - Documents + {% get_site_text 'download_order_documents' %}

{% get_site_text 'order_com_contact' %} - - - + + + {% for k, row in contact_rows.items %} @@ -169,10 +163,10 @@

{% get_site_text 'order_approval' %}

{% csrf_token %}
InitiativePackage(s)Contact Email{% get_site_text 'initiative' %}{% get_site_text 'package' %}{% get_site_text 'email' %}
- - - - + + + + {% for signup in order.packagesignup_set.all %} diff --git a/src/templates/package/checkout/order_form.html b/src/templates/package/checkout/order_form.html index a45d109..958cbef 100644 --- a/src/templates/package/checkout/order_form.html +++ b/src/templates/package/checkout/order_form.html @@ -13,7 +13,7 @@ {% block breadcrumb %} - + {% endblock breadcrumb %} diff --git a/src/templates/package/checkout/order_provisional.html b/src/templates/package/checkout/order_provisional.html index f1124b2..24d85cb 100644 --- a/src/templates/package/checkout/order_provisional.html +++ b/src/templates/package/checkout/order_provisional.html @@ -4,16 +4,16 @@ {% block css %} - + {% endblock %} {% block title %} - Order No. {{ order.order_number }} + {% get_site_text 'order_number' %} {{ order.order_number }} {% endblock title %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} @@ -21,12 +21,12 @@
-

Your quotation

+

{% get_site_text 'your_quotation' %}

- We have generated a quotation for you to review, with a unique quotation number {{ order.order_number }}. Please refer to this quotation number if you would like to discuss any details with us. + {% get_site_text 'quotation_generated' %} {{ order.order_number }}. {% get_site_text 'quotation_reference' %}

-
Quotations logo
@@ -38,23 +38,19 @@

Your quotation

{% get_site_text 'order_text_1' %}

- Download - Order - Documents + {% get_site_text 'download_order_documents' %}

{% get_site_text 'order_text_2' %}
Package NameCostApproved by Initiative?Approval{% get_site_text 'package' %}{% get_site_text 'cost' %}{% get_site_text 'approved_by_initiative' %}{% get_site_text 'approval' %}
- - - + + + {% for k, row in contact_rows.items %} - + {% endfor %} @@ -66,11 +62,11 @@

Your quotation

{% csrf_token %}
InitiativePackage(s)Contact Email{% get_site_text 'initiative' %}{% get_site_text 'packages' %}{% get_site_text 'contact_email' %}
{{ row.initiative.name }}{% for package in row.packages %}{% if forloop.last and not forloop.first %} & - {% elif not forloop.first and not forloop.last %}, {% endif %} - {{ package.name }}{% endfor %}{% for package in row.packages %}{{ package.name }}{% if not forloop.last %}, {% endif %}{% endfor %} {{ row.contact_email }}
- - - - - + + + + + {% for signup in order.packagesignup_set.all %} @@ -80,7 +76,7 @@

Your quotation

{% if signup.initiative_approved %} {{ signup.initiative_approved }} {% else %} - Not approved + {% get_site_text 'not_approved' %} {% endif %} @@ -113,9 +109,9 @@

Your quotation

- Build your quote image -

Your quotation

+

{% get_site_text 'your_quotation' %}

@@ -123,7 +119,7 @@

Your quotation

-

We have generated a quotation for you to review, with a unique quotation number {{ order.order_number }}. Please refer to this quotation number if you would like to discuss any details with us.

+

{% get_site_text 'quotation_generated' %} {{ order.order_number }}. {% get_site_text 'quotation_reference' %}

@@ -132,53 +128,39 @@

Your quotation

{% block body-mobile %}
-

Order Documents

-

- We've zipped up the documents for all of the packages you are signing up for. Please read them and then - either - approve the signup below or contact the initiative to discuss the terms. Also, some types of signup (those going through Jisc Collections) require you to input your details on an external site. Please check the links below carefully under the "external" column. -

+

{% get_site_text 'order_documents_button' %}

+

{% get_site_text 'order_documents_description' %}

- Download - Order - Documents + {% get_site_text 'download_order_documents' %}

-

Contact Details

-

Once you have read the order documents you can contact the initiatives to - discuss - them or approve the signup. If necessary the package manager can upload new terms. Once you and the - package - manager have both approved the signup terms will be locked in place.

+

{% get_site_text 'contact_details' %}

+

{% get_site_text 'contact_details_description' %}

Package NameCostApproved by Provider?ApprovalExternal{% get_site_text 'package_name' %}{% get_site_text 'cost' %}{% get_site_text 'approved_by_provider' %}{% get_site_text 'approval' %}{% get_site_text 'external' %}
@@ -89,7 +85,7 @@

Your quotation

{% else %} {% endif %}
- - - + + + {% for k, row in contact_rows.items %} - + {% endfor %}
InitiativePackage(s)Contact Email{% get_site_text 'initiative' %}{% get_site_text 'packages' %}{% get_site_text 'contact_email' %}
{{ row.initiative.name }}{% for package in row.packages %}{% if forloop.last and not forloop.first %} & - {% elif not forloop.first and not forloop.last %}, {% endif %} - {{ package.name }}{% endfor %}{% for package in row.packages %}{{ package.name }}{% if not forloop.last %}, {% endif %}{% endfor %} {{ row.contact_email }}
-

Approval

-

When you are happy with the package terms you can mark the signup as - approved. You - may need to wait for the Initiative to approve the signup also in order to finalise the signup.

+

{% get_site_text 'approval' %}

+

{% get_site_text 'approval_description' %}

{% csrf_token %} - - - - - + + + + + {% for signup in order.packagesignup_set.all %} @@ -188,7 +170,7 @@

Approval

{% if signup.initiative_approved %} {{ signup.initiative_approved }} {% else %} - Not approved + {% get_site_text 'not_approved' %} {% endif %} diff --git a/src/templates/package/create_form_element.html b/src/templates/package/create_form_element.html index 397f773..17faad1 100644 --- a/src/templates/package/create_form_element.html +++ b/src/templates/package/create_form_element.html @@ -3,12 +3,12 @@ {% load crispy_forms_tags %} -{% block title %}New Form Element{% endblock title %} +{% block title %}{% get_site_text 'new_form_element' %}{% endblock title %} {% block breadcrumb %} - - - + + + {% endblock breadcrumb %} {% block content %} @@ -16,7 +16,7 @@
-

Add a new or edit an existing form element

+

{% get_site_text 'add_edit_form_element' %}

{% csrf_token %} @@ -44,7 +44,7 @@
- +
@@ -52,4 +52,4 @@
-{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/debugger.html b/src/templates/package/debugger.html index e0c96f1..b39c4a8 100644 --- a/src/templates/package/debugger.html +++ b/src/templates/package/debugger.html @@ -3,15 +3,15 @@ {% load crispy_forms_tags %} {% block title %} - Price Debugger + {% get_site_text 'price_debugger' %} {% endblock title %} {% block page-title %} - Price Debugger + {% get_site_text 'price_debugger' %} {% endblock page-title %} {% block breadcrumb %} - + {% endblock %} {% block content %} @@ -25,41 +25,41 @@ {% csrf_token %}
-

The pricing structure in OBC is complicated. This tool allows you to test your pricing logic works as expected.

+

{% get_site_text 'debugger_intro' %}

-

Understanding how OBC pricings are controlled is a prerequisite:

+

{% get_site_text 'debugger_prerequisite' %}

    -
  1. Platform Managers at OBC setup bandings that have tiers that are either banded or based on FTE (“Jisc Collections” → “Band 1”, “Band 2” / “Carnegie” → “Big (1000 FTE)”, “Medium (500 FTE)”, “Small (100 FTE)”).
  2. -
  3. Packages have associated bandings (“Open Book Publishers Membership” can say it uses “Jisc” and “Carnegie”).
  4. -
  5. Packages can specify which currencies they provide an associated banding in. So Jisc Collections, “Band 1”, could be paid for in GBP or USD.
  6. -
  7. Packages can set the default banding that they wish to use.
  8. -
  9. Packages set prices for currency-associated-bandings (BandingTypeCurrencyEntries). (E.g. a price for Jisc Band 1 in GBP).
  10. -
  11. Payment processors are associated with a currency-associated banding. So LYRASIS are set to handle all prices in Jisc Band 1 in USD.
  12. +
  13. {% get_site_text 'debugger_platform_managers' %}
  14. +
  15. {% get_site_text 'debugger_packages_associated_bandings' %}
  16. +
  17. {% get_site_text 'debugger_packages_specify_currencies' %}
  18. +
  19. {% get_site_text 'debugger_packages_set_default_banding' %}
  20. +
  21. {% get_site_text 'debugger_packages_set_prices' %}
  22. +
  23. {% get_site_text 'debugger_payment_processors_associated' %}
-

The default flow for applying prices is:

+

{% get_site_text 'debugger_flow_intro' %}

    -
  1. Find any/all prices in the user’s preferred currency.
  2. -
  3. Check if we can match this against the user’s stored information (FTE/bandings).
  4. -
  5. If not, fall back to package default banding and currency.
  6. -
  7. Check if we can match this against the user’s stored information (FTE/bandings).
  8. -
  9. If not, ask user to give their details.
  10. +
  11. {% get_site_text 'debugger_find_prices' %}
  12. +
  13. {% get_site_text 'debugger_check_match_user_info' %}
  14. +
  15. {% get_site_text 'debugger_fallback_default_banding' %}
  16. +
  17. {% get_site_text 'debugger_check_match_user_info' %}
  18. +
  19. {% get_site_text 'debugger_ask_user_details' %}

- +

@@ -67,7 +67,7 @@ {% if result %}
-

Debug result

+

{% get_site_text 'debug_result' %}

@@ -77,4 +77,4 @@
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/src/templates/package/documents/add_document.html b/src/templates/package/documents/add_document.html index 4772f53..2fdc378 100644 --- a/src/templates/package/documents/add_document.html +++ b/src/templates/package/documents/add_document.html @@ -3,12 +3,12 @@ {% block breadcrumb %} - - + - - + + {% endblock breadcrumb %} {% block content %} @@ -16,20 +16,20 @@
-

{% if revision %}Revise Document{% else %}Add New Document{% endif %}

+

{% if revision %}{% get_site_text 'revise_document' %}{% else %}{% get_site_text 'add_new_document' %}{% endif %}

{% if revision %} -

Revise document for {{ package.name }}

+

{% get_site_text 'revise_document_for' %} {{ package.name }}

{% else %} -

Add a new document to {{ iniatiative.name }}'s package, {{ package.name }}.

+

{% get_site_text 'add_new_document_to' %} {{ initiative.name }}{% get_site_text 's_package' %} {{ package.name }}.

{% endif %}
{% csrf_token %}
-

Metadata

+

{% get_site_text 'metadata' %}

{% bootstrap_form form %} - +
@@ -37,4 +37,4 @@

Metadata

-{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/documents/list_documents.html b/src/templates/package/documents/list_documents.html index 39aff0b..b929903 100644 --- a/src/templates/package/documents/list_documents.html +++ b/src/templates/package/documents/list_documents.html @@ -1,14 +1,14 @@ {% extends "base/admin_base.html" %} {% block title %} -List Documents +{% get_site_text 'list_documents' %} {% endblock title %} {% block breadcrumb %} - - + + - + {% endblock breadcrumb %} {% block content %} @@ -17,10 +17,10 @@
-

Documents Affiliated with “{{ package.name }}” ({{ initiative.name }})

-

Warning: all documents here will be public and visible to everybody. These are designed to be contracts and other details of the package.

+

{% get_site_text 'documents_affiliated_with' %} “{{ package.name }}” ({{ initiative.name }})

+

{% get_site_text 'documents_warning' %}

- Add New Document + {% get_site_text 'add_new_document' %}

@@ -28,21 +28,21 @@

Documents Affiliated with “{{ package.name }}” ({{ initiative.name }})

- - - - - + + + + + {% for document in documents %} - - - - + + + + {% endfor %} @@ -55,39 +55,40 @@

Documents Affiliated with “{{ package.name }}” ({{ initiative.name }})

Package NameCostApproved by Initiative?ApprovalExternal{% get_site_text 'package_name' %}{% get_site_text 'cost' %}{% get_site_text 'approved_by_initiative' %}{% get_site_text 'approval' %}{% get_site_text 'external' %}
@@ -197,7 +179,7 @@

Approval

{% else %} {% endif %}
NameViewReviseVersionsDelete{% get_site_text 'name' %}{% get_site_text 'view' %}{% get_site_text 'revise_document' %}{% get_site_text 'versions' %}{% get_site_text 'delete' %}
{{ document.name }}View DocumentRevise DocumentView Versions
{% csrf_token %}
{% get_site_text 'view_document' %}{% get_site_text 'revise_document' %}{% get_site_text 'view_versions' %}
{% csrf_token %}
- - - - + + + + {% for version in document.packagedocumenthistorical_set.all %} - + {% empty %} - + {% endfor %}
IDNameDownloadRevert{% get_site_text 'id' %}{% get_site_text 'name' %}{% get_site_text 'download' %}{% get_site_text 'revert' %}
#{{ version.pk }} {{ version.name }}Download{% get_site_text 'download' %}
{% csrf_token %} - +
No versions recorded for this document.
{% get_site_text 'no_versions_recorded' %}
diff --git a/src/templates/package/elements/basket_form.html b/src/templates/package/elements/basket_form.html index 25c42b1..9a41d91 100644 --- a/src/templates/package/elements/basket_form.html +++ b/src/templates/package/elements/basket_form.html @@ -1,8 +1,8 @@ {% load static django_bootstrap5 conflicts crispy_forms_tags currency_display %}
-

{% get_site_text "quotation_proceed_h" %}

- {% get_site_text "quotation_anongather" %} +

{% get_site_text "quotation_proceed_h" prefetched %}

+ {% get_site_text "quotation_anongather" prefetched %}
{% csrf_token %} {% crispy fte_form %} diff --git a/src/templates/package/elements/package.html b/src/templates/package/elements/package.html index 7e6f7b4..f709e6e 100644 --- a/src/templates/package/elements/package.html +++ b/src/templates/package/elements/package.html @@ -8,7 +8,7 @@
{% endfor %}
{% else %} -
Initiative: {{ package.initiative.name }}
+
{% get_site_text 'initiative' %}: {{ package.initiative.name }}
{% endif %}

{{ package.description|safe }}

{{ package.display_price }}

diff --git a/src/templates/package/elements/view_basket_desktop.html b/src/templates/package/elements/view_basket_desktop.html index 73a39be..b633b3a 100644 --- a/src/templates/package/elements/view_basket_desktop.html +++ b/src/templates/package/elements/view_basket_desktop.html @@ -6,28 +6,28 @@
-

{% get_site_text 'discuss_quote' %}

+

{% get_site_text 'discuss_quote' prefetched %}

Contact us image
{% get_site_text_no_edit 'contact_us' %} + href="mailto:{{ request.site.contact_email }}">{% get_site_text_no_edit 'contact_us' prefetched %}
{% endif %}
-

{% get_site_text 'quotation_packages' %}

+

{% get_site_text 'quotation_packages' prefetched %}

{% csrf_token %} - - - + + + {% for package in basket.packages.all %} @@ -46,7 +46,7 @@

{% get_site_text 'quotation_packages' %}

@@ -70,14 +70,14 @@

{% get_site_text 'quotation_packages' %}

{% endfor %} {% if not basket.meta_packages.all and not basket.packages.all %} - + {% endif %}
OfferCostRemove{% get_site_text 'offer' %}{% get_site_text 'cost' %}{% get_site_text 'remove' %}
There are no offers in this quote.{% get_site_text 'no_offers' %}
@@ -88,7 +88,7 @@

{% get_site_text 'quotation_packages' %}

{% if basket.list_of_conflicting_packages %}
- Duplicate Offers: Your selection includes one or more duplicate offers. Any duplicate offers are automatically removed from your quote to ensure you are only charged once. + {% get_site_text 'duplicate_offers' %}{% get_site_text 'duplicate_offers_description' %}
- {% get_site_text 'totals' %} + {% get_site_text 'annual_sub_total' prefetched %} {% if basket.conflicts %} -

Totals ignore duplicate offers. +

{% get_site_text 'totals_ignore_duplicates' %}

{% endif %}
@@ -120,7 +120,7 @@

{% get_site_text 'quotation_packages' %}

{{ v|currency_display:k }}
{% endfor %} {% else %} - Complete form below to see price. + {% get_site_text 'complete_form' %} {% endif %} {% endif %}
- *{% get_site_text 'curr_con_warning' %} + *{% get_site_text 'curr_con_warning' prefetched %}
- {% get_site_text 'obc_percentage' %} + {% get_site_text 'obc_percentage_annual' prefetched %} {% for k,v in site_percentage.items %} @@ -146,7 +146,7 @@

{% get_site_text 'quotation_packages' %}

{% endif %}
- Total + {% get_site_text 'annual_total' %} {% if converted_total %} @@ -161,7 +161,7 @@

{% get_site_text 'quotation_packages' %}

{% if not converted_currency == user_currency.currency %}
- *No offers have yet been set up in your chosen currency. Instead, we have provided an indicative price. We recommend you get in touch with us at info@openbookcollective.org so we can provide you with a custom quotation. + {% get_site_text 'indicative_price_warning' %}
- - + + {% for package in basket.packages.all %} @@ -17,7 +17,7 @@

{% get_site_text 'quotation_packages' %}

@@ -28,7 +28,7 @@

{% get_site_text 'quotation_packages' %}

@@ -40,8 +40,8 @@

{% get_site_text 'quotation_breakdown' %}

{% csrf_token %}
OfferRemove{% get_site_text 'offer' %}{% get_site_text 'remove' %}
- - + + {% for cost in package_costs %} {% get_site_text 'quotation_breakdown' %} Months {% for document in cost.package.packagedocument_set.all %} {% if document.pdf_file %}Contract document Contract{% endif %} + src="{% static "frontend/img/contract.png" %}" alt="{% get_site_text 'contract' %}"> + {% get_site_text 'contract' %}{% endif %} {% endfor %} {% endfor %} - - - - + + + + @@ -47,7 +47,7 @@

{% block title %}Form Elements Setup for {{ package.name }}{% endblock %}{{ element.form_element.get_kind_display }}

- + {% endfor %} @@ -57,4 +57,4 @@

{% block title %}Form Elements Setup for {{ package.name }}{% endblock %} -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/info.html b/src/templates/package/info.html index 002835d..c69cf34 100644 --- a/src/templates/package/info.html +++ b/src/templates/package/info.html @@ -2,8 +2,8 @@ {% load static %} {% block css %} - - + + {% endblock %} {% block title %} @@ -30,14 +30,14 @@

{{ package.name }}

{% if package_type == 'package' %} {{ package.initiative.name }}{% else %}{{ package.initiative_list|length }} - initiatives{% endif %} + {% get_site_text 'initiatives' %}{% endif %}
- {{ package.display_contract_length_months }} month contract + {% get_site_text 'months_contract' %}
- {{ package.pricing_display }} + {% if package.pre_calc %}{{ package.pre_calc }}{% else %}{{ package.pricing_display }}{% endif %}
@@ -48,7 +48,7 @@

{{ package.name }}

@@ -63,7 +63,7 @@ @@ -87,7 +87,7 @@

{% get_site_text 'included_packages' %}

{% endfor %} {% if request.site.enable_standards %} -

Standards

+

{% get_site_text 'standards' %}

{% get_site_text 'standards_info' %}
    {% for standard, packages in package.standards.items %} @@ -100,22 +100,22 @@

    Standards

{% empty %} -
  • No packages adhere to our standards.
  • +
  • {% get_site_text 'no_packages_standards' %}
  • {% endfor %} {% endif %} {% else %} {% if request.site.enable_standards %} -

    Standards

    +

    {% get_site_text 'standards' %}

    {% get_site_text 'standards_info' %} {% endif %} @@ -125,29 +125,29 @@

    Standards

    {{ package.name }} icon.
    • {% if package_type == 'package' %} {{ package.initiative.name }}{% else %}{{ package.initiative_list|length }} - initiatives{% endif %}
    • -
    • {{ package.display_contract_length_months }} month contract
    • -
    • Pricing {{ package.pricing_display }}
    • + {% get_site_text 'initiatives' %}{% endif %} +
    • {% get_site_text "months_contract" %}
    • +
    • {% if package.pre_calc %}{{ package.pre_calc }}{% else %}{{ package.pricing_display }}{% endif %} {% get_site_text 'per_year' %}
    • {% if package.meta_packages %}
    • - Included in + {% get_site_text 'included_in' %} {% for mp in package.meta_packages %}{{ mp.name }}{% if not forloop.last %}, {% endif %}{% endfor %}
    • {% endif %} {% if package.meta_only %} -
    • Only available in collectives
    • +
    • {% get_site_text 'only_in_collectives' %}
    • {% endif %}
    {% if package_type == 'meta' %} - Add to Quote + {% get_site_text 'add_to_quote' %} {% elif not package.meta_only %} - Add to Quote + {% get_site_text 'add_to_quote' %} {% endif %}
    @@ -159,7 +159,7 @@

    Standards

    @@ -167,14 +167,14 @@ @@ -182,12 +182,14 @@ {% endblock body %} + + {% block header-mobile %}
    - Build your quote image

    {{ package.name }}

    @@ -224,7 +226,7 @@

    Standards

    {% empty %} -
  • No packages adhere to our standards.
  • +
  • {% get_site_text 'no_packages_standards' %}
  • {% endfor %} {% endif %} @@ -237,10 +239,10 @@

    Standards

  • {{ standard.standard.standard_name }}
  • {% empty %} -
  • This package adheres to none of our standards.
  • +
  • {% get_site_text 'no_standards' %}
  • {% endfor %} {% if package.attestationhistory_set.all %} -
  • View attestation history +
  • {% get_site_text 'view_attestation_history' %}
  • {% endif %} @@ -259,20 +261,20 @@

    Standards

  • {% if package_type == 'package' %} {{ package.initiative.name }}{% else %}{{ package.initiative_list|length }} initiatives{% endif %}
  • -
  • {{ package.display_contract_length_months }} month contract
  • -
  • Pricing {{ package.pricing_display }}
  • +
  • {% get_site_text "months_contract" %}
  • +
  • {% if package.pre_calc %}{{ package.pre_calc }}{% else %}{{ package.pricing_display }}{% endif %} {% get_site_text 'per_year' %}
  • {% if package.meta_packages %}
  • - Included in + {% get_site_text 'included_in' %} {% for mp in package.meta_packages %}{{ mp.name }}{% if not forloop.last %}, {% endif %}{% endfor %}
  • {% endif %} {% if meta %} - Add to Quote + {% get_site_text 'add_to_quote' %} {% else %} - Add to Quote + {% get_site_text 'add_to_quote' %} {% endif %}
    @@ -297,4 +299,4 @@

    Standards

    $('table').addClass('table-responsive'); }); -{% endblock js %} \ No newline at end of file +{% endblock js %} diff --git a/src/templates/package/list.html b/src/templates/package/list.html index baec2e9..caa9a5b 100644 --- a/src/templates/package/list.html +++ b/src/templates/package/list.html @@ -2,33 +2,33 @@ {% block breadcrumb %} - + {% endblock %} {% block content %}
    -

    Packages

    -

    View and select packages available for signup.

    +

    {% get_site_text 'packages' %}

    +

    {% get_site_text 'view_select_packages' %}

    -

    Packages

    -

    Individual packages.

    +

    {% get_site_text 'packages' %}

    +

    {% get_site_text 'individual_packages' %}

    {% for package in packages %} {% include "package/elements/package.html" %} {% empty %} -

    There are no Packages to display.

    +

    {% get_site_text 'no_packages_display' %}

    {% endfor %}
    -

    Meta Packages

    -

    Groups of packages.

    +

    {% get_site_text 'meta_packages' %}

    +

    {% get_site_text 'groups_of_packages' %}

    {% for package in meta_packages %} {% include "package/elements/package.html" with meta="meta" %} {% empty %} -

    There are no Meta Packages to display.

    +

    {% get_site_text 'no_meta_packages_display' %}

    {% endfor %}
    -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/list_form_elements.html b/src/templates/package/list_form_elements.html index d8d20a2..ca7d587 100644 --- a/src/templates/package/list_form_elements.html +++ b/src/templates/package/list_form_elements.html @@ -2,8 +2,8 @@ {% load static %} {% block breadcrumb %} - - + + {% endblock %} {% block dashboard_js %} @@ -21,10 +21,10 @@
    -

    {% block title %}Form Elements Setup{% endblock %}

    -

    This page allows platform managers to specify form elements that will be available to initiatives.

    +

    {% block title %}{% get_site_text 'form_elements_setup' %}{% endblock %}

    +

    {% get_site_text 'form_elements_lead' %}

    - Add New

    + {% get_site_text 'add_new' %}

    @@ -33,10 +33,10 @@

    {% block title %}Form Elements Setup{% endblock %}

    - - - - + + + + @@ -50,7 +50,7 @@

    {% block title %}Form Elements Setup{% endblock %}

    - + {% endfor %} @@ -60,4 +60,4 @@

    {% block title %}Form Elements Setup{% endblock %}

    -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/manage/contacts.html b/src/templates/package/manage/contacts.html index 580da8c..d4e43d3 100644 --- a/src/templates/package/manage/contacts.html +++ b/src/templates/package/manage/contacts.html @@ -1,13 +1,13 @@ {% extends "base/admin_base.html" %} {% load booleans %} -{% block title %}Manage Contacts for {{ banding_type.name }}{% endblock %} -{% block page-title %}Manage Contacts for {{ banding_type.name }}{% endblock %} +{% block title %}{% get_site_text 'manage_contacts_for' %} {{ banding_type.name }}{% endblock %} +{% block page-title %}{% get_site_text 'manage_contacts_for' %} {{ banding_type.name }}{% endblock %} {% block breadcrumb %} - + - + {% endblock %} {% block dashboard_js %} @@ -22,21 +22,21 @@

    - Select contacts for event types. These contacts will be sent an email when the event occurs.
    - Warning: Contacts have access to signup data which may be sensitive. + {% get_site_text 'select_contacts_for_events' %}
    + {% get_site_text 'contacts_access_warning' %}

    - Add a New Contact + {% get_site_text 'add_new_contact' %}

    {% csrf_token %}
    PackagePrice{% get_site_text 'package' %}{% get_site_text 'price' %}
    {% if cost.banding and cost.cost %}{{ cost.cost.value }} {% if cost.cost.country %}{{ cost.cost.country.currency }}{% else %} {{ cost.cost.default_currency }}{% endif %} {% else %} - No banding or price found. + {% get_site_text 'no_banding_or_price_found' %} {% endif %}
    - Totals + {% get_site_text 'totals' %} {% if basket.conflicts %} -

    Totals ignore duplicate offers. +

    {% get_site_text 'totals_ignore_duplicates' %}

    {% endif %}
    @@ -88,28 +89,28 @@

    {% get_site_text 'quotation_breakdown' %}

    {% get_site_text 'quotation_additional' %}

    {% get_site_text 'quotation_add_text' %} -

    Add

    +

    {% get_site_text 'add' %}

    {% if basket.package_set %} - {% include "package/elements/basket_form.html" %} + {% include "package/elements/basket_form.html" with prefetched=prefetched %} {% endif %}
    -

    Want to discuss your quote?

    +

    {% get_site_text 'discuss_quote' %}

    Contact us image + src="{% static "frontend/img/quote_lightbulb.png" %}" alt="{% get_site_text 'contact_us' %}">
    - +
    - \ No newline at end of file + diff --git a/src/templates/package/form_elements/list_package_form_elements.html b/src/templates/package/form_elements/list_package_form_elements.html index 8d76b41..c03f660 100644 --- a/src/templates/package/form_elements/list_package_form_elements.html +++ b/src/templates/package/form_elements/list_package_form_elements.html @@ -2,10 +2,10 @@ {% load static %} {% block breadcrumb %} - - + + - + {% endblock %} {% block dashboard_js %} @@ -20,8 +20,8 @@
    -

    {% block title %}Form Elements Setup for {{ package.name }}{% endblock %}

    -

    Please select the data that you require from users in order to signup to this package.

    +

    {% block title %}{% get_site_text 'form_elements_setup_for' %} {{ package.name }}{% endblock %}

    +

    {% get_site_text 'select_data_for_signup' %}

    {% csrf_token %} {{ field_list }} @@ -33,10 +33,10 @@

    {% block title %}Form Elements Setup for {{ package.name }}{% endblock %}

    NameTypeRequiredChoices{% get_site_text 'name' %}{% get_site_text 'type' %}{% get_site_text 'required' %}{% get_site_text 'choices' %}
    {% if element.form_element.required %}{% else %}{% endif %} {{ element.form_element.choices }}{% csrf_token %}
    {% csrf_token %}
    NameTypeRequiredChoices{% get_site_text 'name' %}{% get_site_text 'type' %}{% get_site_text 'required' %}{% get_site_text 'choices' %}
    {% if element.required %}{% else %}{% endif %} {{ element.choices }} Edit{% csrf_token %}
    {% csrf_token %}
    - - - - + + + + @@ -51,7 +51,7 @@
    Signup NotificationAccess Control NotificationNameEmail{% get_site_text 'signup_notification' %}{% get_site_text 'access_control_notification' %}{% get_site_text 'name' %}{% get_site_text 'email' %}

    - +

    diff --git a/src/templates/package/manage/create_package.html b/src/templates/package/manage/create_package.html index 6e8f995..b202fe0 100644 --- a/src/templates/package/manage/create_package.html +++ b/src/templates/package/manage/create_package.html @@ -3,10 +3,10 @@ {% block breadcrumb %} - - - + + {% endblock breadcrumb %} {% block content %} @@ -14,18 +14,18 @@
    -

    {% if package %}Edit {{ package.name }}{% else %}Add New Package{% endif %}

    -

    Add new and edit existing package.

    +

    {% if package %}{% get_site_text 'edit_package' %} {{ package.name }}{% else %}{% get_site_text 'add_new_package' %}{% endif %}

    +

    {% get_site_text 'add_edit_package_lead' %}

    -

    Before this package is displayed for signup it will be reviewed by a member of staff.

    +

    {% get_site_text 'package_review_notice' %}

    {% csrf_token %}
    -

    Metadata

    +

    {% get_site_text 'metadata' %}

    {% bootstrap_form form %} - +
    @@ -33,4 +33,4 @@

    Metadata

    -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/manage/new_edit_standard_attest.html b/src/templates/package/manage/new_edit_standard_attest.html index 833f26f..dbb7bc8 100644 --- a/src/templates/package/manage/new_edit_standard_attest.html +++ b/src/templates/package/manage/new_edit_standard_attest.html @@ -3,11 +3,11 @@ {% load crispy_forms_tags %} -{% block title %}Standard Attestation Configuration{% endblock title %} +{% block title %}{% get_site_text 'standard_attestation_configuration' %}{% endblock title %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block content %} @@ -19,7 +19,7 @@

    {{ standard.standard_name }}

    -

    Add a new or edit an existing standard attestation

    +

    {% get_site_text 'add_edit_standard_attestation' %}

    {{ standard.text|safe }}
    {% csrf_token %} @@ -32,7 +32,7 @@

    {{ standard.standard_name }}

    - +
    @@ -40,4 +40,4 @@

    {{ standard.standard_name }}

    -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/manage/package.html b/src/templates/package/manage/package.html index eccd70b..4315930 100644 --- a/src/templates/package/manage/package.html +++ b/src/templates/package/manage/package.html @@ -4,8 +4,8 @@ {% block breadcrumb %} - - + + {% endblock breadcrumb %} @@ -14,18 +14,18 @@
    -

    {% if package %}Edit {{ package.name }}{% else %}Add New Package{% endif %}

    -

    Add new and edit existing package.

    +

    {% if package %}{% get_site_text 'edit_package' %} {{ package.name }}{% else %}{% get_site_text 'add_new_package' %}{% endif %}

    +

    {% get_site_text 'add_edit_package_lead' %}

    {% csrf_token %}
    -

    Metadata

    +

    {% get_site_text 'metadata' %}

    {% crispy form %}
    - +
    @@ -33,4 +33,4 @@

    Metadata

    -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/manage/standards.html b/src/templates/package/manage/standards.html index 94d2150..459e9dd 100644 --- a/src/templates/package/manage/standards.html +++ b/src/templates/package/manage/standards.html @@ -1,14 +1,14 @@ {% extends "base/admin_base.html" %} {% block title %} -Initiatives +{% get_site_text 'initiatives' %} {% endblock title %} {% block breadcrumb %} - - - - + + + + {% endblock breadcrumb %} {% block dashboard_js %} @@ -21,11 +21,11 @@
    -

    Standards Attested to by “{{ package.name }}”

    -

    The following standards are attested to by this package.

    +

    {% get_site_text 'standards_attested_to_by' %} “{{ package.name }}”

    +

    {% get_site_text 'standards_attested_description' %}

    {% csrf_token %} - {{ standards_list }} + {{ standards_list }}


    @@ -33,10 +33,10 @@

    Standards Attested to by “{{ package.name }}”

    - - - - + + + + @@ -44,8 +44,8 @@

    Standards Attested to by “{{ package.name }}”

    - - + + {% endfor %} diff --git a/src/templates/package/manage/start_package.html b/src/templates/package/manage/start_package.html index 553eaa5..1c02841 100644 --- a/src/templates/package/manage/start_package.html +++ b/src/templates/package/manage/start_package.html @@ -1,12 +1,12 @@ {% extends "base/admin_base.html" %} {% load django_bootstrap5 crispy_forms_tags %} -{% block title %}Create New Package{% endblock %} +{% block title %}{% get_site_text 'create_new_package' %}{% endblock %} {% block breadcrumb %} - - - + + {% endblock %} @@ -15,17 +15,17 @@
    -

    Create New Package

    -

    Select a banding type for this package{% if user.is_staff %} or manage available bandings{% endif %}.

    +

    {% get_site_text 'create_new_package' %}

    +

    {% get_site_text 'select_banding_type' %}{% if user.is_staff %} {% get_site_text 'or' %} {% get_site_text 'manage_available_bandings' %}{% endif %}.

    {% csrf_token %}
    NameURLEdit AttestationRemove Attestation{% get_site_text 'name' %}{% get_site_text 'url' %}{% get_site_text 'edit_attestation' %}{% get_site_text 'remove_attestation' %}
    {{ attest.standard.standard_name }} {{ attest.details_url }}Edit Attestation
    {% csrf_token %}
    {% get_site_text 'edit_attestation' %}
    {% csrf_token %}
    - - - + + + {% for type in banding_types %} @@ -42,4 +42,4 @@

    Create New Package

    -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/manage_basket.html b/src/templates/package/manage_basket.html index d2b4b48..8497ec6 100644 --- a/src/templates/package/manage_basket.html +++ b/src/templates/package/manage_basket.html @@ -3,16 +3,15 @@ {% block css %} - + {% endblock %} {% block title %} - Quotations + {% get_site_text 'quotations' %} {% endblock title %} {% block breadcrumb %} - {% endblock breadcrumb %} @@ -21,9 +20,9 @@

    {% get_site_text_no_edit 'oa_inits_header' %}

    -

    Create and add packages to your quotations.

    +

    {% get_site_text 'create_add_packages' %}

    -
    Quotations logo
    @@ -38,7 +37,7 @@

    {% get_site_text_no_edit 'oa_inits_header' %}

    - Build your quote image

    {% get_site_text 'oa_inits_header' %}

    @@ -48,7 +47,7 @@

    {% get_site_text 'oa_inits_header' %}

    -

    Create and add packages to your quotations.

    +

    {% get_site_text 'create_add_packages' %}

    diff --git a/src/templates/package/media/list_media_files.html b/src/templates/package/media/list_media_files.html index 504065f..641b629 100644 --- a/src/templates/package/media/list_media_files.html +++ b/src/templates/package/media/list_media_files.html @@ -2,14 +2,14 @@ {% load crispy_forms_tags %} {% block title %} -List Documents +{% get_site_text 'list_documents' %} {% endblock title %} {% block breadcrumb %} - - + + - + {% endblock breadcrumb %} {% block content %} @@ -18,16 +18,16 @@
    -

    Media Files Affiliated with “{{ package.name }}” ({{ package.initiative.name }})

    -

    Media Files are accessible only to libraries who have subscribed to one of your packages.

    +

    {% get_site_text 'media_files_affiliated_with' %} “{{ package.name }}” ({{ package.initiative.name }})

    +

    {% get_site_text 'media_files_accessible' %}

    -

    Load New File

    +

    {% get_site_text 'load_new_file' %}

    {% csrf_token %} {{ new_file_form|crispy }} - +

    @@ -37,19 +37,19 @@

    Load New File

    NameUses FTESelect{% get_site_text 'name' %}{% get_site_text 'uses_fte' %}{% get_site_text 'select' %}
    - - - - + + + + {% for file in media_files %} - + - + {% endfor %} @@ -68,33 +68,33 @@
    NameViewDate UploadedDelete{% get_site_text 'name' %}{% get_site_text 'view' %}{% get_site_text 'date_uploaded' %}{% get_site_text 'delete' %}
    {{ file.name }}Download File{% get_site_text 'download_file' %} {{ file.date_uploaded }}
    {% csrf_token %}
    {% csrf_token %}
    - - - - + + + + {% for version in document.packagedocumenthistorical_set.all %} - + {% empty %} - + {% endfor %}
    IDNameDownloadRevert{% get_site_text 'id' %}{% get_site_text 'name' %}{% get_site_text 'download_file' %}{% get_site_text 'revert' %}
    #{{ version.pk }} {{ version.name }}Download{% get_site_text 'download_file' %}
    {% csrf_token %} - +
    No versions recorded for this document.
    {% get_site_text 'no_versions' %}
    diff --git a/src/templates/package/process_signup.html b/src/templates/package/process_signup.html index 0a75a8b..6d903fd 100644 --- a/src/templates/package/process_signup.html +++ b/src/templates/package/process_signup.html @@ -2,12 +2,12 @@ {% load crispy_forms_tags %} {% block title %} -Initiatives +{% get_site_text 'initiatives' %} {% endblock title %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block dashboard_js %} @@ -19,15 +19,15 @@
    -

    Signup to {{ signup.associated_package }} by {% if signup.associated_user.profile.institution.institution_name %}{{ signup.associated_user.profile.institution.institution_name }}{% else %}{{ signup.associated_user.profile }}{% endif %}

    -

    Signup Information

    - Warning: This page may contain sensitive personal data subject to GDPR or other data protection legislation.

    +

    {% get_site_text 'signup_to' %} {{ signup.associated_package }} {% get_site_text 'by' %} {% if signup.associated_user.profile.institution.institution_name %}{{ signup.associated_user.profile.institution.institution_name }}{% else %}{{ signup.associated_user.profile }}{% endif %}

    +

    {% get_site_text 'signup_information' %}

    + {% get_site_text 'gdpr_warning' %}

    - - + + @@ -38,36 +38,36 @@

    Signup to {{ signup.associated_package }} by {% if signup.associated_user.pr {% endfor %}

    - +
    Data FieldValue{% get_site_text 'data_field' %}{% get_site_text 'value' %}
    Price{% get_site_text 'price' %} {{ signup.formatted_price }}
    -

    Organisation Approval Status

    -

    If the organisation signing up has approved then this means they are not requesting any changes to the package terms and you can go ahead an approve it.

    +

    {% get_site_text 'organisation_approval_status' %}

    +

    {% get_site_text 'organisation_approval_explanation' %}

    {% if signup.organisation_approved %} - Approved on:{{ signup.organisation_approved }}. + {% get_site_text 'approved_on' %}:{{ signup.organisation_approved }}. {% else %} - Not approved. + {% get_site_text 'not_approved' %}. {% endif %}

    -

    Initiative Approval Status

    +

    {% get_site_text 'initiative_approval_status' %}

    {% if signup.initiative_approved %} - This signup was approved on: {{ signup.initiative_approved }}. + {% get_site_text 'signup_approved_on' %}: {{ signup.initiative_approved }}. {% else %} -

    This signup has not been approved.

    +

    {% get_site_text 'signup_not_approved' %}

    {% csrf_token %} - +
    {% endif %}
    {% if signup.associated_order.status == 'provisional' and not signup.organisation_approved %} -

    Documents

    -

    You can upload a custom document for the package if the organisation signing up has requested changes to standard terms. This document must incorporate all documents for this agreement as it will override all others.

    +

    {% get_site_text 'documents' %}

    +

    {% get_site_text 'upload_custom_document' %}

    {% crispy form %} {% endif %}
    diff --git a/src/templates/package/signups.html b/src/templates/package/signups.html index 701f00c..f2d3ed6 100644 --- a/src/templates/package/signups.html +++ b/src/templates/package/signups.html @@ -1,12 +1,12 @@ {% extends "base/admin_base.html" %} {% block title %} -Initiatives +{% get_site_text 'initiatives' %} {% endblock title %} {% block breadcrumb %} - - + + {% endblock breadcrumb %} {% block dashboard_js %} @@ -19,27 +19,27 @@
    -

    Signups

    -

    Signups to Your Initiatives

    -

    You can also export this data to CSV. -
    (Warning: This file may contain sensitive personal data subject to GDPR or other data protection legislation.)

    +

    {% get_site_text 'signups' %}

    +

    {% get_site_text 'signups_lead' %}

    +

    {% get_site_text 'export_csv' %} {% get_site_text 'export_csv_link' %}. +
    {% get_site_text 'gdpr_warning' %}



    - - - - - - - - - - - + + + + + + + + + + + @@ -57,20 +57,20 @@

    Signups

    {% if signup.organisation_approved %} {{ signup.organisation_approved }} {% else %} - Not approved + {% get_site_text 'not_approved' %} {% endif %} diff --git a/src/templates/package/staff/approval.html b/src/templates/package/staff/approval.html index 95d60a7..dd1f935 100644 --- a/src/templates/package/staff/approval.html +++ b/src/templates/package/staff/approval.html @@ -1,11 +1,11 @@ {% extends "base/admin_base.html" %} -{% block title %}Package Approval{% endblock %} -{% block page-title %}Package Approval{% endblock %} +{% block title %}{% get_site_text 'package_approval' %}{% endblock %} +{% block page-title %}{% get_site_text 'package_approval' %}{% endblock %} {% block breadcrumb %} - - + + {% endblock %} {% block content %} @@ -14,22 +14,20 @@
    -

    - Listed here are packages that have not been approved for display. -

    +

    {% get_site_text 'packages_not_approved' %}

    {% csrf_token %}
    IDOrder No.PackageInitiativePriceUserView DataSignup DateApproved by OrgApproved by InitiativeStatus{% get_site_text 'id' %}{% get_site_text 'order_number' %}{% get_site_text 'package' %}{% get_site_text 'initiative' %}{% get_site_text 'price' %}{% get_site_text 'user' %}{% get_site_text 'view_data' %}{% get_site_text 'signup_date' %}{% get_site_text 'approved_by_org' %}{% get_site_text 'approved_by_initiative' %}{% get_site_text 'status' %}
    {% if signup.initiative_approved %} {{ signup.initiative_approved }} {% else %} - Not approved + {% get_site_text 'not_approved' %} {% endif %} - {% if signup.organisation_approved and signup.initiative_approved %}View Signup{% else %}Process Signup{% endif %} + {% if signup.organisation_approved and signup.initiative_approved %}{% get_site_text 'view' %}{% else %}{% get_site_text 'process_signup' %}{% endif %}
    - - - - - - - - + + + + + + + + @@ -41,17 +39,17 @@ {% endfor %} @@ -68,7 +66,7 @@ @@ -79,7 +77,8 @@ @@ -95,10 +94,10 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/src/templates/package/staff/list_meta_packages.html b/src/templates/package/staff/list_meta_packages.html index 56f447d..ec28a9f 100644 --- a/src/templates/package/staff/list_meta_packages.html +++ b/src/templates/package/staff/list_meta_packages.html @@ -1,9 +1,9 @@ {% extends "base/admin_base.html" %} {% load django_bootstrap5 crispy_forms_tags booleans %} -{% block title %}{% if package %}Manage Meta Package{% else %}Create New Meta Package{% endif %}{% endblock %} +{% block title %}{% if package %}{% get_site_text 'manage_meta_package' %}{% else %}{% get_site_text 'create_new_meta_package' %}{% endif %}{% endblock %} {% block breadcrumb %} - + {% endblock %} {% block content %} @@ -11,18 +11,18 @@
    -

    Meta Packages

    -

    A list of all the meta packages available (both active and inactive). As meta packages span multiple initiatives they can only be managed by staff.

    -

    Add New Meta Package

    +

    {% get_site_text 'meta_packages' %}

    +

    {% get_site_text 'meta_packages_description' %}

    +

    {% get_site_text 'add_new_meta_package' %}

    Package NameInitiativeBanding TypeDefault Country/CurrencyDescriptionApproveEditDelete{% get_site_text 'package_name' %}{% get_site_text 'initiative' %}{% get_site_text 'banding_type' %}{% get_site_text 'default_country_currency' %}{% get_site_text 'description' %}{% get_site_text 'approve' %}{% get_site_text 'edit' %}{% get_site_text 'delete' %}
    {{ package.default_country }} - View + {% get_site_text 'view' %} - + - Edit + {% get_site_text 'edit' %} - +
    - - - - - - + + + + + + @@ -45,10 +45,10 @@

    Meta Packages

    {% boolean_font_awesome package.active %} {% endfor %} @@ -59,4 +59,4 @@

    Meta Packages

    -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/staff/meta_package.html b/src/templates/package/staff/meta_package.html index 945fc10..580cfd3 100644 --- a/src/templates/package/staff/meta_package.html +++ b/src/templates/package/staff/meta_package.html @@ -1,14 +1,14 @@ {% extends "base/admin_base.html" %} {% load django_bootstrap5 crispy_forms_tags %} -{% block title %}{% if package %}Manage Meta Package{% else %}Create New Meta Package{% endif %}{% endblock %} +{% block title %}{% if package %}{% get_site_text 'manage_meta_package' %}{% else %}{% get_site_text 'create_new_meta_package' %}{% endif %}{% endblock %} {% block dashboard_js %} {% include "elements/select_2_includes.html" with target="#id_contact" %} {% endblock %} {% block breadcrumb %} - - + + {% endblock %} {% block content %} @@ -16,11 +16,11 @@
    -

    {% if package %}Manage Meta Package{% else %}Create New Meta Package{% endif %}

    -

    Meta Packages are a group of other packages that a library can subscribe to.

    +

    {% if package %}{% get_site_text 'manage_meta_package' %}{% else %}{% get_site_text 'create_new_meta_package' %}{% endif %}

    +

    {% get_site_text 'meta_packages_group_description' %}

    {% crispy form %}
    -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/package/view_basket.html b/src/templates/package/view_basket.html index a001e37..9b68c88 100644 --- a/src/templates/package/view_basket.html +++ b/src/templates/package/view_basket.html @@ -7,13 +7,13 @@ {% endblock %} {% block title %} - {% get_site_text_no_edit 'quotation_header' %}: {{ basket.name }} + {% get_site_text_no_edit 'quotation_header' prefetched %}: {{ basket.name }} {% endblock title %} {% block breadcrumb %} - - - + + + {% endblock breadcrumb %} @@ -21,8 +21,8 @@
    -

    {% get_site_text 'quotation_header' %}: {{ basket.name }}

    -

    {% get_site_text 'quotation_sub_header' %}

    +

    {% get_site_text 'quotation_header' prefetched %}: {{ basket.name }}

    +

    {% get_site_text 'quotation_sub_header' prefetched %}

    Quotations logo
    @@ -31,7 +31,7 @@

    {% get_site_text 'quotation_header' %}: {{ basket.name }}< {% endblock header %} {% block body %} - {% include "package/elements/view_basket_desktop.html" %} + {% include "package/elements/view_basket_desktop.html" with prefetched=prefetched%} {% endblock body %} @@ -41,7 +41,7 @@

    {% get_site_text 'quotation_header' %}: {{ basket.name }}<
    Build your quote image -

    {% get_site_text 'quotation_header' %}: {{ basket.name }}

    +

    {% get_site_text 'quotation_header' prefetched %}: {{ basket.name }}

    @@ -49,7 +49,7 @@

    {% get_site_text 'quotation_header' %}: {{ basket.name }}<
    -

    {% get_site_text 'quotation_sub_header' %}

    +

    {% get_site_text 'quotation_sub_header' prefetched %}

    diff --git a/src/templates/package/view_baskets.html b/src/templates/package/view_baskets.html index 9efd95b..70f3434 100644 --- a/src/templates/package/view_baskets.html +++ b/src/templates/package/view_baskets.html @@ -3,11 +3,11 @@ {% block css %} - + {% endblock %} {% block title %} - Quotations + {% get_site_text 'quotations' %} {% endblock title %} {% block breadcrumb %} @@ -23,7 +23,7 @@

    {% get_site_text 'baskets_header' %}

    {% get_site_text 'baskets_sub_header' %}

    -
    Quotations logo
    @@ -49,27 +49,27 @@

    {% get_site_text 'baskets_active_quote' %}

    {% endif %}
    Package Name# PackagesContactActiveEditDelete{% get_site_text 'package_name' %}{% get_site_text 'number_of_packages' %}{% get_site_text 'contact' %}{% get_site_text 'active' %}{% get_site_text 'edit' %}{% get_site_text 'delete' %}
    - Edit + {% get_site_text 'edit' %} - Delete + {% get_site_text 'delete' %}
    - - - - + + + + {% for basket in baskets %} + {% get_site_text 'view' %} {% empty %} - + {% endfor %}
    Quotation NameNumber of PackagesViewDelete{% get_site_text 'quotation_name' %}{% get_site_text 'number_of_packages' %}{% get_site_text 'view' %}{% get_site_text 'delete' %}
    {{ basket.name }} {{ basket.package_set|length }} - View Quote {% csrf_token %} - +
    No active quotes to display.{% get_site_text 'no_active_quotes' %}
    @@ -80,8 +80,8 @@

    {% get_site_text 'baskets_active_quote' %}

    - Build your quote image + {% get_site_text 'build_your_quote' %}

    {% get_site_text 'baskets_header' %}

    @@ -99,23 +99,23 @@

    {% get_site_text 'baskets_active_quote' %}

    {% endif %} - - - - + + + + {% for basket in baskets %} - + {% empty %} - + {% endfor %}
    Quotation NameNumber of PackagesViewDelete{% get_site_text 'quotation_name' %}{% get_site_text 'number_of_packages' %}{% get_site_text 'view' %}{% get_site_text 'delete' %}
    {{ basket.name }} {{ basket.package_set|length }}View Quote{% get_site_text 'view' %} - +
    No active quotes.{% get_site_text 'no_active_quotes' %}
    @@ -127,4 +127,4 @@

    {% get_site_text 'baskets_active_quote' %}

    $('[data-toggle="tooltip"]').tooltip(); }); -{% endblock js %} \ No newline at end of file +{% endblock js %} diff --git a/src/templates/registration/login.html b/src/templates/registration/login.html index 1659d1a..32f7460 100644 --- a/src/templates/registration/login.html +++ b/src/templates/registration/login.html @@ -6,18 +6,17 @@
    -

    Login

    -

    Your username will be the email address you used to register your account.

    +

    {% get_site_text 'login' %}

    +

    {% get_site_text 'username_email_info' %}

    {% if form.errors %} -

    Your username and password didn't match. Please try again.

    +

    {% get_site_text 'login_error' %}

    {% endif %} {% if next %} {% if user.is_authenticated %} -

    Your account doesn't have access to this page. To proceed, - please login with an account that has access.

    +

    {% get_site_text 'no_access_info' %}

    {% else %} -

    Please login to see this page.

    +

    {% get_site_text 'login_to_see_page' %}

    {% endif %} {% endif %} @@ -26,12 +25,12 @@

    Login

    {% bootstrap_form form %}
    - - Reset Password + + {% get_site_text 'reset_password' %}
    -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/src/templates/registration/password_reset_complete.html b/src/templates/registration/password_reset_complete.html index bdc86e3..6d24386 100644 --- a/src/templates/registration/password_reset_complete.html +++ b/src/templates/registration/password_reset_complete.html @@ -11,9 +11,9 @@
    - -
    -
    - -
    -
    @@ -49,25 +43,19 @@

    {% block title %}{% trans "Reset your password" %}{% endblock %}

    {% block body-mobile %} - {% endblock body-mobile %} diff --git a/src/templates/registration/password_reset_confirm.html b/src/templates/registration/password_reset_confirm.html index cef9cd3..bcada71 100644 --- a/src/templates/registration/password_reset_confirm.html +++ b/src/templates/registration/password_reset_confirm.html @@ -2,7 +2,7 @@ {% load i18n two_factor static %} {% block js %} - + {% endblock js %} {% block body %} @@ -15,9 +15,9 @@
    @@ -66,37 +60,30 @@

    {% block title %}{% trans "Reset your password" %}{% endblock %}

    {% block body-mobile %} - - {% endblock body-mobile %} diff --git a/src/templates/registration/password_reset_done.html b/src/templates/registration/password_reset_done.html index e480773..e9def86 100644 --- a/src/templates/registration/password_reset_done.html +++ b/src/templates/registration/password_reset_done.html @@ -11,9 +11,9 @@
    - -
    -
    - -
    -
    @@ -49,24 +43,17 @@

    {% block title %}{% trans "Reset your password" %}{% endblock %}

    {% block body-mobile %} - - {% endblock body-mobile %} diff --git a/src/templates/registration/password_reset_email.html b/src/templates/registration/password_reset_email.html index e494775..11d826b 100644 --- a/src/templates/registration/password_reset_email.html +++ b/src/templates/registration/password_reset_email.html @@ -1,15 +1,15 @@ {% load i18n %}{% autoescape off %} -Dear {{ user.first_name }} {{ user.last_name }}, +{% get_site_text 'email_greeting' %} {{ user.first_name }} {{ user.last_name }}, -{% blocktranslate %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktranslate %} +{% get_site_text 'password_reset_request' %} {{ site_name }}. -{% translate "Please go to the following page and choose a new password:" %} +{% get_site_text 'password_reset_instruction' %} {% block reset_link %} {{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %} {% endblock %} -{% translate 'Your username, in case you’ve forgotten:' %} {{ user.get_username }} - +{% get_site_text 'username_reminder' %} {{ user.get_username }} + {{ site_name }} -{% endautoescape %} \ No newline at end of file +{% endautoescape %} diff --git a/src/templates/registration/password_reset_form.html b/src/templates/registration/password_reset_form.html index a02fc7b..1a6b64d 100644 --- a/src/templates/registration/password_reset_form.html +++ b/src/templates/registration/password_reset_form.html @@ -11,9 +11,9 @@
    @@ -53,9 +47,9 @@

    {% block title %}{% trans "Reset your password" %}{% endblock %}

    {% block body-mobile %} @@ -63,18 +57,12 @@

    {% trans "Reset password" %}

    - -{% endblock body-mobile %} \ No newline at end of file +{% endblock body-mobile %} diff --git a/src/templates/summary/buttons.html b/src/templates/summary/buttons.html index 2989d64..0de39ad 100644 --- a/src/templates/summary/buttons.html +++ b/src/templates/summary/buttons.html @@ -1,7 +1,7 @@ \ No newline at end of file + {% get_site_text 'pricing' %} + diff --git a/src/templates/summary/desktop/summary.html b/src/templates/summary/desktop/summary.html index a4509b6..3961a72 100644 --- a/src/templates/summary/desktop/summary.html +++ b/src/templates/summary/desktop/summary.html @@ -14,17 +14,17 @@ {% if request.site.enable_standards %} -

    Standards

    +

    {% get_site_text 'standards' %}

    {% get_site_text 'standards_info' %} @@ -41,7 +41,7 @@

    Standards

    @@ -57,9 +57,10 @@ - \ No newline at end of file + diff --git a/src/templates/summary/highlights.html b/src/templates/summary/highlights.html index bff8d0a..9d16ec1 100644 --- a/src/templates/summary/highlights.html +++ b/src/templates/summary/highlights.html @@ -7,8 +7,8 @@

    {% for highlight in package.highlights %}
    -

    {{ highlight.title }}

    -
    +

    {{ highlight.title }}

    +
    {{ highlight.body|safe }}
    @@ -16,4 +16,4 @@

    {% endfor %}

    - \ No newline at end of file + diff --git a/src/templates/summary/latest_publications.html b/src/templates/summary/latest_publications.html index 6c67999..cf96be7 100644 --- a/src/templates/summary/latest_publications.html +++ b/src/templates/summary/latest_publications.html @@ -4,7 +4,7 @@ {% endblock js %} -
    +

    {% get_site_text 'latest_publications' %}

    @@ -40,8 +40,7 @@

    {% if package_type and package_type == 'meta' %} View Our Full - Catalogue + class="megenta-pill-button-more">{% get_site_text 'view_our_full_catalogue' %} {% else %} View Our Full @@ -50,4 +49,4 @@

    - \ No newline at end of file + diff --git a/src/templates/summary/meta_package.html b/src/templates/summary/meta_package.html index 002835d..10d5ab6 100644 --- a/src/templates/summary/meta_package.html +++ b/src/templates/summary/meta_package.html @@ -20,7 +20,7 @@
    -

    {{ package.name }}

    +

    {% get_site_text 'package_name' %}

    @@ -30,10 +30,10 @@

    {{ package.name }}

    {% if package_type == 'package' %} {{ package.initiative.name }}{% else %}{{ package.initiative_list|length }} - initiatives{% endif %} + {% get_site_text 'initiatives' %}{% endif %}
    - {{ package.display_contract_length_months }} month contract + {% get_site_text 'months_contract' %}
    @@ -48,7 +48,7 @@

    {{ package.name }}

    @@ -56,14 +56,15 @@
    @@ -87,7 +88,7 @@

    {% get_site_text 'included_packages' %}

    {% endfor %} {% if request.site.enable_standards %} -

    Standards

    +

    {% get_site_text 'standards' %}

    {% get_site_text 'standards_info' %}
      {% for standard, packages in package.standards.items %} @@ -100,22 +101,22 @@

      Standards

    {% empty %} -
  • No packages adhere to our standards.
  • +
  • {% get_site_text 'no_packages_standards' %}
  • {% endfor %} {% endif %} {% else %} {% if request.site.enable_standards %} -

    Standards

    +

    {% get_site_text 'standards' %}

    {% get_site_text 'standards_info' %} {% endif %} @@ -131,23 +132,24 @@

    Standards

  • {% if package_type == 'package' %} {{ package.initiative.name }}{% else %}{{ package.initiative_list|length }} initiatives{% endif %}
  • -
  • {{ package.display_contract_length_months }} month contract
  • -
  • Pricing {{ package.pricing_display }}
  • +
  • + {% get_site_text 'months_contract' %}
  • +
  • Pricing {{ package.pricing_display }} {% get_site_text 'per_year' %}
  • {% if package.meta_packages %}
  • - Included in + {% get_site_text 'included_in' %} {% for mp in package.meta_packages %}{{ mp.name }}{% if not forloop.last %}, {% endif %}{% endfor %}
  • {% endif %} {% if package.meta_only %} -
  • Only available in collectives
  • +
  • {% get_site_text 'only_in_collectives' %}
  • {% endif %} {% if package_type == 'meta' %} - Add to Quote + {% get_site_text 'add_to_quote' %} {% elif not package.meta_only %} - Add to Quote + {% get_site_text 'add_to_quote' %} {% endif %}
    @@ -159,7 +161,7 @@

    Standards

    @@ -167,14 +169,14 @@ @@ -188,7 +190,7 @@
    Build your quote image + alt="{% get_site_text 'build_your_quote' %}"/>

    {{ package.name }}

    @@ -210,7 +212,7 @@

    {% get_site_text 'included_packages' %}

    {% endfor %} {% if request.site.enable_standards %} -

    Standards

    +

    {% get_site_text 'standards' %}

    {% get_site_text 'standards_info' %}
      {% for standard, packages in package.standards.items %} @@ -224,23 +226,23 @@

      Standards

    {% empty %} -
  • No packages adhere to our standards.
  • +
  • {% get_site_text 'no_packages_display' %}
  • {% endfor %} {% endif %} {% else %} {% if request.site.enable_standards %} -

    Standards

    +

    {% get_site_text 'standards' %}

    {% get_site_text 'standards_info' %} @@ -259,8 +261,8 @@

    Standards

  • {% if package_type == 'package' %} {{ package.initiative.name }}{% else %}{{ package.initiative_list|length }} initiatives{% endif %}
  • -
  • {{ package.display_contract_length_months }} month contract
  • -
  • Pricing {{ package.pricing_display }}
  • +
  • {% get_site_text "months_contract" %}
  • +
  • Pricing {{ package.pricing_display }} {% get_site_text 'per_year' %}
  • {% if package.meta_packages %}
  • Included in @@ -270,9 +272,9 @@

    Standards

    {% endif %} {% if meta %} - Add to Quote + {% get_site_text 'add' %} {% else %} - Add to Quote + {% get_site_text 'add' %} {% endif %} @@ -297,4 +299,4 @@

    Standards

    $('table').addClass('table-responsive'); }); -{% endblock js %} \ No newline at end of file +{% endblock js %} diff --git a/src/templates/summary/mobile/summary.html b/src/templates/summary/mobile/summary.html index 630a61b..325bb39 100644 --- a/src/templates/summary/mobile/summary.html +++ b/src/templates/summary/mobile/summary.html @@ -12,17 +12,17 @@ {{ initiative.description|safe }} {% if request.site.enable_standards %} -

    Standards

    +

    {% get_site_text 'standards' %}

    {% get_site_text 'standards_info' %} @@ -33,4 +33,4 @@

    Standards

    {% include "summary/sidebar.html" %}
    - \ No newline at end of file + diff --git a/src/templates/summary/more_info.html b/src/templates/summary/more_info.html index 3ec679d..48530f2 100644 --- a/src/templates/summary/more_info.html +++ b/src/templates/summary/more_info.html @@ -6,11 +6,11 @@ {% endblock %} -{% block title %}{{ initiative.name }} - More Info{% endblock title %} +{% block title %}{{ initiative.name }} - {% get_site_text 'more_info' %}{% endblock title %} {% block breadcrumb %}
  • - + {% endblock breadcrumb %} @@ -30,7 +30,7 @@

    {{ initiative.name }}

    @@ -41,7 +41,7 @@

    {{ initiative.name }}

    @@ -56,7 +56,7 @@ @@ -74,7 +74,7 @@
    Build your quote image + alt="{% get_site_text 'build_your_quote' %}"/>

    {{ initiative.name }}

    @@ -93,4 +93,4 @@

    {{ initiative.name }}

    $('table').addClass('table-responsive'); }); -{% endblock js %} \ No newline at end of file +{% endblock js %} diff --git a/src/templates/summary/pricing.html b/src/templates/summary/pricing.html index 5353a1b..2544c5b 100644 --- a/src/templates/summary/pricing.html +++ b/src/templates/summary/pricing.html @@ -6,11 +6,11 @@ {% endblock %} -{% block title %}{{ initiative.name }} - Pricing{% endblock title %} +{% block title %}{{ initiative.name }} - {% get_site_text 'pricing' %}{% endblock title %} {% block breadcrumb %} - + {% endblock breadcrumb %} @@ -30,7 +30,7 @@

    {{ initiative.name }}

    @@ -41,7 +41,7 @@

    {{ initiative.name }}

    @@ -49,14 +49,14 @@ @@ -93,4 +93,4 @@

    {{ initiative.name }}

    $('table').addClass('table-responsive'); }); -{% endblock js %} \ No newline at end of file +{% endblock js %} diff --git a/src/templates/summary/sidebar.html b/src/templates/summary/sidebar.html index 2dd830b..02d185b 100644 --- a/src/templates/summary/sidebar.html +++ b/src/templates/summary/sidebar.html @@ -14,7 +14,7 @@
  • {{ package.display_contract_length_months }} month contract
  • -
  • Pricing {{ package.pricing_display }}
  • +
  • {% if package.pre_calc %}{{ package.pre_calc }}{% else %}{{ package.pricing_display }}{% endif %}
  • {% if package.meta_packages %}
  • Included in @@ -27,7 +27,7 @@ diff --git a/src/templates/summary/summary.html b/src/templates/summary/summary.html index e576580..b9e39cd 100644 --- a/src/templates/summary/summary.html +++ b/src/templates/summary/summary.html @@ -6,11 +6,11 @@ {% endblock %} -{% block title %}{{ initiative.name }} - Summary{% endblock title %} +{% block title %}{{ initiative.name }} - {% get_site_text 'summary' %}{% endblock title %} {% block breadcrumb %}
  • - + {% endblock breadcrumb %} @@ -30,7 +30,7 @@

    {{ initiative.name }}

    @@ -41,7 +41,7 @@

    {{ initiative.name }}

    @@ -56,7 +56,7 @@ @@ -102,4 +102,4 @@

    {{ initiative.name }}

    $('table').addClass('table-responsive'); }); -{% endblock js %} \ No newline at end of file +{% endblock js %} diff --git a/src/templates/thoth/advanced_search.html b/src/templates/thoth/advanced_search.html index 53c7f47..4117475 100644 --- a/src/templates/thoth/advanced_search.html +++ b/src/templates/thoth/advanced_search.html @@ -7,7 +7,7 @@ {% endblock js %} {% block title %} -Books +{% get_site_text 'books' %} {% endblock title %} @@ -15,10 +15,10 @@
    -

    Build an Advanced Search

    -

    The collective catalogue displays books that are in the Thoth system and that are supported by the work and funding model of the Open Book Collective.

    +

    {% get_site_text 'build_advanced_search' %}

    +

    {% get_site_text 'collective_catalogue_description' %}

    -
    Collective Catalogue
    +
    {% get_site_text 'collective_catalogue' %}
    {% endblock %} @@ -28,21 +28,21 @@

    Build an Advanced Search

    {% csrf_token %} -

    Add to Search

    - -

    Note: to search for author names, place your query in quotation marks to match a full name

    +

    {% get_site_text 'add_to_search' %}

    + +

    {% get_site_text 'author_search_note' %}

    -

    Search Configuration

    +

    {% get_site_text 'search_configuration' %}

    - + {% for element in search_elements %} - + {% endfor %}
    TypeValueRemove
    {% get_site_text 'type' %}{% get_site_text 'value' %}{% get_site_text 'remove' %}
    {{ element.get_search_type_display }} {{ element.text_content }}
    {% csrf_token %}
    {% csrf_token %}
    @@ -52,12 +52,12 @@

    Search Configuration

    {% csrf_token %} {% if request.user.is_authenticated %} - {% csrf_token %} - + {% csrf_token %} + {% endif %} - + {% if request.user.is_authenticated %} -

    Manage your searches

    +

    {% get_site_text 'manage_your_searches' %}

    {% endif %}
    @@ -79,6 +79,7 @@

    Search Configuration

    {{ work.publisher }}

    {{ work.full_title|truncatechars:35 }}

    +

    {{ work.year }}

    {% for contribution in work.contribution_set.all %}{% if forloop.first %}{{ contribution.contributor }}{% if contribution.institution %} ({{ contribution.institution }}){% endif %}{% endif %}{% if not forloop.first %}{% if forloop.last %}, and {{ contribution.contributor }}{% if contribution.institution %} ({{ contribution.institution }}){% endif %}{% else %}, {{ contribution.contributor }}{% if contribution.institution %} ({{ contribution.institution }}){% endif %}{% endif %}{% endif %}{% endfor %}

    @@ -92,11 +93,11 @@

    {{ work.full_title|truncatechars:35 }}

    @@ -107,7 +108,7 @@

    {{ work.full_title|truncatechars:35 }}

    Collective Catalogue -

    Collective Catalogue

    +

    {% get_site_text 'collective_catalogue' %}

    {% for contribution in work.contribution_set.all %}{% if forloop.first %}{{ contribution.contributor }}{% if contribution.institution %} ({{ contribution.institution }}){% endif %}{% endif %}{% if not forloop.first %}{% if forloop.last %}, and {{ contribution.contributor }}{% if contribution.institution %} ({{ contribution.institution }}){% endif %}{% else %}, {{ contribution.contributor }}{% if contribution.institution %} ({{ contribution.institution }}){% endif %}{% endif %}{% endif %}{% endfor %}

    @@ -118,7 +119,7 @@

    Collective Catalogue

    -

    This is the collective catalogue, showcasing publications from presses at the Open Book Collective.

    +

    {% get_site_text 'collective_catalogue_description' %}

    @@ -133,9 +134,15 @@

    Collective Catalogue

    @@ -159,6 +166,7 @@

    Collective Catalogue

    {{ work.publisher }}

    {{ work.full_title|truncatechars:35 }}

    +

    {{ work.year }}

    {% for contribution in work.contribution_set.all %} {{ contribution.contributor }}{% if contribution.institution %} ({{ contribution.institution }}){% endif %}{% if not forloop.first %}{% if forloop.last %}, and {% else %}, {% endif %}{% endif %} @@ -174,12 +182,12 @@

    {{ work.full_title|truncatechars:35 }}

    - {% if works.has_previous %}first | previous{% endif %} - {% if works.has_next %}| next | last{% endif %} + {% if works.has_previous %}{% get_site_text 'first' %} | {% get_site_text 'previous' %}{% endif %} + {% if works.has_next %}| {% get_site_text 'next' %} | {% get_site_text 'last' %}{% endif %} -
    Page {{ works.number }} of {{ works.paginator.num_pages }} +
    {% get_site_text 'page_of' %} {{ works.number }} {% get_site_text 'of' %} {{ works.paginator.num_pages }}
    diff --git a/src/templates/thoth/all.html b/src/templates/thoth/all.html index 2f4ef43..8a1e94a 100644 --- a/src/templates/thoth/all.html +++ b/src/templates/thoth/all.html @@ -8,7 +8,7 @@ {% endblock js %} {% block title %} -Books +{% get_site_text 'books' %} {% endblock title %} @@ -29,34 +29,33 @@

    {% get_site_text 'books_header' %}

    -

    Search

    - Search the catalogue +

    {% get_site_text 'search' %}

    + {% get_site_text 'search_the_catalogue' %}
    -
    -
    -

    Initiative

    +
    +
    +

    {% get_site_text 'initiative' %}

    -
    -
    -

    Sort

    +
    +
    +

    {% get_site_text 'sort' %}

    -
    -

    Latest Titles

    +

    {% get_site_text 'latest_titles' %}

    @@ -75,6 +74,7 @@

    Latest Titles

    {{ work.publisher }}

    {{ work.full_title|truncatechars:35 }}

    +

    {{ work.year }}

    {% for contribution in work.contribution_set.all %}{% if forloop.first %}{{ contribution.contributor }}{% if contribution.institution %} ({{ contribution.institution }}){% endif %}{% endif %}{% if not forloop.first %}{% if forloop.last %}, and {{ contribution.contributor }}{% if contribution.institution %} ({{ contribution.institution }}){% endif %}{% else %}, {{ contribution.contributor }}{% if contribution.institution %} ({{ contribution.institution }}){% endif %}{% endif %}{% endif %}{% endfor %}

    @@ -88,15 +88,15 @@

    {{ work.full_title|truncatechars:35 }}

    @@ -128,14 +128,20 @@

    {% get_site_text 'books_header' %}