Skip to content

Instantly share code, notes, and snippets.

@aurthurm
Created June 10, 2025 12:01
Show Gist options
  • Save aurthurm/ecade1d9bd939a332001275cb35f97bb to your computer and use it in GitHub Desktop.
Save aurthurm/ecade1d9bd939a332001275cb35f97bb to your computer and use it in GitHub Desktop.
from bika.lims import api
from nmrl.lims.scripts import setup_script_environment
from senaite.core.catalog import SETUP_CATALOG, SAMPLE_CATALOG
import transaction
def fix_services():
services = api.search({"portal_type": "AnalysisService"}, SETUP_CATALOG)
print("Total services found: {}".format(len(services)))
updated_keywords = set()
batch = 0
for brain in services:
service = api.get_object(brain)
result_type = service.getResultType()
result_options = service.getResultOptions()
if result_type == "numeric" and result_options:
print("Updating {} from 'numeric' to 'select'".format(service.Title()))
service.setResultType("select")
service.reindexObject()
updated_keywords.add(service.getKeyword())
batch += 1
if batch >= 10:
transaction.commit()
print("Committed batch of 10 service updates")
batch = 0
if batch:
transaction.commit()
print("Final commit of remaining {} service updates".format(batch))
print("Total services fixed: {}".format(len(updated_keywords)))
return list(updated_keywords)
def search_service(keyword):
results = api.search({
"portal_type": "AnalysisService",
"getKeyword": keyword
}, SETUP_CATALOG)
return api.get_object(results[0]) if results else None
def refresh_samples(app, updated_keywords, commit_interval=10):
query = {
"portal_type": "AnalysisRequest",
"review_state": "sample_received",
"sort_on": "created",
"sort_order": "ascending",
}
brains = api.search(query, SAMPLE_CATALOG)
print("\nFound {} samples in 'sample_received'".format(len(brains)))
updated_count = 0
for brain in brains:
sample = api.get_object(brain)
analyses = sample.getAnalyses()
if not analyses:
continue
print("\nSample: {} with {} analyses".format(sample.getId(), len(analyses)))
to_delete = []
refresh_keywords = []
keep_services = []
for a_brain in analyses:
analysis = api.get_object(a_brain)
state = api.get_review_status(analysis)
if state not in ["received", "unassigned", "assigned"]:
print(" Skipping sample: {} (analysis not in allowed state)".format(sample.getId()))
to_delete = []
break
# TODO: handle worksheets - unassign first but keep position then reassign later to same position
service = analysis.getAnalysisService()
keyword = service.getKeyword()
if keyword in updated_keywords:
to_delete.append(analysis.getId())
refresh_keywords.append(keyword)
else:
keep_services.append(service)
if not to_delete:
continue
# Delete outdated analyses
print(" Deleting: {}".format(to_delete))
sample.manage_delObjects(to_delete)
# Re-attach updated services
refreshed_services = []
for keyword in refresh_keywords:
service = search_service(keyword)
if service:
refreshed_services.append(service)
else:
print(" Missing service for keyword: {}".format(keyword))
final_services = refreshed_services + keep_services
if final_services:
try:
sample.setAnalyses(final_services)
sample.reindexObject()
print(" Refreshed Sample {} with services: {}".format(
sample.getId(),
[s.getKeyword() for s in final_services]
))
except Exception as e:
print(" Failed to update sample {}: {}".format(sample.getId(), str(e)))
continue
updated_count += 1
if updated_count % commit_interval == 0:
transaction.commit()
print(" Committed after {} refreshed samples".format(updated_count))
transaction.commit()
print("\nDone. Total refreshed samples: {}".format(updated_count))
def main(app):
setup_script_environment(app)
updated_keywords = fix_services()
if updated_keywords:
refresh_samples(app, updated_keywords)
else:
print("No services were modified. No samples refreshed.")
if __name__ == "__main__":
main(app)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment