class PostAbstract(models.Model): title = models.CharField(max_length=255) content = models.TextField() published = models.BooleanField(default=False) created = models.DateTimeField(null=True, blank=True) modified = models.DateTimeField(null=True, blank=True) tags = models.ManyToManyField(Tag) slug = models.SlugField(unique=True, null=True, blank=True) search = tsvector_field.SearchVectorField([ tsvector_field.WeightedColumn('title', 'A'), tsvector_field.WeightedColumn('content', 'D'), ], 'english') def __str__(self): return self.title + " " + self.slug def _html(self): return markdown.markdown(self.content, extensions=[ 'markdown.extensions.codehilite', 'markdown.extensions.fenced_code' ]) html = property(_html) def get_absolute_url(self): return reverse('post', kwargs={'slug': self.slug}) @classmethod def unique_slug(cls, slug, n=1): if cls.objects.filter(slug=slug): if n > 1: slug = slug[0:-len(str(n - 1))] + str(n) else: slug += str(n) return cls.unique_slug(slug, n + 1) else: return slug def save(self, *args, **kwargs): ''' On save, update timestamps ''' if not self.id: self.created = timezone.now() self.slug = self.unique_slug(slugify(self.title)) if not self.slug: self.slug = self.unique_slug(slugify(self.title)) self.modified = timezone.now() return super(PostAbstract, self).save(*args, **kwargs) class Meta: abstract = True
def SearchableModel(*args): fields = [tsvector_field.WeightedColumn(name, "A") for name in args] class SearchBase(models.Model): search = tsvector_field.SearchVectorField(fields, "english") class Meta(object): abstract = True return SearchBase
class Migration(migrations.Migration): dependencies = [ ("absentee", "0014_ballotrequest_esign_method"), ] operations = [ migrations.AddField( model_name="ballotrequest", name="search", field=tsvector_field.SearchVectorField( columns=[ tsvector_field.WeightedColumn("email", "A"), tsvector_field.WeightedColumn("last_name", "A"), tsvector_field.WeightedColumn("first_name", "A"), ], language="english", ), ), tsvector_field.IndexSearchVector("ballotrequest", "search"), ]
class Migration(migrations.Migration): dependencies = [ ("verifier", "0016_rename_partner_subscriber"), ] operations = [ migrations.AddField( model_name="lookup", name="search", field=tsvector_field.SearchVectorField( columns=[ tsvector_field.WeightedColumn("email", "A"), tsvector_field.WeightedColumn("last_name", "A"), tsvector_field.WeightedColumn("first_name", "A"), ], language="english", ), ), tsvector_field.IndexSearchVector("lookup", "search"), ]
class Migration(migrations.Migration): dependencies = [("task", "0005_task_refactor")] operations = [ migrations.AddField( model_name="group", name="search", field=tsvector_field.SearchVectorField( columns=[ tsvector_field.WeightedColumn("name", "A"), tsvector_field.WeightedColumn("description", "D"), ], language="german", ), ), RunInSchemas(tsvector_field.IndexSearchVector("group", "search")), migrations.AddField( model_name="task", name="search", field=tsvector_field.SearchVectorField( columns=[ tsvector_field.WeightedColumn("name", "A"), tsvector_field.WeightedColumn("description", "D"), ], language="german", ), ), RunInSchemas(tsvector_field.IndexSearchVector("task", "search")), migrations.AlterField( model_name="job", name="project", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="jobs", to="project.Project", ), ), ]
class Migration(migrations.Migration): initial = True dependencies = [ ('election', '0001_initial'), ('multi_tenant', '0001_initial'), ('action', '0001_initial'), ] operations = [ migrations.CreateModel( name='ReminderRequest', fields=[ ('created_at', models.DateTimeField(auto_now_add=True)), ('modified_at', models.DateTimeField(auto_now=True)), ('uuid', django_smalluuid.models.SmallUUIDField(default=django_smalluuid.models.UUIDDefault(), editable=False, primary_key=True, serialize=False, unique=True)), ('embed_url', models.TextField(blank=True, null=True)), ('utm_campaign', models.TextField(blank=True, null=True)), ('utm_source', models.TextField(blank=True, null=True)), ('utm_medium', models.TextField(blank=True, null=True)), ('utm_term', models.TextField(blank=True, null=True)), ('utm_content', models.TextField(blank=True, null=True)), ('session_id', models.UUIDField(blank=True, null=True)), ('source', models.TextField(blank=True, null=True)), ('sms_opt_in_subscriber', models.BooleanField(db_column='sms_opt_in_partner', default=False, null=True)), ('search', tsvector_field.SearchVectorField(columns=[tsvector_field.WeightedColumn('email', 'A'), tsvector_field.WeightedColumn('last_name', 'A'), tsvector_field.WeightedColumn('first_name', 'A')], language='english')), ('first_name', models.TextField()), ('last_name', models.TextField()), ('date_of_birth', models.DateField(blank=True, null=True)), ('address1', models.TextField(blank=True, null=True)), ('address2', models.TextField(blank=True, null=True)), ('city', models.TextField(blank=True, null=True)), ('zipcode', models.TextField(blank=True, null=True, validators=[django.core.validators.RegexValidator('^[0-9]{5}$', 'Zip codes are 5 digits')])), ('age', models.IntegerField(blank=True, null=True)), ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None)), ('email', models.EmailField(blank=True, max_length=254, null=True)), ('sms_opt_in', models.BooleanField(blank=True, default=None, null=True)), ('action', models.OneToOneField(null=True, on_delete=django.db.models.deletion.PROTECT, to='action.Action')), ('state', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='election.State')), ('subscriber', models.ForeignKey(db_column='partner_id', null=True, on_delete=django.db.models.deletion.PROTECT, to='multi_tenant.Client')), ], options={ 'ordering': ['-created_at'], }, ), ]
class Group(OrderedModel): name = models.CharField(_("Name"), default="", blank=True, max_length=512) description = models.TextField(_("Description"), default="", blank=True) depth = models.PositiveIntegerField(editable=False, db_index=True) parent = models.ForeignKey("self", related_name="groups", null=True, on_delete=models.CASCADE) token = models.BigIntegerField("api token", null=True) job = models.ForeignKey("Job", null=True, related_name="all_groups", on_delete=models.CASCADE) search = tsvector_field.SearchVectorField( [ tsvector_field.WeightedColumn("name", "A"), tsvector_field.WeightedColumn("description", "D"), ], settings.SEARCH_VECTOR_LANGUAGE, ) order_with_respect_to = "parent" objects = GroupManager() class Meta: verbose_name = _("Group") verbose_name_plural = _("Groups") ordering = ("order", ) def __init__(self, *args, **kwargs): if "parent" in kwargs: if "job" not in kwargs: kwargs["job"] = kwargs["parent"].job if "depth" not in kwargs: kwargs["depth"] = kwargs["parent"].depth + 1 super().__init__(*args, **kwargs) def refresh_pks(self): self.parent = self.parent self.job = self.job @property def is_root(self): return self.parent is None @property def _structure(self): return self.job.project.structure @cached_property def code(self): if self.name: code = self._structure.format_group(self.order, self.depth) else: code = "_" return code if self.is_root else "{}.{}".format(self.parent.code, code) def clone_to(self, new_parent, new_order): groups = self.groups.all() tasks = self.tasks.all() self.pk = None self.parent = new_parent self.job = new_parent.job self.order = new_order self.save() for group in groups: group.clone_to(self, group.order) for task in tasks: task.clone_to(self, task.order) def _calc(self, field): total = Decimal(0.0) for group in self.groups.all(): total += getattr(group, field) for task in self.tasks.all(): if field == "estimate" and task.include_estimate: total += task.total elif field == "progress": total += task.progress return total @property def estimate(self): if not hasattr(self, "_estimate"): self._estimate = self._calc("estimate") return self._estimate @estimate.setter def estimate(self, value): self._estimate = value @property def progress(self): if not hasattr(self, "_progress"): self._progress = self._calc("progress") return self._progress @progress.setter def progress(self, value): self._progress = value @cached_property def progress_percent(self): return nice_percent(self.progress, self.estimate) def __str__(self): return self.name
class Task(OrderedModel): name = models.CharField(_("Name"), max_length=512) description = models.TextField(blank=True) search = tsvector_field.SearchVectorField( [ tsvector_field.WeightedColumn("name", "A"), tsvector_field.WeightedColumn("description", "D"), ], settings.SEARCH_VECTOR_LANGUAGE, ) qty = models.DecimalField( _("Quantity"), blank=True, null=True, max_digits=13, decimal_places=3, default=Decimal("0.00"), ) qty_equation = models.CharField(max_length=512, blank=True) complete = models.DecimalField(_("Completed"), max_digits=12, decimal_places=3, default=Decimal("0.00")) unit = models.CharField(_("Unit"), max_length=512, blank=True) price = models.DecimalField(_("Price"), max_digits=12, decimal_places=2, default=Decimal("0.00")) price_equation = models.CharField(max_length=512, blank=True) total = models.DecimalField(_("Total"), max_digits=12, decimal_places=2, default=Decimal("0.00")) total_equation = models.CharField(max_length=512, blank=True) started_on = models.DateField(blank=True, null=True) completed_on = models.DateField(blank=True, null=True) job = models.ForeignKey(Job, related_name="all_tasks", on_delete=models.CASCADE) group = models.ForeignKey(Group, related_name="tasks", on_delete=models.CASCADE) order_with_respect_to = "group" # GAEB Spec 2.7.5.2 variant_group = models.PositiveIntegerField(default=0) variant_serial = models.PositiveIntegerField(default=0) # GAEB Spec 2.7.5.3 is_provisional = models.BooleanField(default=False) APPROVED = "approved" READY = "ready" RUNNING = "running" DONE = "done" STATE_CHOICES = ( (APPROVED, _("Approved")), (READY, _("Ready")), (RUNNING, _("Running")), (DONE, _("Done")), ) status = FSMField(blank=True, choices=STATE_CHOICES) token = models.BigIntegerField("api token", null=True) attachments = GenericRelation("document.Attachment") objects = TaskManager() class Meta: verbose_name = _("Task") verbose_name_plural = _("Task") ordering = ("order", ) def __init__(self, *args, **kwargs): if "group" in kwargs and "job" not in kwargs: kwargs["job"] = kwargs["group"].job super().__init__(*args, **kwargs) def refresh_pks(self): self.group = self.group self.job = self.job @property def is_billable(self): return self.complete > 0 @property def is_variant(self): return self.variant_group > 0 @property def is_time_and_materials(self): return self.qty is None @property def include_estimate(self): return not self.is_provisional and self.variant_serial == 0 @property def lineitem_price(self): """ The task price as defined by lineitems. For most cases, use the Task.price instead of this. """ price = Decimal("0.00") for li in self.lineitems.all(): price += li.total return price @property def price_difference(self): """ Normally the Task.price == lineitem_price and thus diff is 0. In cases where the user has defined the total manually and then the lineitem_price (sum of lineitem totals) != Task.price this will return the amount of the discrepancy. """ return self.price - self.lineitem_price @property def progress(self): if self.is_time_and_materials: progress = Decimal("0.00") for li in self.lineitems.all(): progress += li.progress return progress else: return round(self.price * self.complete, 2) @property def complete_percent(self): if self.is_time_and_materials: expended = Decimal("0.00") qty = Decimal("0.00") for li in self.lineitems.all(): if li.qty is not None: qty += li.qty expended += li.expended return nice_percent(expended, qty) else: return nice_percent(self.complete, self.qty) @property def code(self): code = self.group._structure.format_task(self.order) return "{}.{}".format(self.group.code, code) @property def variant_allocation(self): return ("{}.{}".format(self.variant_group, self.variant_serial) if self.is_variant else "") def __str__(self): return self.name def clone_to(self, new_group, new_order): lineitems = self.lineitems.all() self.pk = None self.group = new_group self.job = new_group.job self.order = new_order self.complete = 0.0 self.started_on = None self.completed_on = None self.status = "" self.save() for lineitem in lineitems: lineitem.clone_to(self)
class Message(models.Model): text = models.TextField(null=True) search = tsvector_field.SearchVectorField([ tsvector_field.WeightedColumn('text', 'A'), ], 'simple') source = models.ForeignKey(Source, on_delete=models.CASCADE, related_name="messages") username = models.CharField(max_length=254, null=True, blank=True) created = models.DateTimeField(auto_created=True, auto_now_add=True, blank=True) # date of create record meta = JSONField(blank=True, null=True) date = models.DateTimeField(db_index=True) # date of create message duplicate = models.ForeignKey(DuplicateIndex, models.SET_NULL, null=True, related_name="messages") internal_id = models.BigIntegerField(null=True) shared = models.BooleanField(default=False) def _recount_dublicates(self): duplicates = self._get_duplicates() for dupe in duplicates: if dupe.duplicate is None: self.duplicate = DuplicateIndex.objects.create() else: self.duplicate = dupe.duplicate if dupe.duplicate is not None and self.duplicate_id != dupe.duplicate_id: dupe.duplicate.delete() dupe.duplicate = self.duplicate dupe.save() self.save() def _get_duplicates(self): # Find duplicates for new messages q = str(self.text) q = re.sub(r"(https?:[\S]+)|(<[^>]*>)", " ", q) # Remove urls and html tags q = re.sub(r"[^\w\s\-\.\/]+", " ", q) # Remove all specials symblos # Fulltext search Query duplicates = Message.objects.exclude(pk=self.pk).extra( where=[ "telegram_watcher_message.search @@ plainto_tsquery('simple', %s)" ], params=[q], ) duplicates_list = list( filter(lambda d: d.text == self.text, duplicates)) return duplicates_list def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if self.created: return super().save(force_insert, force_update, using, update_fields) duplicates = self._get_duplicates() for dupe in duplicates: if self.duplicate is None: self.duplicate = dupe.duplicate or DuplicateIndex.objects.create( ) if dupe.duplicate is not None and self.duplicate_id != dupe.duplicate_id: dupe.duplicate.delete() dupe.duplicate = self.duplicate dupe.save() return super().save(force_insert, force_update, using, update_fields) @classmethod def fulltext_search(cls, search: str, pre_queryset=None): """ :param search: query :param pre_queryset: queryset for filter :return: """ query = search queryset = cls.objects if pre_queryset is not None: queryset = pre_queryset res = queryset.extra( where=[ "telegram_watcher_message.search @@ to_tsquery('simple', %s)" ], params=[query], ) return res def __str__(self): return self.text or "None" class Meta: indexes = [ models.Index(fields=['-date']), # models.Index(fields=['source']), # BrinIndex(fields=['date', 'id', 'source']), ]
class Migration(migrations.Migration): initial = True dependencies = [ ("project", "0001_initial"), ("inventory", "0001_initial"), ("equipment", "0001_initial"), ("accounting", "0001_initial"), ("company", "0001_initial"), ] operations = [ RunInTemplate( migrations.CreateModel( name="ExpendReport", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("timestamp", models.DateTimeField(auto_now_add=True)), ("comment", models.TextField()), ( "expended", models.DecimalField( decimal_places=3, default=Decimal("0.00"), max_digits=13, verbose_name="Expended", ), ), ], options={ "verbose_name": "Expend Report", "verbose_name_plural": "Expend Reports", "ordering": ("-timestamp",), }, ) ), RunInTemplate( migrations.CreateModel( name="Group", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("order", models.PositiveIntegerField(db_index=True)), ( "name", models.CharField( blank=True, default="", max_length=512, verbose_name="Name" ), ), ( "description", models.TextField( blank=True, default="", verbose_name="Description" ), ), ( "depth", models.PositiveIntegerField(db_index=True, editable=False), ), ( "token", models.BigIntegerField(null=True, verbose_name="api token"), ), ( "search", tsvector_field.SearchVectorField( columns=[ tsvector_field.WeightedColumn("name", "A"), tsvector_field.WeightedColumn("description", "D"), ], language="german", ), ), ], options={ "verbose_name": "Group", "verbose_name_plural": "Groups", "ordering": ("order",), }, ) ), RunInTemplate( migrations.CreateModel( name="LineItem", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("order", models.PositiveIntegerField(db_index=True)), ( "name", models.CharField( blank=True, max_length=512, verbose_name="Name" ), ), ( "qty", models.DecimalField( decimal_places=3, default=Decimal("0.00"), max_digits=13, verbose_name="Quantity", ), ), ("qty_equation", models.CharField(blank=True, max_length=512)), ( "expended", models.DecimalField( decimal_places=3, default=Decimal("0.00"), max_digits=12, verbose_name="Expended", ), ), ( "unit", models.CharField( blank=True, max_length=512, verbose_name="Unit" ), ), ( "price", models.DecimalField( decimal_places=2, default=Decimal("0.00"), max_digits=12, verbose_name="Price", ), ), ("price_equation", models.CharField(blank=True, max_length=512)), ( "total", models.DecimalField( decimal_places=2, default=Decimal("0.00"), max_digits=12, verbose_name="Total", ), ), ("total_equation", models.CharField(blank=True, max_length=512)), ( "lineitem_type", models.CharField( choices=[ ("material", "Material"), ("labor", "Labor"), ("equipment", "Equipment"), ("other", "Other"), ], default="other", max_length=128, verbose_name="Line Item Type", ), ), ( "token", models.BigIntegerField(null=True, verbose_name="api token"), ), ("is_hidden", models.BooleanField(default=False)), ( "equipment", models.ForeignKey( null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="lineitems", to="equipment.EquipmentType", ), ), ( "labor", models.ForeignKey( null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="lineitems", to="company.LaborType", ), ), ( "material", models.ForeignKey( null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="lineitems", to="inventory.MaterialType", ), ), ], options={ "verbose_name": "Line Item", "verbose_name_plural": "Line Items", "ordering": ("order",), }, ) ), RunInTemplate( migrations.CreateModel( name="ProgressReport", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("timestamp", models.DateTimeField(auto_now_add=True)), ("comment", models.TextField()), ( "complete", models.DecimalField( decimal_places=3, default=Decimal("0.00"), max_digits=13, verbose_name="Complete", ), ), ], options={ "verbose_name": "Progress Report", "verbose_name_plural": "Progress Reports", "ordering": ("-timestamp",), }, ) ), RunInTemplate( migrations.CreateModel( name="Task", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("order", models.PositiveIntegerField(db_index=True)), ("name", models.CharField(max_length=512, verbose_name="Name")), ("description", models.TextField(blank=True)), ( "search", tsvector_field.SearchVectorField( columns=[ tsvector_field.WeightedColumn("name", "A"), tsvector_field.WeightedColumn("description", "D"), ], language="german", ), ), ( "qty", models.DecimalField( blank=True, decimal_places=3, default=Decimal("0.00"), max_digits=13, null=True, verbose_name="Quantity", ), ), ("qty_equation", models.CharField(blank=True, max_length=512)), ( "complete", models.DecimalField( decimal_places=3, default=Decimal("0.00"), max_digits=12, verbose_name="Completed", ), ), ( "unit", models.CharField( blank=True, max_length=512, verbose_name="Unit" ), ), ( "price", models.DecimalField( decimal_places=2, default=Decimal("0.00"), max_digits=12, verbose_name="Price", ), ), ("price_equation", models.CharField(blank=True, max_length=512)), ( "total", models.DecimalField( decimal_places=2, default=Decimal("0.00"), max_digits=12, verbose_name="Total", ), ), ("total_equation", models.CharField(blank=True, max_length=512)), ("started_on", models.DateField(blank=True, null=True)), ("completed_on", models.DateField(blank=True, null=True)), ("variant_group", models.PositiveIntegerField(default=0)), ("variant_serial", models.PositiveIntegerField(default=0)), ("is_provisional", models.BooleanField(default=False)), ( "status", django_fsm.FSMField( blank=True, choices=[ ("approved", "Approved"), ("ready", "Ready"), ("running", "Running"), ("done", "Done"), ], max_length=50, ), ), ( "token", models.BigIntegerField(null=True, verbose_name="api token"), ), ], options={ "verbose_name": "Task", "verbose_name_plural": "Task", "ordering": ("order",), }, ) ), RunInTemplate( migrations.CreateModel( name="Job", fields=[ ( "root", models.OneToOneField( on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name="+", serialize=False, to="task.Group", ), ), ("is_revenue_recognized", models.BooleanField(default=False)), ("is_locked", models.BooleanField(default=False)), ( "status", django_fsm.FSMField( choices=[ ("draft", "Draft"), ("proposed", "Proposed"), ("approved", "Approved"), ("started", "Started"), ("completed", "Completed"), ], default="draft", max_length=50, ), ), ( "account", models.OneToOneField( null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="job", to="accounting.Account", ), ), ( "project", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="jobs", to="project.Project", ), ), ], options={"verbose_name": "Job", "verbose_name_plural": "Job"}, bases=("task.group",), ) ), RunInTemplate( migrations.AddField( model_name="task", name="group", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="tasks", to="task.Group", ), ) ), RunInTemplate( migrations.AddField( model_name="progressreport", name="task", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="progressreports", to="task.Task", ), ) ), RunInTemplate( migrations.AddField( model_name="progressreport", name="worker", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="progressreports", to="company.Worker", ), ) ), RunInTemplate( migrations.AddField( model_name="lineitem", name="task", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="lineitems", to="task.Task", ), ) ), RunInTemplate( migrations.AddField( model_name="group", name="parent", field=models.ForeignKey( null=True, on_delete=django.db.models.deletion.CASCADE, related_name="groups", to="task.Group", ), ) ), RunInTemplate( migrations.AddField( model_name="expendreport", name="lineitem", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="expendreports", to="task.LineItem", ), ) ), RunInTemplate( migrations.AddField( model_name="expendreport", name="worker", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="expendreports", to="company.Worker", ), ) ), RunInTemplate( migrations.AddField( model_name="task", name="job", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="all_tasks", to="task.Job", ), ) ), RunInTemplate( migrations.AddField( model_name="lineitem", name="job", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="all_lineitems", to="task.Job", ), ) ), RunInTemplate( migrations.AddField( model_name="group", name="job", field=models.ForeignKey( null=True, on_delete=django.db.models.deletion.CASCADE, related_name="all_groups", to="task.Job", ), ) ), ]
class Migration(migrations.Migration): initial = True dependencies = [ ('multi_tenant', '0001_initial'), ] operations = [ migrations.CreateModel( name='Interest', fields=[ ('created_at', models.DateTimeField(auto_now_add=True)), ('modified_at', models.DateTimeField(auto_now=True)), ('uuid', django_smalluuid.models.SmallUUIDField( default=django_smalluuid.models.UUIDDefault(), editable=False, primary_key=True, serialize=False, unique=True)), ('search', tsvector_field.SearchVectorField(columns=[ tsvector_field.WeightedColumn('organization_name', 'A'), tsvector_field.WeightedColumn('first_name', 'A'), tsvector_field.WeightedColumn('last_name', 'A'), tsvector_field.WeightedColumn('email', 'A') ], language='english')), ('organization_name', models.TextField(null=True)), ('website', models.URLField(null=True)), ('first_name', models.TextField(null=True)), ('last_name', models.TextField(null=True)), ('email', models.EmailField(max_length=254, null=True)), ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None)), ('nonprofit', models.BooleanField(null=True)), ('ein', models.TextField(blank=True, null=True)), ('status', common.fields.TurnoutEnumField( default='pending', enum=common.enums.SubscriptionInterestStatus, null=True)), ], options={ 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='Product', fields=[ ('created_at', models.DateTimeField(auto_now_add=True)), ('modified_at', models.DateTimeField(auto_now=True)), ('uuid', django_smalluuid.models.SmallUUIDField( default=django_smalluuid.models.UUIDDefault(), editable=False, primary_key=True, serialize=False, unique=True)), ('months', models.PositiveIntegerField(blank=True, null=True)), ('cost', models.PositiveIntegerField(null=True)), ('public', models.BooleanField(default=True, null=True)), ], options={ 'ordering': ['public', 'months'], }, ), migrations.CreateModel( name='Subscription', fields=[ ('created_at', models.DateTimeField(auto_now_add=True)), ('modified_at', models.DateTimeField(auto_now=True)), ('uuid', django_smalluuid.models.SmallUUIDField( default=django_smalluuid.models.UUIDDefault(), editable=False, primary_key=True, serialize=False, unique=True)), ('expires', models.DateField(blank=True, db_index=True, null=True)), ('interest', models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='subscription.Interest')), ('product', models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='subscription.Product')), ('subscriber', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='multi_tenant.Client')), ], options={ 'abstract': False, }, ), migrations.AddField( model_name='interest', name='product', field=models.ForeignKey( null=True, on_delete=django.db.models.deletion.SET_NULL, to='subscription.Product'), ), ]