This script processes a solution.json
file and combines the menus
key from multiple module JSON files to build a complete menu structure. The resulting menu is sorted by position
at both the menu and submenu levels. The script outputs a new file named generated-solution.json
containing the combined and sorted menu structure.
- The script reads
solution.json
from the current working directory. - The
solution.json
file should have a modules key containing a list of module bundle file names (e.g.,social-protection-bundle.json
,formal-sector-bundle.json
, etc.).
- Each module file listed under the
modules
key should be present in the same directory assolution.json
. - Each module file should contain a
menus
key, which defines its menu structure. - Some modules may also contain a
dependency
key, listing other module bundles that must be processed even if they are not explicitly included insolution.json
.
- The script reads all the module files, extracts their
menus
, and combines them. - If multiple entries share the same
id
, theirsubmenus
are merged instead of duplicating menu items. - Both menus and their
submenus
are sorted by theposition
field.
- If a module has a
dependency
key, the script ensures that the referenced module files are also processed, even if they are not explicitly listed insolution.json
. - Dependencies are resolved recursively to ensure all required modules are included.
- Bundles are collections of multiple related modules grouped together in a single file.
- If a bundle file is listed in
solution.json
, the script will process all its included modules. - Bundles may also have dependencies on other bundles, which are resolved recursively.
- If any module, bundle, or dependency file is missing, the script will print a warning but will continue processing the available files.
{
"modules": [
"social-protection-bundle.json",
"opensearch-report-bundle.json",
"core-bundle.json"
],
"roles": [
"role-eo.json"
]
}
{
"modules": [
"individual.json",
"api-import.json",
"social-protection.json",
"payroll.json",
"payment-cycle.json",
"deduplication.json"
],
"dependency": [
"grievance-bundle.json"
]
}
{
"modules": [
"opensearch-report.json"
]
}
{
"menus": [
{
"position": 1,
"id": "SocialRegistryMainMenu",
"name": "Social Registry",
"icon": "task-icon",
"description": "Social Registry",
"submenus": [
{
"position": 1,
"id": "individual.api_imports"
},
{
"position": 3,
"id": "individual.individuals"
},
{
"position": 4,
"id": "individual.groups"
},
{
"position": 2,
"id": "socialProtection.benefitPlans"
}
]
}
]
}
- Python 3.x installed on your system.
-
Prepare
solution.json
:- Create a file named
solution.json
in the directory where you will run the script. - Add a
modules
key listing all the module bundles you want to include. Example:{ "modules": [ "social-protection-bundle.json", "opensearch-report-bundle.json", "core-bundle.json", "xxxx-bundle.json", "formal-sector-bundle.json" ] }
- Create a file named
-
Prepare
<module>-bundle.json
:- Create a file named
<module>-bundle.json
in the directory where you will run the script. - Add a
modules
key listing all the modules you want to include in bundle. Example:{ "modules": [ "individual.json", "api-import.json", "social-protection.json", "payroll.json", "payment-cycle.json", "deduplication.json" ], "dependency": [ "grievance-bundle.json", "calculation-social-protection-bundle.json" ] }
dependency
key: it means that this bundle is related to other bundle and the additional package must be installed and considered in the final output. For example ifgrievance-bundle.json
is not added insolution.json
- this bundle package will be added in the final output even though is not presented insolution.json
file.
- Create a file named
-
Prepare Module Files:
- Place all the module files listed in
<module>-bundle.json
files in the same directory. Each file should have a structure similar to the example below:{ "menus": [ { "position": 1, "id": "MainMenu", "name": "Main", "icon": "main-icon", "description": "Main menu", "submenus": [ { "position": 1, "id": "submenu1" }, { "position": 2, "id": "submenu2" } ] } ] }
- Place all the module files listed in
-
Run the Script:
- Save the script as
build_solution.py
in the same directory assolution.json
and the module files. - Open a terminal or command prompt and navigate to the directory.
- Run the script using the command:
python3 build_solution.py
- Save the script as
-
View the Output:
- After the script completes, a new files named
generated-menu.json
,generated-roles.json
,fe-openimis.json
andbe-openimis.json
will be created in the same directory. - This file contains the combined and sorted menu structure.
- After the script completes, a new files named
/solution
|-- solution.json
|-- social-protection.json
|-- payroll.json
|-- payment-cycle.json
|-- individual.json
|-- grievance.json
|-- grievance-bundle.json
|-- social-protection-bundle.json
|-- core.json
|-- build_solution.py
- Ensure all files are properly formatted JSON.
- Missing or malformed files will result in warnings but will not stop the script.
- Customize the
solution.json
file to include or exclude specific modules based on your requirements.
Here’s the complete table with all the submenu configurations extracted, including their Name of Submenu
, ID of Submenu
, Filter
, and Route
.
Name of Submenu | ID of Submenu | Filter | Route |
---|---|---|---|
Tasks Management View | task.tasks |
(rights) => rights.includes(RIGHT_TASKS_MANAGEMENT_SEARCH_ALL) |
/tasks |
Tasks Management All View | task.allTasks |
(rights) => rights.includes(RIGHT_TASKS_MANAGEMENT_SEARCH_ALL) |
/AllTasks |
Registers | tools.registers |
(rights) => enablers(rights, RIGHT_REGISTERS) |
/tools/registers |
Extracts | tools.extracts |
(rights) => enablers(rights, RIGHT_EXTRACTS) |
/tools/extracts |
Reports | tools.reports |
(rights) => enablers(rights, RIGHT_REPORTS) |
/tools/reports |
Social Protection Benefit Plans | socialProtection.benefitPlans |
(rights) => rights.includes(RIGHT_BENEFIT_PLAN_SEARCH) |
/benefitPlans |
My Profile | profile.myProfile |
None | /profile/myProfile |
Change Password | profile.changePassword |
None | /profile/changePassword |
Policies | insuree.policies |
(rights) => rights.includes(RIGHT_POLICY) |
/policy/policies |
Payment Point | legalAndFinance.paymentPoint |
(rights) => rights.includes(RIGHT_PAYMENT_POINT_SEARCH) |
/paymentPoints |
Payrolls | legalAndFinance.payrolls |
(rights) => rights.includes(RIGHT_PAYROLL_SEARCH) |
/payrolls |
Payrolls Pending | legalAndFinance.payrollsPending |
(rights) => rights.includes(RIGHT_PAYROLL_SEARCH) |
/payrollsPending |
Payrolls Approved | legalAndFinance.payrollsApproved |
(rights) => rights.includes(RIGHT_PAYROLL_SEARCH) |
/payrollsApproved |
Payrolls Reconciled | legalAndFinance.payrollsReconciled |
(rights) => rights.includes(RIGHT_PAYROLL_SEARCH) |
/payrollsReconciled |
Payments | insuree.payment |
(rights) => rights.includes(RIGHT_PAYMENT) |
/payment/payments |
Payment Cycles | legalAndFinance.paymentCycles |
(rights) => rights.includes(RIGHT_PAYMENT_CYCLE_SEARCH) |
/paymentCycles |
Payers | admin.payers |
(rights) => rights.includes(RIGHT_PAYERS) |
/payer/payers |
Individual Reports | openSearch.individualReports |
None | /individualReports |
Group Reports | openSearch.groupReports |
None | /groupReports |
Beneficiary Reports | openSearch.beneficiaryReports |
None | /beneficiaryReports |
Invoice Reports | openSearch.invoiceReports |
None | /invoiceReports |
Payment Reports | openSearch.paymentReports |
None | /paymentReports |
Grievance Reports | openSearch.grievanceReports |
None | /grievanceReports |
Data Updates Reports | openSearch.dataUpdatesReports |
None | /dataUpdatesReports |
Open Search Config | openSearch.openSearchConfig |
None | /dashboardConfiguration |
Invoices | legalAndFinance.invoices |
(rights) => rights.filter((r) => r >= RIGHT_INVOICE_SEARCH && r <= RIGHT_INVOICE_AMEND).length > 0 |
/invoices |
Bills | legalAndFinance.bills |
(rights) => rights.filter((r) => r >= RIGHT_BILL_SEARCH && r <= RIGHT_BILL_AMEND).length > 0 |
/bills |
Add Family or Group | insuree.addFamilyOrGroup |
(rights) => rights.includes(RIGHT_FAMILY_ADD) |
/insuree/family |
Families or Groups | insuree.familiesOrGroups |
(rights) => rights.includes(RIGHT_FAMILY) |
/insuree/families |
Insurees | insuree.insurees |
(rights) => rights.includes(RIGHT_INSUREE) |
/insuree/insurees |
Individuals | individual.individuals |
(rights) => rights.includes(RIGHT_INDIVIDUAL_SEARCH) |
/individuals |
Groups | individual.groups |
(rights) => rights.includes(RIGHT_GROUP_SEARCH) |
/groups |
API Imports | individual.api_imports |
(rights) => rights.includes(RIGHT_INDIVIDUAL_SEARCH) |
/imports |
Grievances | grievance.grievances |
(rights) => rights.includes(RIGHT_TICKET_SEARCH) |
/ticket/tickets |
Add Grievance | grievance.add |
(rights) => rights.includes(RIGHT_TICKET_ADD) |
/ticket/newTicket |
Role Management | admin.roleManagement |
(rights) => rights.includes(RIGHT_ROLE_SEARCH) |
/roles |
Contribution Plans | admin.contributionPlans |
(rights) => rights.includes(RIGHT_CONTRIBUTION_PLAN_SEARCH) |
/contributionPlans |
Contribution Plan Bundles | admin.contributionPlanBundles |
(rights) => rights.includes(RIGHT_CONTRIBUTION_PLAN_BUNDLE_SEARCH) |
/contributionPlanBundles |
Payment Plans | legalAndFinance.paymentPlans |
(rights) => rights.includes(RIGHT_PAYMENT_PLAN_SEARCH) |
/paymentPlans |
Contribution | insuree.contribution |
(rights) => rights.includes(RIGHT_CONTRIBUTION) |
/contribution/contributions |
Health Facility Claims | claim.healthFacilityClaims |
(rights) => rights.some((r) => r >= RIGHT_CLAIMREVIEW && r <= RIGHT_PROCESS) |
/claim/healthFacilities |
Reviews | claim.reviews |
(rights) => rights.some((r) => r >= RIGHT_CLAIMREVIEW && r <= RIGHT_PROCESS) |
/claim/reviews |
Claim Batch (Batch Run) | claim.claimBatch |
(rights) => !!rights.filter(r => r >= RIGHT_PROCESS && r <= RIGHT_PREVIEW).length |
/claim_batch |
Products | admin.products |
(rights) => rights.includes(RIGHT_PRODUCTS) |
/admin/products |
Health Facilities | admin.healthFacilities |
(rights) => rights.includes(RIGHT_HEALTHFACILITIES) |
/location/healthFacilities |
Medical Services Prices List | admin.services |
(rights) => rights.includes(RIGHT_PRICELISTMS) |
/medical/pricelists/services |
Medical Items Prices List | admin.items |
(rights) => rights.includes(RIGHT_PRICELISTMI) |
/medical/pricelists/items |
Medical Services | admin.medicalServices |
(rights) => rights.includes(RIGHT_MEDICALSERVICES) |
/medical/pricelists/services |
Medical Items | admin.medicalItems |
(rights) => rights.includes(RIGHT_MEDICALITEMS) |
/medical/pricelists/items |
Users | admin.users |
(rights) => rights.includes(RIGHT_USERS) |
/admin/users |
Locations | admin.locations |
(rights) => rights.includes(RIGHT_LOCATIONS) |
/location/locations |
Contracts | legalAndFinance.contracts |
(rights) => rights.includes(RIGHT_POLICYHOLDERCONTRACT_SEARCH) |
/contracts |
- Menu Builder Script explanation
- Conception of deployment Recipe Strategy
- More detailed instruction of menu configuration
- List of possible configurations of submenus items
- Detailed description of technical approach to achieve having menu configurable.
This section explains how the script processes solution.json
to generate two package configuration files:
be-openimis.json
: Contains backend (be-packages
) packages.fe-openimis.json
: Contains frontend (fe-packages
) packages.
Each package is transformed to meet specific naming conventions and structure requirements based on its type
.
-
General Format:
Backend packages are listed under thepip
key. Depending on thetype
, they are transformed as follows:-
Type:
pip
{ "name": "menu", "pip": "openimis-be-menu==v1.8.0" }
-
Type:
github
{ "name": "core", "pip": "git+https://github.com/openimis/openimis-be-core_py.git@develop#egg=openimis-be-core" }
-
-
Naming Rules:
- For
pip
packages, the fullopenimis-be-<module_name>
pattern is retained. - For
github
packages, theopenimis-be-
prefix and_py
suffix are removed, leaving only<module_name>
inname
.
- For
-
General Format:
Frontend packages are listed under thenpm
key. Depending on thetype
, they are transformed as follows:-
Type:
npm
{ "name": "CoreModule", "npm": "@openimis/fe-core@>=v1.7.1" }
-
Type:
github
{ "name": "GrievanceSocialProtectionModule", "npm": "@openimis/fe-grievance_social_protection@https://github.com/openimis/openimis-fe-grievance_social_protection_js#develop" }
-
-
Naming Rules:
- The
name
key is converted to PascalCase with theModule
suffix (e.g.,GrievanceSocialProtectionModule
). - For
npm
packages,_js
suffixes are removed. - For
github
packages, URLs follow the patternopenimis/openimis-<module_name>_js
.
- The
{
"packages": [
{
"name": "menu",
"pip": "openimis-be-menu==v1.8.0"
},
{
"name": "core",
"pip": "git+https://github.com/openimis/openimis-be-core_py.git@develop#egg=openimis-be-core"
}
]
}
{
"packages": [
{
"name": "CoreModule",
"npm": "@openimis/fe-core@>=v1.7.1"
},
{
"name": "GrievanceSocialProtectionModule",
"npm": "@openimis/fe-grievance_social_protection@https://github.com/openimis/openimis-fe-grievance_social_protection_js#develop"
}
]
}
The build_solution.py
script processes multiple JSON module files to generate a consolidated roles file:
generated-roles.json
– Contains merged roles with unique permissions.
- Extract roles from each module's JSON file.
- Merge duplicate role names, ensuring unique permissions are combined.
- Map permissions using
permissions_map.json
to include bothcode
andname
fields. - Sort permissions within each role for better readability.
- Save results to
generated-roles.json
.
Each module file should contain a roles
section like this:
{
"roles": [
{
"roleName": "IMIS Administrator",
"code": "admin",
"permissions": [
"individual.read_individual",
"individual.create_individual",
"individual.update_individual",
"individual.delete_individual"
]
}
]
}
- If multiple modules define the same
roleName
, their permissions are merged (no duplicates). - Roles retain the same
code
from their original definitions. - The final output consolidates all role definitions into a single
generated-roles.json
file. - Permissions are mapped to include
code
andname
usingpermissions_map.json
.
{
"roles": [
{
"roleName": "IMIS Administrator",
"code": "admin",
"permissions": [
"grievance.create_grievance",
"grievance.update_grievance"
]
}
]
}
{
"roles": [
{
"roleName": "IMIS Administrator",
"code": "admin",
"permissions": [
"grievance.delete_grievance",
"grievance.read_grievance"
]
}
]
}
{
"roles": [
{
"roleName": "IMIS Administrator",
"code": "admin",
"permissions": [
{
"name": "grievance.create_grievance",
"code": "127001"
},
{
"name": "grievance.update_grievance",
"code": "127002"
},
{
"name": "grievance.delete_grievance",
"code": "127003"
},
{
"name": "grievance.read_grievance",
"code": "127000"
}
]
}
]
}
-
Load Modules
- Extract roles from each module’s JSON file.
- Each module contains a list of roles with
roleName
,code
, and associatedpermissions
.
-
Merge Roles
- If the same
roleName
appears in multiple modules, their permissions are combined. - Duplicates within the permission lists are removed.
- The
code
remains the same as defined in the original modules.
- If the same
-
Map Permissions
- Each permission string is replaced with a dictionary containing:
"name"
: The original permission string."code"
: The mapped value frompermissions_map.json
.
- If no mapping exists, the permission remains unchanged.
- Each permission string is replaced with a dictionary containing:
-
Generate Output
- The final list of merged roles is structured into a single JSON object.
- The processed roles are saved in
generated-roles.json
.
The processed roles are saved in:
generated-roles.json
This file consolidates all roles across modules, ensuring a structured and non-duplicated set of permissions. Each role in the final output contains:
roleName
: The name of the role.code
: The unique code identifier for the role.permissions
: A merged list of permissions from all modules, each containing:name
: The permission name.code
: The mapped permission code frompermissions_map.json
.
The script supports Docker service configuration through a service.json
file. This allows defining service dependencies, environment files, and Compose YAML file structures. The script generates compose.yml
as output, ensuring correct Docker service configuration.
The service.json
file should be structured as an array of service definitions, where each entry contains:
path
: The Compose YAML file path.env_file
: A list of environment files required for the service.
[
{
"path": "compose.base.yml",
"env_file": [
".env",
".env.redis",
".env.openSearch"
]
},
{
"path": "compose.${DB_DEFAULT:-postgresql}.yml",
"env_file": [
".env.database"
]
},
{
"path": "compose.openSearch.yml",
"env_file": []
},
{
"path": "compose.cache.yml",
"env_file": [
".env",
".env.redis"
]
}
]
After processing service.json
, the script generates compose.yml
, ensuring correct formatting and indentation.
include:
- path: compose.base.yml
env_file:
- .env
- .env.redis
- .env.openSearch
- path: compose.${DB_DEFAULT:-postgresql}.yml
env_file:
- .env.database
- path: compose.openSearch.yml
env_file: []
- path: compose.cache.yml
env_file:
- .env
- .env.redis
- Reads
service.json
– Extracts service definitions, includingpath
andenv_file
. - Formats the data – Ensures proper indentation and list formatting for YAML output.
- Generates
compose.yml
– Writes the structured YAML file for Docker Compose.
Ensure service.json
is present, then execute:
python build_solution.py
This will generate generated-services.yml
in the same directory.
This document provides guidelines on processing Role
and RoleRight
fixtures for data initialization, ensuring foreign key relationships are properly resolved when using Django fixtures.
To load a standard fixture (e.g., role.json
) containing predefined Role data, use:
python manage.py loaddata role.json
This command loads role data into the database.
Since RoleRight
references Role
via a foreign key, but fixtures may store relationships using a natural key (e.g., uuid
, name
), we use a custom command to resolve and replace these references with actual database IDs.
This command allows loading fixtures while resolving foreign key references using a specified field.
python manage.py load_fixture_foreign_key <fixture_file> <field_name>
<fixture_file>
: Path to the fixture file (e.g.,fixtures/core/roles-right.json
)<field_name>
: The field to use as the natural key for resolving foreign keys (e.g.,uuid
,name
)
python manage.py load_fixture_foreign_key fixtures/core/roles-right.json uuid
This command:
- Reads the fixture file.
- Looks up foreign key references in the related model (e.g.,
Role
). - Replaces the natural key field (e.g.,
uuid
) with the actual primary key (id
). - Loads the modified fixture into the database.
- Ensure that the related objects exist in the database before loading fixtures that reference them.
- The command supports multiple fields as natural keys (e.g.,
uuid
,name
, etc.), as specified by the user.
For other fixtures, the standard Django loaddata
command can be used:
python manage.py loaddata <fixture_file>
For example:
python manage.py loaddata fixtures/core/users.json
This ensures the fixture data is loaded directly into the database.