{"id":23421245,"url":"https://github.com/nhsdigital/automated_excel_publications","last_synced_at":"2025-07-11T14:38:54.561Z","repository":{"id":238880362,"uuid":"797773144","full_name":"NHSDigital/automated_excel_publications","owner":"NHSDigital","description":"An example of a module used to automate the production of Excel files.","archived":false,"fork":false,"pushed_at":"2024-05-08T14:33:19.000Z","size":567,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-09T09:44:26.462Z","etag":null,"topics":["excel","reproducible-analytical-pipelines"],"latest_commit_sha":null,"homepage":"https://nhsdigital.github.io/rap-community-of-practice/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NHSDigital.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-08T13:42:06.000Z","updated_at":"2024-06-25T12:18:37.000Z","dependencies_parsed_at":"2024-05-11T23:15:08.154Z","dependency_job_id":null,"html_url":"https://github.com/NHSDigital/automated_excel_publications","commit_stats":null,"previous_names":["nhsdigital/automated_excel_publications"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/NHSDigital/automated_excel_publications","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NHSDigital%2Fautomated_excel_publications","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NHSDigital%2Fautomated_excel_publications/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NHSDigital%2Fautomated_excel_publications/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NHSDigital%2Fautomated_excel_publications/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NHSDigital","download_url":"https://codeload.github.com/NHSDigital/automated_excel_publications/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NHSDigital%2Fautomated_excel_publications/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264833294,"owners_count":23670617,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["excel","reproducible-analytical-pipelines"],"created_at":"2024-12-23T02:14:33.560Z","updated_at":"2025-07-11T14:38:53.994Z","avatar_url":"https://github.com/NHSDigital.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Excel Automation\n\nThis is an example of a module used to automate the production of Excel files.\nNHS England publishes a number of Excel files, and does so with a significant amount of formatting.\n\nThe repo is organised around three example projects, in progressing levels of complexity. The `advanced` project is very close to the real publication [here](https://digital.nhs.uk/data-and-information/publications/statistical/appointments-in-general-practice/march-2022); the other two examples pare this project down to make it more simple, straightforward, and concise.\n\n\u003e **Note**\n\u003e Being adapted from the code used to produce the [Appointments in General Practice](https://digital.nhs.uk/data-and-information/publications/statistical/appointments-in-general-practice) publications, the sample templates used in this repo work with publicly available historical data.\n\n## Ownership\n\nNHS England and NHS Digital have [merged](https://digital.nhs.uk/about-nhs-digital/nhs-digital-merger-with-nhs-england).\n\n**Repository owner**: [NHS England Data Science](https://nhsengland.github.io/datascience/)\n\n**Email**: datascience@nhs.net\n\n_To contact us raise an issue on Github or via email._\n\n## File Structure and Overview\n\n```text\nREADME.md\ntemplates\n   |-- easy_project\n   |   |-- __init__.py\n   |   |-- easy_project.py\n   |   |-- easy_template.xlsx\n   |-- medium_project\n   |   |-- __init__.py\n   |   |-- medium_project.py\n   |   |-- medium_template.xlsx\n   |-- advanced_project\n   |   |-- __init__.py\n   |   |-- advanced_project.py\n   |   |-- advanced_template.xlsx\n   |   |-- table1_cell_tags.py\n   |   |-- table_1.py\ndata\n   |-- appointment_data.csv\n   |-- practices_data.csv\n   |-- table1_data.csv\noutputs\n   |-- .gitkeep\n   |-- advanced_output.xlsx\n   |-- easy_output.xlsx\n   |-- medium_output.xlsx\nmain.py\nconfig.py\nrequirements.txt\nutils.py\n```\n\n\n\n## Installation\n\nThe [RAP Community of Practice resources](https://nhsdigital.github.io/rap-community-of-practice/) can help you if you are unsure about any of the steps below, including [cloning repositories](https://nhsdigital.github.io/rap-community-of-practice/training_resources/git/introduction-to-git/#common-git-commands), and [virtual environments](https://nhsdigital.github.io/rap-community-of-practice/training_resources/python/virtual-environments/why-use-virtual-environments/).\n\n1. Clone this repo to your preferred working destination, and point your terminal to the folder you've cloned.\n2. Next, create and activate a python virtual environment using your preferred tool.\n3. Next we need to install the packages which this project requires. In your terminal, enter\n\n```bash\n    pip install -r requirements.txt\n```\n\nand run the command. If you run into any problems, check that your terminal is pointed to the right place, and that your virtual environment is activated.\n\nYour installation should now be complete, and you should be able to run the project.\n\nThe basic logic of all three example projects is the same:\n\n1. `main.py` calls a `make_excel` function in the relevant project's script.\n2. This function loads in the template `.xlsx` file for the example project.\n3. It then uses various functions from `excel_functions`; one for each sheet of the target template publication.\n\nThese functions all follow the same basic logic, they:\n\n1. Open the relevant data file, as specified in the `config` file.\n2. Select the relevant columns, join and re-order if necessary.\n3. The function specifies a sheet name: open this sheet in the template, and find the cell with `\u003cstart\u003e` in it.\n4. Write the data to this sheet, starting at this cell.\n5. Find the cell with `\u003cend\u003e` written in it.\n6. Delete all rows between the last row which we've written data to, and this `\u003cend\u003e` cell.\n\n\u003e This last step needs further explanation. The nature of the publication is such that the number of rows printed each publication might vary; different months have different numbers of days, new regions might be added to the scope, etc. Given this, the most practical solution is to allow for an over-abundance of white space in the `template` document, and then delete as appropriate.\n\n## Easy Project\n\nThis project writes two simple sheets: `2a` and `2b`. The functions for writing these sheets are straightforward: select the relevant data, and write it to the workbook.\n\n### How To Run the Easy Project\n\n1. Open `main.py`\n2. Comment out the lines which call the other projects:\n\n    ```python\n    #medium_project.make_excel_output()\n    #advanced_project.make_excel_output()\n    ```\n\n    by adding `#` to the beginning of each line\n3. Run\n\n    ```bash\n    python main.py\n    ```\n\n    in the terminal.\n\n### What the Easy Project is Appropriate For\n\nThis project is the appropriate starting point when you have a number of separate CSV files, each of which you want to write to a separate sheet.\n\n### How To Adapt the Easy Project\n\nThe easy project is very simple to adapt. The `data_for_sheet_easy_a.csv` file is written, almost directly, to sheet `Easy A` in the output file.\n\n1. Copy the template .xlsx file, and duplicate the example sheets within your new version, naming the sheets and their columns appropriately\n2. Add your CSV files to the `data` folder, and add functions to the `config.py` file to load these\n3. Copy and rename the `make_and_write_easy_a` function in `utils.py`, and adapt it to your sheet and data source.\n4. Open `easy_project.py` and replace the functions called within `make_excel_output` with your new functions, and change the template path to your new template.\n\n## Medium Project\n\nThis adds a little complexity, in that we will now be handling data from multiple sources.\nSheets 3.x require data from the `appointments` and `practices` CSV files to be joined, according to region  - This is handled by the `combine_practices_with_appts` function. Once the data from these has been joined, we can simply write it to the appropriate sheet.\n\n### How To Run the Medium Project\n\n1. Open `main.py`\n2. Comment out the lines which call the other projects:\n\n    ```python\n    #easy_project.make_excel_output()\n    #advanced_project.make_excel_output()\n    ```\n\n    by adding `#` to the beginning of each line\n3. Run\n\n    ```bash\n    python main.py\n    ```\n\n    in the terminal.\n\n### What the Medium Project is Appropriate For\n\nThis project is the appropriate starting point when you have CSV files which contain more information than you want o appear on any given individual sheet. The functions used in this project do more manipulation on the data once it's been loaded in. Some of the sheets produced here also require that data be joined from two separate CSV files. As such, the functions for producing each sheet here contain more logic than in the 'easy' project.\n\n### How To Adapt the Medium Project\n\nAs in the easy project, load your data into the `data` folder, and create the corresponding functions in `config` to load your data in.\n\nThen, in `utils.py`, create the functions which curates (and possibly join) the data you need for your sheets.\n\nAs you can see in the functions we've got here, we've given the data in the CSV files a `breakdown` column: this means that we're able to easily identify the relevant rows from a 'long' dataset without much logic.\n\nNow, place your template `.xlsx` file in the project folder, making sure that your target sheets contain the `\u003cstart\u003e` and `\u003cend\u003e` tags, as in the example template.\n\nOnce this is done, you can adapt or replace the `medium_project.py` file, and call it from `main.py` in the usual way.\n\n## Advanced Project\n\nThis involved a significant step up in complexity from the medium project.\nMany NHS England publications contain a summary sheet which is heavily formatted, and which contains information about data which will appear in the rest of the sheets in the publication at a more granular level.\n\nThe other sheets have a data layout which more-or-less echoes a Pandas dataframe, and as such a single dataframe can be written without much manipulation.\nTable 1 on the other hand has a number of specific formatting and presentation criteria.\nThese include; including a range of months in the sheet, giving most information twice (once as a count, once as a percentage), displaying the information in a form which is the transpose of the convention (here the data **fields** are the **rows**, and the **entries** are the **columns**), having multiple blank spaces, and so on.\n\nAll of these features present us with difficulties for formatting this sheet. Should we create a very wide dataframe, and write its transpose? Or many small ones, and write them one by one?\n\nThe solution we have implemented is to assign each and every cell in a given 'column' a unique cell identifier, and to provide a matching CSV file specifically for the purpose of populating Table 1.\nThe functions in `table_1.py` then search through for these specific tags and replace them with the appropriate values.\nThis allows us to minimise the risk of introducing errors, and use a simple function in a loop to write each cell of the sheet.\n\n### How To Run the Advanced Project\n\n1. Open `main.py`\n2. Comment out the lines which call the other projects:\n\n    ```python\n    #easy_project.make_excel_output()\n    #medium_project.make_excel_output()\n    ```\n\n    by adding `#` to the beginning of each line\n3. Run\n\n    ```bash\n    python main.py\n    ```\n\n    in the terminal.\n\n### What the Advanced Project is Appropriate For\n\nThis project is more specific than the other two. It is appropriate for adapting to produce NHS England publications which contain a heavily formatted, high-level, summary sheet.\n\n### How To Adapt the Advanced Project\n\nThis involved more work than adapting the other two projects.\n\nWe will deal only with how to adapt the summary page, the `Table 1` sheet, since the other sheets are equivalent to those in the 'medium' project.\n\n1. Make a copy of the publication which you are trying to automate. Leave all of the headers and column names intact, but delete all of the numbers/data from the cells.\n2. Next, fill those empty cells with written tags which correspond meaningfully to the data which will be written there. This is less laborious than it might look; using the column names to populate the tags, and copious amounts of copy-pasting, you can populate your template in \u003c 10 minutes.\n\nThe data used to populate `Table 1` in the example project is specifically written for this purpose; every cell tag combined with a `month` yields a single value. Writing your CSV data in this format makes it much easier to incorporate into this pipeline, and save you from having to write much logic here.\n\nIf your summary sheet is formatted in a way similar to the example, then you will be able to implement it without changing too much of the code. You will need to create an equivalent to `cell_1_tags.py`, and populate it with the tags from your project.\n\nYou might also need to replace `month` with whichever index it is that you are iterating over, and adjust the logic accordingly.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnhsdigital%2Fautomated_excel_publications","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnhsdigital%2Fautomated_excel_publications","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnhsdigital%2Fautomated_excel_publications/lists"}