Add variables to Id Server

Hello,

i need help with one alteration in senaite idserver

i need that samples have the name (prefix- counter - month - year)

{sampleType}{seq:03d}{month}{year}

i did this by adding a new variable to idserver.py, but suddenly stop working.

could someone help me my knowledge of python and senaite is limited

# The variables map hold the values that might get into the constructed id
variables = {
    "context": context,
    "id": api.get_id(context),
    "portal_type": portal_type,
    "year": get_current_year(),
    "month": get_current_month(),
    "yymmdd": get_yymmdd(),
    "parent": api.get_parent(context),
    "seq": 0,
    "alpha": Alphanumber(0),
}

# Augment the variables map depending on the portal type
if portal_type in AR_TYPES:
    now = DateTime()
    sampling_date = context.getSamplingDate()
    sampling_date = sampling_date and DT2dt(sampling_date) or DT2dt(now)
    date_sampled = context.getDateSampled()
    date_sampled = date_sampled and DT2dt(date_sampled) or DT2dt(now)
    test_count = 1

    variables.update({
        "clientId": context.getClientID(),
        "dateSampled": date_sampled,
        "samplingDate": sampling_date,
        "sampleType": context.getSampleType().getPrefix(),
        "test_count": test_count
    })

    # Partition
    if portal_type == "AnalysisRequestPartition":
        parent_ar = context.getParentAnalysisRequest()
        parent_ar_id = api.get_id(parent_ar)
        parent_base_id = strip_suffix(parent_ar_id)
        partition_count = get_partition_count(context)
        variables.update({
            "parent_analysisrequest": parent_ar,
            "parent_ar_id": parent_ar_id,
            "parent_base_id": parent_base_id,
            "partition_count": partition_count,
        })

    # Retest
    elif portal_type == "AnalysisRequestRetest":
        # Note: we use "parent" instead of "invalidated" for simplicity
        parent_ar = context.getInvalidated()
        parent_ar_id = api.get_id(parent_ar)
        parent_base_id = strip_suffix(parent_ar_id)
        # keep the full ID if the retracted AR is a partition
        if context.isPartition():
            parent_base_id = parent_ar_id
        retest_count = get_retest_count(context)
        test_count = test_count + retest_count
        variables.update({
            "parent_analysisrequest": parent_ar,
            "parent_ar_id": parent_ar_id,
            "parent_base_id": parent_base_id,
            "retest_count": retest_count,
            "test_count": test_count,
        })

    # Secondary
    elif portal_type == "AnalysisRequestSecondary":
        primary_ar = context.getPrimaryAnalysisRequest()
        primary_ar_id = api.get_id(primary_ar)
        parent_base_id = strip_suffix(primary_ar_id)
        secondary_count = get_secondary_count(context)
        variables.update({
            "parent_analysisrequest": primary_ar,
            "parent_ar_id": primary_ar_id,
            "parent_base_id": parent_base_id,
            "secondary_count": secondary_count,
        })

elif portal_type == "ARReport":
    variables.update({
        "clientId": context.aq_parent.getClientID(),
    })

# Look for a variables adapter
adapter = queryAdapter(context, IIdServerVariables)
if adapter:
    vars = adapter.get_variables(**kw)
    variables.update(vars)

return variables

def split(string, separator="-"):
“”" split a string on the given separator
“”"
if not isinstance(string, basestring):
return []
return string.split(separator)

def to_int(thing, default=0):
“”“Convert a thing to an integer
“””
try:
return int(thing)
except (TypeError, ValueError):
return default

def slice(string, separator="-", start=None, end=None):
“”“Slice out a segment of a string, which is splitted on both the wildcards
and the separator passed in, if any
“””
# split by wildcards/keywords first
# AR-{sampleType}-{parentId}{alpha:3a2d}
segments = filter(None, re.split(’({.+?})’, string))
# [‘AR-’, ‘{sampleType}’, ‘-’, ‘{parentId}’, ‘{alpha:3a2d}’]
if separator:
# Keep track of singleton separators as empties
# We need to do this to prevent duplicates later, when splitting
segments = map(lambda seg: seg!=separator and seg or “”, segments)
# [‘AR-’, ‘{sampleType}’, ‘’, ‘{parentId}’, ‘{alpha:3a2d}’]
# Split each segment at the given separator
segments = map(lambda seg: split(seg, separator), segments)
# [[‘AR’, ‘’], [’{sampleType}’], [’’], [’{parentId}’], [’{alpha:3a2d}’]]
# Flatten the list
segments = list(itertools.chain.from_iterable(segments))
# [‘AR’, ‘’, ‘{sampleType}’, ‘’, ‘{parentId}’, ‘{alpha:3a2d}’]
# And replace empties with separator
segments = map(lambda seg: seg!="" and seg or separator, segments)
# [‘AR’, ‘-’, ‘{sampleType}’, ‘-’, ‘{parentId}’, ‘{alpha:3a2d}’]

# Get the start and end positions from the segments without separator
cleaned_segments = filter(lambda seg: seg!=separator, segments)
start_pos = to_int(start, 0)
# Note "end" is not a position, but the number of elements to join!
end_pos = to_int(end, len(cleaned_segments) - start_pos) + start_pos - 1

# Map the positions against the segments with separator
start = segments.index(cleaned_segments[start_pos])
end = segments.index(cleaned_segments[end_pos]) + 1

# Return all segments joined
sliced_parts = segments[start:end]
return "".join(sliced_parts)

def get_current_year():
“”“Returns the current year as a two digit string
“””
return DateTime().strftime("%Y")[2:]

def get_current_month():
“”“Returns the current month as a two digit string
“””
return DateTime().strftime("%m")[2:]

def get_yymmdd():
“”“Returns the current date in yymmdd format
“””
return datetime.now().strftime("%y%m%d")

def make_storage_key(portal_type, prefix=None):
“”“Make a storage (dict-) key for the number generator
“””
key = portal_type.lower()
if prefix:
key = “{}-{}”.format(key, prefix)
return key

@rmmc81, ID Server does not support “month” wildcard by default. However, you can add your own wildcard. Just extend and modify the way the ID server behaves by taking advantage of the hook as explained here: https://github.com/senaite/senaite.core/pull/1586

Your adapter should look similar to:

from datetime import datetime
from zope import interface
from bika.lims.interfaces import IIdServerVariables


class IDServerVariablesAdapter(object):
    """An adapter for the generation of Variables for ID Server
    """
    interface.implements(IIdServerVariables)

    def __init__(self, context):
        self.context = context

    def get_variables(self, **kw):
        return {
            "month": datetime.now().strftime("%m")
        }

Hello xispa

Thanks for your reply.

I have been trying to use your solution but not working as expected.

Could you help me a little more?

What files exactly should I change?

Could i make the changes a give them to you so you could review them ?

Thanks

Rui

I was assuming you already had your own add-on. If is not the case (which is strongly recommended), then modify senaite.core by adding the “month” variable here: https://github.com/senaite/senaite.core/blob/1.3.x/bika/lims/idserver.py#L216

You will have the “month” wildcard available for IDs after you restart the instance.