Skip to content
This repository was archived by the owner on Mar 21, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion credits/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ class Profile(models.Model):
"""
For testing, track the number of "credits".
"""
user = models.OneToOneField('auth.User', related_name='profile')
user = models.OneToOneField(
'auth.User',
related_name='profile',
on_delete=models.CASCADE,
)
credits = models.PositiveIntegerField(default=0)


Expand Down
6 changes: 3 additions & 3 deletions drip/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ def change_view(self, request, object_id, extra_context=None):
request, object_id, extra_context=self.build_extra_context(extra_context))

def get_urls(self):
from django.conf.urls import patterns, url
from django.conf.urls import url
urls = super(DripAdmin, self).get_urls()
my_urls = patterns('',
my_urls = [
url(
r'^(?P<drip_id>[\d]+)/timeline/(?P<into_past>[\d]+)/(?P<into_future>[\d]+)/$',
self.av(self.timeline),
Expand All @@ -100,7 +100,7 @@ def get_urls(self):
self.av(self.view_drip_email),
name='view_drip_email'
)
)
]
return my_urls + urls
admin.site.register(Drip, DripAdmin)

Expand Down
12 changes: 5 additions & 7 deletions drip/drips.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.conf import settings
from django.db.models import Q
from django.template import Context, Template
from django.utils.importlib import import_module
from importlib import import_module
from django.core.mail import EmailMultiAlternatives
from django.utils.html import strip_tags

Expand Down Expand Up @@ -184,12 +184,10 @@ def apply_queryset_rules(self, qs):
##################

def get_queryset(self):
try:
return self._queryset
except AttributeError:
self._queryset = self.apply_queryset_rules(self.queryset())\
.distinct()
return self._queryset
queryset = getattr(self,'_queryset', None)
if queryset is None:
self._queryset = self.apply_queryset_rules(self.queryset()).distinct()
return self._queryset

def run(self):
"""
Expand Down
145 changes: 145 additions & 0 deletions drip/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import re
import datetime
from decimal import Decimal

STRFDATETIME = re.compile('([dgGhHis])')
STRFDATETIME_REPL = lambda x: '%%(%s)s' % x.group()

def parse(string):
"""
Parse a string into a timedelta object.
>>> parse("1 day")
datetime.timedelta(1)
>>> parse("2 days")
datetime.timedelta(2)
>>> parse("1 d")
datetime.timedelta(1)
>>> parse("1 hour")
datetime.timedelta(0, 3600)
>>> parse("1 hours")
datetime.timedelta(0, 3600)
>>> parse("1 hr")
datetime.timedelta(0, 3600)
>>> parse("1 hrs")
datetime.timedelta(0, 3600)
>>> parse("1h")
datetime.timedelta(0, 3600)
>>> parse("1wk")
datetime.timedelta(7)
>>> parse("1 week")
datetime.timedelta(7)
>>> parse("1 weeks")
datetime.timedelta(7)
>>> parse("2 wks")
datetime.timedelta(14)
>>> parse("1 sec")
datetime.timedelta(0, 1)
>>> parse("1 secs")
datetime.timedelta(0, 1)
>>> parse("1 s")
datetime.timedelta(0, 1)
>>> parse("1 second")
datetime.timedelta(0, 1)
>>> parse("1 seconds")
datetime.timedelta(0, 1)
>>> parse("1 minute")
datetime.timedelta(0, 60)
>>> parse("1 min")
datetime.timedelta(0, 60)
>>> parse("1 m")
datetime.timedelta(0, 60)
>>> parse("1 minutes")
datetime.timedelta(0, 60)
>>> parse("1 mins")
datetime.timedelta(0, 60)
>>> parse("2 ws")
Traceback (most recent call last):
...
TypeError: '2 ws' is not a valid time interval
>>> parse("2 ds")
Traceback (most recent call last):
...
TypeError: '2 ds' is not a valid time interval
>>> parse("2 hs")
Traceback (most recent call last):
...
TypeError: '2 hs' is not a valid time interval
>>> parse("2 ms")
Traceback (most recent call last):
...
TypeError: '2 ms' is not a valid time interval
>>> parse("2 ss")
Traceback (most recent call last):
...
TypeError: '2 ss' is not a valid time interval
>>> parse("")
Traceback (most recent call last):
...
TypeError: '' is not a valid time interval
>>> parse("1.5 days")
datetime.timedelta(1, 43200)
>>> parse("3 weeks")
datetime.timedelta(21)
>>> parse("4.2 hours")
datetime.timedelta(0, 15120)
>>> parse(".5 hours")
datetime.timedelta(0, 1800)
>>> parse(" hours")
Traceback (most recent call last):
...
TypeError: 'hours' is not a valid time interval
>>> parse("1 hour, 5 mins")
datetime.timedelta(0, 3900)
>>> parse("-2 days")
datetime.timedelta(-2)
>>> parse("-1 day 0:00:01")
datetime.timedelta(-1, 1)
>>> parse("-1 day, -1:01:01")
datetime.timedelta(-2, 82739)
>>> parse("-1 weeks, 2 days, -3 hours, 4 minutes, -5 seconds")
datetime.timedelta(-5, 11045)
>>> parse("0 seconds")
datetime.timedelta(0)
>>> parse("0 days")
datetime.timedelta(0)
>>> parse("0 weeks")
datetime.timedelta(0)
>>> zero = datetime.timedelta(0)
>>> parse(nice_repr(zero))
datetime.timedelta(0)
>>> parse(nice_repr(zero, 'minimal'))
datetime.timedelta(0)
>>> parse(nice_repr(zero, 'short'))
datetime.timedelta(0)
>>> parse(' 50 days 00:00:00 ')
datetime.timedelta(50)
"""
string = string.strip()

if string == "":
raise TypeError("'%s' is not a valid time interval" % string)
# This is the format we get from sometimes Postgres, sqlite,
# and from serialization
d = re.match(r'^((?P<days>[-+]?\d+) days?,? )?(?P<sign>[-+]?)(?P<hours>\d+):'
r'(?P<minutes>\d+)(:(?P<seconds>\d+(\.\d+)?))?$',
str(string))
if d:
d = d.groupdict(0)
if d['sign'] == '-':
for k in 'hours', 'minutes', 'seconds':
d[k] = '-' + d[k]
d.pop('sign', None)
else:
# This is the more flexible format
d = re.match(
r'^((?P<weeks>-?((\d*\.\d+)|\d+))\W*w((ee)?(k(s)?)?)(,)?\W*)?'
r'((?P<days>-?((\d*\.\d+)|\d+))\W*d(ay(s)?)?(,)?\W*)?'
r'((?P<hours>-?((\d*\.\d+)|\d+))\W*h(ou)?(r(s)?)?(,)?\W*)?'
r'((?P<minutes>-?((\d*\.\d+)|\d+))\W*m(in(ute)?(s)?)?(,)?\W*)?'
r'((?P<seconds>-?((\d*\.\d+)|\d+))\W*s(ec(ond)?(s)?)?)?\W*$',
string)
if not d:
raise TypeError("'%s' is not a valid time interval" % string)
d = d.groupdict(0)

return datetime.timedelta(**dict(( (k, float(v)) for k,v in d.items())))
186 changes: 58 additions & 128 deletions drip/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,128 +1,58 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models


class Migration(SchemaMigration):

def forwards(self, orm):
# Adding model 'Drip'
db.create_table('drip_drip', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('lastchanged', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
('subject_template', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
('body_html_template', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
))
db.send_create_signal('drip', ['Drip'])

# Adding model 'SentDrip'
db.create_table('drip_sentdrip', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('drip', self.gf('django.db.models.fields.related.ForeignKey')(related_name='sent_drips', to=orm['drip.Drip'])),
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='sent_drips', to=orm['auth.User'])),
('subject', self.gf('django.db.models.fields.TextField')()),
('body', self.gf('django.db.models.fields.TextField')()),
))
db.send_create_signal('drip', ['SentDrip'])

# Adding model 'QuerySetRule'
db.create_table('drip_querysetrule', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('lastchanged', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
('drip', self.gf('django.db.models.fields.related.ForeignKey')(related_name='queryset_rules', to=orm['drip.Drip'])),
('method_type', self.gf('django.db.models.fields.CharField')(default='filter', max_length=12)),
('field_name', self.gf('django.db.models.fields.CharField')(max_length=128)),
('lookup_type', self.gf('django.db.models.fields.CharField')(default='exact', max_length=12)),
('field_value', self.gf('django.db.models.fields.CharField')(max_length=255)),
))
db.send_create_signal('drip', ['QuerySetRule'])


def backwards(self, orm):
# Deleting model 'Drip'
db.delete_table('drip_drip')

# Deleting model 'SentDrip'
db.delete_table('drip_sentdrip')

# Deleting model 'QuerySetRule'
db.delete_table('drip_querysetrule')


models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'drip.drip': {
'Meta': {'object_name': 'Drip'},
'body_html_template': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lastchanged': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'subject_template': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
},
'drip.querysetrule': {
'Meta': {'object_name': 'QuerySetRule'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'drip': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'queryset_rules'", 'to': "orm['drip.Drip']"}),
'field_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'field_value': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lastchanged': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'lookup_type': ('django.db.models.fields.CharField', [], {'default': "'exact'", 'max_length': '12'}),
'method_type': ('django.db.models.fields.CharField', [], {'default': "'filter'", 'max_length': '12'})
},
'drip.sentdrip': {
'Meta': {'object_name': 'SentDrip'},
'body': ('django.db.models.fields.TextField', [], {}),
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'drip': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sent_drips'", 'to': "orm['drip.Drip']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'subject': ('django.db.models.fields.TextField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sent_drips'", 'to': "orm['auth.User']"})
}
}

complete_apps = ['drip']
# Generated by Django 3.0.6 on 2020-05-29 20:38

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Drip',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateTimeField(auto_now_add=True)),
('lastchanged', models.DateTimeField(auto_now=True)),
('name', models.CharField(help_text='A unique name for this drip.', max_length=255, unique=True, verbose_name='Drip Name')),
('enabled', models.BooleanField(default=False)),
('from_email', models.EmailField(blank=True, help_text='Set a custom from email.', max_length=254, null=True)),
('from_email_name', models.CharField(blank=True, help_text='Set a name for a custom from email.', max_length=150, null=True)),
('subject_template', models.TextField(blank=True, null=True)),
('body_html_template', models.TextField(blank=True, help_text='You will have settings and user in the context.', null=True)),
('message_class', models.CharField(blank=True, default='default', max_length=120)),
],
),
migrations.CreateModel(
name='SentDrip',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateTimeField(auto_now_add=True)),
('subject', models.TextField()),
('body', models.TextField()),
('from_email', models.EmailField(default=None, max_length=254, null=True)),
('from_email_name', models.CharField(default=None, max_length=150, null=True)),
('drip', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sent_drips', to='drip.Drip')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sent_drips', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='QuerySetRule',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateTimeField(auto_now_add=True)),
('lastchanged', models.DateTimeField(auto_now=True)),
('method_type', models.CharField(choices=[('filter', 'Filter'), ('exclude', 'Exclude')], default='filter', max_length=12)),
('field_name', models.CharField(max_length=128, verbose_name='Field name of User')),
('lookup_type', models.CharField(choices=[('exact', 'exactly'), ('iexact', 'exactly (case insensitive)'), ('contains', 'contains'), ('icontains', 'contains (case insensitive)'), ('regex', 'regex'), ('iregex', 'contains (case insensitive)'), ('gt', 'greater than'), ('gte', 'greater than or equal to'), ('lt', 'less than'), ('lte', 'less than or equal to'), ('startswith', 'starts with'), ('endswith', 'starts with'), ('istartswith', 'ends with (case insensitive)'), ('iendswith', 'ends with (case insensitive)')], default='exact', max_length=12)),
('field_value', models.CharField(help_text='Can be anything from a number, to a string. Or, do `now-7 days` or `today+3 days` for fancy timedelta.', max_length=255)),
('drip', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='queryset_rules', to='drip.Drip')),
],
),
]
Loading