class Media(models.Model): MEDIA_TYPES = Choices( (3, _('Books')), (4, _('Maps')), (8, _('Other documents')), (6, _('Photos')), (10, _('PR materials')), (9, _('Presentations')), (1, _('SLM questionnaires')), (2, _('Training materials')), (5, _('Videos')), ) title = models.CharField( 'Title', max_length=255, unique=True ) abstract = models.TextField( 'Abstract', blank=True ) teaser_image = models.ForeignKey( 'wagtailimages.Image', verbose_name='Teaser image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) @property def image(self): return self.teaser_image or None video = models.URLField( verbose_name='Video', blank=True, ) year = models.PositiveIntegerField( 'Year', default=get_default_year_now, validators=[MaxValueValidator(4000)], blank=True, null=True ) languages = models.ManyToManyField( verbose_name='Languages', to=Language, blank=True ) content = StreamField( CORE_BLOCKS, blank=True, ) file = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, on_delete=models.PROTECT, related_name='+', help_text='This field is only used if the content is empty.', ) author = models.CharField( 'Author', max_length=255, blank=True, ) countries = models.ManyToManyField( verbose_name='Countries', to=Country, blank=True, ) continent = models.ForeignKey( to=Continent, blank=True, null=True, ) media_type = models.IntegerField( choices=MEDIA_TYPES, db_column='media_type_id') class Meta: verbose_name = 'Media' verbose_name_plural = 'Media' def __str__(self): return self.title def get_absolute_url(self): return reverse('media:detail', args=[self.id]) panels = [ FieldPanel('title'), FieldPanel('abstract'), FieldPanel('media_type'), FieldPanel('video'), DocumentChooserPanel('file'), ImageChooserPanel('teaser_image'), FieldPanel('author'), FieldPanel('countries'), FieldPanel('continent'), FieldPanel('year'), FieldPanel('languages'), StreamFieldPanel('content'), ]
class Problem(SoftDeletableModel, TimeStampedModel): STATUS_CHOICES = Choices( (0, 'draft', _('Draft')), (1, 'published', _('Published')), ) LEVEL_CHOICES = Choices( (1, 'level1', _('Level 1')), (2, 'level2', _('Level 2')), (3, 'level3', _('Level 3')), (4, 'level4', _('Level 4')), (5, 'level5', _('Level 5')), ) author = models.ForeignKey( settings.AUTH_USER_MODEL, verbose_name=_('Author'), db_index=True, null=True, blank=True, editable=True, on_delete=models.SET_NULL, related_name='%(app_label)s_%(class)s_owned', ) title = models.CharField( verbose_name=_('Problem title'), max_length=255, ) content = models.TextField(verbose_name=_('Problem content'), ) input_description = models.TextField(verbose_name=_('Input description'), ) output_description = models.TextField( verbose_name=_('Output description'), ) sample_input = models.TextField(verbose_name=_('Sample input data'), ) sample_output = models.TextField(verbose_name=_('Sample output data'), ) time_limit = models.IntegerField( verbose_name=_('Time limit'), help_text=_('Seconds'), ) memory_limit = models.IntegerField( verbose_name=_('Memory limit'), help_text=_('MB'), ) level = models.IntegerField( verbose_name=_('Level'), choices=LEVEL_CHOICES, default=LEVEL_CHOICES.level1, db_index=True, ) category = TreeForeignKey( 'quest.Category', verbose_name=_('Category'), null=True, blank=True, db_index=True, on_delete=models.SET_NULL, ) status = models.IntegerField( verbose_name=_('Status'), choices=STATUS_CHOICES, default=STATUS_CHOICES.draft, db_index=True, ) class Meta: verbose_name = _('Problem') verbose_name_plural = _('Problems') def __str__(self): return self.title
# ("epilepsy", "Epilepsy"), # ("diabetes", "Diabetes"), # ("other_chronic_medical_condition", "Other Chronic Medical Condition"), # ("other_genetic_condition", "Other Genetic Condition"), # ("gifted_advanced_learning_needs", "Gifted/Advanced learning needs"), # ("adopted", "Adopted"), # ("has_older_sibling", "Has at least one older sibling"), # ("has_younger_sibling", "Has at least one younger sibling"), ) # Keeping for now, though this will probably not be needed. MULTIPLE_BIRTH_CHOICES = Choices( ("twin", _("Twin")), ("triplet", _("Triplet")), ("quadruplet", _("Quadruplet")), ("quintuplet", _("Quintuplet")), ("sextuplet", _("Sextuplet")), ("septuplet", _("Septuplet")), ("octuplet", _("Octuplet")), ) # Ranked by # of speakers globally, except English is on top. LANGUAGES = ( ("en", "English"), ("am", "Amharic"), ("bn", "Bengali"), ("bho", "Bhojpuri"), ("my", "Burmese"), ("ceb", "Cebuano"), ("hne", "Chhattisgarhi"), ("nl", "Dutch"),
class Outreach(TimeStampedModel): EAV_TYPE = 'outreach' RESULT = Choices(('graduated', _('Graduated')), ('failed', _('Failed'))) LANGUAGES = Choices(('english', _('English')), ('french', _('French'))) YES_NO = Choices(('yes', _('Yes')), ('no', _('No'))) student = models.ForeignKey( Student, blank=False, null=True, related_name='alp_enrollment', ) partner = models.ForeignKey( PartnerOrganization, blank=True, null=True, related_name='+', ) owner = models.ForeignKey( settings.AUTH_USER_MODEL, blank=False, null=True, related_name='+', ) modified_by = models.ForeignKey( settings.AUTH_USER_MODEL, blank=True, null=True, related_name='modifications', ) school = models.ForeignKey( School, blank=True, null=True, related_name='alp_school', ) location = models.ForeignKey( Location, blank=True, null=True, related_name='+', ) last_class_level = models.ForeignKey( ClassLevel, blank=True, null=True, related_name='+', ) average_distance = models.CharField(max_length=10, blank=True, null=True, choices=Choices( u'<= 2.5km', u'> 2.5km', u'> 10km')) exam_year = models.CharField(max_length=4, blank=True, null=True, choices=((str(x), x) for x in range(1990, 2051))) exam_month = models.CharField(max_length=2, blank=True, null=True, choices=Person.MONTHS) exam_day = models.CharField(max_length=2, blank=True, null=True, choices=((str(x), x) for x in range(1, 33))) alp_round = models.ForeignKey( ALPRound, blank=True, null=True, related_name='+', ) section = models.ForeignKey( Section, blank=True, null=True, related_name='+', verbose_name='Current Section', ) classroom = models.ForeignKey(ClassRoom, blank=True, null=True, related_name='+') alp_year = models.CharField( max_length=20, blank=True, null=True, ) status = models.BooleanField(blank=True, default=True) enrolled_in_this_school = models.BooleanField(blank=True, default=True) not_enrolled_in_this_school = models.BooleanField(blank=True, default=False) exam_not_exist_in_school = models.BooleanField(blank=True, default=False) registered_in_unhcr = models.CharField(max_length=50, blank=True, null=True, choices=YES_NO) last_education_level = models.ForeignKey(ClassRoom, blank=True, null=True, related_name='+') last_education_year = models.CharField( max_length=10, blank=True, null=True, choices=((str(x - 1) + '/' + str(x), str(x - 1) + '/' + str(x)) for x in range(2001, 2021))) last_year_result = models.CharField(max_length=50, blank=True, null=True, choices=RESULT) participated_in_alp = models.CharField(max_length=50, blank=True, null=True, choices=YES_NO) last_informal_edu_level = models.ForeignKey( EducationLevel, blank=True, null=True, related_name='+', ) last_informal_edu_year = models.CharField( max_length=10, blank=True, null=True, choices=((str(x - 1) + '/' + str(x), str(x - 1) + '/' + str(x)) for x in range(2001, 2021))) last_informal_edu_result = models.CharField(max_length=50, blank=True, null=True, choices=RESULT) last_informal_edu_round = models.ForeignKey( ALPRound, blank=True, null=True, related_name='+', ) last_informal_edu_final_result = models.ForeignKey( ClassLevel, blank=True, null=True, related_name='+', ) exam_result_arabic = models.FloatField( blank=True, null=True, default=0, ) exam_result_language = models.FloatField( blank=True, null=True, default=0, ) exam_result_math = models.FloatField( blank=True, null=True, default=0, ) exam_result_science = models.FloatField( blank=True, null=True, default=0, ) exam_language = models.CharField(max_length=50, blank=True, null=True, choices=LANGUAGES) exam_corrector_arabic = models.IntegerField( blank=True, null=True, default=0, choices=((x, x) for x in range(0, 101))) exam_corrector_language = models.IntegerField( blank=True, null=True, default=0, choices=((x, x) for x in range(0, 101))) exam_corrector_math = models.IntegerField(blank=True, null=True, default=0, choices=((x, x) for x in range(0, 101))) exam_corrector_science = models.IntegerField( blank=True, null=True, default=0, choices=((x, x) for x in range(0, 101))) registered_in_school = models.CharField(max_length=50, blank=True, null=True, choices=YES_NO) level = models.ForeignKey( EducationLevel, blank=True, null=True, related_name='+', verbose_name='Entrance Test (Pre-Test)', ) registered_in_level = models.ForeignKey( EducationLevel, blank=True, null=True, related_name='+', verbose_name='Current level', ) assigned_to_level = models.ForeignKey( EducationLevel, blank=True, null=True, related_name='+', verbose_name='Pre-test result', ) exam_school = models.ForeignKey( School, blank=True, null=True, related_name='+', ) deleted = models.BooleanField(blank=True, default=False) post_exam_result_arabic = models.FloatField( blank=True, null=True, default=0, ) post_exam_result_language = models.FloatField( blank=True, null=True, default=0, ) post_exam_result_math = models.FloatField( blank=True, null=True, default=0, ) post_exam_result_science = models.FloatField( blank=True, null=True, default=0, ) post_exam_language = models.CharField(max_length=50, blank=True, null=True, choices=LANGUAGES) post_exam_corrector_arabic = models.IntegerField( blank=True, null=True, default=0, choices=((x, x) for x in range(0, 101))) post_exam_corrector_language = models.IntegerField( blank=True, null=True, default=0, choices=((x, x) for x in range(0, 101))) post_exam_corrector_math = models.IntegerField( blank=True, null=True, default=0, choices=((x, x) for x in range(0, 101))) post_exam_corrector_science = models.IntegerField( blank=True, null=True, default=0, choices=((x, x) for x in range(0, 101))) refer_to_level = models.ForeignKey( ClassLevel, blank=True, null=True, related_name='+', verbose_name='Post-test result', ) dropout_status = models.BooleanField(blank=True, default=False) outreach_barcode = models.CharField( max_length=50, blank=True, null=True, ) new_registry = models.CharField(max_length=50, blank=True, null=True, choices=Choices((1, _("Yes")), (0, _("No")))) student_outreached = models.CharField(max_length=50, blank=True, null=True, choices=Choices((1, _("Yes")), (0, _("No")))) have_barcode = models.CharField(max_length=50, blank=True, null=True, choices=Choices((1, _("Yes")), (0, _("No")))) objects = OutreachManager() drop_objects = OutreachDropoutManager() class Meta: ordering = ['id'] verbose_name = "All ALP data" @property def student_fullname(self): if self.student: return self.student.__unicode__() return '' @property def student_mother_fullname(self): if self.student: return self.student.mother_fullname return '' @property def exam_total(self): total = 0 # if self.exam_result_arabic: # total += self.exam_result_arabic if self.exam_result_language: total += self.exam_result_language if self.exam_result_math: total += self.exam_result_math if self.exam_result_science: total += self.exam_result_science return total @property def pretest_total(self): if self.level: return "{}/{}".format(self.exam_total, self.level.note) return 0 @property def posttest_total(self): if self.level: return "{}/{}".format(self.post_exam_total, '80') return 0 @property def next_level(self): if self.refer_to_level: return self.refer_to_level return '' @property def post_exam_total(self): total = 0 if self.post_exam_result_arabic: total += self.post_exam_result_arabic if self.post_exam_result_language: total += self.post_exam_result_language if self.post_exam_result_math: total += self.post_exam_result_math if self.post_exam_result_science: total += self.post_exam_result_science return total @property def student_age(self): if self.student: return self.student.age return 0 @property def student_sex(self): if self.student: return self.student.sex return '' @property def student_number(self): if self.student: return self.student.number return '' @property def student_nationality(self): if self.student: return self.student.nationality return '' def calculate_pre_result(self): self.assigned_to_level = assign_to_level(self.level, self.exam_total) def calculate_post_result(self): self.refer_to_level = refer_to_level(self.student_age, self.registered_in_level, self.post_exam_total) def __unicode__(self): if self.student: return self.student.__unicode__() return str(self.id)
class TicketFilter(django_filters.FilterSet): STATUS = Choices( 'Open', 'Close', 'Pending', ) SOURCE = Choices( 'Call_in', 'Call_out', 'CUG', 'Viber', 'Skype', 'Message', ) username = django_filters.CharFilter( field_name='client_id', lookup_expr='icontains', widget=forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'username' }), ) branch = django_filters.ModelChoiceFilter( label='Client', field_name='branch_id', queryset=Branch.objects.all(), empty_label='Select Branch', widget=forms.Select(attrs={'class': 'form-control'}), ) problem_domain = django_filters.ModelChoiceFilter( label='Problem Domain', field_name='problem_domain_id', queryset=ProblemDomain.objects.all(), empty_label='Select Problem Domain', widget=forms.Select(attrs={'class': 'form-control'}), ) status = django_filters.ChoiceFilter( choices=STATUS, label='Status', empty_label='Select Status', widget=forms.Select(attrs={'class': 'form-control'}), ) source = django_filters.ChoiceFilter( choices=SOURCE, label='Source', empty_label='Select Source', widget=forms.Select(attrs={'class': 'form-control'}), ) date = django_filters.DateFilter( label='Date', field_name='issue_date', lookup_expr='icontains', widget=forms.DateInput( attrs={ 'class': 'form-control', 'id': 'datepicker', 'type': 'text', 'placeholder': 'YYYY - MM - DD' }), ) class Meta: model = Ticket fields = [ 'client_id', 'branch_id', 'problem_domain_id', 'status', 'source', 'issue_date' ]
class StatusFieldChoicesName(models.Model): NAMED_STATUS = Choices((0, "no", "No"), (1, "yes", "Yes")) status = StatusField(choices_name='NAMED_STATUS')
class Monitor(models.Model): COUNTIES = Choices(*County.names) LOCATION = Choices('inside', 'outside') LAST_ACTIVE_LIMIT = 60 * 10 PAYLOAD_SCHEMA = None SENSORS = [''] id = SmallUUIDField( default=uuid_default(), primary_key=True, db_index=True, editable=False, verbose_name='ID' ) name = models.CharField(max_length=250) created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) access_key = models.UUIDField(default=uuid.uuid4) is_hidden = models.BooleanField(default=False, help_text="Hides the monitor on the map.") is_sjvair = models.BooleanField(default=False, help_text="Is this monitor part of the SJVAir network?") # Where is this sensor setup? position = models.PointField(null=True, db_index=True) county = models.CharField(max_length=20, blank=True, choices=COUNTIES) location = models.CharField(max_length=10, choices=LOCATION) notes = models.TextField(blank=True, help_text="Notes for internal use.") latest = models.ForeignKey('monitors.Entry', blank=True, null=True, related_name='latest_for', on_delete=models.SET_NULL) default_sensor = models.CharField(max_length=50, default='', blank=True) pm25_calibration_formula = models.CharField(max_length=255, blank=True, default='', validators=[validate_formula]) objects = InheritanceManager() class Meta: ordering = ('name',) def __str__(self): return self.name @classmethod def subclasses(cls): return cls.objects.get_queryset()._get_subclasses_recurse(cls) @property def device(self): return self.__class__.__name__ @property def is_active(self): if not self.latest_id: return False now = timezone.now() cutoff = timedelta(seconds=self.LAST_ACTIVE_LIMIT) return (now - self.latest.timestamp) < cutoff def get_absolute_url(self): return f'/#/monitor/{self.pk}' def get_current_pm25_average(self, minutes): end_time = timezone.now() start_time = end_time - timedelta(minutes=minutes) queryset = self.entries.filter( timestamp__range=(start_time, end_time), sensor=self.default_sensor, pm25__isnull=False, ) aggregate = queryset.aggregate(average=Avg('pm25')) return aggregate['average'] def get_pm25_calibration_formula(self): # Check for a formula set on this specific monitor. if self.pm25_calibration_formula: return self.pm25_calibration_formula # Fallback to the formula for this kind of monitor in this county. try: return Calibration.objects.values_list('pm25_formula', flat=True).get( county=self.county, monitor_type=self._meta.model_name ) except Calibration.DoesNotExist: # Default to an empty string return '' def create_entry(self, payload, sensor=None): return Entry( monitor=self, payload=payload, sensor=sensor or '', position=self.position, location=self.location, is_processed=False, ) def process_entry(self, entry): entry.position = self.position entry.location = self.location entry.calibrate_pm25(self.get_pm25_calibration_formula()) # entry.calculate_aqi() entry.calculate_averages() entry.is_processed = True return entry # TODO: rename to update_if_latest() def check_latest(self, entry): from camp.api.v1.monitors.serializers import EntrySerializer if self.latest_id: is_latest = make_aware(entry.timestamp) > self.latest.timestamp else: is_latest = True if entry.sensor == self.default_sensor and is_latest: self.latest = entry self.save() # TODO: Don't save here. def save(self, *args, **kwargs): if self.position: # TODO: Can we do this only when self.position is updated? self.county = County.lookup(self.position) super().save(*args, **kwargs)
import os import uuid from django.db import models from django.urls import reverse from model_utils import Choices from model_utils.models import TimeStampedModel from common import constants CABIN_STATUS_CHOICES = Choices(('none', 'No cabins'), ('mixed', 'Mixed cabins/camping'), ('full', 'All cabins')) ROUTE_POINT_CHOICES = Choices(('trailhead', 'Trailhead'), ('peak', 'Top 100 peak'), ('cabin', 'Cabin')) ITINERARY_REST_CHOICES = Choices(('cabin', 'Cabin'), ('campground', 'Campground')) class Cabin(TimeStampedModel): name = models.CharField(max_length=255) name_zh = models.CharField(max_length=255, verbose_name='Name (中文)', help_text=("Cabin's Chinese name, i.e. 南湖山屋")) description = models.TextField( help_text= "A description of cabin amenities, water supply, and any special features. Approximately 250-300 characters" ) capacity_beds = models.IntegerField( null=True, blank=True, help_text="Number of bed slots available")
""" from django.contrib.auth.models import User from django.db import models from django.dispatch import receiver from django.utils.translation import ugettext_lazy as _ from openedx.core.djangoapps.xmodule_django.models import CourseKeyField from model_utils import Choices from .api import add_course_goal, remove_course_goal from course_modes.models import CourseMode from student.models import CourseEnrollment # Each goal is represented by a goal key and a string description. GOAL_KEY_CHOICES = Choices( ('certify', _('Earn a certificate')), ('complete', _('Complete the course')), ('explore', _('Explore the course')), ('unsure', _('Not sure yet')), ) class CourseGoal(models.Model): """ Represents a course goal set by a user on the course home page. """ user = models.ForeignKey(User, blank=False) course_key = CourseKeyField(max_length=255, db_index=True) goal_key = models.CharField(max_length=100, choices=GOAL_KEY_CHOICES, default=GOAL_KEY_CHOICES.unsure) def __unicode__(self):
class Protein(Authorable, StatusModel, TimeStampedModel): """ Protein class to store individual proteins, each with a unique AA sequence and name """ STATUS = Choices("pending", "approved", "hidden") MONOMER = "m" DIMER = "d" TANDEM_DIMER = "td" WEAK_DIMER = "wd" TETRAMER = "t" AGG_CHOICES = ( (MONOMER, "Monomer"), (DIMER, "Dimer"), (TANDEM_DIMER, "Tandem dimer"), (WEAK_DIMER, "Weak dimer"), (TETRAMER, "Tetramer"), ) BASIC = "b" PHOTOACTIVATABLE = "pa" PHOTOSWITCHABLE = "ps" PHOTOCONVERTIBLE = "pc" MULTIPHOTOCHROMIC = "mp" TIMER = "t" OTHER = "o" SWITCHING_CHOICES = ( (BASIC, "Basic"), (PHOTOACTIVATABLE, "Photoactivatable"), (PHOTOSWITCHABLE, "Photoswitchable"), (PHOTOCONVERTIBLE, "Photoconvertible"), (MULTIPHOTOCHROMIC, "Multi-photochromic"), # both convertible and switchable (OTHER, "Multistate"), (TIMER, "Timer"), ) BILIRUBIN = "br" BILIVERDIN = "bv" FLAVIN = "fl" PHYCOCYANOBILIN = "pc" RIBITYL_LUMAZINE = "rl" COFACTOR_CHOICES = ( (BILIRUBIN, "Bilirubin"), (BILIVERDIN, "Biliverdin"), (FLAVIN, "Flavin"), (PHYCOCYANOBILIN, "Phycocyanobilin"), (RIBITYL_LUMAZINE, "ribityl-lumazine"), ) # Attributes # uuid = models.UUIDField(default=uuid_lib.uuid4, editable=False, unique=True) # for API uuid = models.CharField( max_length=5, default=prot_uuid, editable=False, unique=True, db_index=True, verbose_name="FPbase ID", ) name = models.CharField(max_length=128, help_text="Name of the fluorescent protein", db_index=True) slug = models.SlugField( max_length=64, unique=True, help_text="URL slug for the protein") # for generating urls base_name = models.CharField( max_length=128) # easily searchable "family" name aliases = ArrayField(models.CharField(max_length=200), blank=True, null=True) chromophore = models.CharField(max_length=5, null=True, blank=True) seq_validated = models.BooleanField( default=False, help_text="Sequence has been validated by a moderator") # seq must be nullable because of uniqueness contraints seq = SequenceField( unique=True, blank=True, null=True, verbose_name="Sequence", help_text="Amino acid sequence (IPG ID is preferred)", ) seq_comment = models.CharField( max_length=512, blank=True, help_text="if necessary, comment on source of sequence", ) pdb = ArrayField( models.CharField(max_length=4), blank=True, null=True, verbose_name="Protein DataBank IDs", ) genbank = models.CharField( max_length=12, null=True, blank=True, unique=True, verbose_name="Genbank Accession", help_text="NCBI Genbank Accession", ) uniprot = models.CharField( max_length=10, null=True, blank=True, unique=True, verbose_name="UniProtKB Accession", validators=[validate_uniprot], ) ipg_id = models.CharField( max_length=12, null=True, blank=True, unique=True, verbose_name="IPG ID", help_text="Identical Protein Group ID at Pubmed", ) # identical protein group uid mw = models.FloatField(null=True, blank=True, help_text="Molecular Weight") # molecular weight agg = models.CharField( max_length=2, choices=AGG_CHOICES, blank=True, verbose_name="Oligomerization", help_text="Oligomerization tendency", ) oser = models.FloatField(null=True, blank=True, help_text="OSER score") # molecular weight switch_type = models.CharField( max_length=2, choices=SWITCHING_CHOICES, blank=True, default="b", verbose_name="Switching Type", help_text="Photoswitching type (basic if none)", ) blurb = models.TextField(max_length=512, blank=True, help_text="Brief descriptive blurb") cofactor = models.CharField( max_length=2, choices=COFACTOR_CHOICES, blank=True, help_text="Required for fluorescence", ) # Relations parent_organism = models.ForeignKey( "Organism", related_name="proteins", verbose_name="Parental organism", on_delete=models.SET_NULL, blank=True, null=True, help_text="Organism from which the protein was engineered", ) primary_reference = models.ForeignKey( Reference, related_name="primary_proteins", verbose_name="Primary Reference", blank=True, null=True, on_delete=models.SET_NULL, help_text="Preferably the publication that introduced the protein", ) # usually, the original paper that published the protein references = models.ManyToManyField( Reference, related_name="proteins", blank=True) # all papers that reference the protein # FRET_partner = models.ManyToManyField('self', symmetrical=False, through='FRETpair', blank=True) default_state = models.ForeignKey( "State", related_name="default_for", blank=True, null=True, on_delete=models.SET_NULL, ) # __original_ipg_id = None # managers objects = ProteinManager() visible = QueryManager(~Q(status="hidden")) # def __init__(self, *args, **kwargs): # super().__init__(*args, **kwargs) # # store IPG_ID so that we know if it changes # self.__original_ipg_id = self.ipg_id def mutations_from_root(self): try: root = self.lineage.get_root() if root.protein.seq and self.seq: muts = root.protein.seq.mutations_to(self.seq) return muts except ObjectDoesNotExist: return None @property def mless(self): return mless(self.name) @property def description(self): return util.long_blurb(self) @property def _base_name(self): '''return core name of protein, stripping prefixes like "m" or "Tag"''' return get_base_name(self.name) @property def versions(self): return Version.objects.get_for_object(self) def last_approved_version(self): if self.status == "approved": return self try: return (Version.objects.get_for_object(self).filter( serialized_data__contains='"status": "approved"').first()) except Exception: return None @property def additional_references(self): return self.references.exclude( id=self.primary_reference_id).order_by("-year") @property def em_css(self): if self.states.count() > 1: from collections import OrderedDict stops = OrderedDict({st.emhex: "" for st in self.states.all()}) bgs = [] stepsize = int(100 / (len(stops) + 1)) sub = 0 for i, _hex in enumerate(stops): if _hex == "#000": sub = 18 bgs.append("{} {}%".format(_hex, (i + 1) * stepsize - sub)) return "linear-gradient(90deg, {})".format(", ".join(bgs)) elif self.default_state: return self.default_state.emhex else: return "repeating-linear-gradient(-45deg,#333,#333 8px,#444 8px,#444 16px);" @property def em_svg(self): if self.states.count() > 1: stops = [st.emhex for st in self.states.all()] stepsize = int(100 / (len(stops) + 1)) svgdef = "linear:" for i, color in enumerate(stops): perc = (i + 1) * stepsize if color == "#000": perc *= 0.2 svgdef += '<stop offset="{}%" style="stop-color:{};" />'.format( perc, color) return svgdef if self.default_state: return self.default_state.emhex return "?" @property def color(self): try: return get_color_group(self.default_state.ex_max, self.default_state.em_max)[0] except Exception: return "" # Methods def __str__(self): return self.name def get_absolute_url(self): return reverse("proteins:protein-detail", args=[self.slug]) def has_default(self): return bool(self.default_state) def mutations_to(self, other, **kwargs): if isinstance(other, Protein): other = other.seq if not (self.seq and other): return None return self.seq.mutations_to(other, **kwargs) def mutations_from(self, other, **kwargs): if isinstance(other, Protein): other = other.seq if not (self.seq and other): return None return other.seq.mutations_to(self.seq, **kwargs) def has_spectra(self): for state in self.states.all(): if state.has_spectra(): return True return False def has_bleach_measurements(self): return self.states.filter(bleach_measurements__isnull=False).exists() def d3_spectra(self): spectra = [] for state in self.states.all(): spectra.extend(state.d3_dicts()) return json.dumps(spectra) def spectra_img(self, fmt="svg", output=None, **kwargs): spectra = list( Spectrum.objects.filter(owner_state__protein=self).exclude( subtype="2p")) title = self.name if kwargs.pop("title", False) else None if kwargs.get("twitter", False): title = self.name info = "" if self.default_state: info += "Ex/Em λ: {}/{}".format(self.default_state.ex_max, self.default_state.em_max) info += "\nEC: {} QY: {}".format(self.default_state.ext_coeff, self.default_state.qy) return spectra_fig(spectra, fmt, output, title=title, info=info, **kwargs) def set_default_state(self): # FIXME: should allow control of default states in form # if only 1 state, make it the default state if not self.default_state or self.default_state.is_dark: if self.states.count() == 1 and not self.states.first().is_dark: self.default_state = self.states.first() # otherwise use farthest red non-dark state elif self.states.count() > 1: self.default_state = (self.states.exclude( is_dark=True).order_by("-em_max").first()) def clean(self): errors = {} # Don't allow basic switch_types to have more than one state. # if self.switch_type == 'b' and self.states.count() > 1: # errors.update({'switch_type': 'Basic (non photoconvertible) proteins ' # 'cannot have more than one state.'}) if self.pdb: self.pdb = list(set(self.pdb)) for item in self.pdb: if (Protein.objects.exclude(id=self.id).filter( pdb__contains=[item]).exists()): p = Protein.objects.filter(pdb__contains=[item]).first() errors.update({ "pdb": "PDB ID {} is already in use by protein {}".format( item, p.name) }) if errors: raise ValidationError(errors) def save(self, *args, **kwargs): # if the IPG ID has changed... refetch the sequence # if self.ipg_id != self.__original_ipg_id: # s = fetch_ipg_sequence(uid=self.ipg_id) # self.seq = s[1] if s else None self.slug = slugify(self.name) self.base_name = self._base_name self.set_default_state() super().save(*args, **kwargs) # self.__original_ipg_id = self.ipg_id # Meta class Meta: ordering = ["name"] def history(self, ignoreKeys=[]): from proteins.util.history import get_history return get_history(self, ignoreKeys) # ################################## # for algolia index def is_visible(self): return self.status != "hidden" def img_url(self): if self.has_spectra(): return ("https://www.fpbase.org" + reverse("proteins:spectra-img", args=[self.slug]) + ".png?xlabels=0&xlim=400,800") else: return None def tags(self): tags = [ self.get_switch_type_display(), self.get_agg_display(), self.color ] return [i for i in tags if i] def date_published(self, norm=False): d = None if self.primary_reference: d = self.primary_reference.date if norm: return (d.year - 1992) / (datetime.datetime.now().year - 1992) if d else 0 return datetime.datetime.combine( d, datetime.datetime.min.time()) if d else None def n_faves(self, norm=False): nf = (Favorite.objects.for_model(Protein).filter( target_object_id=self.id).count()) if norm: from collections import Counter mx = Counter( Favorite.objects.for_model(Protein).values_list( "target_object_id", flat=True)).most_common(1) if mx: mx = mx[0][1] else: mx = 1 return nf / mx return nf def n_cols(self): return ProteinCollection.objects.filter(proteins=self.id).count() def ga_views(self, period="month", norm=False): from proteins.extrest.ga import cached_ga_popular hits = cached_ga_popular()[period] for slug, name, rating in hits: if slug == self.slug: return rating / max(list(zip(*hits))[2]) if norm else rating return 0 def switchType(self): return self.get_switch_type_display() def _agg(self): return self.get_agg_display() def url(self): return self.get_absolute_url() def ex(self): if not self.states.exists(): return None ex = [s.ex_max for s in self.states.all()] return ex[0] if len(ex) == 1 else ex def em(self): if not self.states.exists(): return None em = [s.em_max for s in self.states.all()] return em[0] if len(em) == 1 else em def pka(self): if not self.states.exists(): return None n = [s.pka for s in self.states.all()] return n[0] if len(n) == 1 else n def ec(self): if not self.states.exists(): return None n = [s.ext_coeff for s in self.states.all()] return n[0] if len(n) == 1 else n def qy(self): if not self.states.exists(): return None n = [s.qy for s in self.states.all()] return n[0] if len(n) == 1 else n def rank(self): # max rank is 1 return (0.5 * self.date_published(norm=True) + 0.6 * self.ga_views(norm=True) + 1.0 * self.n_faves(norm=True)) / 2.5 def local_brightness(self): if self.states.exists(): return max([s.local_brightness for s in self.states.all()]) def first_author(self): if self.primary_reference and self.primary_reference.first_author: return self.primary_reference.first_author.family
class Shirt(models.Model): related_fields = ["collection", "fabric", "size_option", "size", "hem", "placket", "pocket", "back", "custom_buttons_type", "custom_buttons", "shawl", "yoke"] CLASP_OPTIONS = Choices((False, _(u'Не использовать застежку')), (True, _(u'Использовать застежку'))) is_template = models.BooleanField(_(u'Используется как шаблон'), default=False) is_standard = models.BooleanField(_(u'Используется как стандартный вариант'), default=False, editable=False) collection = models.ForeignKey(Collection, verbose_name=_(u'Коллекция'), related_name='shirts', null=True) code = models.CharField(_(u'Артикул'), max_length=255, null=True) individualization = models.TextField(_(u'Индивидуализация'), null=True, blank=True) fabric = models.ForeignKey(Fabric, verbose_name=_(u'Ткань'), null=True) showcase_image = models.ImageField(_(u'Изображение для витрины'), blank=False, null=True, upload_to='showcase') showcase_image_list = ImageSpecField(source='showcase_image', processors=[ResizeToFill(*settings.SHOWCASE_IMAGE_SIZE)], format='JPEG', options={'quality': 100}) showcase_image_detail = ImageSpecField(source='showcase_image', processors=[ResizeToFill(*settings.SHOWCASE_DETAILS_IMAGE_SIZE)], format='JPEG', options={'quality': 100}) size_option = models.ForeignKey('dictionaries.SizeOptions', verbose_name=_(u'Выбранный вариант размера')) size = models.ForeignKey('dictionaries.Size', verbose_name=_(u'Размер'), blank=True, null=True) hem = models.ForeignKey('dictionaries.HemType', verbose_name=_(u'Низ'), related_name='hem_shirts') placket = models.ForeignKey('dictionaries.PlacketType', verbose_name=_(u'Полочка'), related_name='placket_shirts') pocket = models.ForeignKey('dictionaries.PocketType', verbose_name=_(u'Карман'), related_name='pocket_shirts') sleeve = models.ForeignKey('dictionaries.SleeveType', verbose_name=_(u'Рукав'), related_name='sleeve_shirts', default=ResolveDefault(SleeveType)) fit = ChainedForeignKey(Fit, verbose_name=_(u'Талия'), chained_field='collection', chained_model_field='collections', show_all=False, blank=True, null=True) tuck = ChainedForeignKey('dictionaries.TuckType', verbose_name=_(u'Вытачки'), chained_field='collection', chained_model_field='collections', show_all=False) back = models.ForeignKey('dictionaries.BackType', verbose_name=_(u'Спинка'), related_name='back_shirts') custom_buttons_type = models.ForeignKey('dictionaries.CustomButtonsType', verbose_name=_(u'Тип кастомных пуговиц'), null=True, blank=True, related_name='back_shirts') custom_buttons = ChainedForeignKey(CustomButtons, verbose_name=_(u'Кастомные пуговицы'), chained_field='custom_buttons_type', chained_model_field='type', show_all=False, null=True, blank=True) shawl = models.ForeignKey(ShawlOptions, verbose_name=_(u'Платок'), null=True, related_name='shirts', default=ResolveDefault(ShawlOptions)) yoke = models.ForeignKey('dictionaries.YokeType', verbose_name=_(u'Кокетка'), null=True) clasp = models.BooleanField(_(u'Застежка под штифты'), choices=CLASP_OPTIONS, default=False) STITCH = Choices(('none', _(u'0 мм (без отстрочки)')), ('1mm', _(u'1 мм (только со съемными косточками)')), ('5mm', _(u'5 мм'))) stitch = models.CharField(_(u'Ширина отстрочки'), max_length=10, choices=STITCH) price = models.DecimalField(_(u'Цена'), max_digits=10, decimal_places=2, editable=False, null=True) def save(self, *args, **kwargs): cuff = getattr(self, 'cuff', None) if cuff and not self.sleeve.cuffs: if cuff.id: cuff.delete() super(Shirt, self).save(*args, **kwargs) class Meta: ordering = ('code',) verbose_name = _(u'Сорочка') verbose_name_plural = _(u'Сорочки') def __unicode__(self): return self.code if self.code else self.id
class AssessmentWorkflow(TimeStampedModel, StatusModel): """Tracks the open-ended assessment status of a student submission. It's important to note that although we track the status as an explicit field here, it is not the canonical status. This is because the determination of what we need to do in order to be "done" is specified by the OpenAssessmentBlock problem definition and can change. So every time we are asked where the student is, we have to query the peer, self, and later other assessment APIs with the latest requirements (e.g. "number of submissions you have to assess = 5"). The "status" field on this model is an after the fact recording of the last known state of that information so we can search easily. """ STEPS = ASSESSMENT_API_DICT.keys() STATUSES = [ "waiting", # User has done all necessary assessment but hasn't been # graded yet -- we're waiting for assessments of their # submission by others. "done", # Complete "cancelled" # User submission has been cancelled. ] STATUS_VALUES = STEPS + STATUSES STATUS = Choices(*STATUS_VALUES) # implicit "status" field # For now, we use a simple scoring mechanism: # Once a student has completed all assessments, # we search assessment APIs # in priority order until one of the APIs provides a score. # We then use that score as the student's overall score. # This Django setting is a list of assessment steps (defined in `settings.ORA2_ASSESSMENTS`) # in descending priority order. DEFAULT_ASSESSMENT_SCORE_PRIORITY = ['peer', 'self'] ASSESSMENT_SCORE_PRIORITY = getattr( settings, 'ORA2_ASSESSMENT_SCORE_PRIORITY', DEFAULT_ASSESSMENT_SCORE_PRIORITY ) STAFF_ANNOTATION_TYPE = "staff_defined" submission_uuid = models.CharField(max_length=36, db_index=True, unique=True) uuid = models.UUIDField(db_index=True, unique=True, default=uuid4) # These values are used to find workflows for a particular item # in a course without needing to look up the submissions for that item. # Because submissions are immutable, we can safely duplicate the values # here without violating data integrity. course_id = models.CharField(max_length=255, blank=False, db_index=True) item_id = models.CharField(max_length=255, blank=False, db_index=True) class Meta: ordering = ["-created"] # TODO: In migration, need a non-unique index on (course_id, item_id, status) app_label = "workflow" def __init__(self, *args, **kwargs): super(AssessmentWorkflow, self).__init__(*args, **kwargs) if 'staff' not in AssessmentWorkflow.STEPS: new_list = ['staff'] new_list.extend(AssessmentWorkflow.STEPS) AssessmentWorkflow.STEPS = new_list AssessmentWorkflow.STATUS_VALUES = AssessmentWorkflow.STEPS + AssessmentWorkflow.STATUSES AssessmentWorkflow.STATUS = Choices(*AssessmentWorkflow.STATUS_VALUES) if 'staff' not in AssessmentWorkflow.ASSESSMENT_SCORE_PRIORITY: new_list = ['staff'] new_list.extend(AssessmentWorkflow.ASSESSMENT_SCORE_PRIORITY) AssessmentWorkflow.ASSESSMENT_SCORE_PRIORITY = new_list @classmethod @transaction.atomic def start_workflow(cls, submission_uuid, step_names, on_init_params): """ Start a new workflow. Args: submission_uuid (str): The UUID of the submission associated with this workflow. step_names (list): The names of the assessment steps in the workflow. on_init_params (dict): The parameters to pass to each assessment module on init. Keys are the assessment step names. Returns: AssessmentWorkflow Raises: SubmissionNotFoundError SubmissionRequestError SubmissionInternalError DatabaseError Assessment-module specific errors """ submission_dict = sub_api.get_submission_and_student(submission_uuid) staff_auto_added = False if 'staff' not in step_names: staff_auto_added = True new_list = ['staff'] new_list.extend(step_names) step_names = new_list # Create the workflow and step models in the database # For now, set the status to waiting; we'll modify it later # based on the first step in the workflow. workflow = cls.objects.create( submission_uuid=submission_uuid, status=AssessmentWorkflow.STATUS.waiting, course_id=submission_dict['student_item']['course_id'], item_id=submission_dict['student_item']['item_id'] ) workflow_steps = [ AssessmentWorkflowStep.objects.create( workflow=workflow, name=step, order_num=i ) for i, step in enumerate(step_names) ] workflow.steps.add(*workflow_steps) # Initialize the assessment APIs has_started_first_step = False for step in workflow_steps: api = step.api() if api is not None: # Initialize the assessment module # We do this for every assessment module on_init_func = getattr(api, 'on_init', lambda submission_uuid, **params: None) on_init_func(submission_uuid, **on_init_params.get(step.name, {})) # If we auto-added a staff step, it is optional and should be marked complete immediately if step.name == "staff" and staff_auto_added: step.assessment_completed_at = now() step.save() # For the first valid step, update the workflow status # and notify the assessment module that it's being started if not has_started_first_step: # Update the workflow workflow.status = step.name workflow.save() # Notify the assessment module that it's being started on_start_func = getattr(api, 'on_start', lambda submission_uuid: None) on_start_func(submission_uuid) # Remember that we've already started the first step has_started_first_step = True # Update the workflow (in case some of the assessment modules are automatically complete) # We do NOT pass in requirements, on the assumption that any assessment module # that accepts requirements would NOT automatically complete. workflow.update_from_assessments(None) # Return the newly created workflow return workflow @property def score(self): """Latest score for the submission we're tracking. Returns: score (dict): The latest score for this workflow, or None if the workflow is incomplete. """ score = None if self.status == self.STATUS.done: score = sub_api.get_latest_score_for_submission(self.submission_uuid) return score def status_details(self): """ Returns workflow status in the form of a dictionary. Each step in the workflow is a key, and each key maps to a dictionary defining whether the step is complete (submitter requirements fulfilled) and graded (the submission has been assessed). """ status_dict = {} steps = self._get_steps() for step in steps: status_dict[step.name] = { "complete": step.is_submitter_complete(), "graded": step.is_assessment_complete(), } return status_dict def get_score(self, assessment_requirements, step_for_name): """Iterate through the assessment APIs in priority order and return the first reported score. Args: assessment_requirements (dict): Dictionary passed to the assessment API. This defines the requirements for each assessment step; the APIs can refer to this to decide whether the requirements have been met. Note that the requirements could change if the author updates the problem definition. step_for_name (dict): a key value pair for step name: step Returns: score dict. """ score = None for assessment_step_name in self.ASSESSMENT_SCORE_PRIORITY: # Check if the problem contains this assessment type assessment_step = step_for_name.get(assessment_step_name) # Query the corresponding assessment API for a score # If we find one, then stop looking if assessment_step is not None: # Check if the assessment API defines a score function at all get_score_func = getattr(assessment_step.api(), 'get_score', None) if get_score_func is not None: if assessment_requirements is None: step_requirements = None else: step_requirements = assessment_requirements.get(assessment_step_name, {}) score = get_score_func(self.submission_uuid, step_requirements) if assessment_step_name == self.STATUS.staff and score == None: if step_requirements and step_requirements.get('required', False): break # A staff score was not found, and one is required. Return None continue # A staff score was not found, but it is not required, so try the next type of score break return score def update_from_assessments(self, assessment_requirements, override_submitter_requirements=False): """Query assessment APIs and change our status if appropriate. If the status is done, we do nothing. Once something is done, we never move back to any other status. If an assessment API says that our submitter's requirements are met, then move to the next assessment. For example, in peer assessment, if the submitter we're tracking has assessed the required number of submissions, they're allowed to continue. If the submitter has finished all the assessments, then we change their status to `waiting`. If we're in the `waiting` status, and an assessment API says it can score this submission, then we record the score in the submissions API and move our `status` to `done`. By convention, if `assessment_requirements` is `None`, then assessment modules that need requirements should automatically say that they're incomplete. This allows us to update the workflow even when we don't know the current state of the problem. For example, if we're updating the workflow at the completion of an asynchronous call, we won't necessarily know the current state of the problem, but we would still want to update assessments that don't have any requirements. Args: assessment_requirements (dict): Dictionary passed to the assessment API. This defines the requirements for each assessment step; the APIs can refer to this to decide whether the requirements have been met. Note that the requirements could change if the author updates the problem definition. override_submitter_requirements (bool): If True, the presence of a new staff score will cause all of the submitter's requirements to be fulfilled, moving the workflow to DONE and exposing their grade. """ if self.status == self.STATUS.cancelled: return # Update our AssessmentWorkflowStep models with the latest from our APIs steps = self._get_steps() step_for_name = {step.name: step for step in steps} new_staff_score = self.get_score(assessment_requirements, {'staff': step_for_name.get('staff', None)}) if new_staff_score: # new_staff_score is just the most recent staff score, it may already be recorded in sub_api old_score = sub_api.get_latest_score_for_submission(self.submission_uuid) if ( # Does a prior score exist? Is it a staff score? Do the points earned match? not old_score or not self.STAFF_ANNOTATION_TYPE in [ annotation['annotation_type'] for annotation in old_score['annotations'] ] or old_score['points_earned'] != new_staff_score['points_earned'] ): # Set the staff score using submissions api, and log that fact self.set_staff_score(new_staff_score) self.save() logger.info(( u"Workflow for submission UUID {uuid} has updated score using staff assessment." ).format(uuid=self.submission_uuid)) # Update the assessment_completed_at field for all steps # All steps are considered "assessment complete", as the staff score will override all for step in steps: common_now = now() step.assessment_completed_at = common_now if override_submitter_requirements: step.submitter_completed_at = common_now step.save() if self.status == self.STATUS.done: return # Go through each step and update its status. for step in steps: step.update(self.submission_uuid, assessment_requirements) # Fetch name of the first step that the submitter hasn't yet completed. new_status = next( (step.name for step in steps if step.submitter_completed_at is None), self.STATUS.waiting # if nothing's left to complete, we're waiting ) # If the submitter is beginning the next assessment, notify the # appropriate assessment API. new_step = step_for_name.get(new_status) if new_step is not None: on_start_func = getattr(new_step.api(), 'on_start', None) if on_start_func is not None: on_start_func(self.submission_uuid) # If the submitter has done all they need to do, let's check to see if # all steps have been fully assessed (i.e. we can score it). if ( new_status == self.STATUS.waiting and all(step.assessment_completed_at for step in steps) ): score = self.get_score(assessment_requirements, step_for_name) # If we found a score, then we're done if score is not None: # Only set the score if it's not a staff score, in which case it will have already been set above if score.get("staff_id") is None: self.set_score(score) new_status = self.STATUS.done # Finally save our changes if the status has changed if self.status != new_status: self.status = new_status self.save() logger.info(( u"Workflow for submission UUID {uuid} has updated status to {status}" ).format(uuid=self.submission_uuid, status=new_status)) def _get_steps(self): """ Simple helper function for retrieving all the steps in the given Workflow. """ # A staff step must always be available, to allow for staff overrides try: self.steps.get(name=self.STATUS.staff) except AssessmentWorkflowStep.DoesNotExist: for step in list(self.steps.all()): step.order_num += 1 staff_step, _ = AssessmentWorkflowStep.objects.get_or_create( name=self.STATUS.staff, order_num=0, assessment_completed_at=now(), workflow=self, ) self.steps.add( staff_step ) # Do not return steps that are not recognized in the AssessmentWorkflow. steps = list(self.steps.filter(name__in=AssessmentWorkflow.STEPS)) if not steps: # If no steps exist for this AssessmentWorkflow, assume # peer -> self for backwards compatibility, with an optional staff override self.steps.add( AssessmentWorkflowStep(name=self.STATUS.staff, order_num=0, assessment_completed_at=now()), AssessmentWorkflowStep(name=self.STATUS.peer, order_num=1), AssessmentWorkflowStep(name=self.STATUS.self, order_num=2) ) steps = list(self.steps.all()) return steps def set_staff_score(self, score, reason=None): """ Set a staff score for the workflow. Allows for staff scores to be set on a submission, with annotations to provide an audit trail if needed. This method can be used for both required staff grading, and staff overrides. Args: score (dict): A dict containing 'points_earned', 'points_possible', and 'staff_id'. is_override (bool): Optionally True if staff is overriding a previous score. reason (string): An optional parameter specifying the reason for the staff grade. A default value will be used in the event that this parameter is not provided. """ if reason is None: reason = "A staff member has defined the score for this submission" sub_dict = sub_api.get_submission_and_student(self.submission_uuid) sub_api.reset_score( sub_dict['student_item']['student_id'], self.course_id, self.item_id, emit_signal=False ) sub_api.set_score( self.submission_uuid, score["points_earned"], score["points_possible"], annotation_creator=score["staff_id"], annotation_type=self.STAFF_ANNOTATION_TYPE, annotation_reason=reason ) def set_score(self, score): """ Set a score for the workflow. Scores are persisted via the Submissions API, separate from the Workflow Data. Score is associated with the same submission_uuid as this workflow Args: score (dict): A dict containing 'points_earned' and 'points_possible'. """ if not self.staff_score_exists(): sub_api.set_score( self.submission_uuid, score["points_earned"], score["points_possible"] ) def staff_score_exists(self): """ Check if a staff score exists for this submission. """ steps = self._get_steps() step_for_name = {step.name: step for step in steps} staff_step = step_for_name.get("staff") if staff_step is not None: get_latest_func = getattr(staff_step.api(), 'get_latest_assessment', None) if get_latest_func is not None: staff_assessment = get_latest_func(self.submission_uuid) if staff_assessment is not None: return True return False def cancel(self, assessment_requirements): """ Cancel workflow for all steps. Set the points earned to 0 and workflow status to cancelled. Args: assessment_requirements (dict): Dictionary that currently looks like: `{"peer": {"must_grade": <int>, "must_be_graded_by": <int>}}` `must_grade` is the number of assessments a student must complete. `must_be_graded_by` is the number of assessments a submission must receive to be scored. `must_grade` should be greater than `must_be_graded_by` to ensure that everyone will get scored. The intention is to eventually pass in more assessment sequence specific requirements in this dict. """ steps = self._get_steps() step_for_name = {step.name: step for step in steps} # Cancel the workflow for each step. for step in steps: on_cancel_func = getattr(step.api(), 'on_cancel', None) if on_cancel_func is not None: on_cancel_func(self.submission_uuid) try: score = self.get_score(assessment_requirements, step_for_name) except AssessmentError as exc: logger.info("TNL-5799, exception in get_score during cancellation. {}".format(exc)) score = None # Set the points_earned to 0. if score is not None: score['points_earned'] = 0 self.set_score(score) # Save status if it is not cancelled. if self.status != self.STATUS.cancelled: self.status = self.STATUS.cancelled self.save() logger.info( u"Workflow for submission UUID {uuid} has updated status to {status}".format( uuid=self.submission_uuid, status=self.STATUS.cancelled ) ) @classmethod def cancel_workflow(cls, submission_uuid, comments, cancelled_by_id, assessment_requirements): """ Add an entry in AssessmentWorkflowCancellation table for a AssessmentWorkflow. AssessmentWorkflow which has been cancelled is no longer included in the peer grading pool. Args: submission_uuid (str): The UUID of the workflow's submission. comments (str): The reason for cancellation. cancelled_by_id (str): The ID of the user who cancelled the peer workflow. assessment_requirements (dict): Dictionary that currently looks like: `{"peer": {"must_grade": <int>, "must_be_graded_by": <int>}}` `must_grade` is the number of assessments a student must complete. `must_be_graded_by` is the number of assessments a submission must receive to be scored. `must_grade` should be greater than `must_be_graded_by` to ensure that everyone will get scored. The intention is to eventually pass in more assessment sequence specific requirements in this dict. """ try: workflow = cls.objects.get(submission_uuid=submission_uuid) AssessmentWorkflowCancellation.create(workflow=workflow, comments=comments, cancelled_by_id=cancelled_by_id) # Cancel the related step's workflow. workflow.cancel(assessment_requirements) except (cls.DoesNotExist, cls.MultipleObjectsReturned): error_message = u"Error finding workflow for submission UUID {}.".format(submission_uuid) logger.exception(error_message) raise AssessmentWorkflowError(error_message) except DatabaseError: error_message = u"Error creating assessment workflow cancellation for submission UUID {}.".format( submission_uuid) logger.exception(error_message) raise AssessmentWorkflowInternalError(error_message) @classmethod def get_by_submission_uuid(cls, submission_uuid): """ Retrieve the Assessment Workflow associated with the given submission UUID. Args: submission_uuid (str): The string representation of the UUID belonging to the associated Assessment Workflow. Returns: workflow (AssessmentWorkflow): The most recent assessment workflow associated with this submission UUID. Raises: AssessmentWorkflowError: Thrown when no workflow can be found for the associated submission UUID. This should always exist before a student is allow to request submissions for peer assessment. """ try: return cls.objects.get(submission_uuid=submission_uuid) except cls.DoesNotExist: return None except DatabaseError as exc: message = u"Error finding workflow for submission UUID {} due to error: {}.".format(submission_uuid, exc) logger.exception(message) raise AssessmentWorkflowError(message) @property def is_cancelled(self): """ Check if assessment workflow is cancelled. Returns: True/False """ return self.cancellations.exists()
def userlist(request): email = request.session.get('email', None) if email is None: return Response(e, status=status.HTTP_404_NOT_FOUND, template_name=None, content_type=None) draw = request.GET.get('draw', '') length = request.GET.get('length', '') start = request.GET.get('start', '') search_value = request.GET.get('search[value]', '') order_column = request.GET.get('order[0][column]', '') order = request.GET.get('order[0][dir]', '') if draw: draw = int(draw) else: draw = int(1) if length: length = int(length) else: length = int(10) if start: start = int(start) else: start = int(0) ORDER_COLUMN_CHOICES = Choices( ('0', 'account_no'), ) order_column = ORDER_COLUMN_CHOICES[order_column] if order == 'asc': order_column = '-' + order_column queryset = CustomerAccountdetail.objects.all().select_related('acc_user') print(queryset.query) total = queryset.count() count = queryset.count() queryset = queryset.order_by(order_column)[start:start + length] result = dict() CanresultData = list() for data in queryset: print(data) resultData = dict() resultData['account_no'] = data.account_no resultData['first_name'] = data.acc_user.first_name resultData['last_name'] = data.acc_user.last_name resultData['address'] = data.address if data.acc_user.is_superuser == 1: resultData['profile_type'] = 'Manager' else: resultData['profile_type'] = 'Customer' resultData['email'] = data.acc_user.email date = format(data.acc_user.date_joined, 'd/m/Y H:i') resultData['created_at'] = date resultData['status'] = data.acc_user.is_active resultData['id'] = data.acc_user.id CanresultData.append(resultData) result['data'] = CanresultData result['draw'] = draw result['recordsTotal'] = total result['recordsFiltered'] = count return JsonResponse(result, safe=False)
from apps.patients.models import (PatientMedication, ) from apps.plans.models import (CarePlanTemplate, CarePlan, ) FREQUENCY_CHOICES = ( ('once', 'Once'), ('daily', 'Daily'), ('every_other_day', 'Every Other Day'), ('weekly', 'Weekly'), ('weekdays', 'Weekdays'), ('weekends', 'Weekends'), ) CATEGORY_CHOICES = Choices( ('notes', 'Notes'), ('interaction', 'Patient Interaction'), ('coordination', 'Care Team Coordination'), ) class StateMixin(object): def check_if_missed(self): """ This method will only be used for PatientTask and MedicationTask. By default, we set it to False to disregard this. This method should be overridden in PatientTask and MedicationTask model to allow for custom condition for `missed` state """ return False
class Status(StatusModel): STATUS = Choices( ("active", _("active")), ("deleted", _("deleted")), ("on_hold", _("on hold")), )
class AbstractConfig(BaseConfig): """ Abstract model implementing the NetJSON DeviceConfiguration object """ device = models.OneToOneField(get_model_name('config', 'Device'), on_delete=models.CASCADE) templates = SortedManyToManyField( get_model_name('config', 'Template'), related_name='config_relations', verbose_name=_('templates'), base_class=TemplatesThrough, blank=True, help_text=_('configuration templates, applied from first to last'), ) vpn = models.ManyToManyField( get_model_name('config', 'Vpn'), through=get_model_name('config', 'VpnClient'), related_name='vpn_relations', blank=True, ) STATUS = Choices('modified', 'applied', 'error') status = StatusField( _('configuration status'), help_text=_( '"modified" means the configuration is not applied yet; \n' '"applied" means the configuration is applied successfully; \n' '"error" means the configuration caused issues and it was rolled back;' ), ) context = JSONField( blank=True, default=dict, help_text=_('Additional ' '<a href="http://netjsonconfig.openwisp.org/' 'en/stable/general/basics.html#context" target="_blank">' 'context (configuration variables)</a> in JSON format'), load_kwargs={'object_pairs_hook': collections.OrderedDict}, dump_kwargs={'indent': 4}, ) _CHECKSUM_CACHE_TIMEOUT = 60 * 60 * 24 * 30 # 10 days class Meta: abstract = True verbose_name = _('configuration') verbose_name_plural = _('configurations') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # for internal usage self._just_created = False self._initial_status = self.status self._send_config_modified_after_save = False self._send_config_status_changed = False def __str__(self): if self._has_device(): return self.name return str(self.pk) @property def name(self): """ returns device name (kept for backward compatibility with pre 0.6 versions) """ if self._has_device(): return self.device.name return str(self.pk) @property def mac_address(self): """ returns device mac address (kept for backward compatibility with pre 0.6 versions) """ return self.device.mac_address @property def key(self): """ returns device key (kept for backward compatibility with pre 0.6 versions) """ return self.device.key @cache_memoize(timeout=_CHECKSUM_CACHE_TIMEOUT, args_rewrite=get_cached_checksum_args_rewrite) def get_cached_checksum(self): """ Handles caching, timeout=None means value is cached indefinitely (invalidation handled on post_save/post_delete signal) """ logger.debug(f'calculating checksum for config ID {self.pk}') return self.checksum @classmethod def get_template_model(cls): return cls.templates.rel.model @classmethod def _get_templates_from_pk_set(cls, pk_set): """ Retrieves templates from pk_set Called in ``clean_templates``, may be reused in third party apps """ # coming from signal if isinstance(pk_set, set): template_model = cls.get_template_model() templates = template_model.objects.filter(pk__in=list(pk_set)) # coming from admin ModelForm else: templates = pk_set return templates @classmethod def clean_templates(cls, action, instance, pk_set, **kwargs): """ validates resulting configuration of config + templates raises a ValidationError if invalid must be called from forms or APIs this method is called from a django signal (m2m_changed) see config.apps.DjangoNetjsonconfigApp.connect_signals """ templates = cls.clean_templates_org(action, instance, pk_set, **kwargs) if not templates: return backend = instance.get_backend_instance(template_instances=templates) try: cls.clean_netjsonconfig_backend(backend) except ValidationError as e: message = 'There is a conflict with the specified templates. {0}' message = message.format(e.message) raise ValidationError(message) @classmethod def templates_changed(cls, action, instance, **kwargs): """ this method is called from a django signal (m2m_changed) see config.apps.ConfigConfig.connect_signals NOTE: post_clear is ignored because it is used by the sortedm2m package to reorder templates (m2m relationships are first cleared and then added back), there fore we need to ignore it to avoid emitting signals twice """ if action not in ['post_add', 'post_remove']: return # use atomic to ensure any code bound to # be executed via transaction.on_commit # is executed after the whole block with transaction.atomic(): # do not send config modified signal if # config instance has just been created if not instance._just_created: # sends only config modified signal instance._send_config_modified_signal( action='m2m_templates_changed') if instance.status != 'modified': # sends both status modified and config modified signals instance.set_status_modified(send_config_modified_signal=False) @classmethod def manage_vpn_clients(cls, action, instance, pk_set, **kwargs): """ automatically manages associated vpn clients if the instance is using templates which have type set to "VPN" and "auto_cert" set to True. This method is called from a django signal (m2m_changed) see config.apps.DjangoNetjsonconfigApp.connect_signals """ if action not in ['post_add', 'post_remove', 'post_clear']: return vpn_client_model = cls.vpn.through # coming from signal if isinstance(pk_set, set): template_model = cls.get_template_model() templates = template_model.objects.filter(pk__in=list(pk_set)) # coming from admin ModelForm else: templates = pk_set # when clearing all templates if action == 'post_clear': for client in instance.vpnclient_set.all(): client.delete() return # when adding or removing specific templates for template in templates.filter(type='vpn'): if action == 'post_add': client = vpn_client_model(config=instance, vpn=template.vpn, auto_cert=template.auto_cert) client.full_clean() client.save() elif action == 'post_remove': for client in instance.vpnclient_set.filter(vpn=template.vpn): client.delete() @classmethod def clean_templates_org(cls, action, instance, pk_set, **kwargs): if action != 'pre_add': return False templates = cls._get_templates_from_pk_set(pk_set) # when using the admin, templates will be a list # we need to get the queryset from this list in order to proceed if not isinstance(templates, models.QuerySet): template_model = cls.templates.rel.model pk_list = [template.pk for template in templates] templates = template_model.objects.filter(pk__in=pk_list) # lookg for invalid templates invalids = (templates.exclude( organization=instance.device.organization).exclude( organization=None).values('name')) if templates and invalids: names = '' for invalid in invalids: names = '{0}, {1}'.format(names, invalid['name']) names = names[2:] message = _('The following templates are owned by organizations ' 'which do not match the organization of this ' 'configuration: {0}').format(names) raise ValidationError(message) # return valid templates in order to save computation # in the following operations return templates @classmethod def enforce_required_templates(cls, action, instance, pk_set, **kwargs): """ This method is called from a django signal (m2m_changed), see config.apps.ConfigConfig.connect_signals. It raises a PermissionDenied if a required template is unassigned from a config. It adds back required templates on post_clear events (post-clear is used by sortedm2m to assign templates). """ if action not in ['pre_remove', 'post_clear']: return False # trying to remove a required template will raise PermissionDenied if action == 'pre_remove': templates = cls._get_templates_from_pk_set(pk_set) if templates.filter(required=True).exists(): raise PermissionDenied( _('Required templates cannot be removed from the configuration' )) if action == 'post_clear': # retrieve required templates related to this # device and ensure they're always present required_templates = (cls.get_template_model().objects.filter( required=True).filter( models.Q(organization=instance.device.organization) | models.Q(organization=None))) if required_templates.exists(): instance.templates.add(*required_templates.order_by( 'name').values_list('pk', flat=True)) def get_default_templates(self): """ retrieves default templates of a Config object may be redefined with a custom logic if needed """ queryset = self.templates.model.objects.filter(default=True) try: org_id = self.device.organization_id except ObjectDoesNotExist: org_id = None return get_default_templates_queryset(organization_id=org_id, queryset=queryset, backend=self.backend) def clean(self): """ * validates context field * modifies status if key attributes of the configuration have changed (queries the database) """ super().clean() if not self.context: self.context = {} if not isinstance(self.context, dict): raise ValidationError( {'context': _('the supplied value is not a JSON object')}) if self._state.adding: return current = self.__class__.objects.get(pk=self.pk) for attr in ['backend', 'config', 'context']: if getattr(self, attr) != getattr(current, attr): if self.status != 'modified': self.set_status_modified(save=False) else: # config modified signal is always sent # regardless of the current status self._send_config_modified_after_save = True break def save(self, *args, **kwargs): created = self._state.adding self._just_created = created result = super().save(*args, **kwargs) if created: default_templates = self.get_default_templates() if default_templates: self.templates.add(*default_templates) if not created and self._send_config_modified_after_save: self._send_config_modified_signal(action='config_changed') self._send_config_modified_after_save = False if self._send_config_status_changed: self._send_config_status_changed_signal() self._send_config_status_changed = False self._initial_status = self.status return result def _send_config_modified_signal(self, action): """ Emits ``config_modified`` signal. Called also by Template when templates of a device are modified """ assert action in [ 'config_changed', 'related_template_changed', 'm2m_templates_changed', ] config_modified.send( sender=self.__class__, instance=self, previous_status=self._initial_status, action=action, # kept for backward compatibility config=self, device=self.device, ) def _send_config_status_changed_signal(self): """ Emits ``config_status_changed`` signal. Called also by Template when templates of a device are modified """ config_status_changed.send(sender=self.__class__, instance=self) def _set_status(self, status, save=True): self.status = status self._send_config_status_changed = True if save: self.save(update_fields=['status']) def set_status_modified(self, save=True, send_config_modified_signal=True): if send_config_modified_signal: self._send_config_modified_after_save = True self._set_status('modified', save) def set_status_applied(self, save=True): self._set_status('applied', save) def set_status_error(self, save=True): self._set_status('error', save) def _has_device(self): return hasattr(self, 'device') def get_vpn_context(self): c = super().get_context() for vpnclient in self.vpnclient_set.all().select_related( 'vpn', 'cert'): vpn = vpnclient.vpn vpn_id = vpn.pk.hex context_keys = vpn._get_auto_context_keys() ca = vpn.ca cert = vpnclient.cert # CA ca_filename = 'ca-{0}-{1}.pem'.format( ca.pk, ca.common_name.replace(' ', '_')) ca_path = '{0}/{1}'.format(app_settings.CERT_PATH, ca_filename) # update context c.update({ context_keys['ca_path']: ca_path, context_keys['ca_contents']: ca.certificate, }) # conditional needed for VPN without x509 authentication # eg: simple password authentication if cert: # cert cert_filename = 'client-{0}.pem'.format(vpn_id) cert_path = '{0}/{1}'.format(app_settings.CERT_PATH, cert_filename) # key key_filename = 'key-{0}.pem'.format(vpn_id) key_path = '{0}/{1}'.format(app_settings.CERT_PATH, key_filename) # update context c.update({ context_keys['cert_path']: cert_path, context_keys['cert_contents']: cert.certificate, context_keys['key_path']: key_path, context_keys['key_contents']: cert.private_key, }) return c def get_context(self, system=False): """ additional context passed to netjsonconfig """ c = collections.OrderedDict() extra = {} if self._has_device(): c.update([ ('name', self.name), ('mac_address', self.mac_address), ('id', str(self.device.id)), ('key', self.key), ]) if self.context and not system: extra.update(self.context) extra.update(self.get_vpn_context()) if app_settings.HARDWARE_ID_ENABLED and self._has_device(): extra.update({'hardware_id': str(self.device.hardware_id)}) c.update(sorted(extra.items())) return c def get_system_context(self): return self.get_context(system=True)
class StatusFieldDefaultNotFilled(models.Model): STATUS = Choices((0, "no", "No"), (1, "yes", "Yes")) status = StatusField()
class InstitutionCharge(Charge): """ This is a charge in the institution (city council, city government, mayor). """ MAYOR_CHARGE = 1 ASSESSOR_CHARGE = 2 COUNSELOR_CHARGE = 3 COUNCIL_PRES_CHARGE = 4 COUNCIL_VICE_CHARGE = 5 COMMITTEE_MEMBER_CHARGE = 6 CHARGE_TYPES = Choices( (MAYOR_CHARGE, _('Mayor')), (ASSESSOR_CHARGE, _('Town government member')), (COUNCIL_PRES_CHARGE, _('Counsil president')), (COUNCIL_VICE_CHARGE, _('Counsil vice president')), (COUNSELOR_CHARGE, _('Counselor')), (COMMITTEE_MEMBER_CHARGE, _('Committee member')), ) substitutes = models.OneToOneField('InstitutionCharge', blank=True, null=True, related_name='reverse_substitute_set', on_delete=models.PROTECT, verbose_name=_('in substitution of')) substituted_by = models.OneToOneField( 'InstitutionCharge', blank=True, null=True, related_name='reverse_substituted_by_set', on_delete=models.PROTECT, verbose_name=_('substituted by')) institution = models.ForeignKey('Institution', on_delete=models.PROTECT, verbose_name=_('institution'), related_name='charge_set') charge_type = models.IntegerField(_('charge type'), choices=CHARGE_TYPES) op_charge_id = models.IntegerField(_('openpolis institution charge ID'), blank=True, null=True) n_rebel_votations = models.IntegerField(default=0) n_present_votations = models.IntegerField(default=0) n_absent_votations = models.IntegerField(default=0) class Meta(Charge.Meta): db_table = u'people_institution_charge' verbose_name = _('institution charge') verbose_name_plural = _('institution charges') def __unicode__(self): # TODO: implement ``get_charge_type_display()`` method return u'%s - %s dal %s' % (self.person, self.get_charge_type_display(), self.start_date.strftime('%d/%m/%Y')) # TODO: model validation: check that ``substitutes`` and ``substituted_by`` fields # point to ``InstitutionCharge``s of the same kind @property def presented_acts(self): """ The QuerySet of acts presented by this charge. """ return self.presented_act_set.all() @property def received_acts(self): """ The QuerySet of acts received by this charge. """ return self.received_act_set.all() @property def council_group(self): """ Returns the city council's group this charge currently belongs to (if any). If the charge doesn't belong to any council's group -- e.g. because (s)he is not a counselor, return ``None``. """ # this property only make sense for counselors if self.charge_type == InstitutionCharge.COUNSELOR_CHARGE: # This query should return only one record, since a counselor # may belong to only one council group at a time. # If multiple records match the query, instead, a ``MultipleObjectsReturned`` # exception will be raised, providing a useful integrity check for data group = Group.objects.get(groupcharge__charge__id=self.id, groupcharge__end_date__isnull=True) return group else: return None def compute_rebellion_cache(self): """ Re-compute the number of votations where the charge has vote differently from her group and update the n_rebel_votations counter """ self.n_rebel_votations = self.chargevote_set.filter( is_rebel=True).count() self.save() def compute_presence_cache(self): """ Re-compute the number of votations where the charge was present/absent and update the respective counters """ from open_municipio.votations.models import ChargeVote absent = ChargeVote.ABSENT self.n_present_votations = self.chargevote_set.exclude( vote=absent).count() self.n_absent_votations = self.chargevote_set.filter( vote=absent).count() self.save()
from django.db import models from django.utils.translation import ugettext_lazy as _ from helpers.constants import GENDERS from model_utils import Choices from model_utils.models import TimeStampedModel OCCUPATIONS = Choices( ("developer", _("Developer")), ("student", _("Student")), ("researcher", _("Researcher")), ("unemployed", _("Unemployed")), ("other", _("Other")), ) GRANT_TYPES = Choices( ("diversity", _("Diversity")), ("unemployed", _("Unemployed")), ("speaker", _("Speaker")), ) INTERESTED_IN_VOLUNTEERING = Choices( ("no", _("No")), ("yes", _("Yes")), ("absolutely", _("My soul is yours to take!"))) class Grant(TimeStampedModel): name = models.CharField(_("name"), max_length=300) full_name = models.CharField(_("full name"), max_length=300) conference = models.ForeignKey( "conferences.Conference", on_delete=models.CASCADE,
class Person(models.Model, MonitorizedItem): FEMALE_SEX = 0 MALE_SEX = 1 SEX = Choices( (MALE_SEX, _('Male')), (FEMALE_SEX, _('Female')), ) first_name = models.CharField(_('first name'), max_length=128) last_name = models.CharField(_('last name'), max_length=128) birth_date = models.DateField(_('birth date')) birth_location = models.CharField(_('birth location'), blank=True, max_length=128) slug = models.SlugField(unique=True, blank=True, null=True, max_length=128) sex = models.IntegerField(_('sex'), choices=SEX) op_politician_id = models.IntegerField(_('openpolis politician ID'), blank=True, null=True) # manager to handle the list of monitoring having as content_object this instance monitoring_set = generic.GenericRelation(Monitoring, object_id_field='object_pk') class Meta: verbose_name = _('person') verbose_name_plural = _('people') def __unicode__(self): return u'%s %s' % (self.first_name, self.last_name) def save(self, *args, **kwargs): if self.slug is None: self.slug = slugify( "%s %s %s" % (self.first_name, self.last_name, self.birth_date)) super(Person, self).save(*args, **kwargs) @permalink def get_absolute_url(self): return 'om_politician_detail', (), {'slug': self.slug} @property def full_name(self): return "%s %s" % (self.first_name, self.last_name) @property def all_institution_charges(self): """ Returns the QuerySet of all institution charges held by this person during his/her career. """ return self.institutioncharge_set.all() @property def current_institution_charges(self): """ Returns a QuerySet of institution charges currently held by this person. """ return self.institutioncharge_set.current() @property def resources(self): """ Returns the list of resources associated with this person """ return self.resource_set.all() @property def content_type_id(self): """ Return id of the content type associated with this instance. """ return ContentType.objects.get_for_model(self).id
class User(AbstractUser): email = models.EmailField(unique=True, null=True) phone = models.CharField(max_length=30, blank=True, null=True, default=None, validators=[validate_mobile_phone]) ROLE = Choices( (1, 'student', 'Student'), (2, 'trainer', 'Trainer'), (2, 'company', 'Company'), ) role = models.PositiveIntegerField(choices=ROLE, blank=True, null=True) USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['username'] objects = CustomUserManager() @property def name(self): name = self.get_full_name() if not name: name = self.username return name def get_student(self): return self.students.select_related('training').last() def notification_register(self): data = { 'token': default_token_generator.make_token(self), 'uid': int_to_base36(self.id), 'host': settings.HOST, 'user': self, 'email_title': 'Aktivasi Akun' } send = mail.send( [self.email], settings.DEFAULT_FROM_EMAIL, subject='Aktivasi Akun', html_message=render_to_string('emails/register.html', context=data) ) return send def notification_status_training(self, training_materials): data = { 'host': settings.HOST, 'user': self, 'training_materials': training_materials, 'email_title': 'Status Pelatihan' } send = mail.send( [self.email], settings.DEFAULT_FROM_EMAIL, subject='Status Pelatihan', html_message=render_to_string('emails/training-status.html', context=data) ) return send def get_count_training_status(self): count_status = self.training_status.aggregate( graduate=Count( Case(When(status=TrainingStatus.STATUS.graduate, then=1), output_field=IntegerField()) ), not_yet=Count( Case(When(status=TrainingStatus.STATUS.not_yet, then=1), output_field=IntegerField()) ), repeat=Count( Case(When(status=TrainingStatus.STATUS.repeat, then=1), output_field=IntegerField()) ) ) return count_status def indicator_reached(self, status): if status['graduate'] >= settings.INDICATOR_GRADUATED and status['not_yet'] == 0: return True return False def save_training_status_to_log(self): LogTrainingStatus.objects.bulk_create([ LogTrainingStatus( code=training.training_material.code, title=training.training_material.title, status=training.status, user=self, student=self.get_student() ) for training in self.training_status.select_related('training_material') ]) def delete_training_status(self): self.training_status.all().delete()
class Institution(Body): """ Institutional bodies can be of different types (as specified by the ``institution_type`` field). This model has a relation with itself, in order to map hierarchical bodies (joint committees, ...). """ MAYOR = 1 CITY_GOVERNMENT = 2 COUNCIL = 3 COMMITTEE = 4 JOINT_COMMITTEE = 5 INSTITUTION_TYPES = Choices( (MAYOR, _('Mayor')), (COUNCIL, _('Council')), (CITY_GOVERNMENT, _('Town government')), (COMMITTEE, _('Committee')), (JOINT_COMMITTEE, _('Joint committee')), ) parent = models.ForeignKey('Institution', related_name='sub_body_set', blank=True, null=True) institution_type = models.IntegerField(choices=INSTITUTION_TYPES) class Meta(Body.Meta): verbose_name = _('institution') verbose_name_plural = _('institutions') def save(self, *args, **kwargs): """slugify name on first save""" if not self.id: self.slug = slugify(self.name) super(Institution, self).save(*args, **kwargs) def get_absolute_url(self): if self.institution_type == self.MAYOR: return reverse("om_institution_mayor") elif self.institution_type == self.CITY_GOVERNMENT: return reverse("om_institution_citygov") elif self.institution_type == self.COUNCIL: return reverse("om_institution_council") elif self.institution_type == self.COMMITTEE: return reverse("om_institution_committee", kwargs={'slug': self.slug}) @property def charges(self): """ The QuerySet of all *current* charges (``InstitutionCharge`` instances) associated with this institution. """ return self.charge_set.current() @property def emitted_acts(self): """ The QuerySet of all acts emitted by this institution. Note that the objects comprising the resulting QuerySet aren't generic ``Act`` instances, but instances of specific ``Act`` subclasses (i.e. ``Deliberation``, ``Motion``, etc.). This is made possible by the fact that the default manager for the ``Act`` model is ``model_utils.managers.InheritanceManager``, and this manager class declares ``use_for_related_fields = True``. See `Django docs`_ for details. .. _`Django docs`: https://docs.djangoproject.com/en/1.3/topics/db/managers/#controlling-automatic-manager-types """ # NOTE: See also Django bug #14891 return self.emitted_act_set.all().select_subclasses()
class AbstractConfig(BaseConfig): """ Abstract model implementing the NetJSON DeviceConfiguration object """ device = models.OneToOneField('django_netjsonconfig.Device', on_delete=models.CASCADE) STATUS = Choices('modified', 'applied', 'error') status = StatusField( _('configuration status'), help_text=_( '"modified" means the configuration is not applied yet; \n' '"applied" means the configuration is applied successfully; \n' '"error" means the configuration caused issues and it was rolled back;' )) context = JSONField( null=True, blank=True, help_text=_('Additional ' '<a href="http://netjsonconfig.openwisp.org/' 'en/stable/general/basics.html#context" target="_blank">' 'context (configuration variables)</a> in JSON format')) class Meta: abstract = True verbose_name = _('configuration') verbose_name_plural = _('configurations') def __str__(self): if self._has_device(): return self.name return str(self.pk) def clean(self): """ modifies status if key attributes of the configuration have changed (queries the database) """ super(AbstractConfig, self).clean() if self._state.adding: return current = self.__class__.objects.get(pk=self.pk) for attr in ['backend', 'config', 'context']: if getattr(self, attr) != getattr(current, attr): self.set_status_modified(save=False) break def save(self, *args, **kwargs): result = super(AbstractConfig, self).save(*args, **kwargs) if not self._state.adding and getattr( self, '_send_config_modified_after_save', False): self._send_config_modified_signal() return result def _send_config_modified_signal(self): """ sends signal ``config_modified`` """ config_modified.send(sender=self.__class__, config=self, device=self.device) def _set_status(self, status, save=True): self.status = status if save: self.save() def set_status_modified(self, save=True): self._set_status('modified', save) if save: self._send_config_modified_signal() else: # set this attribute that will be # checked in the save method self._send_config_modified_after_save = True def set_status_applied(self, save=True): self._set_status('applied', save) def set_status_error(self, save=True): self._set_status('error', save) def _has_device(self): return hasattr(self, 'device') def get_context(self): """ additional context passed to netjsonconfig """ c = {} if self._has_device(): c.update({ 'id': str(self.device.id), 'key': self.key, 'name': self.name, 'mac_address': self.mac_address }) if self.context: c.update(self.context) c.update(app_settings.CONTEXT) if app_settings.HARDWARE_ID_ENABLED and self._has_device(): c.update({'hardware_id': self.device.hardware_id}) return c @property def name(self): """ returns device name (kept for backward compatibility with pre 0.6 versions) """ if self._has_device(): return self.device.name return str(self.pk) @property def mac_address(self): """ returns device mac address (kept for backward compatibility with pre 0.6 versions) """ return self.device.mac_address @property def key(self): """ returns device key (kept for backward compatibility with pre 0.6 versions) """ return self.device.key
class Notification(models.Model): """ Action model describing the actor acting out a verb (on an optional target). Nomenclature based on http://activitystrea.ms/specs/atom/1.0/ Generalized Format:: <actor> <verb> <time> <actor> <verb> <target> <time> <actor> <verb> <action_object> <target> <time> Examples:: <justquick> <reached level 60> <1 minute ago> <brosner> <commented on> <pinax/pinax> <2 hours ago> <washingtontimes> <started follow> <justquick> <8 minutes ago> <mitsuhiko> <closed> <issue 70> on <mitsuhiko/flask> <about 2 hours ago> Unicode Representation:: justquick reached level 60 1 minute ago mitsuhiko closed issue 70 on mitsuhiko/flask 3 hours ago HTML Representation:: <a href="http://oebfare.zcom/">brosner</a> commented on <a href="http://github.com/pinax/pinax">pinax/pinax</a> 2 hours ago """ LEVELS = Choices('success', 'info', 'warning', 'error') level = models.CharField(choices=LEVELS, default=LEVELS.info, max_length=20) recipient = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, related_name='notifications') unread = models.BooleanField(default=True, blank=False) actor_content_type = models.ForeignKey(ContentType, related_name='notify_actor') actor_object_id = models.CharField(max_length=255) actor = GenericForeignKey('actor_content_type', 'actor_object_id') verb = models.CharField(max_length=255) description = models.TextField(blank=True, null=True) target_content_type = models.ForeignKey(ContentType, related_name='notify_target', blank=True, null=True) target_object_id = models.CharField(max_length=255, blank=True, null=True) target = GenericForeignKey('target_content_type', 'target_object_id') action_object_content_type = models.ForeignKey( ContentType, related_name='notify_action_object', blank=True, null=True) action_object_object_id = models.CharField(max_length=255, blank=True, null=True) action_object = GenericForeignKey('action_object_content_type', 'action_object_object_id') timestamp = models.DateTimeField(default=now) public = models.BooleanField(default=True) deleted = models.BooleanField(default=False) emailed = models.BooleanField(default=False) data = JSONField(blank=True, null=True) objects = managers.PassThroughManager.for_queryset_class( NotificationQuerySet)() class Meta: ordering = ('-timestamp', ) app_label = 'notifications' def __unicode__(self): ctx = { 'actor': self.actor, 'verb': self.verb, 'action_object': self.action_object, 'target': self.target, 'timesince': self.timesince() } if self.target: if self.action_object: return u'%(actor)s %(verb)s %(action_object)s on %(target)s %(timesince)s ago' % ctx return u'%(actor)s %(verb)s %(target)s %(timesince)s ago' % ctx if self.action_object: return u'%(actor)s %(verb)s %(action_object)s %(timesince)s ago' % ctx return u'%(actor)s %(verb)s %(timesince)s ago' % ctx def __str__(self): #Adds support for Python 3 return self.__unicode__() def timesince(self, now=None): """ Shortcut for the ``django.utils.timesince.timesince`` function of the current timestamp. """ from django.utils.timesince import timesince as timesince_ return timesince_(self.timestamp, now) @property def slug(self): return id2slug(self.id) def mark_as_read(self): if self.unread: self.unread = False self.save() def mark_as_unread(self): if not self.unread: self.unread = True self.save()
from model_utils import Choices FEE_TYPE = Choices( ('fixed', 'Fixed'), ('percentage', 'Percentage'), ) CACHE_KEY_CONFIG = 'system_config.{}' CACHE_KEY_FEE = 'system_fee.{}' CACHE_KEY_COUNTRY_DEFAULT = 'system_country_default.{}' CACHE_KEY_SYSTEM_NOTIFICATION = 'system_notification.{}' CACHE_KEY_SYSTEM_REMINDER = 'system_reminder.{}' CACHE_KEY_BONUS = 'system_bonus.{}'
from __future__ import unicode_literals from model_utils import Choices DIRECTION = Choices( ('1', 'incoming', 'Incoming'), ('2', 'outgoing', 'Outgoing'), )
* Use timestamp to control current running price, which means price can pre-define """ from django.db import models from model_utils import Choices from django.core.validators import RegexValidator, MinValueValidator from demosite.utils import ItemType from decimal import Decimal import hashlib from random import randrange # Choices class for model Field TYPE_CHOICES = Choices() for item in ItemType: TYPE_CHOICES += Choices((item.value, item.name)) class Item(models.Model): """Item model design Parameters ---------- sku: Char Field Unique ascii & number max 8 length sku code name: Char Field Name of the item item_type: Interger Field Only allow single item type
class Order(StatusModel): MAX_STEPS = 4 RECEIVED = "received" FORMAT_RECEIVED = "Rebut" PREPARING = 'preparing' FORMAT_PREPARING = 'En procés' ON_DELIVERY = "on_delivery" FORMAT_ON_DELIVERY = "En camí" PAID = 'paid' FORMAT_PAID = 'Pagat' CANCELLED = 'cancelled' FORMAT_CANCELLED = 'Cancel·lat' @classmethod def get_status_step(cls, status): result = 1 if status == cls.PREPARING: result = 2 elif status == cls.ON_DELIVERY: result = 3 elif status == cls.PAID: result = 4 elif status == cls.CANCELLED: result = 5 return result STATUS = Choices( RECEIVED, PREPARING, ON_DELIVERY, PAID, CANCELLED ) PAYMENT_CARD = 'card' PAYMENT_CASH = 'cash' PAYMENT_OPTIONS = Choices('card', 'cash') # Relations # Attributes - Mandatory slug = models.SlugField(default=uuid.uuid4, editable=False) timestamp = models.DateTimeField(auto_now_add=True, verbose_name='Hora') seat_number = models.PositiveSmallIntegerField(verbose_name='Cadira') total_price = models.DecimalField(default=0.00, decimal_places=2, max_digits=20, verbose_name='Preu') status = models.CharField(max_length=32, choices=STATUS, default='received', verbose_name='estat') payment_method = models.CharField(max_length=16, choices=PAYMENT_OPTIONS, default=PAYMENT_CARD, verbose_name='Pagament') # Attributes - Optional payment_amount = models.DecimalField(decimal_places=2, max_digits=20, null=True, default=None) comment = models.TextField(null=True, blank=True) # Object Manager objects = OrderManager() # Custom Properties @property def status_steps(self): return self.get_status_step(status=self.status) @property def payment_change(self): return self.total_price - self.payment_amount if self.payment_method == self.PAYMENT_CASH else None # Methods def save(self, *args, **kwargs): items = self.items.all() self.total_price = items.aggregate(Sum('total_price'))['total_price__sum'] if items.exists() else 0.00 if self.payment_method != self.PAYMENT_CASH: self.payment_amount = None super().save(*args, **kwargs) def get_edit_url(self): return reverse('update_order', kwargs={'pk': self.id}) def get_delete_url(self): return reverse('delete_order', kwargs={'pk': self.id}) # Meta and String class Meta: ordering = ['timestamp'] verbose_name = _('order') verbose_name_plural = _('orders') def __str__(self): return str(self.seat_number)
class AbstractConfig(BaseConfig): """ Abstract model implementing the NetJSON DeviceConfiguration object """ device = models.OneToOneField('django_netjsonconfig.Device', on_delete=models.CASCADE) STATUS = Choices('modified', 'running', 'error') status = StatusField(help_text=_( 'modified means the configuration is not applied yet; ' 'running means applied and running; ' 'error means the configuration caused issues and it was rolledback')) last_ip = models.GenericIPAddressField( blank=True, null=True, help_text=_('indicates the last ip from which the ' 'configuration was downloaded from ' '(except downloads from this page)')) class Meta: abstract = True verbose_name = _('configuration') verbose_name_plural = _('configurations') def __str__(self): if self._has_device(): return self.name return str(self.pk) def clean(self): """ modifies status if key attributes of the configuration have changed (queries the database) """ super(AbstractConfig, self).clean() if self._state.adding: return current = self.__class__.objects.get(pk=self.pk) for attr in ['backend', 'config']: if getattr(self, attr) != getattr(current, attr): self.set_status_modified(save=False) break def save(self, *args, **kwargs): result = super(AbstractConfig, self).save(*args, **kwargs) if not self._state.adding and getattr( self, '_send_config_modified_after_save', False): self._send_config_modified_signal() return result def _send_config_modified_signal(self): """ sends signal ``config_modified`` """ config_modified.send(sender=self.__class__, config=self, device=self.device) def _set_status(self, status, save=True): self.status = status if save: self.save() def set_status_modified(self, save=True): self._set_status('modified', save) if save: self._send_config_modified_signal() else: # set this attribute that will be # checked in the save method self._send_config_modified_after_save = True def set_status_running(self, save=True): self._set_status('running', save) def set_status_error(self, save=True): self._set_status('error', save) def _has_device(self): return hasattr(self, 'device') def get_context(self): """ additional context passed to netjsonconfig """ c = {} if self._has_device(): c.update({ 'id': str(self.device.id), 'key': self.key, 'name': self.name, 'mac_address': self.mac_address }) c.update(app_settings.CONTEXT) return c @property def name(self): """ returns device name (kept for backward compatibility with pre 0.6 versions) """ if self._has_device(): return self.device.name return str(self.pk) @property def mac_address(self): """ returns device mac address (kept for backward compatibility with pre 0.6 versions) """ return self.device.mac_address @property def key(self): """ returns device key (kept for backward compatibility with pre 0.6 versions) """ return self.device.key
class DataRequest(BaseRequest, StatusModel): DATA_TYPE_CHOICES = Choices( ('interpreted', _('Interpreted')), ('raw', _('Raw')), ('processed', _('Processed')), ('other', _('Other')), ) DATASET_USE_CHOICES = Choices( ('commercial', _('Commercial')), ('noncommercial', _('Non-commercial')), ) REJECTION_REASON_CHOICES = Choices( ('reason1', _('Reason 1')), ('reason2', _('Reason 2')), ('reason3', _('Reason 3')), ) profile_request = models.ForeignKey(ProfileRequest, null=True, blank=True) jurisdiction_shapefile = models.ForeignKey(Layer, null=True, blank=True) project_summary = models.TextField(_('Summary of Project/Program'), null=True, blank=True) data_type = TaggableManager(_('data_types'), blank=True, help_text="Data Type Selected") data_class_other = models.CharField(_('Requester-specified Data Type'), null=True, blank=True, max_length=50) data_type_requested = models.CharField( _('Type of Data Requested'), choices=DATA_TYPE_CHOICES, default=DATA_TYPE_CHOICES.processed, max_length=15, ) purpose = models.TextField(_('Purpose of Data'), null=True, blank=True) intended_use_of_dataset = models.CharField( _('Intended Use of Dataset'), choices=DATASET_USE_CHOICES, default=DATASET_USE_CHOICES.commercial, max_length=15, ) #For place name place_name = models.TextField(_('Geolocation name provided by Google'), null=True, blank=True) area_coverage = models.DecimalField( _('Area of Coverage'), max_digits=30, decimal_places=4, help_text=_('Sqr KMs'), default=0, null=True, blank=True, ) #For jurisdiction data size juris_data_size = models.FloatField( _('Data size of requested jurisdiction in bytes'), null=True, blank=True, default=0) #For request letter request_letter = models.ForeignKey(Document, null=True, blank=True) suc = TaggableManager(_('SUCs'), blank=True, help_text="SUC jurisdictions within this ROI", through=SUCTaggedRequest, related_name="suc_request_tag") suc_notified = models.BooleanField(null=False, blank=False, default=False) suc_notified_date = models.DateTimeField(null=True, blank=True) forwarded = models.BooleanField(null=False, blank=False, default=False) forwarded_date = models.DateTimeField(null=True, blank=True) class Meta: app_label = "datarequests" verbose_name = _('Data Request') verbose_name_plural = _('Data Requests') ordering = ('-created', ) def __init__(self, *args, **kwargs): models.Model.__init__(self, *args, **kwargs) #self.status = self.STATUS.unconfirmed def __unicode__(self): if self.profile_request or self.profile: return (_('{} request by {} {}').format( self.status, unidecode(self.get_first_name()), unidecode(self.get_last_name()), )) else: return (_('Data request # {}').format(self.pk)) def get_absolute_url(self): return reverse('datarequests:data_request_detail', kwargs={'pk': self.pk}) def set_status(self, status, administrator=None): self.status = status self.administrator = administrator self.save() def assign_jurisdiction(self): # Link shapefile to account uj = None try: uj = UserJurisdiction.objects.get(user=self.profile) except ObjectDoesNotExist: uj = UserJurisdiction() uj.user = self.profile uj.jurisdiction_shapefile = self.jurisdiction_shapefile uj.save() #Add view permission on resource resource = self.jurisdiction_shapefile perms = resource.get_all_level_info() perms["users"][self.profile.username] = ["view_resourcebase"] resource.set_permissions(perms) def get_first_name(self): if self.profile: return self.profile.first_name if self.profile_request: return self.profile_request.first_name def get_last_name(self): if self.profile: return self.profile.last_name if self.profile_request: return self.profile_request.last_name def get_email(self): if self.profile: return self.profile.email if self.profile_request: return self.profile_request.email def get_contact_number(self): if self.profile: return self.profile.voice if self.profile_request: return self.profile_request.contact_number def get_organization(self): if self.profile: return self.profile.organization if self.profile_request: return self.profile_request.organization def get_organization_type(self): if self.profile_request: return self.profile_request.org_type elif self.profile: return self.profile.org_type else: return None def to_values_list(self, fields=[ 'id', 'name', 'email', 'contact_number', 'organization', 'project_summary', 'created', 'status', 'data_size', 'area_coverage', 'has_profile_request' ]): out = [] for f in fields: if f is 'id': out.append(getattr(self, 'pk')) elif f is 'name': first_name = unidecode(self.get_first_name()) last_name = unidecode(self.get_last_name()) out.append(first_name + " " + last_name) elif f is 'email': out.append(self.get_email()) elif f is 'contact_number': out.append(self.get_contact_number()) elif f is 'organization': if self.get_organization(): out.append(unidecode(self.get_organization())) else: out.append(None) elif f is 'created': created = getattr(self, f) out.append( str(created.month) + "/" + str(created.day) + "/" + str(created.year)) elif f == 'status update': date_of_action = getattr(self, 'status_changed') if date_of_action: out.append( str(date_of_action.month) + "/" + str(date_of_action.day) + "/" + str(date_of_action.year)) else: out.append('') elif f is 'org_type' or f is 'organization_type': out.append(self.get_organization_type()) elif f is 'has_letter': if self.request_letter: out.append('yes') else: out.append('no') elif f is 'has_shapefile': if self.jurisdiction_shapefile: out.append('yes') else: out.append('no') elif f is 'data_request_status': if self.data_request: out.append(data_request.status) else: out.append("NA") elif f is 'rejection_reason': out.append(str(getattr(self, 'rejection_reason'))) elif f is 'place_name': out.append(str(getattr(self, 'place_name'))) elif f is 'juris_data_size': out.append(str(getattr(self, 'juris_data_size'))) elif f is 'area_coverage': out.append(str(getattr(self, 'area_coverage'))) elif f is 'has_profile_request': if self.profile_request: out.append('yes') else: out.append('no') elif f is 'has_account': if self.profile: out.append('yes') else: out.append('no') else: try: val = getattr(self, f) if isinstance(val, unicode): out.append(unidecode(val)) else: out.append(str(val)) except Exception: out.append("NA") return out def send_email(self, subj, msg, html_msg): text_content = msg html_content = html_msg email_subject = _(subj) msg = EmailMultiAlternatives(email_subject, text_content, settings.DEFAULT_FROM_EMAIL, [ self.get_email(), ]) msg.attach_alternative(html_content, "text/html") msg.send() def send_new_request_notif_to_admins(self, request_type="Data"): site = Site.objects.get_current() text_content = email_utils.NEW_REQUEST_EMAIL_TEXT.format( request_type, settings.BASEURL + self.get_absolute_url()) html_content = email_utils.NEW_REQUEST_EMAIL_HTML.format( request_type, settings.BASEURL + self.get_absolute_url(), settings.BASEURL + self.get_absolute_url()) email_subject = "[LiPAD] A new request has been submitted" self.send_email(email_subject, text_content, html_content) def send_approval_email(self, username): site = Site.objects.get_current() profile_url = (reverse('profile_detail', kwargs={'username': username})) profile_url = iri_to_uri(profile_url) text_content = email_utils.DATA_APPROVAL_TEXT.format( unidecode(self.get_first_name()), local_settings.LIPAD_SUPPORT_MAIL) html_content = email_utils.DATA_APPROVAL_HTML.format( unidecode(self.get_first_name()), local_settings.LIPAD_SUPPORT_MAIL, local_settings.LIPAD_SUPPORT_MAIL) email_subject = _('[LiPAD] Data Request Status') self.send_email(email_subject, text_content, html_content) def send_rejection_email(self): additional_details = 'Additional Details: ' + str( self.additional_rejection_reason) text_content = email_utils.DATA_REJECTION_TEXT.format( unidecode(self.get_first_name()), self.rejection_reason, additional_details, local_settings.LIPAD_SUPPORT_MAIL, ) html_content = email_utils.DATA_REJECTION_HTML.format( unidecode(self.get_first_name()), self.rejection_reason, additional_details, local_settings.LIPAD_SUPPORT_MAIL, local_settings.LIPAD_SUPPORT_MAIL) email_subject = _('[LiPAD] Data Request Status') self.send_email(email_subject, text_content, html_content) def send_suc_notification(self, suc=None): if not suc: if len(self.suc.names()) > 1: suc = "UPD" else: suc = self.suc.names()[0] suc_contacts = SUC_Contact.objects.filter( institution_abrv=suc).exclude(position="Program Leader") suc_pl = SUC_Contact.objects.get(institution_abrv=suc, position="Program Leader") organization = "" if self.get_organization(): organization = unidecode(self.get_organization()) data_classes = "" for n in self.data_type.names(): data_classes += str(n) data_classes += ", " + str(self.data_class_other) text_content = email_utils.DATA_SUC_REQUEST_NOTIFICATION_TEXT.format( suc_pl.salutation, suc_pl.name, unidecode(self.get_first_name()), unidecode(self.get_last_name()), organization, self.get_email(), self.project_summary, data_classes, self.purpose, ) html_content = email_utils.DATA_SUC_REQUEST_NOTIFICATION_HTML.format( suc_pl.salutation, suc_pl.name, unidecode(self.get_first_name()), unidecode(self.get_last_name()), organization, self.get_email(), self.project_summary, data_classes, self.purpose, ) cc = suc_contacts.values_list('email_address', flat=True) email_subject = _('[LiPAD] Data Request Forwarding') msg = EmailMultiAlternatives(email_subject, text_content, settings.DEFAULT_FROM_EMAIL, [ suc_pl.email_address, ], cc=cc) msg.attach_alternative(html_content, "text/html") msg.send() self.suc_notified = True self.suc_notified_date = timezone.now() self.save() def send_jurisdiction(self, suc=None): if not suc: if len(self.suc.names()) == 1: suc = self.suc.names()[0] else: suc = "UPD" suc_contacts = SUC_Contact.objects.filter( institution_abrv=suc).exclude(position="Program Leader") suc_pl = SUC_Contact.objects.get(institution_abrv=suc, position="Program Leader") text_content = email_utils.DATA_SUC_JURISDICTION_TEXT.format( suc_pl.salutation, suc_pl.name, settings.BASEURL + self.jurisdiction_shapefile.get_absolute_url(), ) html_content = email_utils.DATA_SUC_JURISDICTION_HTML.format( suc_pl.salutation, suc_pl.name, settings.BASEURL + self.jurisdiction_shapefile.get_absolute_url(), settings.BASEURL + self.jurisdiction_shapefile.get_absolute_url(), ) cc = suc_contacts.values_list('email_address', flat=True) email_subject = _('[LiPAD] Data Request Forwarding') msg = EmailMultiAlternatives(email_subject, text_content, settings.DEFAULT_FROM_EMAIL, [ suc_pl.email_address, ], cc=cc) username = suc_pl.username if not suc == "UPD": resource = self.jurisdiction_shapefile perms = resource.get_all_level_info() #user_level_perms = getattr(perms, "user", {}) user_perms = getattr(perms["users"], str(username), []) if "view_resourcebase" not in user_perms: user_perms.append("view_resourcebase") if "download_resourcebase" not in user_perms: user_perms.append("download_resourcebase") perms["users"][suc_pl.username] = user_perms resource.set_permissions(perms) msg.attach_alternative(html_content, "text/html") msg.send() self.forwarded = True self.forwarded_date = timezone.now() self.save() def notify_user_preforward(self, suc=None): if not suc: if len(self.suc.names()) == 1: suc = self.suc.names()[0] else: suc = "UPD" suc_contact = SUC_Contact.objects.get(institution_abrv=suc, position="Program Leader") text_content = email_utils.DATA_USER_PRE_FORWARD_NOTIFICATION_TEXT.format( self.get_first_name(), self.get_last_name(), suc_contact.institution_full, suc_contact.institution_abrv, suc_contact.salutation, suc_contact.name) html_content = email_utils.DATA_USER_PRE_FORWARD_NOTIFICATION_HTML.format( self.get_first_name(), self.get_last_name(), suc_contact.institution_full, suc_contact.institution_abrv, suc_contact.salutation, suc_contact.name) email_subject = _('[LiPAD] Data Request Forwarding') msg = EmailMultiAlternatives( email_subject, text_content, settings.DEFAULT_FROM_EMAIL, [ self.get_email(), ], ) msg.attach_alternative(html_content, "text/html") msg.send() def notify_user_forward(self, suc=None): if not suc: if len(self.suc.names()) == 1: suc = self.suc.names()[0] else: suc = "UPD" text_content = email_utils.DATA_USER_FORWARD_NOTIFICATION_TEXT.format( self.get_first_name(), self.get_last_name(), suc) html_content = email_utils.DATA_USER_FORWARD_NOTIFICATION_HTML.format( self.get_first_name(), self.get_last_name(), suc) email_subject = _('[LiPAD] Data Request Forwarding') msg = EmailMultiAlternatives( email_subject, text_content, settings.DEFAULT_FROM_EMAIL, [ self.get_email(), ], ) msg.attach_alternative(html_content, "text/html") msg.send()