class Academic(AuditMixin, CommonMixin, db.Model): id = db.Column(db.Integer(), primary_key=True) google_scholar_id = db.Column(db.String(255)) name = db.Column(db.String(500)) affiliation = db.Column(db.String(500)) cited_by = db.Column(db.Integer) h_index = db.Column(db.Integer) i10_index = db.Column(db.Integer) is_updating = db.Column(db.Boolean) @property def pubmed_name(self): firstname, *_, lastname = self.name.split() return f'{lastname} {firstname[0]}'
class ParticipantIdentifierSource(db.Model): __tablename__ = 'participant_identifier_source' id = db.Column(db.Integer, primary_key=True) linked_minimum_patient_identifier_source_id = db.Column( db.Integer, db.ForeignKey("participant_identifier_source.id"), nullable=True) type = db.Column(db.String(100), nullable=False) study_id = db.Column(db.Integer, db.ForeignKey(Study.id), nullable=True) study = db.relationship( Study, backref=db.backref("participant_identifier_sources")) __mapper_args__ = { 'polymorphic_identity': 'participant_identifier_source', 'polymorphic_on': type, } last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship(User) identifiers = db.relationship( "ParticipantIdentifier", secondary=participant_identifiers__participant_identifier_sources, back_populates="sources", collection_class=set) def __str__(self): return f"{self.type.name}: {self.identifier}"
class DemographicsRequestColumn(db.Model): id = db.Column(db.Integer, primary_key=True) demographics_request_id = db.Column(db.Integer, db.ForeignKey(DemographicsRequest.id)) demographics_request = db.relationship( DemographicsRequest, foreign_keys=[demographics_request_id], backref=db.backref("columns")) name = db.Column(db.String(500)) last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship( User, foreign_keys=[last_updated_by_user_id]) def __lt__(self, other): return self.name < other.name def __repr__(self): fields = '; '.join([ f'{key}="{value}"' for key, value in self.__dict__.items() if key[0] != '_' ]) return f'[{type(self).__name__}({fields})]'
class Organisation(db.Model, CommonMixin): CARDIOVASCULAR = 'BRC Cardiovascular Theme' LIFESTYLE = 'BRC Lifestyle Theme' PRECICION = 'BRC Precision Medicine Theme' RESPIRATORY = 'BRC Respiratory Theme' LDC = 'Leicester Diabetes Centre' PRC = 'Patient Recruitment Centre' RandI = 'R&I' OTHER = 'Other - please specify' all_organisations = [ CARDIOVASCULAR, LIFESTYLE, PRECICION, RESPIRATORY, LDC, PRC, RandI, OTHER ] id = db.Column(db.Integer(), primary_key=True) name = db.Column(db.String(255)) @classmethod def get_organisation(cls, name): return Organisation.query.filter_by(name=name).one() @classmethod def get_other(cls): return cls.get_organisation(Organisation.OTHER)
class Blinding(db.Model): id = db.Column(db.Integer, primary_key=True) unblind_id = db.Column(db.String(100), nullable=False) blinding_type_id = db.Column(db.Integer, db.ForeignKey(BlindingType.id), nullable=False) blinding_type = db.relationship(BlindingType, backref=db.backref("blindings")) pseudo_random_id_id = db.Column(db.Integer, db.ForeignKey(PseudoRandomId.id), nullable=False) pseudo_random_id = db.relationship(PseudoRandomId) last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship(User) def __repr__(self): return '; '.join([ self.blinding_type.name, self.unblind_id, self.pseudo_random_id.full_code, ]) def __lt__(self, other): return self.name < other.name
class BlindingSet(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) study_id = db.Column(db.Integer, db.ForeignKey(Study.id), nullable=False) study = db.relationship(Study, backref=db.backref("blinding_sets")) def __repr__(self): return self.name def __lt__(self, other): return self.name < other.name def get_blind_ids(self, unblind_id, user): result = [] for bt in self.blinding_types: blind_id = bt.get_blind_id(unblind_id, user) if blind_id: result.append(blind_id) return result def get_unblind_id(self, blind_id): for bt in self.blinding_types: unblind_id = bt.get_unblind_id(blind_id) if unblind_id: return unblind_id
class BioresourceId(db.Model): id = db.Column(db.Integer, primary_key=True) bioresource_id_provider_id = db.Column( db.Integer, db.ForeignKey(BioresourceIdProvider.id)) bioresource_id_provider = db.relationship(BioresourceIdProvider) number = db.Column(db.Integer, nullable=False) check_character = db.Column(db.String(1), nullable=False) legacy_number = db.Column(db.Integer) last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship(User) def __repr__(self): return str(self.__class__) + ": " + str(self.__dict__) def __str__(self): return self.full_code @property def full_code(self): return self.bioresource_id_provider.prefix + str( self.number) + self.check_character @property def barcode(self): return self.full_code
class Study(db.Model): id = db.Column(db.Integer(), primary_key=True) name = db.Column(db.String(100)) date_created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) allow_duplicate_study_number = db.Column(db.Boolean, nullable=False, default=False) allow_empty_study_number = db.Column(db.Boolean, nullable=False, default=False) study_number_format = db.Column(db.String(50)) study_number_name = db.Column(db.String(100)) field_group_id = db.Column(db.Integer(), db.ForeignKey(FieldGroup.id)) field_group = db.relationship(FieldGroup, backref=db.backref("study")) owners = db.relationship( User, secondary=studies_owners, backref=db.backref("owned_studies", lazy="dynamic"), ) collaborators = db.relationship( User, secondary=studies_collaborators, backref=db.backref("collaborator_studies", lazy="dynamic"), ) def __str__(self): return self.name @property def upload_count(self): return len([u for u in self.uploads if not u.deleted]) def upload_count_for_user(self, user): return len( [u for u in self.uploads if not u.deleted and u.uploader == user]) @property def outstanding_upload_count(self): return len( [u for u in self.uploads if not u.deleted and not u.completed]) def get_study_number_name(self): return self.study_number_name or 'Study Number'
class TaskAssignedUser(AuditMixin, CommonMixin, db.Model): id = db.Column(db.Integer(), primary_key=True) task_id = db.Column(db.Integer, db.ForeignKey(Task.id), nullable=False) task = db.relationship(Task, backref="assigned_user_history") user_id = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False) user = db.relationship(User) notes = db.Column(db.String(255))
class PseudoRandomId(db.Model): id = db.Column(db.Integer, primary_key=True) pseudo_random_id_provider_id = db.Column( db.Integer, db.ForeignKey(PseudoRandomIdProvider.id)) pseudo_random_id_provider = db.relationship(PseudoRandomIdProvider) ordinal = db.Column(db.Integer, nullable=False) unique_code = db.Column(db.Integer, nullable=False) check_character = db.Column(db.String(1), nullable=False) full_code = db.Column(db.String(20), nullable=False) last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship(User) @property def barcode(self): return self.full_code
class AbstractSection(AuditMixin, CommonMixin, db.Model): id = db.Column(db.Integer(), primary_key=True) publication_id = db.Column(db.Integer(), db.ForeignKey(Publication.id)) publication = db.relationship(Publication, lazy="joined", backref='abstracts') label = db.Column(db.String(200)) text = db.Column(db.UnicodeText())
class LabelPack(db.Model): id = db.Column(db.Integer, primary_key=True) type = db.Column(db.String(100), nullable=False) study_id = db.Column(db.Integer, db.ForeignKey(Study.id)) study = db.relationship(Study, backref=db.backref("label_packs")) __mapper_args__ = { "polymorphic_identity": "Pack", "polymorphic_on": type, } def user_defined_participant_id(self): return False def allow_batch_printing(self): return True @property def name(self): return re.sub('([a-z])([A-Z])', r'\1 \2', self.__class__.__name__) def print(self, count): for _ in range(count): current_app.logger.info( f'Printing label for study {self.study.name}') self._do_print() db.session.commit() time.sleep(current_app.config['PRINTING_SET_SLEEP']) def save_participant_id(self, participant_id): pit = ParticipantIdentifierType.get_study_participant_id() pi = ParticipantIdentifier.query.filter_by( participant_identifier_type_id=pit.id, identifier=participant_id, ).one_or_none() if pi is None: db.session.add( ParticipantIdentifier( participant_identifier_type_id=pit.id, identifier=participant_id, last_updated_by_user_id=current_user.id, )) def _do_print(self, participant_id=None): pass def __repr__(self): return self.name def __lt__(self, other): return self.name < other.name
class LegacyIdProvider(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) prefix = db.Column(db.String(10), nullable=False) number_fixed_length = db.Column(db.Integer, nullable=False) last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship(User) def allocate_id(self, user): found = 1 tries = 0 prospective_id = 0 while found > 0: prospective_id = random.randint(1, 900000) tries = tries + 1 found = LegacyId.query.filter( LegacyId.legacy_id_provider_id == self.id).filter( LegacyId.number == prospective_id).count() actual_id = LegacyId( legacy_id_provider=self, number=prospective_id, last_updated_by_user=user, ) db.session.add(actual_id) return actual_id def allocate_ids(self, count, user): result = [] for _ in range(count): result.append(self.allocate_id(user)) return result def __repr__(self): return str(self.__class__) + ": " + str(self.__dict__)
class TaskStatus(AuditMixin, CommonMixin, db.Model): id = db.Column(db.Integer(), primary_key=True) task_id = db.Column(db.Integer, db.ForeignKey(Task.id), nullable=False) task = db.relationship(Task, backref="status_history") notes = db.Column(db.String(255)) task_status_type_id = db.Column(db.Integer, db.ForeignKey(TaskStatusType.id), nullable=False) task_status_type = db.relationship(TaskStatusType, backref="assigned_tasks")
class Site(db.Model): LBRC = "Leicester Biomedical Research Centre" id = db.Column(db.Integer(), primary_key=True) name = db.Column(db.String(255)) number = db.Column(db.String(20)) date_created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) def __str__(self): return self.name @property def name_and_number(self): number_portion = "" if self.number: number_portion = " ({})".format(self.number) return self.name + number_portion
class Upload(db.Model): id = db.Column(db.Integer(), primary_key=True) study_id = db.Column(db.Integer(), db.ForeignKey(Study.id)) study_number = db.Column(db.String(20)) uploader_id = db.Column(db.Integer(), db.ForeignKey(User.id)) date_created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) study = db.relationship(Study, backref=db.backref("uploads")) uploader = db.relationship(User) completed = db.Column(db.Boolean, default=0) deleted = db.Column(db.Boolean, default=0)
class Publication(AuditMixin, CommonMixin, db.Model): id = db.Column(db.Integer(), primary_key=True) pm_id = db.Column(db.Integer()) journal = db.Column(db.String(200)) published_date = db.Column(db.Date) title = db.Column(db.UnicodeText()) academics = db.relationship("Academic", secondary=academics_publications, collection_class=set, backref=db.backref("publications", lazy="joined"))
class Service(AuditMixin, CommonMixin, db.Model): id = db.Column(db.Integer(), primary_key=True) name = db.Column(db.String(255)) generic_recipients = db.Column(db.String(255)) suppress_owner_email = db.Column(db.Boolean) field_group_id = db.Column(db.Integer, db.ForeignKey(FieldGroup.id)) field_group = db.relationship(FieldGroup) introduction = db.Column(db.UnicodeText()) def __str__(self): return self.name def get_field_for_field_name(self, field_name): if self.field_group: return self.field_group.get_field_for_field_name(field_name) @property def notification_email_addresses(self): return list( filter(len, [ r.email for r in self.owners if not self.suppress_owner_email ] + re.split(r'[;,\s]+', self.generic_recipients or '')))
class SequentialIdProvider(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) prefix = db.Column(db.String(10)) zero_fill_size = db.Column(db.Integer) last_number = db.Column(db.Integer, nullable=False, default=0) last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship(User) def allocate_ids(self, count, user): start = self.last_number + 1 self.last_number = self.last_number + count self.last_updated_by_user = user self.last_updated_datetime = datetime.utcnow() format_string = self.prefix or "" if self.zero_fill_size: format_string += "{:0" + str(self.zero_fill_size) + "d}" else: format_string += "{}" return [ SequentialId(format_string.format(i)) for i in range(start, self.last_number + 1) ] def allocate_id(self, user): return self.allocate_ids(1, user)[0] def __repr__(self): return str(self.__class__) + ": " + str(self.__dict__)
class Study(db.Model): id = db.Column(db.Integer, primary_key=True) edge_id = db.Column(db.Integer, nullable=True) name = db.Column(db.String(50), nullable=False) def __str__(self): return self.name or "" def get_blind_ids(self, unblind_id, user): result = [] for bs in self.blinding_sets: for bt in [t for t in bs.blinding_types if not t.deleted]: blind_id = bt.get_blind_id(unblind_id, user) if blind_id: result.append(blind_id) return result
class BlindingType(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) blinding_set_id = db.Column(db.Integer, db.ForeignKey(BlindingSet.id), nullable=False) blinding_set = db.relationship(BlindingSet, backref=db.backref("blinding_types")) pseudo_random_id_provider_id = db.Column(db.Integer, db.ForeignKey( PseudoRandomIdProvider.id), nullable=False) pseudo_random_id_provider = db.relationship(PseudoRandomIdProvider) deleted = db.Column(db.Boolean, nullable=False, default=False) duplicate_number = db.Column(db.Integer, default=0, nullable=False) def __repr__(self): return self.name def __lt__(self, other): return self.name < other.name def get_blind_id(self, unblind_id, user): blinding = (Blinding.query.filter_by( blinding_type_id=self.id).filter_by(unblind_id=unblind_id).first()) if not blinding: pseudo_random_id = self.pseudo_random_id_provider.allocate_id(user) blinding = Blinding( unblind_id=unblind_id, blinding_type=self, pseudo_random_id=pseudo_random_id, last_updated_by_user=user, ) return blinding def get_unblind_id(self, blind_id): blinding = (Blinding.query.filter_by( blinding_type_id=self.id).join(PseudoRandomId).filter_by( full_code=blind_id).first()) return blinding
class ParticipantIdentifier(db.Model): __tablename__ = 'participant_identifier' id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String(100), nullable=False) participant_identifier_type_id = db.Column( db.Integer, db.ForeignKey(ParticipantIdentifierType.id)) participant_identifier_type = db.relationship(ParticipantIdentifierType) last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship(User) sources = db.relationship( "ParticipantIdentifierSource", secondary=participant_identifiers__participant_identifier_sources, back_populates="identifiers", collection_class=set) def __str__(self): return f"{self.type.name}: {self.identifier}"
class ParticipantIdentifierType(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship(User) def __str__(self): return self.name @staticmethod def get_type(type_name): return ParticipantIdentifierType.query.filter_by( name=type_name, ).one_or_none() @staticmethod def get_study_participant_id(): return ParticipantIdentifierType.get_type( ParticipantIdentifierTypeName.STUDY_PARTICIPANT_ID)
class UploadFile(db.Model): id = db.Column(db.Integer(), primary_key=True) upload_id = db.Column(db.Integer(), db.ForeignKey(Upload.id)) upload = db.relationship(Upload, backref=db.backref("files")) field_id = db.Column(db.Integer(), db.ForeignKey(Field.id)) field = db.relationship(Field) filename = db.Column(db.String(500)) def get_download_filename(self): if len(self.field.download_filename_format or '') == 0: return self.filename else: return self.field.download_filename_format.format( file=self) + os.path.splitext(self.filename)[-1] def filepath(self): return os.path.join( secure_filename("{}_{}".format(self.upload.study.id, self.upload.study.name)), secure_filename("{}_{}_{}".format(self.id, self.upload.study_number, self.filename)), )
class DemographicsRequest(db.Model): id = db.Column(db.Integer, primary_key=True) created_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) filename = db.Column(db.String(500)) extension = db.Column(db.String(100), nullable=False) owner_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) owner = db.relationship(User, foreign_keys=[owner_user_id], backref=db.backref("demographic_requests")) submitted_datetime = db.Column(db.DateTime) deleted_datetime = db.Column(db.DateTime) paused_datetime = db.Column(db.DateTime) data_extracted_datetime = db.Column(db.DateTime) pmi_data_pre_completed_datetime = db.Column(db.DateTime) pmi_data_post_completed_datetime = db.Column(db.DateTime) lookup_completed_datetime = db.Column(db.DateTime) result_created_datetime = db.Column(db.DateTime) result_downloaded_datetime = db.Column(db.DateTime) error_datetime = db.Column(db.DateTime) error_message = db.Column(db.Text) last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship( User, foreign_keys=[last_updated_by_user_id]) column_definition = db.relationship("DemographicsRequestColumnDefinition", uselist=False, back_populates="demographics_request") skip_pmi = db.Column(db.Boolean, default=False) __mapper_args__ = { "polymorphic_on": extension, } @property def filepath(self): return os.path.join( current_app.config["FILE_UPLOAD_DIRECTORY"], secure_filename( "{}_{}".format(self.last_updated_by_user.id, self.last_updated_by_user.full_name)), secure_filename("{}_{}".format(self.id, self.filename)), ) @property def result_filename(self): return secure_filename("{}_result_{}".format(self.id, self.filename)) @property def result_filepath(self): return os.path.join( current_app.config["FILE_UPLOAD_DIRECTORY"], secure_filename( "{}_{}".format(self.last_updated_by_user.id, self.last_updated_by_user.full_name)), self.result_filename, ) def __lt__(self, other): return self.created_datetime < other.created_datetime @property def data_extracted(self): return self.data_extracted_datetime is not None @property def paused(self): return self.paused_datetime is not None @property def columns_defined(self): return self.column_definition and self.column_definition.is_valid @property def awaiting_submission(self): return self.columns_defined and self.submitted_datetime is None @property def submitted(self): return self.submitted_datetime is not None @property def deleted(self): return self.deleted_datetime is not None @property def result_created(self): return self.result_created_datetime is not None @property def pmi_data_pre_completed(self): return self.pmi_data_pre_completed_datetime is not None @property def pmi_data_post_completed(self): return self.pmi_data_post_completed_datetime is not None @property def result_downloaded(self): return self.result_downloaded_datetime is not None @property def lookup_completed(self): return self.lookup_completed_datetime is not None @property def in_error(self): return self.error_datetime is not None @property def requires_column_definition(self): return not self.deleted and not self.submitted and not self.in_error @property def requires_submission(self): return not self.deleted and self.awaiting_submission and not self.in_error @property def can_be_resubmitted(self): return not self.deleted and self.submitted and not self.result_created and not self.in_error @property def can_be_paused(self): return not self.deleted and self.submitted and not self.result_created and not self.paused and not self.in_error @property def can_be_downloaded(self): return not self.deleted and self.result_created and not self.in_error @property def can_be_deleted(self): return not self.deleted @property def status(self): if self.deleted: return 'Deleted' elif self.in_error: return 'Error' elif self.paused: return 'Paused' if not self.columns_defined: return 'Uploaded' elif not self.submitted: return 'Awaiting Submission' elif not self.data_extracted: return f'Extracting Data' elif not self.pmi_data_pre_completed and not self.skip_pmi: return f'Fetching PMI details {self.prepmi_count} of {self.data_count} before spine lookup' elif not self.lookup_completed: return f'Fetching Demographics {self.fetched_count} of {self.data_count}' elif not self.pmi_data_post_completed and not self.skip_pmi: return f'Fetching PMI details {self.postpmi_count} of {self.data_count} after spine lookup' elif not self.result_created: return 'Processing Demographics' elif not self.result_downloaded: return 'Ready to Download' else: return 'Downloaded' @property def data_count(self): return DemographicsRequestData.query.filter( DemographicsRequestData.demographics_request_id == self.id).count() @property def fetched_count(self): return DemographicsRequestData.query.filter( DemographicsRequestData.demographics_request_id == self.id).filter( DemographicsRequestData.processed_datetime.isnot( None)).count() @property def prepmi_count(self): return DemographicsRequestData.query.filter( DemographicsRequestData.demographics_request_id == self.id).filter( DemographicsRequestData.pmi_pre_processed_datetime.isnot( None)).count() @property def postpmi_count(self): return DemographicsRequestData.query.filter( DemographicsRequestData.demographics_request_id == self.id).filter( DemographicsRequestData.pmi_post_processed_datetime.isnot( None)).count() def get_most_likely_uhl_system_number_column_id(self): return self._get_most_likely_column_id('(uhl|\bs).*(number|no|)') def get_most_likely_nhs_number_column_id(self): return self._get_most_likely_column_id('nhs.*(number|no|)') def get_most_likely_family_name_column_id(self): return self._get_most_likely_column_id('(surname|(family|last).*name)') def get_most_likely_given_name_column_id(self): return self._get_most_likely_column_id('(first|given|fore).*name|name') def get_most_likely_gender_column_id(self): return self._get_most_likely_column_id('(gender|sex)') def get_most_likely_dob_column_id(self): return self._get_most_likely_column_id('(dob|date.*birth|birth.*date)') def get_most_likely_postcode_column_id(self): return self._get_most_likely_column_id('(post.*code)') def _get_most_likely_column_id(self, regular_expression): repat = re.compile(regular_expression, re.IGNORECASE) ids = (c.id for c in self.columns if re.search(repat, c.name)) return next(ids, 0) def set_error(self, message): self.error_datetime = datetime.utcnow() self.error_message = message
class PseudoRandomIdProvider(db.Model): # See http://preshing.com/20121224/how-to-generate-a-sequence-of-unique-random-integers/ _PRIME = 999983 # PRIME MOD 4 must equal 3 id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) prefix = db.Column(db.String(10), nullable=False) last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship(User) def _permuteQPR(self, x): if x >= self._PRIME: return x # The 5 integers out of range are mapped to themselves. residue = (x * x) % self._PRIME return residue if (x <= self._PRIME / 2) else self._PRIME - residue def _create_unique_id(self, x): first = (self._permuteQPR(x) + sum([ord(x) for x in self.prefix])) % self._PRIME return self._permuteQPR(first) def _get_id(self, n): uniqueId = self._create_unique_id(n) formattedId = "{}{:0>7d}".format(self.prefix, uniqueId) checkDigit = self._get_checkdigit(formattedId) return "{}{}".format(formattedId, checkDigit) def validate(self, id): code = id[:-1] if id[:len(self.prefix)] != self.prefix: return False if id[-1:] != self._get_checkdigit(code): return False return True def _get_checkdigit(self, id): numerified = sum([ord(x) * i for i, x in enumerate(id)]) return "ABCDEFGHJKLMNPQRSTVWXYZ"[numerified % 23] def _create_pseudo_id(self, ordinal, user): unique_code = self._create_unique_id(ordinal) formatted_code = "{}{:0>7d}".format(self.prefix, unique_code) check_character = self._get_checkdigit(formatted_code) full_code = formatted_code + check_character return PseudoRandomId( pseudo_random_id_provider_id=self.id, ordinal=ordinal, unique_code=unique_code, check_character=check_character, full_code=full_code, last_updated_by_user_id=user.id, ) def allocate_id(self, user): previous_ordinal = db.session.query(db.func.max( PseudoRandomId.ordinal)).scalar() or 0 result = self._create_pseudo_id(previous_ordinal + 1, user) db.session.add(result) return result def allocate_ids(self, count, user): # Bulk inserts objects for speed that does not populate # the object ID, so may cause problems if the object is # used later on. result = [] previous_ordinal = db.session.query(db.func.max( PseudoRandomId.ordinal)).scalar() or 0 for ordinal in range(previous_ordinal + 1, previous_ordinal + count + 1): result.append(self._create_pseudo_id(ordinal, user)) db.session.bulk_save_objects(result) db.session.commit() return result
class BioresourceIdProvider(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) prefix = db.Column(db.String(10), nullable=False) last_updated_datetime = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_updated_by_user_id = db.Column(db.Integer, db.ForeignKey(User.id)) last_updated_by_user = db.relationship(User) def allocate_id(self, user): prospective_id = 0 found = 1 tries = 0 while found > 0: prospective_id = random.randint(1000000, 9999999) tries = tries + 1 found = BioresourceId.query.filter( BioresourceId.bioresource_id_provider_id == self.id).filter( BioresourceId.number == prospective_id).count() actual_id = BioresourceId( bioresource_id_provider=self, number=prospective_id, check_character=self._get_checkdigit(prospective_id), last_updated_by_user=user, ) db.session.add(actual_id) return actual_id def validate(self, id): if id[:len(self.prefix)] != self.prefix: return False number = id[len(self.prefix):-1] if len(number) != 7: return False if id[-1:] != self._get_checkdigit(int(number)): return False return True def _get_checkdigit(self, id): return "ZABCDEFGHJKLMNPQRSTVWXY"[id % 23] def allocate_ids(self, count, user): result = [] for _ in range(count): result.append(self.allocate_id(user)) return result def __repr__(self): return str(self.__class__) + ": " + str(self.__dict__)
class TaskStatusType(db.Model, CommonMixin): CREATED = 'Created' IN_PROGRESS = 'In Progress' DONE = 'Done' AWAITING_INFORMATION = 'Awaiting Information' CANCELLED = 'Cancelled' DECLINED = 'Declined' DUPLICATE = 'Duplicate' all_details = { CREATED: { 'is_complete': False, 'is_active': False, }, IN_PROGRESS: { 'is_complete': False, 'is_active': True, }, DONE: { 'is_complete': True, 'is_active': False, }, AWAITING_INFORMATION: { 'is_complete': False, 'is_active': False, }, CANCELLED: { 'is_complete': True, 'is_active': False, }, DECLINED: { 'is_complete': True, 'is_active': False, }, DUPLICATE: { 'is_complete': True, 'is_active': False, }, } @classmethod def get_task_status(cls, name): return TaskStatusType.query.filter_by(name=name).one() @classmethod def get_created(cls): return cls.get_task_status(TaskStatusType.CREATED) @classmethod def get_created_id(cls): return cls.get_created().id @classmethod def get_in_progress(cls): return cls.get_task_status(TaskStatusType.IN_PROGRESS) @classmethod def get_done(cls): return cls.get_task_status(TaskStatusType.DONE) @classmethod def get_awaiting_information(cls): return cls.get_task_status(TaskStatusType.AWAITING_INFORMATION) @classmethod def get_cancelled(cls): return cls.get_task_status(TaskStatusType.CANCELLED) @classmethod def get_declined(cls): return cls.get_task_status(TaskStatusType.DECLINED) @classmethod def get_duplicate(cls): return cls.get_task_status(TaskStatusType.DUPLICATE) id = db.Column(db.Integer(), primary_key=True) name = db.Column(db.String(255)) is_complete = db.Column(db.Boolean) is_active = db.Column(db.Boolean)