class Delivery_documents
A class that generates delivery documentation (slip and label) as Word documents by querying a Neo4j database for study and customer information and populating predefined templates.
/tf/active/vicechatdev/resources/documents.py
15 - 147
complex
Purpose
This class automates the creation of delivery documentation for laboratory studies. It connects to a Neo4j graph database to retrieve study, customer, site, and organ sample information, then generates two Word documents: a detailed delivery slip listing all samples and a delivery label with barcode. The documents are created in-memory as BytesIO objects for immediate use or download without writing to disk.
Source Code
class Delivery_documents():
"""
Creates a Delivery Slip and Delivery Label Word document using templates
Parameters
----------
site : str
The site the delivery is to be made to
study : str
A string of the study UID
See Also
--------
docxtpl
"""
def __init__(self, study, site='Diepenbeek'):
self.graph = Graph(config.DB_ADDR, auth=config.DB_AUTH, name=config.DB_NAME)
self.study_uid = study
self.site = self.graph.run(f"MATCH (s:Site {{N:'{site}'}}) RETURN s").evaluate()
self.grab_data()
self.generate_slip()
self.generate_label()
def address_to_string(self, address):
return f"{address['Street']} {address['Number']}\n\t\t\t{address['ZIP']}, {address['City']}\n\t\t\t{address['Country']}"
def grab_data(self):
"""
Gather the user and study data to populate the word templates
"""
self.study = self.graph.run(f"MATCH (s:Study {{UID:'{self.study_uid}'}}) return s").evaluate()
self.customer = self.graph.run(f"MATCH (c:Customer)-->(:Study {{UID:'{self.study_uid}'}}) RETURN c").evaluate()
df = self.graph.run(f"""
MATCH (:Study {{UID:'{self.study_uid}'}})-->(g:Group)-->(e:ExpItem)-->(o:Organ)
WITH o, g.N as Group, e.N as Sample, o.N as Organ, o.Alias as Alias, o.external_N as ID
ORDER BY Sample, (CASE WHEN EXISTS(o.Order) THEN o.Order ELSE Organ END)
RETURN Group, Sample, Organ, ID, Alias""").to_data_frame()
df = df.dropna(axis=1, how='all') #drops Alias column of its completely empty
self.df = df.fillna('') #replaces ugly 'NaN' without beautiful empty rows
def generate_slip(self):
"""
Populates the delivery slip template file, creates a virtual word document.
Attributes
----------
slip : io.BytesIO
Virtual Word document containing the delivery slip
"""
delivery_slip = DocxTemplate('./templates/Delivery_slip_tpl.docx')
contact_dict = json.loads(self.study['Contact'])
name=contact_dict['Name']
mail=contact_dict['Mail']
number=contact_dict['Number']
site_address_dict = json.loads(self.site['Address'])
site_address = self.address_to_string(site_address_dict)
address_dict = json.loads(self.customer['Address'])
address = self.address_to_string(address_dict)
int_study = self.study['N']
ext_study = self.study['external_N']
#comprehension makes a list of dictionaries, where each dictionary has 2 keys, one for organ type one for count.
# table_content = [dict(zip(('organ','count'), i)) for i in self.data['Organ'].value_counts().items()]
# self.df.sort_values(by=['group','sample'], inplace=True)
print(self.df.columns)
if 'Alias' in self.df.columns:
table_content = [dict(zip(('group','sample','organ','id','alias'), i)) for _, i in self.df.iterrows()]
alias = True
else:
table_content = [dict(zip(('group','sample','organ','id'), i)) for _, i in self.df.iterrows()]
alias = False
context = {
'ext_study':ext_study,
'int_study':int_study,
'site_address':site_address,
'name':name,
'Alias':alias,
'tbl_contents': table_content,
'address': address,
'contact':name,
'mail':mail,
'number':number,
'recv_location':f"{site_address_dict['Street']} {site_address_dict['Number']}",
'send_party':name,
'send_location': address_dict['City']
}
delivery_slip.render(context)
self.slip = io.BytesIO()
delivery_slip.save(self.slip)
self.slip.seek(0)
def generate_label(self):
"""
Populates the delivery label template file, creates a virtual word document.
Attributes
----------
label : io.BytesIO
Virtual Word document of the delivery label
"""
def uid2image(uid):
encoded = encode(uid.encode('utf-8'))
image = Image.frombytes('RGB', (encoded.width, encoded.height), encoded.pixels)
return image
def image2file(image):
image_file = io.BytesIO()
image.save(image_file, format="PNG")
return image_file
image = uid2image(self.study_uid)
image_file = image2file(image)
delivery_label = DocxTemplate('./templates/Delivery_label_tpl.docx')
contact_dict = json.loads(self.study['Contact'])
name=contact_dict['Name']
mail=contact_dict['Mail']
number=contact_dict['Number']
site_address_dict = json.loads(self.site['Address'])
site_address = self.address_to_string(site_address_dict)
address_dict = json.loads(self.customer['Address'])
address = self.address_to_string(address_dict)
context = {
'barcode': InlineImage(delivery_label, image_file),
'site_address': site_address,
'name':name,
'address': address,
'contact':name,
'mail':mail,
'number':number,
}
delivery_label.render(context)
self.label = io.BytesIO()
delivery_label.save(self.label)
self.label.seek(0)
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
bases |
- | - |
Parameter Details
study: The unique identifier (UID) string for the study. This is used to query the Neo4j database for all related study information, customer details, and sample/organ data.
site: The name of the delivery site location. Defaults to 'Diepenbeek'. This is used to query the database for the site's address information that will appear on the delivery documents.
Return Value
The constructor returns an instance of Delivery_documents with two main attributes: 'slip' (io.BytesIO object containing the delivery slip Word document) and 'label' (io.BytesIO object containing the delivery label Word document). Both documents are ready to be saved to disk or sent as file downloads.
Class Interface
Methods
__init__(self, study: str, site: str = 'Diepenbeek') -> None
Purpose: Initializes the class, connects to Neo4j database, retrieves all necessary data, and automatically generates both delivery slip and label documents
Parameters:
study: The unique identifier (UID) string for the studysite: The name of the delivery site location (defaults to 'Diepenbeek')
Returns: None - sets up instance with slip and label attributes
address_to_string(self, address: dict) -> str
Purpose: Converts an address dictionary to a formatted multi-line string suitable for document display
Parameters:
address: Dictionary containing address fields: 'Street', 'Number', 'ZIP', 'City', 'Country'
Returns: Formatted string with address components separated by newlines and tabs
grab_data(self) -> None
Purpose: Queries the Neo4j database to retrieve study, customer, and organ/sample data, storing results in instance attributes
Returns: None - populates self.study, self.customer, and self.df attributes
generate_slip(self) -> None
Purpose: Generates the delivery slip Word document by populating the template with study and sample data
Returns: None - creates self.slip attribute as io.BytesIO containing the Word document
generate_label(self) -> None
Purpose: Generates the delivery label Word document with barcode by populating the template with study contact and address information
Returns: None - creates self.label attribute as io.BytesIO containing the Word document
Attributes
| Name | Type | Description | Scope |
|---|---|---|---|
graph |
Graph | Neo4j database connection object used for querying study, customer, and site data | instance |
study_uid |
str | The unique identifier for the study being processed | instance |
site |
Node | Neo4j node object containing site information including address | instance |
study |
Node | Neo4j node object containing study information including internal/external study numbers and contact details | instance |
customer |
Node | Neo4j node object containing customer information including address | instance |
df |
pandas.DataFrame | DataFrame containing organized sample/organ data with columns: Group, Sample, Organ, ID, and optionally Alias | instance |
slip |
io.BytesIO | In-memory Word document containing the generated delivery slip, ready for reading or saving | instance |
label |
io.BytesIO | In-memory Word document containing the generated delivery label with barcode, ready for reading or saving | instance |
Dependencies
neo4j_driverconfigdocxpylibdmtxdocxtplPILplotlyjsoniodatetime
Required Imports
from neo4j_driver import *
import config
from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.shared import RGBColor, Pt, Length, Inches, Cm
from docx.enum.table import WD_ROW_HEIGHT_RULE
from pylibdmtx.pylibdmtx import decode, encode
from docxtpl import DocxTemplate, InlineImage
from PIL import Image
import plotly.express as px
import json
import io
import datetime as dt
Usage Example
# Ensure config.py has DB_ADDR, DB_AUTH, DB_NAME defined
# Ensure template files exist in ./templates/ directory
# Create delivery documents for a study
delivery_docs = Delivery_documents(study='STUDY-12345-UID', site='Diepenbeek')
# Save the delivery slip to a file
with open('delivery_slip.docx', 'wb') as f:
f.write(delivery_docs.slip.read())
# Save the delivery label to a file
with open('delivery_label.docx', 'wb') as f:
f.write(delivery_docs.label.read())
# Or use in a web framework to send as download
# return send_file(delivery_docs.slip, as_attachment=True, download_name='slip.docx')
Best Practices
- The class automatically generates both documents upon instantiation, so be aware that database queries and document generation happen immediately in __init__
- The slip and label BytesIO objects have their file pointers at position 0 (via seek(0)), ready for reading
- If you need to read the BytesIO objects multiple times, call seek(0) before each read operation
- Ensure the Neo4j database connection is available and the database schema matches expected node types and properties
- Template files must exist at the specified paths before instantiation or the class will fail
- The class expects specific JSON structure in database fields (Contact, Address) - ensure data integrity
- The generated documents are held in memory; for large-scale operations, consider memory management
- The class does not provide methods to regenerate documents - create a new instance if you need fresh documents
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
class print_client 50.8% similar
-
class Cassette_Printer 47.6% similar
-
function create_document_version 46.4% similar
-
class Study_overview 45.7% similar
-
class DocumentTraining 45.6% similar