class Monitor(models.Model): name = models.CharField(max_length=255) description = models.TextField(blank=True, null=True) engine = models.CharField(max_length=32, default='prometheus') metadata = YAMLField(blank=True, null=True) status = models.CharField(max_length=32, default='active') def client(self): client_class = utils.get_module(self.engine, 'monitor') return client_class(**{ 'name': self.name, 'engine': self.engine, 'metadata': self.metadata }) def widgets(self): return self.metadata.get('widget', {}) def color(self): if self.status == 'active': return 'success' if self.status == 'error': return 'danger' if self.status == 'build': return 'info' else: return 'warning' def __str__(self): return self.name class Meta: ordering = ['name']
class Inventory(models.Model): name = models.CharField(max_length=255) description = models.TextField(blank=True, null=True) engine = models.CharField(max_length=32, default='reclass') metadata = YAMLField(blank=True, null=True) status = models.CharField(max_length=32, default='unknown') def client(self): client_class = utils.get_module(self.engine, 'inventory') return client_class(**{ 'name': self.name, 'engine': self.engine, 'metadata': self.metadata }) def class_list(self, resource=None): return self.client().class_list(resource=None) def inventory(self, resource=None): return self.client().inventory(resource=None) def resource_count(self, resource=None): return len(self.client().inventory(resource=None)) def __str__(self): return self.name class Meta: verbose_name_plural = "Inventories"
class Problem(RespectedOrder, models.Model): lesson = models.ForeignKey(Lesson, related_name='problems') slug = models.SlugField(verbose_name="Слаг для адресной строки") name = models.CharField(verbose_name="Название", max_length=255) description = models.TextField(verbose_name="Описание", default='', blank=True) test_data = YAMLField(verbose_name="Тесты", default='') class Meta: order_with_respect_to = 'lesson' unique_together = ('lesson', 'slug')
class TargetEntity(models.Model): name = models.CharField(max_length=30) specYaml = YAMLField(blank=True) board = models.ForeignKey(BoardEntity, on_delete=models.CASCADE) def get_spec(self): views = [view.specYaml for view in self.viewentity_set.all()] filters = [filter.specYaml for filter in self.filterentity_set.all()] source = { 'type': 'intake', 'uri': os.path.join(path, 'catalog.yml'), 'dask': False } return { 'name': self.name, 'title': self.name, 'source': source, 'views': views, 'filters': filters } def save(self, *args, **kwargs): self.saveSpec() super().save(*args, **kwargs) def saveSpec(self): views = self.get_spec()['views'] filters = self.get_spec()['filters'] source = self.get_spec()['source'] title = self.get_spec()['title'] name = self.get_spec()['name'] p = {'name': name, 'title': title, 'source': source} if len(views) > 0: if views[0]: p['views'] = views if len(filters) > 0: if filters[0]: p['filters'] = filters spec = SpecYamlCreator(**p) self.specYaml = spec.to_yaml()
class Option(models.Model): DESTINATIONS = ( (1, 'payment'), (2, 'donation'), (3, 'payment & donation'), ) name = models.CharField( max_length=255, help_text=_('userfriendly name of the payment option')) institution = models.ForeignKey(Institution) provider = models.CharField(max_length=255, choices=get_providers_choices()) # description = models.TextField() settings = YAMLField(blank=True, null=True, help_text="check settings for each provider") currency = models.CharField(max_length=3, choices=CURRENCIES) destination = models.PositiveIntegerField(choices=DESTINATIONS) def get_provider(self, payment): Provider = provider_factory(self.provider) provider = Provider(dict(self.settings or {}), payment) return provider @property def has_automatic_provider(self): return self.provider in ['cash', 'bank_transfer'] def check_settings(self): Provider = provider_factory(self.provider) return Provider.check_settings(self.settings) def clean(self): settings_errors = self.check_settings() if settings_errors: raise ValidationError( _('There errors in settings: %s' % ", ".join(settings_errors))) def __str__(self): return "%s" % self.name
class StudyCondition(models.Model): '''Groupings of participants within a study.''' study = models.ForeignKey('signalbox.Study', blank=True, null=True) users = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Membership') tag = models.SlugField(max_length=255, default="main") display_name = models.CharField( max_length=30, blank=True, null=True, help_text="""A label for this condition which can be shown to the Participant (e.g. a non-descriptive name used to identify a condition in an experimental session without breaking a blind.""", ) weight = models.IntegerField( default=1, help_text="""Relative weights to allocate users to conditions""") scripts = models.ManyToManyField('signalbox.Script', blank=True) metadata = YAMLField( blank=True, null=True, help_text= """YAML meta data available describing this condition. Can be used on Questionnaires, e.g. to conditionally display questions.""") def expected_n(self): """Return the expected number of participants based on the total currently allocated to the parent study.""" weights = [i.weight for i in self.study.studycondition_set.all()] w = self.weight / sum(weights) return self.study.membership_set.count() * w class Meta: ordering = ['study', 'tag'] app_label = 'signalbox' def __unicode__(self): return "%s > %s " % (self.study.name, self.tag)
class Resource(models.Model): uid = models.CharField(max_length=511) name = models.CharField(max_length=511) monitor = models.ForeignKey(Monitor, on_delete=models.CASCADE) kind = models.CharField(max_length=32) size = models.IntegerField(default=1) metadata = YAMLField(blank=True, null=True) status = models.CharField(max_length=32, default='unknown') def __str__(self): return '{} {}'.format(self.kind, self.name) def color(self): if self.status == 'active': return 'success' if self.status == 'error': return 'danger' if self.status == 'build': return 'info' else: return 'warning' class Meta: ordering = ['name']
class Asker(models.Model): objects = AskerManager() def natural_key(self): return (self.slug, ) allow_unauthenticated_download_of_anonymous_data = models.BooleanField( default=False) anonymous_download_token = ShortUUIDField(auto=True) name = models.CharField(max_length=255, null=True) slug = models.SlugField(max_length=128, help_text="""To use in URLs etc""", unique=True, verbose_name="""Reference code""") success_message = models.TextField( default="Questionnaire complete.", help_text= """Message which appears in banner on users screen on completion of the questionnaire.""" ) redirect_url = models.CharField( max_length=128, default="/profile/", help_text=""""URL to redirect to when Questionnaire is complete.""") show_progress = models.BooleanField(default=False, help_text="""Show a progress field in the header of each page.""") finish_on_last_page = models.BooleanField( default=False, help_text="""Mark the reply as complete when the user gets to the last page. NOTE this implies there should be not questions requiring input on the last page, or these values will never be saved.""" ) step_navigation = models.BooleanField(default=True, help_text="""Allow navigation to steps.""") steps_are_sequential = models.BooleanField(default=True, help_text="""Each page in the questionnaire must be completed before the next; if unchecked then respondents can skip around within the questionnaire and complete the pages 'out of order'.""") hide_menu = models.BooleanField(default=True) system_audio = YAMLField(blank=True, help_text="""A mapping of slugs to text strings or urls which store audio files, for the system to play on errors etc. during IVR calls""" ) width = models.PositiveIntegerField(default=12) def get_phrase(self, key): if self.system_audio: return self.system_audio.get(key, IVR_SYSTEM_MESSAGES.get(key)) else: return IVR_SYSTEM_MESSAGES.get(key, None) def anonymous_replies(self): from signalbox.models import Reply return Reply.objects.filter(asker=self) def anonymous_reply_stats(self): replies = self.anonymous_replies() return { 'Complete total': replies.filter(complete=True).count(), 'Complete past month': replies.filter(complete=True, last_submit__lt=datetime.now() - timedelta(days=30)).count(), 'Incomplete total': replies.filter(complete=False).count(), 'Incomplete past month': replies.filter(complete=False, last_submit__lt=datetime.now() - timedelta(days=30)).count(), } def used_in_studies(self): from signalbox.models import Study return Study.objects.filter(studycondition__scripts__asker=self) def used_in_scripts(self): from signalbox.models import Script return Script.objects.filter(asker=self) def used_in_study_conditions(self): """Return a queryset of the StudyConditions in which this Asker appears.""" from signalbox.models import StudyCondition scripts = self.used_in_scripts() conds = set(StudyCondition.objects.filter(scripts__in=scripts)) return conds def approximate_time_to_complete(self): """Returns the approximate number of minutes the asker will take to complete. Rounded to the nearest 5 minutes (with a minumum of 5 minutes).""" baseround = lambda x, base=5: int(base * round(float(x) / base)) n_questions = Question.objects.filter( page__in=self.askpage_set.all()).count() mins = (n_questions * .5) * .9 return max([5, baseround(mins, 5)]) def scoresheets(self): return itertools.chain( *[i.scoresheets() for i in self.askpage_set.all()]) def summary_scores(self, reply): answers = reply.answer_set.all() return {i.name: i.compute(answers) for i in self.scoresheets()} @contract def questions(self, reply=None): """All questions, filtered by previous answers if a reply is passed in, see methods on Page. :rtype: list """ questionsbypage = [ i.get_questions(reply=reply) for i in self.askpage_set.all() ] for i, pagelist in enumerate(questionsbypage): for q in pagelist: q.on_page = i questions = list(itertools.chain(*questionsbypage)) return questions def first_page(self): return self.askpage_set.all()[0] def page_count(self): return self.askpage_set.all().count() def admin_edit_url(self): return admin_edit_url(self) def get_absolute_url(self): return reverse('preview_asker', kwargs={ 'asker_id': self.id, 'page_num': 0 }) def get_anonymous_url(self, request): return reverse('start_anonymous_survey', kwargs={'asker_id': self.id}) def json_export(self): """Export Asker and related objects as json. Export everything needed to recreate this questionnaire on another signalbox instance (or 3rd party system) with the exception of question assets.""" _asdict = lambda object, fields: { i: str(supergetattr(object, i)) for i in fields } pages = self.askpage_set.all() questionlists = [i.get_questions() for i in pages] questions = list(itertools.chain(*questionlists)) choicesets = set([i.choiceset for i in questions if i.choiceset]) choices = Choice.objects.filter(choiceset__in=choicesets) QUESTION_FIELDS = ('natural_key', 'variable_name', 'order', 'required', 'help_text', 'text', 'choiceset.pk', 'q_type', 'showif') CHOICESET_FIELDS = ('natural_key', 'name') CHOICE_FIELDS = ('choiceset.natural_key', 'is_default_value', 'order', 'label', 'score') qdicts = [_asdict(i, QUESTION_FIELDS) for i in questions] # give a consistent ordering to everything [d.update({'order': i}) for i, d in enumerate(qdicts)] output = { 'meta': { 'name': self.slug, 'show_progress': self.show_progress, 'generated': str(datetime.now()), }, 'questions': qdicts, 'choicesets': [_asdict(i, CHOICESET_FIELDS) for i in choicesets], 'choices': [_asdict(i, CHOICE_FIELDS) for i in choices], 'scoresheets': [i.as_simplified_dict() for i in self.scoresheets()] } jsonstring = json.dumps(output, indent=4) return "{}".format(jsonstring) def reply_count(self): return self.reply_set.all().count() @contract def as_markdown(self): """A helper to convert an asker and associated models to markdown format. :rtype: string """ _fields = "slug name steps_are_sequential redirect_url finish_on_last_page \ show_progress success_message step_navigation system_audio width".split( ) metayaml = yaml.safe_dump({i: getattr(self, i) for i in _fields}, default_flow_style=False) _pages_questions = [(i.as_markdown(), [j.as_markdown() for j in i.get_questions()]) for i in self.askpage_set.all()] qstrings = "\n\n".join(flatten(flatten(_pages_questions))) return "---\n" + metayaml + "---\n\n" + qstrings class Meta: permissions = (("can_preview", "Can preview surveys"), ) verbose_name = "Questionnaire" app_label = "ask" ordering = ['name'] def save(self, *args, **kwargs): # set defaults for system_audio if none exist if self.system_audio: if isinstance(self.system_audio, dict): IVR_SYSTEM_MESSAGES.update(self.system_audio) self.system_audio = IVR_SYSTEM_MESSAGES super(Asker, self).save(*args, **kwargs) def __unicode__(self): return "{}".format(self.name or self.slug)
class FlowRuleException(models.Model): flow_id = models.CharField(max_length=200, blank=False, null=False) participation = models.ForeignKey(Participation, db_index=True) expiration = models.DateTimeField(blank=True, null=True) creator = models.ForeignKey(User, null=True) creation_time = models.DateTimeField(default=now, db_index=True) comment = models.TextField(blank=True, null=True) kind = models.CharField(max_length=50, blank=False, null=False, choices=FLOW_RULE_KIND_CHOICES) rule = YAMLField(blank=False, null=False) active = models.BooleanField(default=True) def __unicode__(self): return "%s exception for '%s' to '%s' in '%s'" % ( self.kind, self.participation.user, self.flow_id, self.participation.course) def clean(self): if (self.kind == flow_rule_kind.grading and self.expiration is not None): raise ValidationError("grading rules may not expire") from course.validation import (ValidationError as ContentValidationError, validate_session_start_rule, validate_session_access_rule, validate_session_grading_rule, ValidationContext) from course.content import (get_course_repo, get_course_commit_sha, get_flow_desc) from relate.utils import dict_to_struct rule = dict_to_struct(self.rule) repo = get_course_repo(self.participation.course) commit_sha = get_course_commit_sha(self.participation.course, self.participation) ctx = ValidationContext(repo=repo, commit_sha=commit_sha) flow_desc = get_flow_desc(repo, self.participation.course, self.flow_id, commit_sha) tags = None if hasattr(flow_desc, "rules"): tags = getattr(flow_desc.rules, "tags", None) try: if self.kind == flow_rule_kind.start: validate_session_start_rule(ctx, unicode(self), rule, tags) elif self.kind == flow_rule_kind.access: validate_session_access_rule(ctx, unicode(self), rule, tags) elif self.kind == flow_rule_kind.grading: validate_session_grading_rule(ctx, unicode(self), rule, tags) else: raise ValidationError("invalid rule kind: " + self.kind) except ContentValidationError as e: raise ValidationError("invalid existing_session_rules: " + str(e))