From a24d386430c059924be2ce8e3fba14ae94bbfb8a Mon Sep 17 00:00:00 2001 From: KingCSharp Date: Wed, 22 Jul 2020 00:24:18 +0530 Subject: [PATCH] SaaS SaaS beta --- accounts/apps.py | 2 +- accounts/migrations/0001_initial.py | 89 +- .../migrations/0002_auto_20190128_1237.py | 52 +- .../migrations/0003_auto_20190201_1840.py | 24 +- accounts/migrations/0004_account_status.py | 14 +- .../migrations/0005_auto_20190212_1334.py | 46 +- .../migrations/0006_auto_20190212_1708.py | 322 ++++- accounts/migrations/0007_email.py | 42 +- .../migrations/0008_account_assigned_to.py | 10 +- .../migrations/0009_auto_20190809_1659.py | 81 +- accounts/migrations/0010_account_teams.py | 12 +- accounts/migrations/0011_account_company.py | 15 +- accounts/tasks.py | 74 +- accounts/tests_celery_tasks.py | 33 +- accounts/urls.py | 68 +- cases/apps.py | 2 +- cases/migrations/0001_initial.py | 84 +- cases/migrations/0002_auto_20190128_1237.py | 36 +- cases/migrations/0003_auto_20190212_1334.py | 18 +- cases/migrations/0004_case_teams.py | 10 +- cases/migrations/0005_case_company.py | 15 +- cases/tasks.py | 31 +- cases/tests_celery_tasks.py | 14 +- cases/urls.py | 59 +- common/admin.py | 1 + common/apps.py | 2 +- common/middleware/get_company.py | 10 +- common/migrations/0001_initial.py | 501 ++++++- common/migrations/0002_auto_20190128_1237.py | 132 +- common/migrations/0003_document.py | 34 +- common/migrations/0004_attachments_case.py | 16 +- common/migrations/0005_auto_20190204_1400.py | 19 +- common/migrations/0006_comment_user.py | 14 +- common/migrations/0007_auto_20190212_1334.py | 45 +- common/migrations/0008_google.py | 41 +- common/migrations/0009_document_shared_to.py | 10 +- common/migrations/0010_apisettings.py | 46 +- common/migrations/0011_auto_20190218_1230.py | 6 +- common/migrations/0012_apisettings_website.py | 8 +- common/migrations/0013_auto_20190508_1540.py | 28 +- common/migrations/0014_auto_20190524_1113.py | 28 +- common/migrations/0015_auto_20190604_1830.py | 28 +- common/migrations/0016_auto_20190624_1816.py | 22 +- common/migrations/0017_auto_20190722_1443.py | 33 +- common/migrations/0018_document_teams.py | 12 +- common/migrations/0019_auto_20200401_0941.py | 289 +++- common/migrations/0020_auto_20200409_1653.py | 10 +- common/migrations/0021_document_company.py | 13 +- common/migrations/0022_auto_20200609_1203.py | 13 +- common/models.py | 4 +- common/tasks.py | 247 ++-- common/templatetags/common_tags.py | 653 +++++++-- common/token_generator.py | 6 +- common/utils.py | 989 +++++++------ common/views.py | 108 +- contacts/apps.py | 2 +- contacts/forms.py | 34 +- contacts/migrations/0001_initial.py | 86 +- .../migrations/0002_auto_20190210_1810.py | 7 +- .../migrations/0002_auto_20190212_1334.py | 26 +- .../migrations/0003_merge_20190214_1427.py | 7 +- contacts/migrations/0004_contact_teams.py | 12 +- contacts/migrations/0005_contact_company.py | 15 +- contacts/tasks.py | 31 +- contacts/tests_celery_tasks.py | 14 +- contacts/urls.py | 55 +- crm/__init__.py | 2 +- crm/celery.py | 6 +- crm/helper.py | 24 +- crm/settings.py | 13 +- docs/source/conf.py | 145 +- emails/admin.py | 1 + emails/apps.py | 2 +- emails/forms.py | 2 +- emails/migrations/0001_initial.py | 35 +- emails/models.py | 3 +- emails/urls.py | 50 +- emails/views.py | 128 +- events/apps.py | 2 +- events/migrations/0001_initial.py | 91 +- .../migrations/0002_event_date_of_meeting.py | 6 +- events/migrations/0003_event_teams.py | 10 +- events/migrations/0004_event_company.py | 15 +- events/tasks.py | 66 +- events/tests_celery_tasks.py | 11 +- events/urls.py | 29 +- invoices/apps.py | 2 +- invoices/migrations/0001_initial.py | 292 +++- .../migrations/0002_auto_20190524_1113.py | 18 +- .../migrations/0003_auto_20190527_1620.py | 12 +- .../migrations/0004_auto_20190603_1844.py | 18 +- invoices/migrations/0005_invoicehistory.py | 306 +++- invoices/migrations/0006_invoice_account.py | 12 +- .../migrations/0007_auto_20190909_1621.py | 5 +- invoices/migrations/0008_invoice_teams.py | 12 +- invoices/migrations/0009_invoice_company.py | 15 +- invoices/models.py | 1 - invoices/tasks.py | 111 +- invoices/tests_celery_tasks.py | 18 +- invoices/urls.py | 46 +- leads/apps.py | 2 +- leads/migrations/0001_initial.py | 159 ++- leads/migrations/0002_lead_tags.py | 10 +- leads/migrations/0003_auto_20190211_1142.py | 6 +- leads/migrations/0004_auto_20190212_1334.py | 33 +- leads/migrations/0005_auto_20190212_1708.py | 305 +++- leads/migrations/0006_auto_20190218_1217.py | 20 +- leads/migrations/0007_auto_20190306_1226.py | 20 +- leads/migrations/0008_auto_20190315_1503.py | 24 +- .../migrations/0009_lead_created_from_site.py | 6 +- leads/migrations/0010_lead_teams.py | 10 +- leads/migrations/0011_auto_20200401_0937.py | 10 +- leads/migrations/0012_lead_company.py | 15 +- leads/signals.py | 2 +- leads/tasks.py | 100 +- leads/tests_celery_tasks.py | 118 +- leads/urls.py | 74 +- manage.py | 2 +- manage_local.py | 2 +- marketing/apps.py | 2 +- marketing/forms.py | 277 ++-- marketing/migrations/0001_initial.py | 416 ++++-- .../migrations/0002_auto_20190307_1227.py | 26 +- marketing/migrations/0003_failedcontact.py | 65 +- .../migrations/0004_auto_20190315_1443.py | 34 +- .../migrations/0005_campaign_timezone.py | 8 +- .../migrations/0006_campaign_attachment.py | 13 +- .../migrations/0007_auto_20190611_1226.py | 37 +- .../migrations/0008_auto_20190612_1905.py | 44 +- .../migrations/0009_auto_20190618_1144.py | 39 +- .../migrations/0010_auto_20190805_1038.py | 60 +- .../migrations/0011_auto_20190904_1143.py | 37 +- .../migrations/0012_auto_20190909_1621.py | 5 +- .../0013_blockeddomain_blockedemail.py | 62 +- marketing/models.py | 228 +-- marketing/search_backends.py | 8 +- marketing/search_indexes.py | 72 +- marketing/tasks.py | 271 ++-- marketing/templatetags/digg_paginator.py | 71 +- marketing/tests_celery_tasks.py | 102 +- marketing/urls.py | 232 ++- marketing/views.py | 1261 ++++++++++------- opportunity/apps.py | 2 +- opportunity/migrations/0001_initial.py | 297 +++- .../migrations/0002_opportunity_tags.py | 10 +- .../migrations/0003_auto_20190212_1334.py | 28 +- .../migrations/0004_opportunity_teams.py | 12 +- .../migrations/0005_opportunity_company.py | 15 +- opportunity/tasks.py | 31 +- opportunity/tests_celery_tasks.py | 14 +- planner/admin.py | 1 + planner/apps.py | 2 +- planner/forms.py | 42 +- planner/migrations/0001_initial.py | 198 ++- planner/migrations/0002_auto_20190212_1334.py | 30 +- planner/models.py | 89 +- setup.py | 48 +- tasks/admin.py | 3 +- tasks/apps.py | 2 +- tasks/celery_tasks.py | 34 +- tasks/migrations/0001_initial.py | 75 +- tasks/migrations/0002_task_created_by.py | 14 +- tasks/migrations/0003_task_created_on.py | 12 +- tasks/migrations/0004_task_teams.py | 10 +- tasks/migrations/0005_task_company.py | 15 +- tasks/tests_celery_tasks.py | 11 +- tasks/urls.py | 27 +- tasks/utils.py | 8 +- teams/apps.py | 2 +- teams/migrations/0001_initial.py | 23 +- teams/migrations/0002_auto_20190624_1250.py | 24 +- teams/migrations/0003_auto_20190909_1621.py | 7 +- teams/migrations/0004_teams_company.py | 15 +- teams/urls.py | 17 +- 174 files changed, 8136 insertions(+), 3589 deletions(-) diff --git a/accounts/apps.py b/accounts/apps.py index 9b3fc5a..fb0257e 100644 --- a/accounts/apps.py +++ b/accounts/apps.py @@ -2,4 +2,4 @@ class AccountsConfig(AppConfig): - name = 'accounts' + name = "accounts" diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py index 931224e..4907b3d 100644 --- a/accounts/migrations/0001_initial.py +++ b/accounts/migrations/0001_initial.py @@ -8,25 +8,84 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Account', + name="Account", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=64, verbose_name='Name')), - ('email', models.EmailField(max_length=254)), - ('phone', phonenumber_field.modelfields.PhoneNumberField(max_length=128, null=True)), - ('industry', models.CharField(blank=True, choices=[('ADVERTISING', 'ADVERTISING'), ('AGRICULTURE', 'AGRICULTURE'), ('APPAREL & ACCESSORIES', 'APPAREL & ACCESSORIES'), ('AUTOMOTIVE', 'AUTOMOTIVE'), ('BANKING', 'BANKING'), ('BIOTECHNOLOGY', 'BIOTECHNOLOGY'), ('BUILDING MATERIALS & EQUIPMENT', 'BUILDING MATERIALS & EQUIPMENT'), ('CHEMICAL', 'CHEMICAL'), ('COMPUTER', 'COMPUTER'), ('EDUCATION', 'EDUCATION'), ('ELECTRONICS', 'ELECTRONICS'), ('ENERGY', 'ENERGY'), ('ENTERTAINMENT & LEISURE', 'ENTERTAINMENT & LEISURE'), ('FINANCE', 'FINANCE'), ('FOOD & BEVERAGE', 'FOOD & BEVERAGE'), ('GROCERY', 'GROCERY'), ('HEALTHCARE', 'HEALTHCARE'), ('INSURANCE', 'INSURANCE'), ('LEGAL', 'LEGAL'), ('MANUFACTURING', 'MANUFACTURING'), ('PUBLISHING', 'PUBLISHING'), ('REAL ESTATE', 'REAL ESTATE'), ('SERVICE', 'SERVICE'), ('SOFTWARE', 'SOFTWARE'), ('SPORTS', 'SPORTS'), ('TECHNOLOGY', 'TECHNOLOGY'), ('TELECOMMUNICATIONS', 'TELECOMMUNICATIONS'), ('TELEVISION', 'TELEVISION'), ('TRANSPORTATION', 'TRANSPORTATION'), ('VENTURE CAPITAL', 'VENTURE CAPITAL')], max_length=255, null=True, verbose_name='Industry Type')), - ('website', models.URLField(blank=True, null=True, verbose_name='Website')), - ('description', models.TextField(blank=True, null=True)), - ('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Created on')), - ('is_active', models.BooleanField(default=False)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=64, verbose_name="Name")), + ("email", models.EmailField(max_length=254)), + ( + "phone", + phonenumber_field.modelfields.PhoneNumberField( + max_length=128, null=True + ), + ), + ( + "industry", + models.CharField( + blank=True, + choices=[ + ("ADVERTISING", "ADVERTISING"), + ("AGRICULTURE", "AGRICULTURE"), + ("APPAREL & ACCESSORIES", "APPAREL & ACCESSORIES"), + ("AUTOMOTIVE", "AUTOMOTIVE"), + ("BANKING", "BANKING"), + ("BIOTECHNOLOGY", "BIOTECHNOLOGY"), + ( + "BUILDING MATERIALS & EQUIPMENT", + "BUILDING MATERIALS & EQUIPMENT", + ), + ("CHEMICAL", "CHEMICAL"), + ("COMPUTER", "COMPUTER"), + ("EDUCATION", "EDUCATION"), + ("ELECTRONICS", "ELECTRONICS"), + ("ENERGY", "ENERGY"), + ("ENTERTAINMENT & LEISURE", "ENTERTAINMENT & LEISURE"), + ("FINANCE", "FINANCE"), + ("FOOD & BEVERAGE", "FOOD & BEVERAGE"), + ("GROCERY", "GROCERY"), + ("HEALTHCARE", "HEALTHCARE"), + ("INSURANCE", "INSURANCE"), + ("LEGAL", "LEGAL"), + ("MANUFACTURING", "MANUFACTURING"), + ("PUBLISHING", "PUBLISHING"), + ("REAL ESTATE", "REAL ESTATE"), + ("SERVICE", "SERVICE"), + ("SOFTWARE", "SOFTWARE"), + ("SPORTS", "SPORTS"), + ("TECHNOLOGY", "TECHNOLOGY"), + ("TELECOMMUNICATIONS", "TELECOMMUNICATIONS"), + ("TELEVISION", "TELEVISION"), + ("TRANSPORTATION", "TRANSPORTATION"), + ("VENTURE CAPITAL", "VENTURE CAPITAL"), + ], + max_length=255, + null=True, + verbose_name="Industry Type", + ), + ), + ( + "website", + models.URLField(blank=True, null=True, verbose_name="Website"), + ), + ("description", models.TextField(blank=True, null=True)), + ( + "created_on", + models.DateTimeField(auto_now_add=True, verbose_name="Created on"), + ), + ("is_active", models.BooleanField(default=False)), ], - options={ - 'ordering': ['-created_on'], - }, + options={"ordering": ["-created_on"],}, ), ] diff --git a/accounts/migrations/0002_auto_20190128_1237.py b/accounts/migrations/0002_auto_20190128_1237.py index 520055e..fe09456 100644 --- a/accounts/migrations/0002_auto_20190128_1237.py +++ b/accounts/migrations/0002_auto_20190128_1237.py @@ -10,35 +10,53 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('accounts', '0001_initial'), - ('common', '0001_initial'), + ("accounts", "0001_initial"), + ("common", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.AddField( - model_name='account', - name='assigned_to', - field=models.ManyToManyField(related_name='account_assigned_to', to=settings.AUTH_USER_MODEL), + model_name="account", + name="assigned_to", + field=models.ManyToManyField( + related_name="account_assigned_to", to=settings.AUTH_USER_MODEL + ), ), migrations.AddField( - model_name='account', - name='billing_address', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='account_billing_address', to='common.Address'), + model_name="account", + name="billing_address", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="account_billing_address", + to="common.Address", + ), ), migrations.AddField( - model_name='account', - name='created_by', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='account_created_by', to=settings.AUTH_USER_MODEL), + model_name="account", + name="created_by", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="account_created_by", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='account', - name='shipping_address', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='account_shipping_address', to='common.Address'), + model_name="account", + name="shipping_address", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="account_shipping_address", + to="common.Address", + ), ), migrations.AddField( - model_name='account', - name='teams', - field=models.ManyToManyField(to='common.Team'), + model_name="account", + name="teams", + field=models.ManyToManyField(to="common.Team"), ), ] diff --git a/accounts/migrations/0003_auto_20190201_1840.py b/accounts/migrations/0003_auto_20190201_1840.py index 7add3ac..40b9538 100644 --- a/accounts/migrations/0003_auto_20190201_1840.py +++ b/accounts/migrations/0003_auto_20190201_1840.py @@ -6,21 +6,29 @@ class Migration(migrations.Migration): dependencies = [ - ('accounts', '0002_auto_20190128_1237'), + ("accounts", "0002_auto_20190128_1237"), ] operations = [ migrations.CreateModel( - name='Tags', + name="Tags", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=20)), - ('slug', models.CharField(blank=True, max_length=20, unique=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=20)), + ("slug", models.CharField(blank=True, max_length=20, unique=True)), ], ), migrations.AddField( - model_name='account', - name='tags', - field=models.ManyToManyField(blank=True, to='accounts.Tags'), + model_name="account", + name="tags", + field=models.ManyToManyField(blank=True, to="accounts.Tags"), ), ] diff --git a/accounts/migrations/0004_account_status.py b/accounts/migrations/0004_account_status.py index 0c61103..c356204 100644 --- a/accounts/migrations/0004_account_status.py +++ b/accounts/migrations/0004_account_status.py @@ -6,21 +6,25 @@ def generate_status(apps, schema_editor): Account = apps.get_model("accounts", "Account") for account in Account.objects.all(): - account.status = 'open' + account.status = "open" account.save() class Migration(migrations.Migration): dependencies = [ - ('accounts', '0003_auto_20190201_1840'), + ("accounts", "0003_auto_20190201_1840"), ] operations = [ migrations.AddField( - model_name='account', - name='status', - field=models.CharField(choices=[('open', 'Open'), ('close', 'Close')], default='open', max_length=64), + model_name="account", + name="status", + field=models.CharField( + choices=[("open", "Open"), ("close", "Close")], + default="open", + max_length=64, + ), ), migrations.RunPython(generate_status), ] diff --git a/accounts/migrations/0005_auto_20190212_1334.py b/accounts/migrations/0005_auto_20190212_1334.py index b8ded1f..c7223ed 100644 --- a/accounts/migrations/0005_auto_20190212_1334.py +++ b/accounts/migrations/0005_auto_20190212_1334.py @@ -8,33 +8,39 @@ class Migration(migrations.Migration): dependencies = [ - ('contacts', '0002_auto_20190212_1334'), - ('leads', '0004_auto_20190212_1334'), - ('accounts', '0004_account_status'), + ("contacts", "0002_auto_20190212_1334"), + ("leads", "0004_auto_20190212_1334"), + ("accounts", "0004_account_status"), ] operations = [ - migrations.RemoveField( - model_name='account', - name='assigned_to', - ), - migrations.RemoveField( - model_name='account', - name='teams', - ), + migrations.RemoveField(model_name="account", name="assigned_to",), + migrations.RemoveField(model_name="account", name="teams",), migrations.AddField( - model_name='account', - name='contacts', - field=models.ManyToManyField(related_name='account_contacts', to='contacts.Contact'), + model_name="account", + name="contacts", + field=models.ManyToManyField( + related_name="account_contacts", to="contacts.Contact" + ), ), migrations.AddField( - model_name='account', - name='leads', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='account_leads', to='leads.Lead'), + model_name="account", + name="leads", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="account_leads", + to="leads.Lead", + ), ), migrations.AlterField( - model_name='account', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='account_created_by', to=settings.AUTH_USER_MODEL), + model_name="account", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="account_created_by", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/accounts/migrations/0006_auto_20190212_1708.py b/accounts/migrations/0006_auto_20190212_1708.py index 7f48687..3812e88 100644 --- a/accounts/migrations/0006_auto_20190212_1708.py +++ b/accounts/migrations/0006_auto_20190212_1708.py @@ -6,57 +6,313 @@ class Migration(migrations.Migration): dependencies = [ - ('accounts', '0005_auto_20190212_1334'), + ("accounts", "0005_auto_20190212_1334"), ] operations = [ migrations.RenameField( - model_name='account', - old_name='leads', - new_name='lead', - ), - migrations.RemoveField( - model_name='account', - name='billing_address', - ), - migrations.RemoveField( - model_name='account', - name='shipping_address', + model_name="account", old_name="leads", new_name="lead", ), + migrations.RemoveField(model_name="account", name="billing_address",), + migrations.RemoveField(model_name="account", name="shipping_address",), migrations.AddField( - model_name='account', - name='billing_address_line', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Address'), + model_name="account", + name="billing_address_line", + field=models.CharField( + blank=True, max_length=255, null=True, verbose_name="Address" + ), ), migrations.AddField( - model_name='account', - name='billing_city', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='City'), + model_name="account", + name="billing_city", + field=models.CharField( + blank=True, max_length=255, null=True, verbose_name="City" + ), ), migrations.AddField( - model_name='account', - name='billing_country', - field=models.CharField(blank=True, choices=[('GB', 'United Kingdom'), ('AF', 'Afghanistan'), ('AX', 'Aland Islands'), ('AL', 'Albania'), ('DZ', 'Algeria'), ('AS', 'American Samoa'), ('AD', 'Andorra'), ('AO', 'Angola'), ('AI', 'Anguilla'), ('AQ', 'Antarctica'), ('AG', 'Antigua and Barbuda'), ('AR', 'Argentina'), ('AM', 'Armenia'), ('AW', 'Aruba'), ('AU', 'Australia'), ('AT', 'Austria'), ('AZ', 'Azerbaijan'), ('BS', 'Bahamas'), ('BH', 'Bahrain'), ('BD', 'Bangladesh'), ('BB', 'Barbados'), ('BY', 'Belarus'), ('BE', 'Belgium'), ('BZ', 'Belize'), ('BJ', 'Benin'), ('BM', 'Bermuda'), ('BT', 'Bhutan'), ('BO', 'Bolivia'), ('BA', 'Bosnia and Herzegovina'), ('BW', 'Botswana'), ('BV', 'Bouvet Island'), ('BR', 'Brazil'), ('IO', 'British Indian Ocean Territory'), ('BN', 'Brunei Darussalam'), ('BG', 'Bulgaria'), ('BF', 'Burkina Faso'), ('BI', 'Burundi'), ('KH', 'Cambodia'), ('CM', 'Cameroon'), ('CA', 'Canada'), ('CV', 'Cape Verde'), ('KY', 'Cayman Islands'), ('CF', 'Central African Republic'), ('TD', 'Chad'), ('CL', 'Chile'), ('CN', 'China'), ('CX', 'Christmas Island'), ('CC', 'Cocos (Keeling) Islands'), ('CO', 'Colombia'), ('KM', 'Comoros'), ('CG', 'Congo'), ('CD', 'Congo, The Democratic Republic of the'), ('CK', 'Cook Islands'), ('CR', 'Costa Rica'), ('CI', "Cote d'Ivoire"), ('HR', 'Croatia'), ('CU', 'Cuba'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DK', 'Denmark'), ('DJ', 'Djibouti'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('EC', 'Ecuador'), ('EG', 'Egypt'), ('SV', 'El Salvador'), ('GQ', 'Equatorial Guinea'), ('ER', 'Eritrea'), ('EE', 'Estonia'), ('ET', 'Ethiopia'), ('FK', 'Falkland Islands (Malvinas)'), ('FO', 'Faroe Islands'), ('FJ', 'Fiji'), ('FI', 'Finland'), ('FR', 'France'), ('GF', 'French Guiana'), ('PF', 'French Polynesia'), ('TF', 'French Southern Territories'), ('GA', 'Gabon'), ('GM', 'Gambia'), ('GE', 'Georgia'), ('DE', 'Germany'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GR', 'Greece'), ('GL', 'Greenland'), ('GD', 'Grenada'), ('GP', 'Guadeloupe'), ('GU', 'Guam'), ('GT', 'Guatemala'), ('GG', 'Guernsey'), ('GN', 'Guinea'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HT', 'Haiti'), ('HM', 'Heard Island and McDonald Islands'), ('VA', 'Holy See (Vatican City State)'), ('HN', 'Honduras'), ('HK', 'Hong Kong'), ('HU', 'Hungary'), ('IS', 'Iceland'), ('IN', 'India'), ('ID', 'Indonesia'), ('IR', 'Iran, Islamic Republic of'), ('IQ', 'Iraq'), ('IE', 'Ireland'), ('IM', 'Isle of Man'), ('IL', 'Israel'), ('IT', 'Italy'), ('JM', 'Jamaica'), ('JP', 'Japan'), ('JE', 'Jersey'), ('JO', 'Jordan'), ('KZ', 'Kazakhstan'), ('KE', 'Kenya'), ('KI', 'Kiribati'), ('KP', "Korea, Democratic People's Republic of"), ('KR', 'Korea, Republic of'), ('KW', 'Kuwait'), ('KG', 'Kyrgyzstan'), ('LA', "Lao People's Democratic Republic"), ('LV', 'Latvia'), ('LB', 'Lebanon'), ('LS', 'Lesotho'), ('LR', 'Liberia'), ('LY', 'Libyan Arab Jamahiriya'), ('LI', 'Liechtenstein'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('MO', 'Macao'), ('MK', 'Macedonia, The Former Yugoslav Republic of'), ('MG', 'Madagascar'), ('MW', 'Malawi'), ('MY', 'Malaysia'), ('MV', 'Maldives'), ('ML', 'Mali'), ('MT', 'Malta'), ('MH', 'Marshall Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MU', 'Mauritius'), ('YT', 'Mayotte'), ('MX', 'Mexico'), ('FM', 'Micronesia, Federated States of'), ('MD', 'Moldova'), ('MC', 'Monaco'), ('MN', 'Mongolia'), ('ME', 'Montenegro'), ('MS', 'Montserrat'), ('MA', 'Morocco'), ('MZ', 'Mozambique'), ('MM', 'Myanmar'), ('NA', 'Namibia'), ('NR', 'Nauru'), ('NP', 'Nepal'), ('NL', 'Netherlands'), ('AN', 'Netherlands Antilles'), ('NC', 'New Caledonia'), ('NZ', 'New Zealand'), ('NI', 'Nicaragua'), ('NE', 'Niger'), ('NG', 'Nigeria'), ('NU', 'Niue'), ('NF', 'Norfolk Island'), ('MP', 'Northern Mariana Islands'), ('NO', 'Norway'), ('OM', 'Oman'), ('PK', 'Pakistan'), ('PW', 'Palau'), ('PS', 'Palestinian Territory, Occupied'), ('PA', 'Panama'), ('PG', 'Papua New Guinea'), ('PY', 'Paraguay'), ('PE', 'Peru'), ('PH', 'Philippines'), ('PN', 'Pitcairn'), ('PL', 'Poland'), ('PT', 'Portugal'), ('PR', 'Puerto Rico'), ('QA', 'Qatar'), ('RE', 'Reunion'), ('RO', 'Romania'), ('RU', 'Russian Federation'), ('RW', 'Rwanda'), ('BL', 'Saint Barthelemy'), ('SH', 'Saint Helena'), ('KN', 'Saint Kitts and Nevis'), ('LC', 'Saint Lucia'), ('MF', 'Saint Martin'), ('PM', 'Saint Pierre and Miquelon'), ('VC', 'Saint Vincent and the Grenadines'), ('WS', 'Samoa'), ('SM', 'San Marino'), ('ST', 'Sao Tome and Principe'), ('SA', 'Saudi Arabia'), ('SN', 'Senegal'), ('RS', 'Serbia'), ('SC', 'Seychelles'), ('SL', 'Sierra Leone'), ('SG', 'Singapore'), ('SK', 'Slovakia'), ('SI', 'Slovenia'), ('SB', 'Solomon Islands'), ('SO', 'Somalia'), ('ZA', 'South Africa'), ('GS', 'South Georgia and the South Sandwich Islands'), ('ES', 'Spain'), ('LK', 'Sri Lanka'), ('SD', 'Sudan'), ('SR', 'Suriname'), ('SJ', 'Svalbard and Jan Mayen'), ('SZ', 'Swaziland'), ('SE', 'Sweden'), ('CH', 'Switzerland'), ('SY', 'Syrian Arab Republic'), ('TW', 'Taiwan, Province of China'), ('TJ', 'Tajikistan'), ('TZ', 'Tanzania, United Republic of'), ('TH', 'Thailand'), ('TL', 'Timor-Leste'), ('TG', 'Togo'), ('TK', 'Tokelau'), ('TO', 'Tonga'), ('TT', 'Trinidad and Tobago'), ('TN', 'Tunisia'), ('TR', 'Turkey'), ('TM', 'Turkmenistan'), ('TC', 'Turks and Caicos Islands'), ('TV', 'Tuvalu'), ('UG', 'Uganda'), ('UA', 'Ukraine'), ('AE', 'United Arab Emirates'), ('US', 'United States'), ('UM', 'United States Minor Outlying Islands'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VU', 'Vanuatu'), ('VE', 'Venezuela'), ('VN', 'Viet Nam'), ('VG', 'Virgin Islands, British'), ('VI', 'Virgin Islands, U.S.'), ('WF', 'Wallis and Futuna'), ('EH', 'Western Sahara'), ('YE', 'Yemen'), ('ZM', 'Zambia'), ('ZW', 'Zimbabwe')], max_length=3, null=True), + model_name="account", + name="billing_country", + field=models.CharField( + blank=True, + choices=[ + ("GB", "United Kingdom"), + ("AF", "Afghanistan"), + ("AX", "Aland Islands"), + ("AL", "Albania"), + ("DZ", "Algeria"), + ("AS", "American Samoa"), + ("AD", "Andorra"), + ("AO", "Angola"), + ("AI", "Anguilla"), + ("AQ", "Antarctica"), + ("AG", "Antigua and Barbuda"), + ("AR", "Argentina"), + ("AM", "Armenia"), + ("AW", "Aruba"), + ("AU", "Australia"), + ("AT", "Austria"), + ("AZ", "Azerbaijan"), + ("BS", "Bahamas"), + ("BH", "Bahrain"), + ("BD", "Bangladesh"), + ("BB", "Barbados"), + ("BY", "Belarus"), + ("BE", "Belgium"), + ("BZ", "Belize"), + ("BJ", "Benin"), + ("BM", "Bermuda"), + ("BT", "Bhutan"), + ("BO", "Bolivia"), + ("BA", "Bosnia and Herzegovina"), + ("BW", "Botswana"), + ("BV", "Bouvet Island"), + ("BR", "Brazil"), + ("IO", "British Indian Ocean Territory"), + ("BN", "Brunei Darussalam"), + ("BG", "Bulgaria"), + ("BF", "Burkina Faso"), + ("BI", "Burundi"), + ("KH", "Cambodia"), + ("CM", "Cameroon"), + ("CA", "Canada"), + ("CV", "Cape Verde"), + ("KY", "Cayman Islands"), + ("CF", "Central African Republic"), + ("TD", "Chad"), + ("CL", "Chile"), + ("CN", "China"), + ("CX", "Christmas Island"), + ("CC", "Cocos (Keeling) Islands"), + ("CO", "Colombia"), + ("KM", "Comoros"), + ("CG", "Congo"), + ("CD", "Congo, The Democratic Republic of the"), + ("CK", "Cook Islands"), + ("CR", "Costa Rica"), + ("CI", "Cote d'Ivoire"), + ("HR", "Croatia"), + ("CU", "Cuba"), + ("CY", "Cyprus"), + ("CZ", "Czech Republic"), + ("DK", "Denmark"), + ("DJ", "Djibouti"), + ("DM", "Dominica"), + ("DO", "Dominican Republic"), + ("EC", "Ecuador"), + ("EG", "Egypt"), + ("SV", "El Salvador"), + ("GQ", "Equatorial Guinea"), + ("ER", "Eritrea"), + ("EE", "Estonia"), + ("ET", "Ethiopia"), + ("FK", "Falkland Islands (Malvinas)"), + ("FO", "Faroe Islands"), + ("FJ", "Fiji"), + ("FI", "Finland"), + ("FR", "France"), + ("GF", "French Guiana"), + ("PF", "French Polynesia"), + ("TF", "French Southern Territories"), + ("GA", "Gabon"), + ("GM", "Gambia"), + ("GE", "Georgia"), + ("DE", "Germany"), + ("GH", "Ghana"), + ("GI", "Gibraltar"), + ("GR", "Greece"), + ("GL", "Greenland"), + ("GD", "Grenada"), + ("GP", "Guadeloupe"), + ("GU", "Guam"), + ("GT", "Guatemala"), + ("GG", "Guernsey"), + ("GN", "Guinea"), + ("GW", "Guinea-Bissau"), + ("GY", "Guyana"), + ("HT", "Haiti"), + ("HM", "Heard Island and McDonald Islands"), + ("VA", "Holy See (Vatican City State)"), + ("HN", "Honduras"), + ("HK", "Hong Kong"), + ("HU", "Hungary"), + ("IS", "Iceland"), + ("IN", "India"), + ("ID", "Indonesia"), + ("IR", "Iran, Islamic Republic of"), + ("IQ", "Iraq"), + ("IE", "Ireland"), + ("IM", "Isle of Man"), + ("IL", "Israel"), + ("IT", "Italy"), + ("JM", "Jamaica"), + ("JP", "Japan"), + ("JE", "Jersey"), + ("JO", "Jordan"), + ("KZ", "Kazakhstan"), + ("KE", "Kenya"), + ("KI", "Kiribati"), + ("KP", "Korea, Democratic People's Republic of"), + ("KR", "Korea, Republic of"), + ("KW", "Kuwait"), + ("KG", "Kyrgyzstan"), + ("LA", "Lao People's Democratic Republic"), + ("LV", "Latvia"), + ("LB", "Lebanon"), + ("LS", "Lesotho"), + ("LR", "Liberia"), + ("LY", "Libyan Arab Jamahiriya"), + ("LI", "Liechtenstein"), + ("LT", "Lithuania"), + ("LU", "Luxembourg"), + ("MO", "Macao"), + ("MK", "Macedonia, The Former Yugoslav Republic of"), + ("MG", "Madagascar"), + ("MW", "Malawi"), + ("MY", "Malaysia"), + ("MV", "Maldives"), + ("ML", "Mali"), + ("MT", "Malta"), + ("MH", "Marshall Islands"), + ("MQ", "Martinique"), + ("MR", "Mauritania"), + ("MU", "Mauritius"), + ("YT", "Mayotte"), + ("MX", "Mexico"), + ("FM", "Micronesia, Federated States of"), + ("MD", "Moldova"), + ("MC", "Monaco"), + ("MN", "Mongolia"), + ("ME", "Montenegro"), + ("MS", "Montserrat"), + ("MA", "Morocco"), + ("MZ", "Mozambique"), + ("MM", "Myanmar"), + ("NA", "Namibia"), + ("NR", "Nauru"), + ("NP", "Nepal"), + ("NL", "Netherlands"), + ("AN", "Netherlands Antilles"), + ("NC", "New Caledonia"), + ("NZ", "New Zealand"), + ("NI", "Nicaragua"), + ("NE", "Niger"), + ("NG", "Nigeria"), + ("NU", "Niue"), + ("NF", "Norfolk Island"), + ("MP", "Northern Mariana Islands"), + ("NO", "Norway"), + ("OM", "Oman"), + ("PK", "Pakistan"), + ("PW", "Palau"), + ("PS", "Palestinian Territory, Occupied"), + ("PA", "Panama"), + ("PG", "Papua New Guinea"), + ("PY", "Paraguay"), + ("PE", "Peru"), + ("PH", "Philippines"), + ("PN", "Pitcairn"), + ("PL", "Poland"), + ("PT", "Portugal"), + ("PR", "Puerto Rico"), + ("QA", "Qatar"), + ("RE", "Reunion"), + ("RO", "Romania"), + ("RU", "Russian Federation"), + ("RW", "Rwanda"), + ("BL", "Saint Barthelemy"), + ("SH", "Saint Helena"), + ("KN", "Saint Kitts and Nevis"), + ("LC", "Saint Lucia"), + ("MF", "Saint Martin"), + ("PM", "Saint Pierre and Miquelon"), + ("VC", "Saint Vincent and the Grenadines"), + ("WS", "Samoa"), + ("SM", "San Marino"), + ("ST", "Sao Tome and Principe"), + ("SA", "Saudi Arabia"), + ("SN", "Senegal"), + ("RS", "Serbia"), + ("SC", "Seychelles"), + ("SL", "Sierra Leone"), + ("SG", "Singapore"), + ("SK", "Slovakia"), + ("SI", "Slovenia"), + ("SB", "Solomon Islands"), + ("SO", "Somalia"), + ("ZA", "South Africa"), + ("GS", "South Georgia and the South Sandwich Islands"), + ("ES", "Spain"), + ("LK", "Sri Lanka"), + ("SD", "Sudan"), + ("SR", "Suriname"), + ("SJ", "Svalbard and Jan Mayen"), + ("SZ", "Swaziland"), + ("SE", "Sweden"), + ("CH", "Switzerland"), + ("SY", "Syrian Arab Republic"), + ("TW", "Taiwan, Province of China"), + ("TJ", "Tajikistan"), + ("TZ", "Tanzania, United Republic of"), + ("TH", "Thailand"), + ("TL", "Timor-Leste"), + ("TG", "Togo"), + ("TK", "Tokelau"), + ("TO", "Tonga"), + ("TT", "Trinidad and Tobago"), + ("TN", "Tunisia"), + ("TR", "Turkey"), + ("TM", "Turkmenistan"), + ("TC", "Turks and Caicos Islands"), + ("TV", "Tuvalu"), + ("UG", "Uganda"), + ("UA", "Ukraine"), + ("AE", "United Arab Emirates"), + ("US", "United States"), + ("UM", "United States Minor Outlying Islands"), + ("UY", "Uruguay"), + ("UZ", "Uzbekistan"), + ("VU", "Vanuatu"), + ("VE", "Venezuela"), + ("VN", "Viet Nam"), + ("VG", "Virgin Islands, British"), + ("VI", "Virgin Islands, U.S."), + ("WF", "Wallis and Futuna"), + ("EH", "Western Sahara"), + ("YE", "Yemen"), + ("ZM", "Zambia"), + ("ZW", "Zimbabwe"), + ], + max_length=3, + null=True, + ), ), migrations.AddField( - model_name='account', - name='billing_postcode', - field=models.CharField(blank=True, max_length=64, null=True, verbose_name='Post/Zip-code'), + model_name="account", + name="billing_postcode", + field=models.CharField( + blank=True, max_length=64, null=True, verbose_name="Post/Zip-code" + ), ), migrations.AddField( - model_name='account', - name='billing_state', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='State'), + model_name="account", + name="billing_state", + field=models.CharField( + blank=True, max_length=255, null=True, verbose_name="State" + ), ), migrations.AddField( - model_name='account', - name='billing_street', - field=models.CharField(blank=True, max_length=55, null=True, verbose_name='Street'), + model_name="account", + name="billing_street", + field=models.CharField( + blank=True, max_length=55, null=True, verbose_name="Street" + ), ), migrations.AddField( - model_name='account', - name='contact_name', - field=models.CharField(default=1, max_length=120, verbose_name='Contact Name'), + model_name="account", + name="contact_name", + field=models.CharField( + default=1, max_length=120, verbose_name="Contact Name" + ), preserve_default=False, ), ] diff --git a/accounts/migrations/0007_email.py b/accounts/migrations/0007_email.py index 26f28b3..614acab 100644 --- a/accounts/migrations/0007_email.py +++ b/accounts/migrations/0007_email.py @@ -7,20 +7,44 @@ class Migration(migrations.Migration): dependencies = [ - ('contacts', '0003_merge_20190214_1427'), - ('accounts', '0006_auto_20190212_1708'), + ("contacts", "0003_merge_20190214_1427"), + ("accounts", "0006_auto_20190212_1708"), ] operations = [ migrations.CreateModel( - name='Email', + name="Email", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sent_at', models.DateTimeField(auto_now_add=True)), - ('message_subject', models.TextField(null=True)), - ('message_body', models.TextField(null=True)), - ('recipient', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='recieved_email', to='contacts.Contact')), - ('sender', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sent_email', to='accounts.Account')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("sent_at", models.DateTimeField(auto_now_add=True)), + ("message_subject", models.TextField(null=True)), + ("message_body", models.TextField(null=True)), + ( + "recipient", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="recieved_email", + to="contacts.Contact", + ), + ), + ( + "sender", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="sent_email", + to="accounts.Account", + ), + ), ], ), ] diff --git a/accounts/migrations/0008_account_assigned_to.py b/accounts/migrations/0008_account_assigned_to.py index 5ed47fc..0c14f30 100644 --- a/accounts/migrations/0008_account_assigned_to.py +++ b/accounts/migrations/0008_account_assigned_to.py @@ -8,13 +8,15 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('accounts', '0007_email'), + ("accounts", "0007_email"), ] operations = [ migrations.AddField( - model_name='account', - name='assigned_to', - field=models.ManyToManyField(related_name='account_assigned_users', to=settings.AUTH_USER_MODEL), + model_name="account", + name="assigned_to", + field=models.ManyToManyField( + related_name="account_assigned_users", to=settings.AUTH_USER_MODEL + ), ), ] diff --git a/accounts/migrations/0009_auto_20190809_1659.py b/accounts/migrations/0009_auto_20190809_1659.py index ecab277..2852b40 100644 --- a/accounts/migrations/0009_auto_20190809_1659.py +++ b/accounts/migrations/0009_auto_20190809_1659.py @@ -8,63 +8,82 @@ class Migration(migrations.Migration): dependencies = [ - ('contacts', '0003_merge_20190214_1427'), - ('accounts', '0008_account_assigned_to'), + ("contacts", "0003_merge_20190214_1427"), + ("accounts", "0008_account_assigned_to"), ] operations = [ migrations.RenameField( - model_name='email', - old_name='sent_at', - new_name='created_on', + model_name="email", old_name="sent_at", new_name="created_on", ), migrations.RenameField( - model_name='email', - old_name='sender', - new_name='from_account', - ), - migrations.RemoveField( - model_name='email', - name='recipient', + model_name="email", old_name="sender", new_name="from_account", ), + migrations.RemoveField(model_name="email", name="recipient",), migrations.AddField( - model_name='email', - name='from_email', + model_name="email", + name="from_email", field=models.EmailField(default=django.utils.timezone.now, max_length=254), preserve_default=False, ), migrations.AddField( - model_name='email', - name='recipients', - field=models.ManyToManyField(related_name='recieved_email', to='contacts.Contact'), + model_name="email", + name="recipients", + field=models.ManyToManyField( + related_name="recieved_email", to="contacts.Contact" + ), ), migrations.AddField( - model_name='email', - name='rendered_message_body', + model_name="email", + name="rendered_message_body", field=models.TextField(null=True), ), migrations.AddField( - model_name='email', - name='scheduled_date_time', + model_name="email", + name="scheduled_date_time", field=models.DateTimeField(null=True), ), migrations.AddField( - model_name='email', - name='scheduled_later', + model_name="email", + name="scheduled_later", field=models.BooleanField(default=False), ), migrations.AddField( - model_name='email', - name='timezone', - field=models.CharField(default='UTC', max_length=100), + model_name="email", + name="timezone", + field=models.CharField(default="UTC", max_length=100), ), migrations.CreateModel( - name='EmailLog', + name="EmailLog", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('is_sent', models.BooleanField(default=False)), - ('contact', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contact_email_log', to='contacts.Contact')), - ('email', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='email_log', to='accounts.Email')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("is_sent", models.BooleanField(default=False)), + ( + "contact", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="contact_email_log", + to="contacts.Contact", + ), + ), + ( + "email", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="email_log", + to="accounts.Email", + ), + ), ], ), ] diff --git a/accounts/migrations/0010_account_teams.py b/accounts/migrations/0010_account_teams.py index 6e57038..f3dee1b 100644 --- a/accounts/migrations/0010_account_teams.py +++ b/accounts/migrations/0010_account_teams.py @@ -6,14 +6,16 @@ class Migration(migrations.Migration): dependencies = [ - ('teams', '0003_auto_20190909_1621'), - ('accounts', '0009_auto_20190809_1659'), + ("teams", "0003_auto_20190909_1621"), + ("accounts", "0009_auto_20190809_1659"), ] operations = [ migrations.AddField( - model_name='account', - name='teams', - field=models.ManyToManyField(related_name='account_teams', to='teams.Teams'), + model_name="account", + name="teams", + field=models.ManyToManyField( + related_name="account_teams", to="teams.Teams" + ), ), ] diff --git a/accounts/migrations/0011_account_company.py b/accounts/migrations/0011_account_company.py index 6665908..4661b64 100644 --- a/accounts/migrations/0011_account_company.py +++ b/accounts/migrations/0011_account_company.py @@ -7,14 +7,19 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0020_auto_20200409_1653'), - ('accounts', '0010_account_teams'), + ("common", "0020_auto_20200409_1653"), + ("accounts", "0010_account_teams"), ] operations = [ migrations.AddField( - model_name='account', - name='company', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Company'), + model_name="account", + name="company", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="common.Company", + ), ), ] diff --git a/accounts/tasks.py b/accounts/tasks.py index f6c9e2d..a53032a 100644 --- a/accounts/tasks.py +++ b/accounts/tasks.py @@ -7,30 +7,36 @@ from django.shortcuts import reverse from django.template import Context, Template from django.template.loader import render_to_string -from django.utils import timezone from accounts.models import Account, Email, EmailLog from common.models import User from common.utils import convert_to_custom_timezone -from contacts.models import Contact from marketing.models import BlockedDomain, BlockedEmail @task def send_email(email_obj_id): email_obj = Email.objects.filter(id=email_obj_id).first() - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) if email_obj: from_email = email_obj.from_email contacts = email_obj.recipients.all() for contact_obj in contacts: - if not EmailLog.objects.filter(email=email_obj, contact=contact_obj, is_sent=True).exists(): - if (contact_obj.email not in blocked_emails) and (contact_obj.email.split('@')[-1] not in blocked_domains): + if not EmailLog.objects.filter( + email=email_obj, contact=contact_obj, is_sent=True + ).exists(): + if (contact_obj.email not in blocked_emails) and ( + contact_obj.email.split("@")[-1] not in blocked_domains + ): html = email_obj.message_body context_data = { - 'email': contact_obj.email if contact_obj.email else '', - 'name': contact_obj.first_name if contact_obj.first_name else '' + ' ' + contact_obj.last_name if contact_obj.last_name else '', + "email": contact_obj.email if contact_obj.email else "", + "name": contact_obj.first_name + if contact_obj.first_name + else "" + " " + contact_obj.last_name + if contact_obj.last_name + else "", } try: html_content = Template(html).render(Context(context_data)) @@ -39,50 +45,55 @@ def send_email(email_obj_id): subject, html_content, from_email=from_email, - to=[contact_obj.email, ] + to=[contact_obj.email, ], ) msg.content_subtype = "html" res = msg.send() if res: email_obj.rendered_message_body = html_content email_obj.save() - EmailLog.objects.create(email=email_obj, contact=contact_obj, is_sent=True) + EmailLog.objects.create( + email=email_obj, contact=contact_obj, is_sent=True + ) except Exception as e: print(e) - pass - @task -def send_email_to_assigned_user(recipients, from_email, domain='demo.django-crm.io', protocol='http'): +def send_email_to_assigned_user( + recipients, from_email, domain="demo.django-crm.io", protocol="http" +): """ Send Mail To Users When they are assigned to a contact """ account = Account.objects.filter(id=from_email).first() created_by = account.created_by - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) for user in recipients: recipients_list = [] user = User.objects.filter(id=user, is_active=True).first() if user: - if (user.email not in blocked_emails) and (user.email.split('@')[-1] not in blocked_domains): + if (user.email not in blocked_emails) and ( + user.email.split("@")[-1] not in blocked_domains + ): recipients_list.append(user.email) context = {} - context["url"] = protocol + '://' + domain + \ - reverse('accounts:view_account', args=(account.id,)) + context["url"] = ( + protocol + + "://" + + domain + + reverse("accounts:view_account", args=(account.id,)) + ) context["user"] = user context["account"] = account context["created_by"] = created_by - subject = 'Assigned a account for you.' + subject = "Assigned a account for you." html_content = render_to_string( - 'assigned_to/account_assigned.html', context=context) - - msg = EmailMessage( - subject, - html_content, - to=recipients_list + "assigned_to/account_assigned.html", context=context ) + + msg = EmailMessage(subject, html_content, to=recipients_list) msg.content_subtype = "html" msg.send() @@ -94,12 +105,11 @@ def send_scheduled_emails(): for each in email_objs: scheduled_date_time = each.scheduled_date_time - sent_time = datetime.now().strftime('%Y-%m-%d %H:%M') - sent_time = datetime.strptime(sent_time, '%Y-%m-%d %H:%M') + sent_time = datetime.now().strftime("%Y-%m-%d %H:%M") + sent_time = datetime.strptime(sent_time, "%Y-%m-%d %H:%M") local_tz = pytz.timezone(settings.TIME_ZONE) sent_time = local_tz.localize(sent_time) - sent_time = convert_to_custom_timezone( - sent_time, each.timezone, to_utc=True) + sent_time = convert_to_custom_timezone(sent_time, each.timezone, to_utc=True) # if ( # str(each.scheduled_date_time.date()) == str(sent_time.date()) and @@ -109,8 +119,8 @@ def send_scheduled_emails(): # ): # send_email.delay(each.id) if ( - str(each.scheduled_date_time.date()) == str(sent_time.date()) and - str(scheduled_date_time.hour) == str(sent_time.hour) and - str(scheduled_date_time.minute) == str(sent_time.minute) + str(each.scheduled_date_time.date()) == str(sent_time.date()) + and str(scheduled_date_time.hour) == str(sent_time.hour) + and str(scheduled_date_time.minute) == str(sent_time.minute) ): send_email.delay(each.id) diff --git a/accounts/tests_celery_tasks.py b/accounts/tests_celery_tasks.py index a92c9c4..62a3ccf 100644 --- a/accounts/tests_celery_tasks.py +++ b/accounts/tests_celery_tasks.py @@ -4,31 +4,38 @@ from django.test.utils import override_settings from accounts.models import Email -from accounts.tasks import (send_email, send_email_to_assigned_user, - send_scheduled_emails) +from accounts.tasks import ( + send_email, + send_email_to_assigned_user, + send_scheduled_emails, +) from accounts.tests import AccountCreateTest class TestCeleryTasks(AccountCreateTest, TestCase): - - @override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - CELERY_ALWAYS_EAGER=True, - BROKER_BACKEND='memory') + @override_settings( + CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, + CELERY_ALWAYS_EAGER=True, + BROKER_BACKEND="memory", + ) def test_celery_tasks(self): email_scheduled = Email.objects.create( - message_subject='message subject', message_body='message body', - scheduled_later=True, timezone='Asia/Kolkata', + message_subject="message subject", + message_body="message body", + scheduled_later=True, + timezone="Asia/Kolkata", from_account=self.account, scheduled_date_time=(datetime.now() - timedelta(minutes=5)), - from_email='from@email.com' + from_email="from@email.com", ) email_scheduled.recipients.add(self.contact.id, self.contact_user1.id) task = send_scheduled_emails.apply() - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) task = send_email.apply((email_scheduled.id,)) - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) task = send_email_to_assigned_user.apply( - ([self.user.id, self.user1.id, ], self.account.id,),) - self.assertEqual('SUCCESS', task.state) + ([self.user.id, self.user1.id,], self.account.id,), + ) + self.assertEqual("SUCCESS", task.state) diff --git a/accounts/urls.py b/accounts/urls.py index c4adfd6..2e0a642 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -1,37 +1,47 @@ from django.urls import path from accounts.views import ( - AccountsListView, CreateAccountView, AccountDetailView, AccountUpdateView, - AccountDeleteView, AddCommentView, UpdateCommentView, DeleteCommentView, - AddAttachmentView, DeleteAttachmentsView, create_mail, # get_account_details, - get_contacts_for_account, get_email_data_for_account, - get_teams_and_users + AccountsListView, + CreateAccountView, + AccountDetailView, + AccountUpdateView, + AccountDeleteView, + AddCommentView, + UpdateCommentView, + DeleteCommentView, + AddAttachmentView, + DeleteAttachmentsView, + create_mail, # get_account_details, + get_contacts_for_account, + get_email_data_for_account, + get_teams_and_users, ) -app_name = 'accounts' +app_name = "accounts" urlpatterns = [ - path('', AccountsListView.as_view(), name='list'), - path('create/', CreateAccountView.as_view(), name='new_account'), - path('/view/', AccountDetailView.as_view(), name="view_account"), - path('/edit/', AccountUpdateView.as_view(), name="edit_account"), - path('/delete/', AccountDeleteView.as_view(), - name="remove_account"), - path('comment/add/', AddCommentView.as_view(), name="add_comment"), - path('comment/edit/', UpdateCommentView.as_view(), name="edit_comment"), - path('comment/remove/', DeleteCommentView.as_view(), - name="remove_comment"), - - path('attachment/add/', AddAttachmentView.as_view(), - name="add_attachment"), - path('attachment/remove/', DeleteAttachmentsView.as_view(), - name="remove_attachment"), - path('create-mail', create_mail, name="create_mail"), - path('get_contacts_for_account/', get_contacts_for_account, - name="get_contacts_for_account"), - path('get_email_data_for_account/', get_email_data_for_account, - name="get_email_data_for_account"), - path('get_teams_and_users/', - get_teams_and_users, name="get_teams_and_users"), + path("", AccountsListView.as_view(), name="list"), + path("create/", CreateAccountView.as_view(), name="new_account"), + path("/view/", AccountDetailView.as_view(), name="view_account"), + path("/edit/", AccountUpdateView.as_view(), name="edit_account"), + path("/delete/", AccountDeleteView.as_view(), name="remove_account"), + path("comment/add/", AddCommentView.as_view(), name="add_comment"), + path("comment/edit/", UpdateCommentView.as_view(), name="edit_comment"), + path("comment/remove/", DeleteCommentView.as_view(), name="remove_comment"), + path("attachment/add/", AddAttachmentView.as_view(), name="add_attachment"), + path( + "attachment/remove/", DeleteAttachmentsView.as_view(), name="remove_attachment" + ), + path("create-mail", create_mail, name="create_mail"), + path( + "get_contacts_for_account/", + get_contacts_for_account, + name="get_contacts_for_account", + ), + path( + "get_email_data_for_account/", + get_email_data_for_account, + name="get_email_data_for_account", + ), + path("get_teams_and_users/", get_teams_and_users, name="get_teams_and_users"), # path('get-account-details//', get_account_details, name="get_account_details"), - ] diff --git a/cases/apps.py b/cases/apps.py index 7c93236..648e0c7 100644 --- a/cases/apps.py +++ b/cases/apps.py @@ -2,4 +2,4 @@ class CasesConfig(AppConfig): - name = 'cases' + name = "cases" diff --git a/cases/migrations/0001_initial.py b/cases/migrations/0001_initial.py index 1803b4a..e13d23e 100644 --- a/cases/migrations/0001_initial.py +++ b/cases/migrations/0001_initial.py @@ -9,26 +9,80 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('accounts', '0001_initial'), + ("accounts", "0001_initial"), ] operations = [ migrations.CreateModel( - name='Case', + name="Case", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=64, verbose_name='Name')), - ('status', models.CharField(choices=[('New', 'New'), ('Assigned', 'Assigned'), ('Pending', 'Pending'), ('Closed', 'Closed'), ('Rejected', 'Rejected'), ('Duplicate', 'Duplicate')], max_length=64)), - ('priority', models.CharField(choices=[('Low', 'Low'), ('Normal', 'Normal'), ('High', 'High'), ('Urgent', 'Urgent')], max_length=64)), - ('case_type', models.CharField(blank=True, choices=[('Question', 'Question'), ('Incident', 'Incident'), ('Problem', 'Problem')], default='', max_length=255, null=True)), - ('closed_on', models.DateField()), - ('description', models.TextField(blank=True, null=True)), - ('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Created on')), - ('is_active', models.BooleanField(default=False)), - ('account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='accounts.Account')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=64, verbose_name="Name")), + ( + "status", + models.CharField( + choices=[ + ("New", "New"), + ("Assigned", "Assigned"), + ("Pending", "Pending"), + ("Closed", "Closed"), + ("Rejected", "Rejected"), + ("Duplicate", "Duplicate"), + ], + max_length=64, + ), + ), + ( + "priority", + models.CharField( + choices=[ + ("Low", "Low"), + ("Normal", "Normal"), + ("High", "High"), + ("Urgent", "Urgent"), + ], + max_length=64, + ), + ), + ( + "case_type", + models.CharField( + blank=True, + choices=[ + ("Question", "Question"), + ("Incident", "Incident"), + ("Problem", "Problem"), + ], + default="", + max_length=255, + null=True, + ), + ), + ("closed_on", models.DateField()), + ("description", models.TextField(blank=True, null=True)), + ( + "created_on", + models.DateTimeField(auto_now_add=True, verbose_name="Created on"), + ), + ("is_active", models.BooleanField(default=False)), + ( + "account", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="accounts.Account", + ), + ), ], - options={ - 'ordering': ['-created_on'], - }, + options={"ordering": ["-created_on"],}, ), ] diff --git a/cases/migrations/0002_auto_20190128_1237.py b/cases/migrations/0002_auto_20190128_1237.py index 8aa043b..dfb03fc 100644 --- a/cases/migrations/0002_auto_20190128_1237.py +++ b/cases/migrations/0002_auto_20190128_1237.py @@ -10,31 +10,37 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('common', '0001_initial'), - ('cases', '0001_initial'), - ('contacts', '0001_initial'), + ("common", "0001_initial"), + ("cases", "0001_initial"), + ("contacts", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.AddField( - model_name='case', - name='assigned_to', - field=models.ManyToManyField(related_name='case_assigned_users', to=settings.AUTH_USER_MODEL), + model_name="case", + name="assigned_to", + field=models.ManyToManyField( + related_name="case_assigned_users", to=settings.AUTH_USER_MODEL + ), ), migrations.AddField( - model_name='case', - name='contacts', - field=models.ManyToManyField(to='contacts.Contact'), + model_name="case", + name="contacts", + field=models.ManyToManyField(to="contacts.Contact"), ), migrations.AddField( - model_name='case', - name='created_by', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='case_created_by', to=settings.AUTH_USER_MODEL), + model_name="case", + name="created_by", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="case_created_by", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='case', - name='teams', - field=models.ManyToManyField(to='common.Team'), + model_name="case", + name="teams", + field=models.ManyToManyField(to="common.Team"), ), ] diff --git a/cases/migrations/0003_auto_20190212_1334.py b/cases/migrations/0003_auto_20190212_1334.py index 4f50908..dd4dea0 100644 --- a/cases/migrations/0003_auto_20190212_1334.py +++ b/cases/migrations/0003_auto_20190212_1334.py @@ -8,17 +8,19 @@ class Migration(migrations.Migration): dependencies = [ - ('cases', '0002_auto_20190128_1237'), + ("cases", "0002_auto_20190128_1237"), ] operations = [ - migrations.RemoveField( - model_name='case', - name='teams', - ), + migrations.RemoveField(model_name="case", name="teams",), migrations.AlterField( - model_name='case', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='case_created_by', to=settings.AUTH_USER_MODEL), + model_name="case", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="case_created_by", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/cases/migrations/0004_case_teams.py b/cases/migrations/0004_case_teams.py index fc5a011..0b63222 100644 --- a/cases/migrations/0004_case_teams.py +++ b/cases/migrations/0004_case_teams.py @@ -6,14 +6,14 @@ class Migration(migrations.Migration): dependencies = [ - ('teams', '0003_auto_20190909_1621'), - ('cases', '0003_auto_20190212_1334'), + ("teams", "0003_auto_20190909_1621"), + ("cases", "0003_auto_20190212_1334"), ] operations = [ migrations.AddField( - model_name='case', - name='teams', - field=models.ManyToManyField(related_name='cases_teams', to='teams.Teams'), + model_name="case", + name="teams", + field=models.ManyToManyField(related_name="cases_teams", to="teams.Teams"), ), ] diff --git a/cases/migrations/0005_case_company.py b/cases/migrations/0005_case_company.py index 431de79..d3dc000 100644 --- a/cases/migrations/0005_case_company.py +++ b/cases/migrations/0005_case_company.py @@ -7,14 +7,19 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0021_document_company'), - ('cases', '0004_case_teams'), + ("common", "0021_document_company"), + ("cases", "0004_case_teams"), ] operations = [ migrations.AddField( - model_name='case', - name='company', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Company'), + model_name="case", + name="company", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="common.Company", + ), ), ] diff --git a/cases/tasks.py b/cases/tasks.py index a0ca46a..643e079 100644 --- a/cases/tasks.py +++ b/cases/tasks.py @@ -11,32 +11,37 @@ @task -def send_email_to_assigned_user(recipients, case_id, domain='demo.django-crm.io', protocol='http'): +def send_email_to_assigned_user( + recipients, case_id, domain="demo.django-crm.io", protocol="http" +): """ Send Mail To Users When they are assigned to a case """ case = Case.objects.get(id=case_id) created_by = case.created_by - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) for user in recipients: recipients_list = [] user = User.objects.filter(id=user, is_active=True).first() if user: - if (user.email not in blocked_emails) and (user.email.split('@')[-1] not in blocked_domains): + if (user.email not in blocked_emails) and ( + user.email.split("@")[-1] not in blocked_domains + ): recipients_list.append(user.email) context = {} - context["url"] = protocol + '://' + domain + \ - reverse('cases:view_case', args=(case.id,)) + context["url"] = ( + protocol + + "://" + + domain + + reverse("cases:view_case", args=(case.id,)) + ) context["user"] = user context["case"] = case context["created_by"] = created_by - subject = 'Assigned to case.' + subject = "Assigned to case." html_content = render_to_string( - 'assigned_to/cases_assigned.html', context=context) - - msg = EmailMessage( - subject, - html_content, - to=recipients_list + "assigned_to/cases_assigned.html", context=context ) + + msg = EmailMessage(subject, html_content, to=recipients_list) msg.content_subtype = "html" msg.send() diff --git a/cases/tests_celery_tasks.py b/cases/tests_celery_tasks.py index 934a898..b639c4c 100644 --- a/cases/tests_celery_tasks.py +++ b/cases/tests_celery_tasks.py @@ -6,11 +6,13 @@ class TestCeleryTasks(CaseCreation, TestCase): - - @override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - CELERY_ALWAYS_EAGER=True, - BROKER_BACKEND='memory') + @override_settings( + CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, + CELERY_ALWAYS_EAGER=True, + BROKER_BACKEND="memory", + ) def test_celery_tasks(self): task = send_email_to_assigned_user.apply( - ([self.user.id, self.user1.id, ], self.case.id,),) - self.assertEqual('SUCCESS', task.state) + ([self.user.id, self.user1.id,], self.case.id,), + ) + self.assertEqual("SUCCESS", task.state) diff --git a/cases/urls.py b/cases/urls.py index 22ad719..47f6cb6 100644 --- a/cases/urls.py +++ b/cases/urls.py @@ -1,33 +1,38 @@ from django.urls import path from cases.views import ( - CasesListView, create_case, CaseDetailView, update_case, RemoveCaseView, - CloseCaseView, select_contact, GetCasesView, AddCommentView, - UpdateCommentView, DeleteCommentView, get_teams_and_users, - AddAttachmentView, DeleteAttachmentsView) + CasesListView, + create_case, + CaseDetailView, + update_case, + RemoveCaseView, + CloseCaseView, + select_contact, + GetCasesView, + AddCommentView, + UpdateCommentView, + DeleteCommentView, + get_teams_and_users, + AddAttachmentView, + DeleteAttachmentsView, +) -app_name = 'cases' +app_name = "cases" urlpatterns = [ - path('', CasesListView.as_view(), name='list'), - path('create/', create_case, name='add_case'), - path('/view/', CaseDetailView.as_view(), name="view_case"), - path('/edit_case/', update_case, name="edit_case"), - path('/remove/', - RemoveCaseView.as_view(), name="remove_case"), - - path('close_case/', CloseCaseView.as_view(), name="close_case"), - path('select_contacts/', select_contact, name="select_contacts"), - path('get/list/', GetCasesView.as_view(), name="get_cases"), - - path('comment/add/', AddCommentView.as_view(), name="add_comment"), - path('comment/edit/', UpdateCommentView.as_view(), name="edit_comment"), - path('comment/remove/', - DeleteCommentView.as_view(), name="remove_comment"), - - path('attachment/add/', - AddAttachmentView.as_view(), name="add_attachment"), - path('attachment/remove/', DeleteAttachmentsView.as_view(), - name="remove_attachment"), - path('get_teams_and_users/', get_teams_and_users, - name="get_teams_and_users"), + path("", CasesListView.as_view(), name="list"), + path("create/", create_case, name="add_case"), + path("/view/", CaseDetailView.as_view(), name="view_case"), + path("/edit_case/", update_case, name="edit_case"), + path("/remove/", RemoveCaseView.as_view(), name="remove_case"), + path("close_case/", CloseCaseView.as_view(), name="close_case"), + path("select_contacts/", select_contact, name="select_contacts"), + path("get/list/", GetCasesView.as_view(), name="get_cases"), + path("comment/add/", AddCommentView.as_view(), name="add_comment"), + path("comment/edit/", UpdateCommentView.as_view(), name="edit_comment"), + path("comment/remove/", DeleteCommentView.as_view(), name="remove_comment"), + path("attachment/add/", AddAttachmentView.as_view(), name="add_attachment"), + path( + "attachment/remove/", DeleteAttachmentsView.as_view(), name="remove_attachment" + ), + path("get_teams_and_users/", get_teams_and_users, name="get_teams_and_users"), ] diff --git a/common/admin.py b/common/admin.py index 9584892..01a7e78 100644 --- a/common/admin.py +++ b/common/admin.py @@ -1,5 +1,6 @@ from django.contrib import admin from common.models import User, Address, Comment, Comment_Files + # Register your models here. admin.site.register(User) diff --git a/common/apps.py b/common/apps.py index 5f2f078..3ce3894 100644 --- a/common/apps.py +++ b/common/apps.py @@ -2,4 +2,4 @@ class CommonConfig(AppConfig): - name = 'common' + name = "common" diff --git a/common/middleware/get_company.py b/common/middleware/get_company.py index 8294b3c..4372039 100644 --- a/common/middleware/get_company.py +++ b/common/middleware/get_company.py @@ -1,8 +1,4 @@ from common.models import Company -from django.conf import settings -from django.contrib.auth import logout -from django.shortcuts import redirect -from django.urls import reverse class GetCompany(object): @@ -14,15 +10,15 @@ def __call__(self, request): return response def process_view(self, request, view_func, view_args, view_kwargs): - company_id = request.session.get('company', None) + company_id = request.session.get("company", None) if company_id: company = Company.objects.get(id=company_id) request.company = company - request.session['company'] = company.id + request.session["company"] = company.id else: host_name = request.META.get("HTTP_HOST") subdomain = host_name.split(".")[0] company = Company.objects.filter(sub_domain=subdomain).first() if company: request.company = company - request.session['company'] = company.id + request.session["company"] = company.id diff --git a/common/migrations/0001_initial.py b/common/migrations/0001_initial.py index babaa5f..13e5d78 100644 --- a/common/migrations/0001_initial.py +++ b/common/migrations/0001_initial.py @@ -12,83 +12,480 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('accounts', '0001_initial'), - ('cases', '0001_initial'), + ("accounts", "0001_initial"), + ("cases", "0001_initial"), ] operations = [ migrations.CreateModel( - name='User', + name="User", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(max_length=100, unique=True)), - ('first_name', models.CharField(blank=True, max_length=150)), - ('last_name', models.CharField(blank=True, max_length=150)), - ('email', models.EmailField(max_length=255, unique=True)), - ('is_active', models.BooleanField(default=True)), - ('is_admin', models.BooleanField(default=False)), - ('is_staff', models.BooleanField(default=False)), - ('date_joined', models.DateTimeField(auto_now_add=True, verbose_name='date joined')), - ('role', models.CharField(choices=[('ADMIN', 'ADMIN'), ('USER', 'USER')], max_length=50)), - ('profile_pic', models.FileField(blank=True, max_length=1000, null=True, upload_to=common.models.img_url)), - ], - options={ - 'abstract': False, - }, - managers=[ - ('objects', django.contrib.auth.models.UserManager()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("password", models.CharField(max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "is_superuser", + models.BooleanField( + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), + ), + ("username", models.CharField(max_length=100, unique=True)), + ("first_name", models.CharField(blank=True, max_length=150)), + ("last_name", models.CharField(blank=True, max_length=150)), + ("email", models.EmailField(max_length=255, unique=True)), + ("is_active", models.BooleanField(default=True)), + ("is_admin", models.BooleanField(default=False)), + ("is_staff", models.BooleanField(default=False)), + ( + "date_joined", + models.DateTimeField(auto_now_add=True, verbose_name="date joined"), + ), + ( + "role", + models.CharField( + choices=[("ADMIN", "ADMIN"), ("USER", "USER")], max_length=50 + ), + ), + ( + "profile_pic", + models.FileField( + blank=True, + max_length=1000, + null=True, + upload_to=common.models.img_url, + ), + ), ], + options={"abstract": False,}, + managers=[("objects", django.contrib.auth.models.UserManager()),], ), migrations.CreateModel( - name='Address', + name="Address", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('address_line', models.CharField(blank=True, max_length=255, null=True, verbose_name='Address')), - ('street', models.CharField(blank=True, max_length=55, null=True, verbose_name='Street')), - ('city', models.CharField(blank=True, max_length=255, null=True, verbose_name='City')), - ('state', models.CharField(blank=True, max_length=255, null=True, verbose_name='State')), - ('postcode', models.CharField(blank=True, max_length=64, null=True, verbose_name='Post/Zip-code')), - ('country', models.CharField(blank=True, choices=[('GB', 'United Kingdom'), ('AF', 'Afghanistan'), ('AX', 'Aland Islands'), ('AL', 'Albania'), ('DZ', 'Algeria'), ('AS', 'American Samoa'), ('AD', 'Andorra'), ('AO', 'Angola'), ('AI', 'Anguilla'), ('AQ', 'Antarctica'), ('AG', 'Antigua and Barbuda'), ('AR', 'Argentina'), ('AM', 'Armenia'), ('AW', 'Aruba'), ('AU', 'Australia'), ('AT', 'Austria'), ('AZ', 'Azerbaijan'), ('BS', 'Bahamas'), ('BH', 'Bahrain'), ('BD', 'Bangladesh'), ('BB', 'Barbados'), ('BY', 'Belarus'), ('BE', 'Belgium'), ('BZ', 'Belize'), ('BJ', 'Benin'), ('BM', 'Bermuda'), ('BT', 'Bhutan'), ('BO', 'Bolivia'), ('BA', 'Bosnia and Herzegovina'), ('BW', 'Botswana'), ('BV', 'Bouvet Island'), ('BR', 'Brazil'), ('IO', 'British Indian Ocean Territory'), ('BN', 'Brunei Darussalam'), ('BG', 'Bulgaria'), ('BF', 'Burkina Faso'), ('BI', 'Burundi'), ('KH', 'Cambodia'), ('CM', 'Cameroon'), ('CA', 'Canada'), ('CV', 'Cape Verde'), ('KY', 'Cayman Islands'), ('CF', 'Central African Republic'), ('TD', 'Chad'), ('CL', 'Chile'), ('CN', 'China'), ('CX', 'Christmas Island'), ('CC', 'Cocos (Keeling) Islands'), ('CO', 'Colombia'), ('KM', 'Comoros'), ('CG', 'Congo'), ('CD', 'Congo, The Democratic Republic of the'), ('CK', 'Cook Islands'), ('CR', 'Costa Rica'), ('CI', "Cote d'Ivoire"), ('HR', 'Croatia'), ('CU', 'Cuba'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DK', 'Denmark'), ('DJ', 'Djibouti'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('EC', 'Ecuador'), ('EG', 'Egypt'), ('SV', 'El Salvador'), ('GQ', 'Equatorial Guinea'), ('ER', 'Eritrea'), ('EE', 'Estonia'), ('ET', 'Ethiopia'), ('FK', 'Falkland Islands (Malvinas)'), ('FO', 'Faroe Islands'), ('FJ', 'Fiji'), ('FI', 'Finland'), ('FR', 'France'), ('GF', 'French Guiana'), ('PF', 'French Polynesia'), ('TF', 'French Southern Territories'), ('GA', 'Gabon'), ('GM', 'Gambia'), ('GE', 'Georgia'), ('DE', 'Germany'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GR', 'Greece'), ('GL', 'Greenland'), ('GD', 'Grenada'), ('GP', 'Guadeloupe'), ('GU', 'Guam'), ('GT', 'Guatemala'), ('GG', 'Guernsey'), ('GN', 'Guinea'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HT', 'Haiti'), ('HM', 'Heard Island and McDonald Islands'), ('VA', 'Holy See (Vatican City State)'), ('HN', 'Honduras'), ('HK', 'Hong Kong'), ('HU', 'Hungary'), ('IS', 'Iceland'), ('IN', 'India'), ('ID', 'Indonesia'), ('IR', 'Iran, Islamic Republic of'), ('IQ', 'Iraq'), ('IE', 'Ireland'), ('IM', 'Isle of Man'), ('IL', 'Israel'), ('IT', 'Italy'), ('JM', 'Jamaica'), ('JP', 'Japan'), ('JE', 'Jersey'), ('JO', 'Jordan'), ('KZ', 'Kazakhstan'), ('KE', 'Kenya'), ('KI', 'Kiribati'), ('KP', "Korea, Democratic People's Republic of"), ('KR', 'Korea, Republic of'), ('KW', 'Kuwait'), ('KG', 'Kyrgyzstan'), ('LA', "Lao People's Democratic Republic"), ('LV', 'Latvia'), ('LB', 'Lebanon'), ('LS', 'Lesotho'), ('LR', 'Liberia'), ('LY', 'Libyan Arab Jamahiriya'), ('LI', 'Liechtenstein'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('MO', 'Macao'), ('MK', 'Macedonia, The Former Yugoslav Republic of'), ('MG', 'Madagascar'), ('MW', 'Malawi'), ('MY', 'Malaysia'), ('MV', 'Maldives'), ('ML', 'Mali'), ('MT', 'Malta'), ('MH', 'Marshall Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MU', 'Mauritius'), ('YT', 'Mayotte'), ('MX', 'Mexico'), ('FM', 'Micronesia, Federated States of'), ('MD', 'Moldova'), ('MC', 'Monaco'), ('MN', 'Mongolia'), ('ME', 'Montenegro'), ('MS', 'Montserrat'), ('MA', 'Morocco'), ('MZ', 'Mozambique'), ('MM', 'Myanmar'), ('NA', 'Namibia'), ('NR', 'Nauru'), ('NP', 'Nepal'), ('NL', 'Netherlands'), ('AN', 'Netherlands Antilles'), ('NC', 'New Caledonia'), ('NZ', 'New Zealand'), ('NI', 'Nicaragua'), ('NE', 'Niger'), ('NG', 'Nigeria'), ('NU', 'Niue'), ('NF', 'Norfolk Island'), ('MP', 'Northern Mariana Islands'), ('NO', 'Norway'), ('OM', 'Oman'), ('PK', 'Pakistan'), ('PW', 'Palau'), ('PS', 'Palestinian Territory, Occupied'), ('PA', 'Panama'), ('PG', 'Papua New Guinea'), ('PY', 'Paraguay'), ('PE', 'Peru'), ('PH', 'Philippines'), ('PN', 'Pitcairn'), ('PL', 'Poland'), ('PT', 'Portugal'), ('PR', 'Puerto Rico'), ('QA', 'Qatar'), ('RE', 'Reunion'), ('RO', 'Romania'), ('RU', 'Russian Federation'), ('RW', 'Rwanda'), ('BL', 'Saint Barthelemy'), ('SH', 'Saint Helena'), ('KN', 'Saint Kitts and Nevis'), ('LC', 'Saint Lucia'), ('MF', 'Saint Martin'), ('PM', 'Saint Pierre and Miquelon'), ('VC', 'Saint Vincent and the Grenadines'), ('WS', 'Samoa'), ('SM', 'San Marino'), ('ST', 'Sao Tome and Principe'), ('SA', 'Saudi Arabia'), ('SN', 'Senegal'), ('RS', 'Serbia'), ('SC', 'Seychelles'), ('SL', 'Sierra Leone'), ('SG', 'Singapore'), ('SK', 'Slovakia'), ('SI', 'Slovenia'), ('SB', 'Solomon Islands'), ('SO', 'Somalia'), ('ZA', 'South Africa'), ('GS', 'South Georgia and the South Sandwich Islands'), ('ES', 'Spain'), ('LK', 'Sri Lanka'), ('SD', 'Sudan'), ('SR', 'Suriname'), ('SJ', 'Svalbard and Jan Mayen'), ('SZ', 'Swaziland'), ('SE', 'Sweden'), ('CH', 'Switzerland'), ('SY', 'Syrian Arab Republic'), ('TW', 'Taiwan, Province of China'), ('TJ', 'Tajikistan'), ('TZ', 'Tanzania, United Republic of'), ('TH', 'Thailand'), ('TL', 'Timor-Leste'), ('TG', 'Togo'), ('TK', 'Tokelau'), ('TO', 'Tonga'), ('TT', 'Trinidad and Tobago'), ('TN', 'Tunisia'), ('TR', 'Turkey'), ('TM', 'Turkmenistan'), ('TC', 'Turks and Caicos Islands'), ('TV', 'Tuvalu'), ('UG', 'Uganda'), ('UA', 'Ukraine'), ('AE', 'United Arab Emirates'), ('US', 'United States'), ('UM', 'United States Minor Outlying Islands'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VU', 'Vanuatu'), ('VE', 'Venezuela'), ('VN', 'Viet Nam'), ('VG', 'Virgin Islands, British'), ('VI', 'Virgin Islands, U.S.'), ('WF', 'Wallis and Futuna'), ('EH', 'Western Sahara'), ('YE', 'Yemen'), ('ZM', 'Zambia'), ('ZW', 'Zimbabwe')], max_length=3, null=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "address_line", + models.CharField( + blank=True, max_length=255, null=True, verbose_name="Address" + ), + ), + ( + "street", + models.CharField( + blank=True, max_length=55, null=True, verbose_name="Street" + ), + ), + ( + "city", + models.CharField( + blank=True, max_length=255, null=True, verbose_name="City" + ), + ), + ( + "state", + models.CharField( + blank=True, max_length=255, null=True, verbose_name="State" + ), + ), + ( + "postcode", + models.CharField( + blank=True, + max_length=64, + null=True, + verbose_name="Post/Zip-code", + ), + ), + ( + "country", + models.CharField( + blank=True, + choices=[ + ("GB", "United Kingdom"), + ("AF", "Afghanistan"), + ("AX", "Aland Islands"), + ("AL", "Albania"), + ("DZ", "Algeria"), + ("AS", "American Samoa"), + ("AD", "Andorra"), + ("AO", "Angola"), + ("AI", "Anguilla"), + ("AQ", "Antarctica"), + ("AG", "Antigua and Barbuda"), + ("AR", "Argentina"), + ("AM", "Armenia"), + ("AW", "Aruba"), + ("AU", "Australia"), + ("AT", "Austria"), + ("AZ", "Azerbaijan"), + ("BS", "Bahamas"), + ("BH", "Bahrain"), + ("BD", "Bangladesh"), + ("BB", "Barbados"), + ("BY", "Belarus"), + ("BE", "Belgium"), + ("BZ", "Belize"), + ("BJ", "Benin"), + ("BM", "Bermuda"), + ("BT", "Bhutan"), + ("BO", "Bolivia"), + ("BA", "Bosnia and Herzegovina"), + ("BW", "Botswana"), + ("BV", "Bouvet Island"), + ("BR", "Brazil"), + ("IO", "British Indian Ocean Territory"), + ("BN", "Brunei Darussalam"), + ("BG", "Bulgaria"), + ("BF", "Burkina Faso"), + ("BI", "Burundi"), + ("KH", "Cambodia"), + ("CM", "Cameroon"), + ("CA", "Canada"), + ("CV", "Cape Verde"), + ("KY", "Cayman Islands"), + ("CF", "Central African Republic"), + ("TD", "Chad"), + ("CL", "Chile"), + ("CN", "China"), + ("CX", "Christmas Island"), + ("CC", "Cocos (Keeling) Islands"), + ("CO", "Colombia"), + ("KM", "Comoros"), + ("CG", "Congo"), + ("CD", "Congo, The Democratic Republic of the"), + ("CK", "Cook Islands"), + ("CR", "Costa Rica"), + ("CI", "Cote d'Ivoire"), + ("HR", "Croatia"), + ("CU", "Cuba"), + ("CY", "Cyprus"), + ("CZ", "Czech Republic"), + ("DK", "Denmark"), + ("DJ", "Djibouti"), + ("DM", "Dominica"), + ("DO", "Dominican Republic"), + ("EC", "Ecuador"), + ("EG", "Egypt"), + ("SV", "El Salvador"), + ("GQ", "Equatorial Guinea"), + ("ER", "Eritrea"), + ("EE", "Estonia"), + ("ET", "Ethiopia"), + ("FK", "Falkland Islands (Malvinas)"), + ("FO", "Faroe Islands"), + ("FJ", "Fiji"), + ("FI", "Finland"), + ("FR", "France"), + ("GF", "French Guiana"), + ("PF", "French Polynesia"), + ("TF", "French Southern Territories"), + ("GA", "Gabon"), + ("GM", "Gambia"), + ("GE", "Georgia"), + ("DE", "Germany"), + ("GH", "Ghana"), + ("GI", "Gibraltar"), + ("GR", "Greece"), + ("GL", "Greenland"), + ("GD", "Grenada"), + ("GP", "Guadeloupe"), + ("GU", "Guam"), + ("GT", "Guatemala"), + ("GG", "Guernsey"), + ("GN", "Guinea"), + ("GW", "Guinea-Bissau"), + ("GY", "Guyana"), + ("HT", "Haiti"), + ("HM", "Heard Island and McDonald Islands"), + ("VA", "Holy See (Vatican City State)"), + ("HN", "Honduras"), + ("HK", "Hong Kong"), + ("HU", "Hungary"), + ("IS", "Iceland"), + ("IN", "India"), + ("ID", "Indonesia"), + ("IR", "Iran, Islamic Republic of"), + ("IQ", "Iraq"), + ("IE", "Ireland"), + ("IM", "Isle of Man"), + ("IL", "Israel"), + ("IT", "Italy"), + ("JM", "Jamaica"), + ("JP", "Japan"), + ("JE", "Jersey"), + ("JO", "Jordan"), + ("KZ", "Kazakhstan"), + ("KE", "Kenya"), + ("KI", "Kiribati"), + ("KP", "Korea, Democratic People's Republic of"), + ("KR", "Korea, Republic of"), + ("KW", "Kuwait"), + ("KG", "Kyrgyzstan"), + ("LA", "Lao People's Democratic Republic"), + ("LV", "Latvia"), + ("LB", "Lebanon"), + ("LS", "Lesotho"), + ("LR", "Liberia"), + ("LY", "Libyan Arab Jamahiriya"), + ("LI", "Liechtenstein"), + ("LT", "Lithuania"), + ("LU", "Luxembourg"), + ("MO", "Macao"), + ("MK", "Macedonia, The Former Yugoslav Republic of"), + ("MG", "Madagascar"), + ("MW", "Malawi"), + ("MY", "Malaysia"), + ("MV", "Maldives"), + ("ML", "Mali"), + ("MT", "Malta"), + ("MH", "Marshall Islands"), + ("MQ", "Martinique"), + ("MR", "Mauritania"), + ("MU", "Mauritius"), + ("YT", "Mayotte"), + ("MX", "Mexico"), + ("FM", "Micronesia, Federated States of"), + ("MD", "Moldova"), + ("MC", "Monaco"), + ("MN", "Mongolia"), + ("ME", "Montenegro"), + ("MS", "Montserrat"), + ("MA", "Morocco"), + ("MZ", "Mozambique"), + ("MM", "Myanmar"), + ("NA", "Namibia"), + ("NR", "Nauru"), + ("NP", "Nepal"), + ("NL", "Netherlands"), + ("AN", "Netherlands Antilles"), + ("NC", "New Caledonia"), + ("NZ", "New Zealand"), + ("NI", "Nicaragua"), + ("NE", "Niger"), + ("NG", "Nigeria"), + ("NU", "Niue"), + ("NF", "Norfolk Island"), + ("MP", "Northern Mariana Islands"), + ("NO", "Norway"), + ("OM", "Oman"), + ("PK", "Pakistan"), + ("PW", "Palau"), + ("PS", "Palestinian Territory, Occupied"), + ("PA", "Panama"), + ("PG", "Papua New Guinea"), + ("PY", "Paraguay"), + ("PE", "Peru"), + ("PH", "Philippines"), + ("PN", "Pitcairn"), + ("PL", "Poland"), + ("PT", "Portugal"), + ("PR", "Puerto Rico"), + ("QA", "Qatar"), + ("RE", "Reunion"), + ("RO", "Romania"), + ("RU", "Russian Federation"), + ("RW", "Rwanda"), + ("BL", "Saint Barthelemy"), + ("SH", "Saint Helena"), + ("KN", "Saint Kitts and Nevis"), + ("LC", "Saint Lucia"), + ("MF", "Saint Martin"), + ("PM", "Saint Pierre and Miquelon"), + ("VC", "Saint Vincent and the Grenadines"), + ("WS", "Samoa"), + ("SM", "San Marino"), + ("ST", "Sao Tome and Principe"), + ("SA", "Saudi Arabia"), + ("SN", "Senegal"), + ("RS", "Serbia"), + ("SC", "Seychelles"), + ("SL", "Sierra Leone"), + ("SG", "Singapore"), + ("SK", "Slovakia"), + ("SI", "Slovenia"), + ("SB", "Solomon Islands"), + ("SO", "Somalia"), + ("ZA", "South Africa"), + ("GS", "South Georgia and the South Sandwich Islands"), + ("ES", "Spain"), + ("LK", "Sri Lanka"), + ("SD", "Sudan"), + ("SR", "Suriname"), + ("SJ", "Svalbard and Jan Mayen"), + ("SZ", "Swaziland"), + ("SE", "Sweden"), + ("CH", "Switzerland"), + ("SY", "Syrian Arab Republic"), + ("TW", "Taiwan, Province of China"), + ("TJ", "Tajikistan"), + ("TZ", "Tanzania, United Republic of"), + ("TH", "Thailand"), + ("TL", "Timor-Leste"), + ("TG", "Togo"), + ("TK", "Tokelau"), + ("TO", "Tonga"), + ("TT", "Trinidad and Tobago"), + ("TN", "Tunisia"), + ("TR", "Turkey"), + ("TM", "Turkmenistan"), + ("TC", "Turks and Caicos Islands"), + ("TV", "Tuvalu"), + ("UG", "Uganda"), + ("UA", "Ukraine"), + ("AE", "United Arab Emirates"), + ("US", "United States"), + ("UM", "United States Minor Outlying Islands"), + ("UY", "Uruguay"), + ("UZ", "Uzbekistan"), + ("VU", "Vanuatu"), + ("VE", "Venezuela"), + ("VN", "Viet Nam"), + ("VG", "Virgin Islands, British"), + ("VI", "Virgin Islands, U.S."), + ("WF", "Wallis and Futuna"), + ("EH", "Western Sahara"), + ("YE", "Yemen"), + ("ZM", "Zambia"), + ("ZW", "Zimbabwe"), + ], + max_length=3, + null=True, + ), + ), ], ), migrations.CreateModel( - name='Attachments', + name="Attachments", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('file_name', models.CharField(max_length=60)), - ('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Created on')), - ('attachment', models.FileField(max_length=1001, upload_to='attachments/%Y/%m/')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("file_name", models.CharField(max_length=60)), + ( + "created_on", + models.DateTimeField(auto_now_add=True, verbose_name="Created on"), + ), + ( + "attachment", + models.FileField(max_length=1001, upload_to="attachments/%Y/%m/"), + ), ], ), migrations.CreateModel( - name='Comment', + name="Comment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('comment', models.CharField(max_length=255)), - ('commented_on', models.DateTimeField(auto_now_add=True)), - ('account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='accounts_comments', to='accounts.Account')), - ('case', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cases', to='cases.Case')), - ('commented_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("comment", models.CharField(max_length=255)), + ("commented_on", models.DateTimeField(auto_now_add=True)), + ( + "account", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="accounts_comments", + to="accounts.Account", + ), + ), + ( + "case", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="cases", + to="cases.Case", + ), + ), + ( + "commented_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='Comment_Files', + name="Comment_Files", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('updated_on', models.DateTimeField(auto_now_add=True)), - ('comment_file', models.FileField(default='', upload_to='comment_files', verbose_name='File')), - ('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='common.Comment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("updated_on", models.DateTimeField(auto_now_add=True)), + ( + "comment_file", + models.FileField( + default="", upload_to="comment_files", verbose_name="File" + ), + ), + ( + "comment", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="common.Comment" + ), + ), ], ), migrations.CreateModel( - name='Team', + name="Team", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=55)), - ('members', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=55)), + ("members", models.ManyToManyField(to=settings.AUTH_USER_MODEL)), ], ), ] diff --git a/common/migrations/0002_auto_20190128_1237.py b/common/migrations/0002_auto_20190128_1237.py index dac2b1e..c03e89d 100644 --- a/common/migrations/0002_auto_20190128_1237.py +++ b/common/migrations/0002_auto_20190128_1237.py @@ -10,63 +10,123 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('contacts', '0001_initial'), - ('common', '0001_initial'), - ('opportunity', '0001_initial'), - ('auth', '0009_alter_user_last_name_max_length'), - ('leads', '0001_initial'), - ('accounts', '0002_auto_20190128_1237'), + ("contacts", "0001_initial"), + ("common", "0001_initial"), + ("opportunity", "0001_initial"), + ("auth", "0009_alter_user_last_name_max_length"), + ("leads", "0001_initial"), + ("accounts", "0002_auto_20190128_1237"), ] operations = [ migrations.AddField( - model_name='comment', - name='contact', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contact_comments', to='contacts.Contact'), + model_name="comment", + name="contact", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="contact_comments", + to="contacts.Contact", + ), ), migrations.AddField( - model_name='comment', - name='lead', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='leads', to='leads.Lead'), + model_name="comment", + name="lead", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="leads", + to="leads.Lead", + ), ), migrations.AddField( - model_name='comment', - name='opportunity', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opportunity_comments', to='opportunity.Opportunity'), + model_name="comment", + name="opportunity", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="opportunity_comments", + to="opportunity.Opportunity", + ), ), migrations.AddField( - model_name='attachments', - name='account', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='account_attachment', to='accounts.Account'), + model_name="attachments", + name="account", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="account_attachment", + to="accounts.Account", + ), ), migrations.AddField( - model_name='attachments', - name='contact', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contact_attachment', to='contacts.Contact'), + model_name="attachments", + name="contact", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="contact_attachment", + to="contacts.Contact", + ), ), migrations.AddField( - model_name='attachments', - name='created_by', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachment_created_by', to=settings.AUTH_USER_MODEL), + model_name="attachments", + name="created_by", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="attachment_created_by", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='attachments', - name='lead', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lead_attachment', to='leads.Lead'), + model_name="attachments", + name="lead", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="lead_attachment", + to="leads.Lead", + ), ), migrations.AddField( - model_name='attachments', - name='opportunity', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opportunity_attachment', to='opportunity.Opportunity'), + model_name="attachments", + name="opportunity", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="opportunity_attachment", + to="opportunity.Opportunity", + ), ), migrations.AddField( - model_name='user', - name='groups', - field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'), + model_name="user", + name="groups", + field=models.ManyToManyField( + blank=True, + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.Group", + verbose_name="groups", + ), ), migrations.AddField( - model_name='user', - name='user_permissions', - field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'), + model_name="user", + name="user_permissions", + field=models.ManyToManyField( + blank=True, + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.Permission", + verbose_name="user permissions", + ), ), ] diff --git a/common/migrations/0003_document.py b/common/migrations/0003_document.py index 2861c5f..d4059c4 100644 --- a/common/migrations/0003_document.py +++ b/common/migrations/0003_document.py @@ -9,18 +9,38 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0002_auto_20190128_1237'), + ("common", "0002_auto_20190128_1237"), ] operations = [ migrations.CreateModel( - name='Document', + name="Document", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(blank=True, max_length=1000, null=True)), - ('document_file', models.FileField(max_length=5000, upload_to=common.models.document_path)), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='document_uploaded', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(blank=True, max_length=1000, null=True)), + ( + "document_file", + models.FileField( + max_length=5000, upload_to=common.models.document_path + ), + ), + ("created_on", models.DateTimeField(auto_now_add=True)), + ( + "created_by", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="document_uploaded", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/common/migrations/0004_attachments_case.py b/common/migrations/0004_attachments_case.py index af324ca..1ac3bab 100644 --- a/common/migrations/0004_attachments_case.py +++ b/common/migrations/0004_attachments_case.py @@ -7,14 +7,20 @@ class Migration(migrations.Migration): dependencies = [ - ('cases', '0002_auto_20190128_1237'), - ('common', '0003_document'), + ("cases", "0002_auto_20190128_1237"), + ("common", "0003_document"), ] operations = [ migrations.AddField( - model_name='attachments', - name='case', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='case_attachment', to='cases.Case'), + model_name="attachments", + name="case", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="case_attachment", + to="cases.Case", + ), ), ] diff --git a/common/migrations/0005_auto_20190204_1400.py b/common/migrations/0005_auto_20190204_1400.py index 3486076..03fb6d4 100644 --- a/common/migrations/0005_auto_20190204_1400.py +++ b/common/migrations/0005_auto_20190204_1400.py @@ -6,25 +6,28 @@ def generate_document_status(apps, schema_editor): Document = apps.get_model("common", "Document") for document in Document.objects.all(): - document.status = 'active' + document.status = "active" document.save() class Migration(migrations.Migration): dependencies = [ - ('common', '0004_attachments_case'), + ("common", "0004_attachments_case"), ] operations = [ migrations.AlterModelOptions( - name='user', - options={'ordering': ['-is_active']}, + name="user", options={"ordering": ["-is_active"]}, ), migrations.AddField( - model_name='document', - name='status', - field=models.CharField(choices=[('active', 'active'), ('inactive', 'inactive')], default='active', max_length=64), + model_name="document", + name="status", + field=models.CharField( + choices=[("active", "active"), ("inactive", "inactive")], + default="active", + max_length=64, + ), ), - migrations.RunPython(generate_document_status) + migrations.RunPython(generate_document_status), ] diff --git a/common/migrations/0006_comment_user.py b/common/migrations/0006_comment_user.py index ad4237e..e3b16f4 100644 --- a/common/migrations/0006_comment_user.py +++ b/common/migrations/0006_comment_user.py @@ -8,13 +8,19 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0005_auto_20190204_1400'), + ("common", "0005_auto_20190204_1400"), ] operations = [ migrations.AddField( - model_name='comment', - name='user', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_comments', to=settings.AUTH_USER_MODEL), + model_name="comment", + name="user", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="user_comments", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/common/migrations/0007_auto_20190212_1334.py b/common/migrations/0007_auto_20190212_1334.py index d5d6615..00a78f3 100644 --- a/common/migrations/0007_auto_20190212_1334.py +++ b/common/migrations/0007_auto_20190212_1334.py @@ -8,31 +8,36 @@ class Migration(migrations.Migration): dependencies = [ - ('contacts', '0002_auto_20190212_1334'), - ('planner', '0002_auto_20190212_1334'), - ('cases', '0003_auto_20190212_1334'), - ('opportunity', '0003_auto_20190212_1334'), - ('leads', '0004_auto_20190212_1334'), - ('accounts', '0005_auto_20190212_1334'), - ('common', '0006_comment_user'), + ("contacts", "0002_auto_20190212_1334"), + ("planner", "0002_auto_20190212_1334"), + ("cases", "0003_auto_20190212_1334"), + ("opportunity", "0003_auto_20190212_1334"), + ("leads", "0004_auto_20190212_1334"), + ("accounts", "0005_auto_20190212_1334"), + ("common", "0006_comment_user"), ] operations = [ - migrations.RemoveField( - model_name='team', - name='members', - ), + migrations.RemoveField(model_name="team", name="members",), migrations.AlterField( - model_name='attachments', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='attachment_created_by', to=settings.AUTH_USER_MODEL), + model_name="attachments", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="attachment_created_by", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='document', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='document_uploaded', to=settings.AUTH_USER_MODEL), - ), - migrations.DeleteModel( - name='Team', + model_name="document", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="document_uploaded", + to=settings.AUTH_USER_MODEL, + ), ), + migrations.DeleteModel(name="Team",), ] diff --git a/common/migrations/0008_google.py b/common/migrations/0008_google.py index 7d4eb1f..075fbd4 100644 --- a/common/migrations/0008_google.py +++ b/common/migrations/0008_google.py @@ -8,24 +8,39 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0007_auto_20190212_1334'), + ("common", "0007_auto_20190212_1334"), ] operations = [ migrations.CreateModel( - name='Google', + name="Google", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('google_id', models.CharField(default='', max_length=200)), - ('google_url', models.CharField(default='', max_length=1000)), - ('verified_email', models.CharField(default='', max_length=200)), - ('family_name', models.CharField(default='', max_length=200)), - ('name', models.CharField(default='', max_length=200)), - ('gender', models.CharField(default='', max_length=10)), - ('dob', models.CharField(default='', max_length=50)), - ('given_name', models.CharField(default='', max_length=200)), - ('email', models.CharField(db_index=True, default='', max_length=200)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='google', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("google_id", models.CharField(default="", max_length=200)), + ("google_url", models.CharField(default="", max_length=1000)), + ("verified_email", models.CharField(default="", max_length=200)), + ("family_name", models.CharField(default="", max_length=200)), + ("name", models.CharField(default="", max_length=200)), + ("gender", models.CharField(default="", max_length=10)), + ("dob", models.CharField(default="", max_length=50)), + ("given_name", models.CharField(default="", max_length=200)), + ("email", models.CharField(db_index=True, default="", max_length=200)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="google", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/common/migrations/0009_document_shared_to.py b/common/migrations/0009_document_shared_to.py index ab6ef43..b1c8e32 100644 --- a/common/migrations/0009_document_shared_to.py +++ b/common/migrations/0009_document_shared_to.py @@ -7,13 +7,15 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0008_google'), + ("common", "0008_google"), ] operations = [ migrations.AddField( - model_name='document', - name='shared_to', - field=models.ManyToManyField(related_name='document_shared_to', to=settings.AUTH_USER_MODEL), + model_name="document", + name="shared_to", + field=models.ManyToManyField( + related_name="document_shared_to", to=settings.AUTH_USER_MODEL + ), ), ] diff --git a/common/migrations/0010_apisettings.py b/common/migrations/0010_apisettings.py index d17d214..c723555 100644 --- a/common/migrations/0010_apisettings.py +++ b/common/migrations/0010_apisettings.py @@ -8,21 +8,47 @@ class Migration(migrations.Migration): dependencies = [ - ('accounts', '0006_auto_20190212_1708'), - ('common', '0009_document_shared_to'), + ("accounts", "0006_auto_20190212_1708"), + ("common", "0009_document_shared_to"), ] operations = [ migrations.CreateModel( - name='APISettings', + name="APISettings", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=1000)), - ('apikey', models.CharField(default='a223998f39691c748515bca13519b7b3', max_length=16)), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='settings_created_by', to=settings.AUTH_USER_MODEL)), - ('lead_assigned_to', models.ManyToManyField(related_name='lead_assignee_users', to=settings.AUTH_USER_MODEL)), - ('tags', models.ManyToManyField(blank=True, to='accounts.Tags')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=1000)), + ( + "apikey", + models.CharField( + default="a223998f39691c748515bca13519b7b3", max_length=16 + ), + ), + ("created_on", models.DateTimeField(auto_now_add=True)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="settings_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "lead_assigned_to", + models.ManyToManyField( + related_name="lead_assignee_users", to=settings.AUTH_USER_MODEL + ), + ), + ("tags", models.ManyToManyField(blank=True, to="accounts.Tags")), ], ), ] diff --git a/common/migrations/0011_auto_20190218_1230.py b/common/migrations/0011_auto_20190218_1230.py index cdc4a12..731e5e5 100644 --- a/common/migrations/0011_auto_20190218_1230.py +++ b/common/migrations/0011_auto_20190218_1230.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0010_apisettings'), + ("common", "0010_apisettings"), ] operations = [ migrations.AlterField( - model_name='apisettings', - name='apikey', + model_name="apisettings", + name="apikey", field=models.CharField(blank=True, max_length=16), ), ] diff --git a/common/migrations/0012_apisettings_website.py b/common/migrations/0012_apisettings_website.py index ca8fa0a..78f656d 100644 --- a/common/migrations/0012_apisettings_website.py +++ b/common/migrations/0012_apisettings_website.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0011_auto_20190218_1230'), + ("common", "0011_auto_20190218_1230"), ] operations = [ migrations.AddField( - model_name='apisettings', - name='website', - field=models.URLField(default='', max_length=255), + model_name="apisettings", + name="website", + field=models.URLField(default="", max_length=255), ), ] diff --git a/common/migrations/0013_auto_20190508_1540.py b/common/migrations/0013_auto_20190508_1540.py index 8873d88..3fb22d0 100644 --- a/common/migrations/0013_auto_20190508_1540.py +++ b/common/migrations/0013_auto_20190508_1540.py @@ -7,19 +7,31 @@ class Migration(migrations.Migration): dependencies = [ - ('tasks', '0002_task_created_by'), - ('common', '0012_apisettings_website'), + ("tasks", "0002_task_created_by"), + ("common", "0012_apisettings_website"), ] operations = [ migrations.AddField( - model_name='attachments', - name='task', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tasks_attachment', to='tasks.Task'), + model_name="attachments", + name="task", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="tasks_attachment", + to="tasks.Task", + ), ), migrations.AddField( - model_name='comment', - name='task', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tasks_comments', to='tasks.Task'), + model_name="comment", + name="task", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="tasks_comments", + to="tasks.Task", + ), ), ] diff --git a/common/migrations/0014_auto_20190524_1113.py b/common/migrations/0014_auto_20190524_1113.py index 46bad34..ceddf6e 100644 --- a/common/migrations/0014_auto_20190524_1113.py +++ b/common/migrations/0014_auto_20190524_1113.py @@ -7,19 +7,31 @@ class Migration(migrations.Migration): dependencies = [ - ('invoices', '0002_auto_20190524_1113'), - ('common', '0013_auto_20190508_1540'), + ("invoices", "0002_auto_20190524_1113"), + ("common", "0013_auto_20190508_1540"), ] operations = [ migrations.AddField( - model_name='attachments', - name='invoice', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='invoice_attachment', to='invoices.Invoice'), + model_name="attachments", + name="invoice", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="invoice_attachment", + to="invoices.Invoice", + ), ), migrations.AddField( - model_name='comment', - name='invoice', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='invoice_comments', to='invoices.Invoice'), + model_name="comment", + name="invoice", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="invoice_comments", + to="invoices.Invoice", + ), ), ] diff --git a/common/migrations/0015_auto_20190604_1830.py b/common/migrations/0015_auto_20190604_1830.py index d5c1c21..61e7b67 100644 --- a/common/migrations/0015_auto_20190604_1830.py +++ b/common/migrations/0015_auto_20190604_1830.py @@ -7,19 +7,31 @@ class Migration(migrations.Migration): dependencies = [ - ('events', '0002_event_date_of_meeting'), - ('common', '0014_auto_20190524_1113'), + ("events", "0002_event_date_of_meeting"), + ("common", "0014_auto_20190524_1113"), ] operations = [ migrations.AddField( - model_name='attachments', - name='event', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='events_attachment', to='events.Event'), + model_name="attachments", + name="event", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="events_attachment", + to="events.Event", + ), ), migrations.AddField( - model_name='comment', - name='event', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='events_comments', to='events.Event'), + model_name="comment", + name="event", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="events_comments", + to="events.Event", + ), ), ] diff --git a/common/migrations/0016_auto_20190624_1816.py b/common/migrations/0016_auto_20190624_1816.py index b1dde93..5d0b445 100644 --- a/common/migrations/0016_auto_20190624_1816.py +++ b/common/migrations/0016_auto_20190624_1816.py @@ -7,23 +7,29 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0015_auto_20190604_1830'), + ("common", "0015_auto_20190604_1830"), ] operations = [ migrations.AddField( - model_name='user', - name='has_marketing_access', + model_name="user", + name="has_marketing_access", field=models.BooleanField(default=False), ), migrations.AddField( - model_name='user', - name='has_sales_access', + model_name="user", + name="has_sales_access", field=models.BooleanField(default=False), ), migrations.AlterField( - model_name='comment', - name='lead', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='leads_comments', to='leads.Lead'), + model_name="comment", + name="lead", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="leads_comments", + to="leads.Lead", + ), ), ] diff --git a/common/migrations/0017_auto_20190722_1443.py b/common/migrations/0017_auto_20190722_1443.py index dc14176..c12797d 100644 --- a/common/migrations/0017_auto_20190722_1443.py +++ b/common/migrations/0017_auto_20190722_1443.py @@ -8,25 +8,38 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0016_auto_20190624_1816'), + ("common", "0016_auto_20190624_1816"), ] operations = [ migrations.AlterModelOptions( - name='apisettings', - options={'ordering': ('-created_on',)}, + name="apisettings", options={"ordering": ("-created_on",)}, ), migrations.AlterModelOptions( - name='document', - options={'ordering': ('-created_on',)}, + name="document", options={"ordering": ("-created_on",)}, ), migrations.CreateModel( - name='Profile', + name="Profile", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('activation_key', models.CharField(max_length=50)), - ('key_expires', models.DateTimeField()), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("activation_key", models.CharField(max_length=50)), + ("key_expires", models.DateTimeField()), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="profile", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/common/migrations/0018_document_teams.py b/common/migrations/0018_document_teams.py index 7eb9b52..3996234 100644 --- a/common/migrations/0018_document_teams.py +++ b/common/migrations/0018_document_teams.py @@ -6,14 +6,16 @@ class Migration(migrations.Migration): dependencies = [ - ('teams', '0003_auto_20190909_1621'), - ('common', '0017_auto_20190722_1443'), + ("teams", "0003_auto_20190909_1621"), + ("common", "0017_auto_20190722_1443"), ] operations = [ migrations.AddField( - model_name='document', - name='teams', - field=models.ManyToManyField(related_name='document_teams', to='teams.Teams'), + model_name="document", + name="teams", + field=models.ManyToManyField( + related_name="document_teams", to="teams.Teams" + ), ), ] diff --git a/common/migrations/0019_auto_20200401_0941.py b/common/migrations/0019_auto_20200401_0941.py index 9839ba2..66fc6d9 100644 --- a/common/migrations/0019_auto_20200401_0941.py +++ b/common/migrations/0019_auto_20200401_0941.py @@ -7,25 +7,292 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0018_document_teams'), + ("common", "0018_document_teams"), ] operations = [ migrations.CreateModel( - name='Company', + name="Company", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('address', models.CharField(max_length=2000)), - ('sub_domain', models.CharField(max_length=30)), - ('user_limit', models.IntegerField(default=5)), - ('country', models.CharField(blank=True, choices=[('GB', 'United Kingdom'), ('AF', 'Afghanistan'), ('AX', 'Aland Islands'), ('AL', 'Albania'), ('DZ', 'Algeria'), ('AS', 'American Samoa'), ('AD', 'Andorra'), ('AO', 'Angola'), ('AI', 'Anguilla'), ('AQ', 'Antarctica'), ('AG', 'Antigua and Barbuda'), ('AR', 'Argentina'), ('AM', 'Armenia'), ('AW', 'Aruba'), ('AU', 'Australia'), ('AT', 'Austria'), ('AZ', 'Azerbaijan'), ('BS', 'Bahamas'), ('BH', 'Bahrain'), ('BD', 'Bangladesh'), ('BB', 'Barbados'), ('BY', 'Belarus'), ('BE', 'Belgium'), ('BZ', 'Belize'), ('BJ', 'Benin'), ('BM', 'Bermuda'), ('BT', 'Bhutan'), ('BO', 'Bolivia'), ('BA', 'Bosnia and Herzegovina'), ('BW', 'Botswana'), ('BV', 'Bouvet Island'), ('BR', 'Brazil'), ('IO', 'British Indian Ocean Territory'), ('BN', 'Brunei Darussalam'), ('BG', 'Bulgaria'), ('BF', 'Burkina Faso'), ('BI', 'Burundi'), ('KH', 'Cambodia'), ('CM', 'Cameroon'), ('CA', 'Canada'), ('CV', 'Cape Verde'), ('KY', 'Cayman Islands'), ('CF', 'Central African Republic'), ('TD', 'Chad'), ('CL', 'Chile'), ('CN', 'China'), ('CX', 'Christmas Island'), ('CC', 'Cocos (Keeling) Islands'), ('CO', 'Colombia'), ('KM', 'Comoros'), ('CG', 'Congo'), ('CD', 'Congo, The Democratic Republic of the'), ('CK', 'Cook Islands'), ('CR', 'Costa Rica'), ('CI', "Cote d'Ivoire"), ('HR', 'Croatia'), ('CU', 'Cuba'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DK', 'Denmark'), ('DJ', 'Djibouti'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('EC', 'Ecuador'), ('EG', 'Egypt'), ('SV', 'El Salvador'), ('GQ', 'Equatorial Guinea'), ('ER', 'Eritrea'), ('EE', 'Estonia'), ('ET', 'Ethiopia'), ('FK', 'Falkland Islands (Malvinas)'), ('FO', 'Faroe Islands'), ('FJ', 'Fiji'), ('FI', 'Finland'), ('FR', 'France'), ('GF', 'French Guiana'), ('PF', 'French Polynesia'), ('TF', 'French Southern Territories'), ('GA', 'Gabon'), ('GM', 'Gambia'), ('GE', 'Georgia'), ('DE', 'Germany'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GR', 'Greece'), ('GL', 'Greenland'), ('GD', 'Grenada'), ('GP', 'Guadeloupe'), ('GU', 'Guam'), ('GT', 'Guatemala'), ('GG', 'Guernsey'), ('GN', 'Guinea'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HT', 'Haiti'), ('HM', 'Heard Island and McDonald Islands'), ('VA', 'Holy See (Vatican City State)'), ('HN', 'Honduras'), ('HK', 'Hong Kong'), ('HU', 'Hungary'), ('IS', 'Iceland'), ('IN', 'India'), ('ID', 'Indonesia'), ('IR', 'Iran, Islamic Republic of'), ('IQ', 'Iraq'), ('IE', 'Ireland'), ('IM', 'Isle of Man'), ('IL', 'Israel'), ('IT', 'Italy'), ('JM', 'Jamaica'), ('JP', 'Japan'), ('JE', 'Jersey'), ('JO', 'Jordan'), ('KZ', 'Kazakhstan'), ('KE', 'Kenya'), ('KI', 'Kiribati'), ('KP', "Korea, Democratic People's Republic of"), ('KR', 'Korea, Republic of'), ('KW', 'Kuwait'), ('KG', 'Kyrgyzstan'), ('LA', "Lao People's Democratic Republic"), ('LV', 'Latvia'), ('LB', 'Lebanon'), ('LS', 'Lesotho'), ('LR', 'Liberia'), ('LY', 'Libyan Arab Jamahiriya'), ('LI', 'Liechtenstein'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('MO', 'Macao'), ('MK', 'Macedonia, The Former Yugoslav Republic of'), ('MG', 'Madagascar'), ('MW', 'Malawi'), ('MY', 'Malaysia'), ('MV', 'Maldives'), ('ML', 'Mali'), ('MT', 'Malta'), ('MH', 'Marshall Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MU', 'Mauritius'), ('YT', 'Mayotte'), ('MX', 'Mexico'), ('FM', 'Micronesia, Federated States of'), ('MD', 'Moldova'), ('MC', 'Monaco'), ('MN', 'Mongolia'), ('ME', 'Montenegro'), ('MS', 'Montserrat'), ('MA', 'Morocco'), ('MZ', 'Mozambique'), ('MM', 'Myanmar'), ('NA', 'Namibia'), ('NR', 'Nauru'), ('NP', 'Nepal'), ('NL', 'Netherlands'), ('AN', 'Netherlands Antilles'), ('NC', 'New Caledonia'), ('NZ', 'New Zealand'), ('NI', 'Nicaragua'), ('NE', 'Niger'), ('NG', 'Nigeria'), ('NU', 'Niue'), ('NF', 'Norfolk Island'), ('MP', 'Northern Mariana Islands'), ('NO', 'Norway'), ('OM', 'Oman'), ('PK', 'Pakistan'), ('PW', 'Palau'), ('PS', 'Palestinian Territory, Occupied'), ('PA', 'Panama'), ('PG', 'Papua New Guinea'), ('PY', 'Paraguay'), ('PE', 'Peru'), ('PH', 'Philippines'), ('PN', 'Pitcairn'), ('PL', 'Poland'), ('PT', 'Portugal'), ('PR', 'Puerto Rico'), ('QA', 'Qatar'), ('RE', 'Reunion'), ('RO', 'Romania'), ('RU', 'Russian Federation'), ('RW', 'Rwanda'), ('BL', 'Saint Barthelemy'), ('SH', 'Saint Helena'), ('KN', 'Saint Kitts and Nevis'), ('LC', 'Saint Lucia'), ('MF', 'Saint Martin'), ('PM', 'Saint Pierre and Miquelon'), ('VC', 'Saint Vincent and the Grenadines'), ('WS', 'Samoa'), ('SM', 'San Marino'), ('ST', 'Sao Tome and Principe'), ('SA', 'Saudi Arabia'), ('SN', 'Senegal'), ('RS', 'Serbia'), ('SC', 'Seychelles'), ('SL', 'Sierra Leone'), ('SG', 'Singapore'), ('SK', 'Slovakia'), ('SI', 'Slovenia'), ('SB', 'Solomon Islands'), ('SO', 'Somalia'), ('ZA', 'South Africa'), ('GS', 'South Georgia and the South Sandwich Islands'), ('ES', 'Spain'), ('LK', 'Sri Lanka'), ('SD', 'Sudan'), ('SR', 'Suriname'), ('SJ', 'Svalbard and Jan Mayen'), ('SZ', 'Swaziland'), ('SE', 'Sweden'), ('CH', 'Switzerland'), ('SY', 'Syrian Arab Republic'), ('TW', 'Taiwan, Province of China'), ('TJ', 'Tajikistan'), ('TZ', 'Tanzania, United Republic of'), ('TH', 'Thailand'), ('TL', 'Timor-Leste'), ('TG', 'Togo'), ('TK', 'Tokelau'), ('TO', 'Tonga'), ('TT', 'Trinidad and Tobago'), ('TN', 'Tunisia'), ('TR', 'Turkey'), ('TM', 'Turkmenistan'), ('TC', 'Turks and Caicos Islands'), ('TV', 'Tuvalu'), ('UG', 'Uganda'), ('UA', 'Ukraine'), ('AE', 'United Arab Emirates'), ('US', 'United States'), ('UM', 'United States Minor Outlying Islands'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VU', 'Vanuatu'), ('VE', 'Venezuela'), ('VN', 'Viet Nam'), ('VG', 'Virgin Islands, British'), ('VI', 'Virgin Islands, U.S.'), ('WF', 'Wallis and Futuna'), ('EH', 'Western Sahara'), ('YE', 'Yemen'), ('ZM', 'Zambia'), ('ZW', 'Zimbabwe')], max_length=3, null=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=100)), + ("address", models.CharField(max_length=2000)), + ("sub_domain", models.CharField(max_length=30)), + ("user_limit", models.IntegerField(default=5)), + ( + "country", + models.CharField( + blank=True, + choices=[ + ("GB", "United Kingdom"), + ("AF", "Afghanistan"), + ("AX", "Aland Islands"), + ("AL", "Albania"), + ("DZ", "Algeria"), + ("AS", "American Samoa"), + ("AD", "Andorra"), + ("AO", "Angola"), + ("AI", "Anguilla"), + ("AQ", "Antarctica"), + ("AG", "Antigua and Barbuda"), + ("AR", "Argentina"), + ("AM", "Armenia"), + ("AW", "Aruba"), + ("AU", "Australia"), + ("AT", "Austria"), + ("AZ", "Azerbaijan"), + ("BS", "Bahamas"), + ("BH", "Bahrain"), + ("BD", "Bangladesh"), + ("BB", "Barbados"), + ("BY", "Belarus"), + ("BE", "Belgium"), + ("BZ", "Belize"), + ("BJ", "Benin"), + ("BM", "Bermuda"), + ("BT", "Bhutan"), + ("BO", "Bolivia"), + ("BA", "Bosnia and Herzegovina"), + ("BW", "Botswana"), + ("BV", "Bouvet Island"), + ("BR", "Brazil"), + ("IO", "British Indian Ocean Territory"), + ("BN", "Brunei Darussalam"), + ("BG", "Bulgaria"), + ("BF", "Burkina Faso"), + ("BI", "Burundi"), + ("KH", "Cambodia"), + ("CM", "Cameroon"), + ("CA", "Canada"), + ("CV", "Cape Verde"), + ("KY", "Cayman Islands"), + ("CF", "Central African Republic"), + ("TD", "Chad"), + ("CL", "Chile"), + ("CN", "China"), + ("CX", "Christmas Island"), + ("CC", "Cocos (Keeling) Islands"), + ("CO", "Colombia"), + ("KM", "Comoros"), + ("CG", "Congo"), + ("CD", "Congo, The Democratic Republic of the"), + ("CK", "Cook Islands"), + ("CR", "Costa Rica"), + ("CI", "Cote d'Ivoire"), + ("HR", "Croatia"), + ("CU", "Cuba"), + ("CY", "Cyprus"), + ("CZ", "Czech Republic"), + ("DK", "Denmark"), + ("DJ", "Djibouti"), + ("DM", "Dominica"), + ("DO", "Dominican Republic"), + ("EC", "Ecuador"), + ("EG", "Egypt"), + ("SV", "El Salvador"), + ("GQ", "Equatorial Guinea"), + ("ER", "Eritrea"), + ("EE", "Estonia"), + ("ET", "Ethiopia"), + ("FK", "Falkland Islands (Malvinas)"), + ("FO", "Faroe Islands"), + ("FJ", "Fiji"), + ("FI", "Finland"), + ("FR", "France"), + ("GF", "French Guiana"), + ("PF", "French Polynesia"), + ("TF", "French Southern Territories"), + ("GA", "Gabon"), + ("GM", "Gambia"), + ("GE", "Georgia"), + ("DE", "Germany"), + ("GH", "Ghana"), + ("GI", "Gibraltar"), + ("GR", "Greece"), + ("GL", "Greenland"), + ("GD", "Grenada"), + ("GP", "Guadeloupe"), + ("GU", "Guam"), + ("GT", "Guatemala"), + ("GG", "Guernsey"), + ("GN", "Guinea"), + ("GW", "Guinea-Bissau"), + ("GY", "Guyana"), + ("HT", "Haiti"), + ("HM", "Heard Island and McDonald Islands"), + ("VA", "Holy See (Vatican City State)"), + ("HN", "Honduras"), + ("HK", "Hong Kong"), + ("HU", "Hungary"), + ("IS", "Iceland"), + ("IN", "India"), + ("ID", "Indonesia"), + ("IR", "Iran, Islamic Republic of"), + ("IQ", "Iraq"), + ("IE", "Ireland"), + ("IM", "Isle of Man"), + ("IL", "Israel"), + ("IT", "Italy"), + ("JM", "Jamaica"), + ("JP", "Japan"), + ("JE", "Jersey"), + ("JO", "Jordan"), + ("KZ", "Kazakhstan"), + ("KE", "Kenya"), + ("KI", "Kiribati"), + ("KP", "Korea, Democratic People's Republic of"), + ("KR", "Korea, Republic of"), + ("KW", "Kuwait"), + ("KG", "Kyrgyzstan"), + ("LA", "Lao People's Democratic Republic"), + ("LV", "Latvia"), + ("LB", "Lebanon"), + ("LS", "Lesotho"), + ("LR", "Liberia"), + ("LY", "Libyan Arab Jamahiriya"), + ("LI", "Liechtenstein"), + ("LT", "Lithuania"), + ("LU", "Luxembourg"), + ("MO", "Macao"), + ("MK", "Macedonia, The Former Yugoslav Republic of"), + ("MG", "Madagascar"), + ("MW", "Malawi"), + ("MY", "Malaysia"), + ("MV", "Maldives"), + ("ML", "Mali"), + ("MT", "Malta"), + ("MH", "Marshall Islands"), + ("MQ", "Martinique"), + ("MR", "Mauritania"), + ("MU", "Mauritius"), + ("YT", "Mayotte"), + ("MX", "Mexico"), + ("FM", "Micronesia, Federated States of"), + ("MD", "Moldova"), + ("MC", "Monaco"), + ("MN", "Mongolia"), + ("ME", "Montenegro"), + ("MS", "Montserrat"), + ("MA", "Morocco"), + ("MZ", "Mozambique"), + ("MM", "Myanmar"), + ("NA", "Namibia"), + ("NR", "Nauru"), + ("NP", "Nepal"), + ("NL", "Netherlands"), + ("AN", "Netherlands Antilles"), + ("NC", "New Caledonia"), + ("NZ", "New Zealand"), + ("NI", "Nicaragua"), + ("NE", "Niger"), + ("NG", "Nigeria"), + ("NU", "Niue"), + ("NF", "Norfolk Island"), + ("MP", "Northern Mariana Islands"), + ("NO", "Norway"), + ("OM", "Oman"), + ("PK", "Pakistan"), + ("PW", "Palau"), + ("PS", "Palestinian Territory, Occupied"), + ("PA", "Panama"), + ("PG", "Papua New Guinea"), + ("PY", "Paraguay"), + ("PE", "Peru"), + ("PH", "Philippines"), + ("PN", "Pitcairn"), + ("PL", "Poland"), + ("PT", "Portugal"), + ("PR", "Puerto Rico"), + ("QA", "Qatar"), + ("RE", "Reunion"), + ("RO", "Romania"), + ("RU", "Russian Federation"), + ("RW", "Rwanda"), + ("BL", "Saint Barthelemy"), + ("SH", "Saint Helena"), + ("KN", "Saint Kitts and Nevis"), + ("LC", "Saint Lucia"), + ("MF", "Saint Martin"), + ("PM", "Saint Pierre and Miquelon"), + ("VC", "Saint Vincent and the Grenadines"), + ("WS", "Samoa"), + ("SM", "San Marino"), + ("ST", "Sao Tome and Principe"), + ("SA", "Saudi Arabia"), + ("SN", "Senegal"), + ("RS", "Serbia"), + ("SC", "Seychelles"), + ("SL", "Sierra Leone"), + ("SG", "Singapore"), + ("SK", "Slovakia"), + ("SI", "Slovenia"), + ("SB", "Solomon Islands"), + ("SO", "Somalia"), + ("ZA", "South Africa"), + ("GS", "South Georgia and the South Sandwich Islands"), + ("ES", "Spain"), + ("LK", "Sri Lanka"), + ("SD", "Sudan"), + ("SR", "Suriname"), + ("SJ", "Svalbard and Jan Mayen"), + ("SZ", "Swaziland"), + ("SE", "Sweden"), + ("CH", "Switzerland"), + ("SY", "Syrian Arab Republic"), + ("TW", "Taiwan, Province of China"), + ("TJ", "Tajikistan"), + ("TZ", "Tanzania, United Republic of"), + ("TH", "Thailand"), + ("TL", "Timor-Leste"), + ("TG", "Togo"), + ("TK", "Tokelau"), + ("TO", "Tonga"), + ("TT", "Trinidad and Tobago"), + ("TN", "Tunisia"), + ("TR", "Turkey"), + ("TM", "Turkmenistan"), + ("TC", "Turks and Caicos Islands"), + ("TV", "Tuvalu"), + ("UG", "Uganda"), + ("UA", "Ukraine"), + ("AE", "United Arab Emirates"), + ("US", "United States"), + ("UM", "United States Minor Outlying Islands"), + ("UY", "Uruguay"), + ("UZ", "Uzbekistan"), + ("VU", "Vanuatu"), + ("VE", "Venezuela"), + ("VN", "Viet Nam"), + ("VG", "Virgin Islands, British"), + ("VI", "Virgin Islands, U.S."), + ("WF", "Wallis and Futuna"), + ("EH", "Western Sahara"), + ("YE", "Yemen"), + ("ZM", "Zambia"), + ("ZW", "Zimbabwe"), + ], + max_length=3, + null=True, + ), + ), ], ), migrations.AddField( - model_name='user', - name='company', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='common.Company'), + model_name="user", + name="company", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to="common.Company", + ), preserve_default=False, ), ] diff --git a/common/migrations/0020_auto_20200409_1653.py b/common/migrations/0020_auto_20200409_1653.py index f744cc1..2ccc6f3 100644 --- a/common/migrations/0020_auto_20200409_1653.py +++ b/common/migrations/0020_auto_20200409_1653.py @@ -6,18 +6,18 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0019_auto_20200401_0941'), + ("common", "0019_auto_20200401_0941"), ] operations = [ migrations.AlterField( - model_name='company', - name='address', + model_name="company", + name="address", field=models.CharField(blank=True, max_length=2000, null=True), ), migrations.AlterField( - model_name='company', - name='name', + model_name="company", + name="name", field=models.CharField(blank=True, max_length=100, null=True), ), ] diff --git a/common/migrations/0021_document_company.py b/common/migrations/0021_document_company.py index 3beafb2..039aac9 100644 --- a/common/migrations/0021_document_company.py +++ b/common/migrations/0021_document_company.py @@ -7,13 +7,18 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0020_auto_20200409_1653'), + ("common", "0020_auto_20200409_1653"), ] operations = [ migrations.AddField( - model_name='document', - name='company', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Company'), + model_name="document", + name="company", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="common.Company", + ), ), ] diff --git a/common/migrations/0022_auto_20200609_1203.py b/common/migrations/0022_auto_20200609_1203.py index 81e49fc..bd9fcc4 100644 --- a/common/migrations/0022_auto_20200609_1203.py +++ b/common/migrations/0022_auto_20200609_1203.py @@ -7,13 +7,18 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0021_document_company'), + ("common", "0021_document_company"), ] operations = [ migrations.AlterField( - model_name='user', - name='company', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='common.Company'), + model_name="user", + name="company", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="common.Company", + ), ), ] diff --git a/common/models.py b/common/models.py index e85ea94..b33e8d2 100644 --- a/common/models.py +++ b/common/models.py @@ -51,7 +51,9 @@ class User(AbstractBaseUser, PermissionsMixin): ) has_sales_access = models.BooleanField(default=False) has_marketing_access = models.BooleanField(default=False) - company = models.ForeignKey(Company, on_delete=models.CASCADE, null=True, blank=True) + company = models.ForeignKey( + Company, on_delete=models.CASCADE, null=True, blank=True + ) USERNAME_FIELD = "email" REQUIRED_FIELDS = [ diff --git a/common/tasks.py b/common/tasks.py index 054ab7d..087323d 100644 --- a/common/tasks.py +++ b/common/tasks.py @@ -16,7 +16,9 @@ @task -def send_email_to_new_user(user_email, created_by, domain='demo.django-crm.io', protocol='http'): +def send_email_to_new_user( + user_email, created_by, domain="demo.django-crm.io", protocol="http" +): """ Send Mail To Users When their account is created """ user_obj = User.objects.filter(email=user_email).first() @@ -26,33 +28,33 @@ def send_email_to_new_user(user_email, created_by, domain='demo.django-crm.io', context = {} context["user_email"] = user_email context["created_by"] = created_by - context["url"] = protocol + '://' + domain - context["uid"] = urlsafe_base64_encode(force_bytes(user_obj.pk)), + context["url"] = protocol + "://" + domain + context["uid"] = (urlsafe_base64_encode(force_bytes(user_obj.pk)),) context["token"] = account_activation_token.make_token(user_obj) time_delta_two_hours = datetime.datetime.strftime( - timezone.now() + datetime.timedelta(hours=2), "%Y-%m-%d-%H-%M-%S") + timezone.now() + datetime.timedelta(hours=2), "%Y-%m-%d-%H-%M-%S" + ) context["token"] = context["token"] activation_key = context["token"] + time_delta_two_hours Profile.objects.create(user=user_obj, activation_key=activation_key) - context["complete_url"] = context["url"] + \ - reverse('common:activate_user', args=( - context['uid'][0], context['token'], activation_key)) + context["complete_url"] = context["url"] + reverse( + "common:activate_user", + args=(context["uid"][0], context["token"], activation_key), + ) recipients = [] recipients.append(user_email) - subject = 'Welcome to Django CRM' - html_content = render_to_string('user_status_in.html', context=context) + subject = "Welcome to Django CRM" + html_content = render_to_string("user_status_in.html", context=context) if recipients: - msg = EmailMessage( - subject, - html_content, - to=recipients - ) + msg = EmailMessage(subject, html_content, to=recipients) msg.content_subtype = "html" msg.send() @task -def send_email_user_mentions(comment_id, called_from, domain='demo.django-crm.io', protocol='http'): +def send_email_user_mentions( + comment_id, called_from, domain="demo.django-crm.io", protocol="http" +): """ Send Mail To Mentioned Users In The Comment """ comment = Comment.objects.filter(id=comment_id).first() if comment: @@ -60,128 +62,175 @@ def send_email_user_mentions(comment_id, called_from, domain='demo.django-crm.io comment_text_list = comment_text.split() recipients = [] for comment_text in comment_text_list: - if comment_text.startswith('@'): - if comment_text.strip('@').strip(',') not in recipients: - if User.objects.filter(username=comment_text.strip('@').strip(','), is_active=True).exists(): - email = User.objects.filter( - username=comment_text.strip('@').strip(',')).first().email + if comment_text.startswith("@"): + if comment_text.strip("@").strip(",") not in recipients: + if User.objects.filter( + username=comment_text.strip("@").strip(","), is_active=True + ).exists(): + email = ( + User.objects.filter( + username=comment_text.strip("@").strip(",") + ) + .first() + .email + ) recipients.append(email) context = {} context["commented_by"] = comment.commented_by context["comment_description"] = comment.comment - if called_from == 'accounts': - context["url"] = protocol + '://' + domain + \ - reverse('accounts:view_account', args=(comment.account.id,)) - subject = 'New comment on Account. ' - elif called_from == 'contacts': - context["url"] = protocol + '://' + domain + \ - reverse('contacts:view_contact', args=(comment.contact.id,)) - subject = 'New comment on Contact. ' - elif called_from == 'leads': - context["url"] = protocol + '://' + domain + \ - reverse('leads:view_lead', args=(comment.lead.id,)) - subject = 'New comment on Lead. ' - elif called_from == 'opportunity': - context["url"] = protocol + '://' + domain + \ - reverse('opportunity:opp_view', args=(comment.opportunity.id,)) - subject = 'New comment on Opportunity. ' - elif called_from == 'cases': - context["url"] = protocol + '://' + domain + \ - reverse('cases:view_case', args=(comment.case.id,)) - subject = 'New comment on Case. ' - elif called_from == 'tasks': - context["url"] = protocol + '://' + domain + \ - reverse('tasks:task_detail', args=(comment.task.id,)) - subject = 'New comment on Task. ' - elif called_from == 'invoices': - context["url"] = protocol + '://' + domain + \ - reverse('invoices:invoice_details', args=(comment.invoice.id,)) - subject = 'New comment on Invoice. ' - elif called_from == 'events': - context["url"] = protocol + '://' + domain + \ - reverse('events:detail_view', args=(comment.event.id,)) - subject = 'New comment on Event. ' + if called_from == "accounts": + context["url"] = ( + protocol + + "://" + + domain + + reverse("accounts:view_account", args=(comment.account.id,)) + ) + subject = "New comment on Account. " + elif called_from == "contacts": + context["url"] = ( + protocol + + "://" + + domain + + reverse("contacts:view_contact", args=(comment.contact.id,)) + ) + subject = "New comment on Contact. " + elif called_from == "leads": + context["url"] = ( + protocol + + "://" + + domain + + reverse("leads:view_lead", args=(comment.lead.id,)) + ) + subject = "New comment on Lead. " + elif called_from == "opportunity": + context["url"] = ( + protocol + + "://" + + domain + + reverse("opportunity:opp_view", args=(comment.opportunity.id,)) + ) + subject = "New comment on Opportunity. " + elif called_from == "cases": + context["url"] = ( + protocol + + "://" + + domain + + reverse("cases:view_case", args=(comment.case.id,)) + ) + subject = "New comment on Case. " + elif called_from == "tasks": + context["url"] = ( + protocol + + "://" + + domain + + reverse("tasks:task_detail", args=(comment.task.id,)) + ) + subject = "New comment on Task. " + elif called_from == "invoices": + context["url"] = ( + protocol + + "://" + + domain + + reverse("invoices:invoice_details", args=(comment.invoice.id,)) + ) + subject = "New comment on Invoice. " + elif called_from == "events": + context["url"] = ( + protocol + + "://" + + domain + + reverse("events:detail_view", args=(comment.event.id,)) + ) + subject = "New comment on Event. " else: - context["url"] = '' + context["url"] = "" # subject = 'Django CRM : comment ' - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) if recipients: for recipient in recipients: - if (recipient not in blocked_emails) and (recipient.split('@')[-1] not in blocked_domains): - recipients_list = [recipient, ] + if (recipient not in blocked_emails) and ( + recipient.split("@")[-1] not in blocked_domains + ): + recipients_list = [ + recipient, + ] context["mentioned_user"] = recipient - html_content = render_to_string('comment_email.html', context=context) + html_content = render_to_string( + "comment_email.html", context=context + ) msg = EmailMessage( subject, html_content, from_email=comment.commented_by.email, - to=recipients_list + to=recipients_list, ) msg.content_subtype = "html" msg.send() @task -def send_email_user_status(user_id, status_changed_user="", domain='demo.django-crm.io', protocol='http'): +def send_email_user_status( + user_id, status_changed_user="", domain="demo.django-crm.io", protocol="http" +): """ Send Mail To Users Regarding their status i.e active or inactive """ user = User.objects.filter(id=user_id).first() if user: context = {} - context["message"] = 'deactivated' + context["message"] = "deactivated" context["email"] = user.email if user.has_sales_access: - context["url"] = protocol + '://' + domain + '/' + context["url"] = protocol + "://" + domain + "/" elif user.has_marketing_access: - context["url"] = protocol + '://' + domain + '/marketing' + context["url"] = protocol + "://" + domain + "/marketing" else: - context["url"] = protocol + '://' + domain + '/' + context["url"] = protocol + "://" + domain + "/" if user.is_active: - context["message"] = 'activated' + context["message"] = "activated" context["status_changed_user"] = status_changed_user - if context["message"] == 'activated': - subject = 'Account Activated ' - html_content = render_to_string('user_status_activate.html', context=context) + if context["message"] == "activated": + subject = "Account Activated " + html_content = render_to_string( + "user_status_activate.html", context=context + ) else: - subject = 'Account Deactivated ' - html_content = render_to_string('user_status_deactivate.html', context=context) + subject = "Account Deactivated " + html_content = render_to_string( + "user_status_deactivate.html", context=context + ) recipients = [] recipients.append(user.email) if recipients: - msg = EmailMessage( - subject, - html_content, - to=recipients - ) + msg = EmailMessage(subject, html_content, to=recipients) msg.content_subtype = "html" msg.send() @task -def send_email_user_delete(user_email, deleted_by="", domain='demo.django-crm.io', protocol='http'): +def send_email_user_delete( + user_email, deleted_by="", domain="demo.django-crm.io", protocol="http" +): """ Send Mail To Users When their account is deleted """ if user_email: context = {} - context["message"] = 'deleted' + context["message"] = "deleted" context["deleted_by"] = deleted_by context["email"] = user_email recipients = [] recipients.append(user_email) - subject = 'CRM : Your account is Deleted. ' - html_content = render_to_string('user_delete_email.html', context=context) + subject = "CRM : Your account is Deleted. " + html_content = render_to_string("user_delete_email.html", context=context) if recipients: - msg = EmailMessage( - subject, - html_content, - to=recipients - ) + msg = EmailMessage(subject, html_content, to=recipients) msg.content_subtype = "html" msg.send() @task -def resend_activation_link_to_user(user_email="", domain='demo.django-crm.io', protocol='http'): +def resend_activation_link_to_user( + user_email="", domain="demo.django-crm.io", protocol="http" +): """ Send Mail To Users When their account is created """ user_obj = User.objects.filter(email=user_email).first() @@ -190,27 +239,27 @@ def resend_activation_link_to_user(user_email="", domain='demo.django-crm.io', p if user_obj: context = {} context["user_email"] = user_email - context["url"] = protocol + '://' + domain - context["uid"] = urlsafe_base64_encode(force_bytes(user_obj.pk)), + context["url"] = protocol + "://" + domain + context["uid"] = (urlsafe_base64_encode(force_bytes(user_obj.pk)),) context["token"] = account_activation_token.make_token(user_obj) time_delta_two_hours = datetime.datetime.strftime( - timezone.now() + datetime.timedelta(hours=2), "%Y-%m-%d-%H-%M-%S") + timezone.now() + datetime.timedelta(hours=2), "%Y-%m-%d-%H-%M-%S" + ) context["token"] = context["token"] activation_key = context["token"] + time_delta_two_hours Profile.objects.filter(user=user_obj).update( - activation_key=activation_key, key_expires=timezone.now() + datetime.timedelta(hours=2)) - context["complete_url"] = context["url"] + \ - reverse('common:activate_user', args=( - context['uid'][0], context['token'], activation_key)) + activation_key=activation_key, + key_expires=timezone.now() + datetime.timedelta(hours=2), + ) + context["complete_url"] = context["url"] + reverse( + "common:activate_user", + args=(context["uid"][0], context["token"], activation_key), + ) recipients = [] recipients.append(user_email) - subject = 'Welcome to Django CRM' - html_content = render_to_string('user_status_in.html', context=context) + subject = "Welcome to Django CRM" + html_content = render_to_string("user_status_in.html", context=context) if recipients: - msg = EmailMessage( - subject, - html_content, - to=recipients - ) + msg = EmailMessage(subject, html_content, to=recipients) msg.content_subtype = "html" msg.send() diff --git a/common/templatetags/common_tags.py b/common/templatetags/common_tags.py index d247634..a484690 100644 --- a/common/templatetags/common_tags.py +++ b/common/templatetags/common_tags.py @@ -1,120 +1,596 @@ from django import template + register = template.Library() def is_document_file_image(ext): - image_ext_list = ['bmp', 'dds', 'gif', 'jpg', 'jpeg', 'png', 'psd', 'pspimage', - 'tga', 'thm', 'tif', 'tiff', 'yuv'] + image_ext_list = [ + "bmp", + "dds", + "gif", + "jpg", + "jpeg", + "png", + "psd", + "pspimage", + "tga", + "thm", + "tif", + "tiff", + "yuv", + ] return ext.lower() in image_ext_list def is_document_file_audio(ext): - audio_ext_list = ['aif', 'iff', 'm3u', 'm4a', 'mid', 'mp3', - 'mpa', 'wav', 'wma'] + audio_ext_list = ["aif", "iff", "m3u", "m4a", "mid", "mp3", "mpa", "wav", "wma"] return ext.lower() in audio_ext_list def is_document_file_video(ext): - video_ext_list = ['3g2', '3gp', 'asf', 'avi', 'flv', 'm4v', 'mov', - 'mp4', 'mpg', 'rm', 'srt', 'swf', 'vob', 'wmv'] + video_ext_list = [ + "3g2", + "3gp", + "asf", + "avi", + "flv", + "m4v", + "mov", + "mp4", + "mpg", + "rm", + "srt", + "swf", + "vob", + "wmv", + ] return ext.lower() in video_ext_list def is_document_file_pdf(ext): - pdf_ext_list = ['indd', 'pct', 'pdf'] + pdf_ext_list = ["indd", "pct", "pdf"] return ext.lower() in pdf_ext_list def is_document_file_code(ext): - code_ext_list = ['aspx', 'json', 'jsp', 'do', 'htm', 'html', 'ser', - 'php', 'jad', 'cfm', 'xml', 'js', 'pod', 'asp', - 'atomsvc', 'rdf', 'pou', 'jsf', 'abs', 'pl', 'asm', - 'srz', 'luac', 'cod', 'lib', 'arxml', 'bas', 'ejs', - 'fs', 'hbs', 's', 'ss', 'cms', 'pyc', 'vcxproj', - 'jse', 'smali', 'xla', 'lxk', 'pdb', 'src', 'cs', - 'ipb', 'ave', 'mst', 'vls', 'rcc', 'sax', 'scr', - 'dtd', 'axd', 'mrl', 'xsl', 'ino', 'spr', 'xsd', - 'cgi', 'isa', 'ws', 'rss', 'dvb', 'nupkg', 'xlm', - 'v4e', 'rss', 'prg', 'form', 'bat', 'mrc', 'asi', - 'jdp', 'fmb', 'graphml', 'gcode', 'aia', 'py', 'atp', - 'mzp', 'o', 'scs', 'mm', 'cpp', 'java', 'gypi', 'idb', - 'txml', 'c', 'vip', 'tra', 'rc', 'action', 'vlx', - 'asta', 'pyo', 'lua', 'gml', 'prl', 'rfs', 'cpb', - 'sh', 'rbf', 'gp', 'phtml', 'bp', 'scb', 'sln', 'vbp', - 'wbf', 'bdt', 'mac', 'rpy', 'eaf', 'mc', 'mwp', 'gnt', - 'h', 'swift', 'e', 'styl', 'cxx', 'as', 'liquid', - 'dep', 'fas', 'vbs', 'aps', 'vbe', 'lss', 'cmake', - 'resx', 'csb', 'dpk', 'pdml', 'txx', 'dbg', 'jsa', - 'sxs', 'sasf', 'pm', 'csx', 'r', 'wml', 'au3', 'stm', - 'cls', 'cc', 'ins', 'jsc', 'dwp', 'rpg', 'arb', 'bml', - 'inc', 'eld', 'sct', 'sm', 'wbt', 'csproj', 'tcz', - 'html5', 'gbl', 'cmd', 'dlg', 'tpl', 'rbt', 'xcp', - 'tpm', 'qry', 'mfa', 'ptx', 'lsp', 'pag', 'ebc', - 'php3', 'cob', 'csc', 'pyt', 'dwt', 'rb', 'wsdl', - 'lap', 'textile', 'sfx', 'x', 'a5r', 'dbp', 'pmp', - 'ipr', 'fwx', 'pbl', 'vbw', 'phl', 'cbl', 'pas', - 'mom', 'dbmdl', 'lol', 'wdl', 'ppam', 'plx', 'vb', - 'cgx', 'lst', 'lmp', 'vd', 'bcp', 'thtml', 'scpt', - 'isu', 'mrd', 'perl', 'dtx', 'f', 'wpk', 'ipf', 'ptl', - 'luca', 'hx', 'uvproj', 'qvs', 'vba', 'xjb', - 'appxupload', 'ti', 'svn-base', 'bsc', 'mak', - 'vcproj', 'dsd', 'ksh', 'pyw', 'bxml', 'mo', 'irc', - 'gcl', 'dbml', 'mlv', 'wsf', 'tcl', 'dqy', 'ssi', - 'pbxproj', 'bal', 'trt', 'sal', 'hkp', 'vbi', 'dob', - 'htc', 'p', 'ats', 'seam', 'loc', 'pli', 'rptproj', - 'pxml', 'pkb', 'dpr', 'scss', 'dsb', 'bb', 'vbproj', - 'ash', 'rml', 'nbk', 'nvi', 'lmv', 'mw', 'jl', 'dso', - 'cba', 'jks', 'ary', 'run', 'vps', 'clm', 'brml', - 'msha', 'mdp', 'tmh', 'rdf', 'jsx', 'sdl', 'ptxml', - 'fxl', 'wmw', 'dcr', 'bcc', 'cbp', 'bmo', 'bsv', - 'less', 'gss', 'ctl', 'rpyc', 'ascx', 'odc', 'wiki', - 'obr', 'l', 'axs', 'bpr', 'ppa', 'rpo', 'sqlproj', - 'smm', 'dsr', 'arq', 'din', 'jml', 'jsonp', 'ml', - 'rc2', 'myapp', 'cla', 'xme', 'obj', 'jsdtscope', - 'gyp', 'datasource', 'cp', 'rh', 'lpx', 'a2w', 'ctp', - 'ulp', 'nt', 'script', 'bxl', 'gs', 'xslt', 'mg', - 'pch', 'mhl', 'zpd', 'psm1', 'asz', 'm', 'jacl', - 'pym', 'rws', 'acu', 'ssq', 'wxs', 'coffee', 'ncb', - 'akt', 'pyx', 'zero', 'hs', 'mkb', 'tru', 'xul', - 'mfl', 'sca', 'sbr', 'master', 'opv', 'matlab', - 'sami', 'agc', 'slim', 'tea', 'pbl', 'm51', 'mec', - 'asc', 'gch', 'enml', 'ino', 'kst', 'jade', 'dfb', - 'ips', 'rgs', 'vbx', 'cspkg', 'ncx', 'brs', 'wfs', - 'ifp', 'nse', 'xtx', 'j', 'cx', 'ps1', 'nas', 'mk', - 'ccs', 'vrp', 'lnp', 'cml', 'c#', 'idl', 'exp', 'apb', - 'nsi', 'asmx', 'tdo', 'pjt', 'fdt', 's5d', 'mvba', - 'mf', 'odl', 'bzs', 'jardesc', 'tgml', 'moc', 'wxi', - 'cpz', 'fsx', 'jav', 'ocb', 'agi', 'tec', 'txl', - 'amw', 'mscr', 'dfd', 'dpd', 'pun', 'f95', 'vdproj', - 'xsc', 'diff', 'wxl', 'dgml', 'airi', 'kmt', 'ksc', - 'io', 'rbw', 'sas', 'vcp', 'resources', 'param', - 'cg', 'hlsl', 'vssscc', 'bgm', 'xn', 'targets', 'sl', - 'gsc', 'qs', 'owl', 'devpak', 'phps', 'hdf', 'pri', - 'nbin', 'xaml', 's4e', 'scm', 'tk', 'poc', 'uix', - 'clw', 'factorypath', 's43', 'awd', 'htr', 'php2', - 'classpath', 'pickle', 'rob', 'msil', 'ebx', 'tsq', - 'lml', 'f90', 'lds', 'vup', 'pbi', 'swt', 'vap', 'ig', - 'pdo', 'frt', 'fcg', 'c++', 'xcl', 'dfn', 'aar', - 'for', 're', 'twig', 'ebm', 'dhtml', 'hc', 'pro', - 'ahk', 'rule', 'bsh', 'jcs', 'zrx', 'wsdd', 'csp', - 'drc', 'appxsym'] + code_ext_list = [ + "aspx", + "json", + "jsp", + "do", + "htm", + "html", + "ser", + "php", + "jad", + "cfm", + "xml", + "js", + "pod", + "asp", + "atomsvc", + "rdf", + "pou", + "jsf", + "abs", + "pl", + "asm", + "srz", + "luac", + "cod", + "lib", + "arxml", + "bas", + "ejs", + "fs", + "hbs", + "s", + "ss", + "cms", + "pyc", + "vcxproj", + "jse", + "smali", + "xla", + "lxk", + "pdb", + "src", + "cs", + "ipb", + "ave", + "mst", + "vls", + "rcc", + "sax", + "scr", + "dtd", + "axd", + "mrl", + "xsl", + "ino", + "spr", + "xsd", + "cgi", + "isa", + "ws", + "rss", + "dvb", + "nupkg", + "xlm", + "v4e", + "rss", + "prg", + "form", + "bat", + "mrc", + "asi", + "jdp", + "fmb", + "graphml", + "gcode", + "aia", + "py", + "atp", + "mzp", + "o", + "scs", + "mm", + "cpp", + "java", + "gypi", + "idb", + "txml", + "c", + "vip", + "tra", + "rc", + "action", + "vlx", + "asta", + "pyo", + "lua", + "gml", + "prl", + "rfs", + "cpb", + "sh", + "rbf", + "gp", + "phtml", + "bp", + "scb", + "sln", + "vbp", + "wbf", + "bdt", + "mac", + "rpy", + "eaf", + "mc", + "mwp", + "gnt", + "h", + "swift", + "e", + "styl", + "cxx", + "as", + "liquid", + "dep", + "fas", + "vbs", + "aps", + "vbe", + "lss", + "cmake", + "resx", + "csb", + "dpk", + "pdml", + "txx", + "dbg", + "jsa", + "sxs", + "sasf", + "pm", + "csx", + "r", + "wml", + "au3", + "stm", + "cls", + "cc", + "ins", + "jsc", + "dwp", + "rpg", + "arb", + "bml", + "inc", + "eld", + "sct", + "sm", + "wbt", + "csproj", + "tcz", + "html5", + "gbl", + "cmd", + "dlg", + "tpl", + "rbt", + "xcp", + "tpm", + "qry", + "mfa", + "ptx", + "lsp", + "pag", + "ebc", + "php3", + "cob", + "csc", + "pyt", + "dwt", + "rb", + "wsdl", + "lap", + "textile", + "sfx", + "x", + "a5r", + "dbp", + "pmp", + "ipr", + "fwx", + "pbl", + "vbw", + "phl", + "cbl", + "pas", + "mom", + "dbmdl", + "lol", + "wdl", + "ppam", + "plx", + "vb", + "cgx", + "lst", + "lmp", + "vd", + "bcp", + "thtml", + "scpt", + "isu", + "mrd", + "perl", + "dtx", + "f", + "wpk", + "ipf", + "ptl", + "luca", + "hx", + "uvproj", + "qvs", + "vba", + "xjb", + "appxupload", + "ti", + "svn-base", + "bsc", + "mak", + "vcproj", + "dsd", + "ksh", + "pyw", + "bxml", + "mo", + "irc", + "gcl", + "dbml", + "mlv", + "wsf", + "tcl", + "dqy", + "ssi", + "pbxproj", + "bal", + "trt", + "sal", + "hkp", + "vbi", + "dob", + "htc", + "p", + "ats", + "seam", + "loc", + "pli", + "rptproj", + "pxml", + "pkb", + "dpr", + "scss", + "dsb", + "bb", + "vbproj", + "ash", + "rml", + "nbk", + "nvi", + "lmv", + "mw", + "jl", + "dso", + "cba", + "jks", + "ary", + "run", + "vps", + "clm", + "brml", + "msha", + "mdp", + "tmh", + "rdf", + "jsx", + "sdl", + "ptxml", + "fxl", + "wmw", + "dcr", + "bcc", + "cbp", + "bmo", + "bsv", + "less", + "gss", + "ctl", + "rpyc", + "ascx", + "odc", + "wiki", + "obr", + "l", + "axs", + "bpr", + "ppa", + "rpo", + "sqlproj", + "smm", + "dsr", + "arq", + "din", + "jml", + "jsonp", + "ml", + "rc2", + "myapp", + "cla", + "xme", + "obj", + "jsdtscope", + "gyp", + "datasource", + "cp", + "rh", + "lpx", + "a2w", + "ctp", + "ulp", + "nt", + "script", + "bxl", + "gs", + "xslt", + "mg", + "pch", + "mhl", + "zpd", + "psm1", + "asz", + "m", + "jacl", + "pym", + "rws", + "acu", + "ssq", + "wxs", + "coffee", + "ncb", + "akt", + "pyx", + "zero", + "hs", + "mkb", + "tru", + "xul", + "mfl", + "sca", + "sbr", + "master", + "opv", + "matlab", + "sami", + "agc", + "slim", + "tea", + "pbl", + "m51", + "mec", + "asc", + "gch", + "enml", + "ino", + "kst", + "jade", + "dfb", + "ips", + "rgs", + "vbx", + "cspkg", + "ncx", + "brs", + "wfs", + "ifp", + "nse", + "xtx", + "j", + "cx", + "ps1", + "nas", + "mk", + "ccs", + "vrp", + "lnp", + "cml", + "c#", + "idl", + "exp", + "apb", + "nsi", + "asmx", + "tdo", + "pjt", + "fdt", + "s5d", + "mvba", + "mf", + "odl", + "bzs", + "jardesc", + "tgml", + "moc", + "wxi", + "cpz", + "fsx", + "jav", + "ocb", + "agi", + "tec", + "txl", + "amw", + "mscr", + "dfd", + "dpd", + "pun", + "f95", + "vdproj", + "xsc", + "diff", + "wxl", + "dgml", + "airi", + "kmt", + "ksc", + "io", + "rbw", + "sas", + "vcp", + "resources", + "param", + "cg", + "hlsl", + "vssscc", + "bgm", + "xn", + "targets", + "sl", + "gsc", + "qs", + "owl", + "devpak", + "phps", + "hdf", + "pri", + "nbin", + "xaml", + "s4e", + "scm", + "tk", + "poc", + "uix", + "clw", + "factorypath", + "s43", + "awd", + "htr", + "php2", + "classpath", + "pickle", + "rob", + "msil", + "ebx", + "tsq", + "lml", + "f90", + "lds", + "vup", + "pbi", + "swt", + "vap", + "ig", + "pdo", + "frt", + "fcg", + "c++", + "xcl", + "dfn", + "aar", + "for", + "re", + "twig", + "ebm", + "dhtml", + "hc", + "pro", + "ahk", + "rule", + "bsh", + "jcs", + "zrx", + "wsdd", + "csp", + "drc", + "appxsym", + ] return ext.lower() in code_ext_list def is_document_file_text(ext): - text_ext_list = ['doc', 'docx', 'log', 'msg', 'odt', 'pages', 'rtf', - 'tex', 'txt', 'wpd', 'wps'] + text_ext_list = [ + "doc", + "docx", + "log", + "msg", + "odt", + "pages", + "rtf", + "tex", + "txt", + "wpd", + "wps", + ] return ext.lower() in text_ext_list def is_document_file_sheet(ext): - sheet_ext_list = ['csv', 'xls', 'xlsx', - 'xlsm', 'xlsb', 'xltx', 'xltm', 'xlt'] + sheet_ext_list = ["csv", "xls", "xlsx", "xlsm", "xlsb", "xltx", "xltm", "xlt"] return ext.lower() in sheet_ext_list def is_document_file_zip(ext): - ext_list = ['zip', '7Z', 'gz', 'rar', 'ZIPX', 'ACE', 'tar', ] + ext_list = [ + "zip", + "7Z", + "gz", + "rar", + "ZIPX", + "ACE", + "tar", + ] return ext.lower() in ext_list @@ -125,13 +601,18 @@ def subtract(value, arg): @register.filter def delete_condition(user, task): - if user == task.created_by or user.role == 'ADMIN': + if user == task.created_by or user.role == "ADMIN": return True return False @register.filter def view_edit_condition(user, task): - if user == task.created_by or user.role == 'ADMIN' or user.has_sales_access or user in task.assigned_to.all(): + if ( + user == task.created_by + or user.role == "ADMIN" + or user.has_sales_access + or user in task.assigned_to.all() + ): return True return False diff --git a/common/token_generator.py b/common/token_generator.py index dbdcc20..a44ea4a 100644 --- a/common/token_generator.py +++ b/common/token_generator.py @@ -1,4 +1,5 @@ from django.contrib.auth.tokens import PasswordResetTokenGenerator + # from django.utils import six import six @@ -8,8 +9,9 @@ class TokenGenerator(PasswordResetTokenGenerator): def _make_hash_value(self, user, timestamp): return ( - six.text_type(user.pk) + six.text_type(timestamp) + - six.text_type(user.is_active) + six.text_type(user.pk) + + six.text_type(timestamp) + + six.text_type(user.is_active) ) diff --git a/common/utils.py b/common/utils.py index d9be550..bca8ee5 100644 --- a/common/utils.py +++ b/common/utils.py @@ -3,541 +3,532 @@ INDCHOICES = ( - ('ADVERTISING', 'ADVERTISING'), - ('AGRICULTURE', 'AGRICULTURE'), - ('APPAREL & ACCESSORIES', 'APPAREL & ACCESSORIES'), - ('AUTOMOTIVE', 'AUTOMOTIVE'), - ('BANKING', 'BANKING'), - ('BIOTECHNOLOGY', 'BIOTECHNOLOGY'), - ('BUILDING MATERIALS & EQUIPMENT', 'BUILDING MATERIALS & EQUIPMENT'), - ('CHEMICAL', 'CHEMICAL'), - ('COMPUTER', 'COMPUTER'), - ('EDUCATION', 'EDUCATION'), - ('ELECTRONICS', 'ELECTRONICS'), - ('ENERGY', 'ENERGY'), - ('ENTERTAINMENT & LEISURE', 'ENTERTAINMENT & LEISURE'), - ('FINANCE', 'FINANCE'), - ('FOOD & BEVERAGE', 'FOOD & BEVERAGE'), - ('GROCERY', 'GROCERY'), - ('HEALTHCARE', 'HEALTHCARE'), - ('INSURANCE', 'INSURANCE'), - ('LEGAL', 'LEGAL'), - ('MANUFACTURING', 'MANUFACTURING'), - ('PUBLISHING', 'PUBLISHING'), - ('REAL ESTATE', 'REAL ESTATE'), - ('SERVICE', 'SERVICE'), - ('SOFTWARE', 'SOFTWARE'), - ('SPORTS', 'SPORTS'), - ('TECHNOLOGY', 'TECHNOLOGY'), - ('TELECOMMUNICATIONS', 'TELECOMMUNICATIONS'), - ('TELEVISION', 'TELEVISION'), - ('TRANSPORTATION', 'TRANSPORTATION'), - ('VENTURE CAPITAL', 'VENTURE CAPITAL'), + ("ADVERTISING", "ADVERTISING"), + ("AGRICULTURE", "AGRICULTURE"), + ("APPAREL & ACCESSORIES", "APPAREL & ACCESSORIES"), + ("AUTOMOTIVE", "AUTOMOTIVE"), + ("BANKING", "BANKING"), + ("BIOTECHNOLOGY", "BIOTECHNOLOGY"), + ("BUILDING MATERIALS & EQUIPMENT", "BUILDING MATERIALS & EQUIPMENT"), + ("CHEMICAL", "CHEMICAL"), + ("COMPUTER", "COMPUTER"), + ("EDUCATION", "EDUCATION"), + ("ELECTRONICS", "ELECTRONICS"), + ("ENERGY", "ENERGY"), + ("ENTERTAINMENT & LEISURE", "ENTERTAINMENT & LEISURE"), + ("FINANCE", "FINANCE"), + ("FOOD & BEVERAGE", "FOOD & BEVERAGE"), + ("GROCERY", "GROCERY"), + ("HEALTHCARE", "HEALTHCARE"), + ("INSURANCE", "INSURANCE"), + ("LEGAL", "LEGAL"), + ("MANUFACTURING", "MANUFACTURING"), + ("PUBLISHING", "PUBLISHING"), + ("REAL ESTATE", "REAL ESTATE"), + ("SERVICE", "SERVICE"), + ("SOFTWARE", "SOFTWARE"), + ("SPORTS", "SPORTS"), + ("TECHNOLOGY", "TECHNOLOGY"), + ("TELECOMMUNICATIONS", "TELECOMMUNICATIONS"), + ("TELEVISION", "TELEVISION"), + ("TRANSPORTATION", "TRANSPORTATION"), + ("VENTURE CAPITAL", "VENTURE CAPITAL"), ) TYPECHOICES = ( - ('CUSTOMER', 'CUSTOMER'), - ('INVESTOR', 'INVESTOR'), - ('PARTNER', 'PARTNER'), - ('RESELLER', 'RESELLER'), + ("CUSTOMER", "CUSTOMER"), + ("INVESTOR", "INVESTOR"), + ("PARTNER", "PARTNER"), + ("RESELLER", "RESELLER"), ) ROLES = ( - ('ADMIN', 'ADMIN'), - ('USER', 'USER'), + ("ADMIN", "ADMIN"), + ("USER", "USER"), ) LEAD_STATUS = ( - ('assigned', 'Assigned'), - ('in process', 'In Process'), - ('converted', 'Converted'), - ('recycled', 'Recycled'), - ('closed', 'Closed') + ("assigned", "Assigned"), + ("in process", "In Process"), + ("converted", "Converted"), + ("recycled", "Recycled"), + ("closed", "Closed"), ) LEAD_SOURCE = ( - ('call', 'Call'), - ('email', 'Email'), - ('existing customer', 'Existing Customer'), - ('partner', 'Partner'), - ('public relations', 'Public Relations'), - ('compaign', 'Campaign'), - ('other', 'Other'), + ("call", "Call"), + ("email", "Email"), + ("existing customer", "Existing Customer"), + ("partner", "Partner"), + ("public relations", "Public Relations"), + ("compaign", "Campaign"), + ("other", "Other"), ) STATUS_CHOICE = ( ("New", "New"), - ('Assigned', 'Assigned'), - ('Pending', 'Pending'), - ('Closed', 'Closed'), - ('Rejected', 'Rejected'), - ('Duplicate', 'Duplicate'), + ("Assigned", "Assigned"), + ("Pending", "Pending"), + ("Closed", "Closed"), + ("Rejected", "Rejected"), + ("Duplicate", "Duplicate"), ) PRIORITY_CHOICE = ( ("Low", "Low"), - ('Normal', 'Normal'), - ('High', 'High'), - ('Urgent', 'Urgent') + ("Normal", "Normal"), + ("High", "High"), + ("Urgent", "Urgent"), ) -CASE_TYPE = ( - ("Question", "Question"), - ('Incident', 'Incident'), - ('Problem', 'Problem') -) +CASE_TYPE = (("Question", "Question"), ("Incident", "Incident"), ("Problem", "Problem")) STAGES = ( - ('QUALIFICATION', 'QUALIFICATION'), - ('NEEDS ANALYSIS', 'NEEDS ANALYSIS'), - ('VALUE PROPOSITION', 'VALUE PROPOSITION'), - ('ID.DECISION MAKERS', 'ID.DECISION MAKERS'), - ('PERCEPTION ANALYSIS', 'PERCEPTION ANALYSIS'), - ('PROPOSAL/PRICE QUOTE', 'PROPOSAL/PRICE QUOTE'), - ('NEGOTIATION/REVIEW', 'NEGOTIATION/REVIEW'), - ('CLOSED WON', 'CLOSED WON'), - ('CLOSED LOST', 'CLOSED LOST'), + ("QUALIFICATION", "QUALIFICATION"), + ("NEEDS ANALYSIS", "NEEDS ANALYSIS"), + ("VALUE PROPOSITION", "VALUE PROPOSITION"), + ("ID.DECISION MAKERS", "ID.DECISION MAKERS"), + ("PERCEPTION ANALYSIS", "PERCEPTION ANALYSIS"), + ("PROPOSAL/PRICE QUOTE", "PROPOSAL/PRICE QUOTE"), + ("NEGOTIATION/REVIEW", "NEGOTIATION/REVIEW"), + ("CLOSED WON", "CLOSED WON"), + ("CLOSED LOST", "CLOSED LOST"), ) SOURCES = ( - ('NONE', 'NONE'), - ('CALL', 'CALL'), - ('EMAIL', ' EMAIL'), - ('EXISTING CUSTOMER', 'EXISTING CUSTOMER'), - ('PARTNER', 'PARTNER'), - ('PUBLIC RELATIONS', 'PUBLIC RELATIONS'), - ('CAMPAIGN', 'CAMPAIGN'), - ('WEBSITE', 'WEBSITE'), - ('OTHER', 'OTHER'), + ("NONE", "NONE"), + ("CALL", "CALL"), + ("EMAIL", " EMAIL"), + ("EXISTING CUSTOMER", "EXISTING CUSTOMER"), + ("PARTNER", "PARTNER"), + ("PUBLIC RELATIONS", "PUBLIC RELATIONS"), + ("CAMPAIGN", "CAMPAIGN"), + ("WEBSITE", "WEBSITE"), + ("OTHER", "OTHER"), ) -EVENT_PARENT_TYPE = ( - (10, 'Account'), - (13, 'Lead'), - (14, 'Opportunity'), - (11, 'Case') -) +EVENT_PARENT_TYPE = ((10, "Account"), (13, "Lead"), (14, "Opportunity"), (11, "Case")) EVENT_STATUS = ( - ('Planned', 'Planned'), - ('Held', 'Held'), - ('Not Held', 'Not Held'), - ('Not Started', 'Not Started'), - ('Started', 'Started'), - ('Completed', 'Completed'), - ('Canceled', 'Canceled'), - ('Deferred', 'Deferred') + ("Planned", "Planned"), + ("Held", "Held"), + ("Not Held", "Not Held"), + ("Not Started", "Not Started"), + ("Started", "Started"), + ("Completed", "Completed"), + ("Canceled", "Canceled"), + ("Deferred", "Deferred"), ) COUNTRIES = ( - ('GB', _('United Kingdom')), - ('AF', _('Afghanistan')), - ('AX', _('Aland Islands')), - ('AL', _('Albania')), - ('DZ', _('Algeria')), - ('AS', _('American Samoa')), - ('AD', _('Andorra')), - ('AO', _('Angola')), - ('AI', _('Anguilla')), - ('AQ', _('Antarctica')), - ('AG', _('Antigua and Barbuda')), - ('AR', _('Argentina')), - ('AM', _('Armenia')), - ('AW', _('Aruba')), - ('AU', _('Australia')), - ('AT', _('Austria')), - ('AZ', _('Azerbaijan')), - ('BS', _('Bahamas')), - ('BH', _('Bahrain')), - ('BD', _('Bangladesh')), - ('BB', _('Barbados')), - ('BY', _('Belarus')), - ('BE', _('Belgium')), - ('BZ', _('Belize')), - ('BJ', _('Benin')), - ('BM', _('Bermuda')), - ('BT', _('Bhutan')), - ('BO', _('Bolivia')), - ('BA', _('Bosnia and Herzegovina')), - ('BW', _('Botswana')), - ('BV', _('Bouvet Island')), - ('BR', _('Brazil')), - ('IO', _('British Indian Ocean Territory')), - ('BN', _('Brunei Darussalam')), - ('BG', _('Bulgaria')), - ('BF', _('Burkina Faso')), - ('BI', _('Burundi')), - ('KH', _('Cambodia')), - ('CM', _('Cameroon')), - ('CA', _('Canada')), - ('CV', _('Cape Verde')), - ('KY', _('Cayman Islands')), - ('CF', _('Central African Republic')), - ('TD', _('Chad')), - ('CL', _('Chile')), - ('CN', _('China')), - ('CX', _('Christmas Island')), - ('CC', _('Cocos (Keeling) Islands')), - ('CO', _('Colombia')), - ('KM', _('Comoros')), - ('CG', _('Congo')), - ('CD', _('Congo, The Democratic Republic of the')), - ('CK', _('Cook Islands')), - ('CR', _('Costa Rica')), - ('CI', _('Cote d\'Ivoire')), - ('HR', _('Croatia')), - ('CU', _('Cuba')), - ('CY', _('Cyprus')), - ('CZ', _('Czech Republic')), - ('DK', _('Denmark')), - ('DJ', _('Djibouti')), - ('DM', _('Dominica')), - ('DO', _('Dominican Republic')), - ('EC', _('Ecuador')), - ('EG', _('Egypt')), - ('SV', _('El Salvador')), - ('GQ', _('Equatorial Guinea')), - ('ER', _('Eritrea')), - ('EE', _('Estonia')), - ('ET', _('Ethiopia')), - ('FK', _('Falkland Islands (Malvinas)')), - ('FO', _('Faroe Islands')), - ('FJ', _('Fiji')), - ('FI', _('Finland')), - ('FR', _('France')), - ('GF', _('French Guiana')), - ('PF', _('French Polynesia')), - ('TF', _('French Southern Territories')), - ('GA', _('Gabon')), - ('GM', _('Gambia')), - ('GE', _('Georgia')), - ('DE', _('Germany')), - ('GH', _('Ghana')), - ('GI', _('Gibraltar')), - ('GR', _('Greece')), - ('GL', _('Greenland')), - ('GD', _('Grenada')), - ('GP', _('Guadeloupe')), - ('GU', _('Guam')), - ('GT', _('Guatemala')), - ('GG', _('Guernsey')), - ('GN', _('Guinea')), - ('GW', _('Guinea-Bissau')), - ('GY', _('Guyana')), - ('HT', _('Haiti')), - ('HM', _('Heard Island and McDonald Islands')), - ('VA', _('Holy See (Vatican City State)')), - ('HN', _('Honduras')), - ('HK', _('Hong Kong')), - ('HU', _('Hungary')), - ('IS', _('Iceland')), - ('IN', _('India')), - ('ID', _('Indonesia')), - ('IR', _('Iran, Islamic Republic of')), - ('IQ', _('Iraq')), - ('IE', _('Ireland')), - ('IM', _('Isle of Man')), - ('IL', _('Israel')), - ('IT', _('Italy')), - ('JM', _('Jamaica')), - ('JP', _('Japan')), - ('JE', _('Jersey')), - ('JO', _('Jordan')), - ('KZ', _('Kazakhstan')), - ('KE', _('Kenya')), - ('KI', _('Kiribati')), - ('KP', _('Korea, Democratic People\'s Republic of')), - ('KR', _('Korea, Republic of')), - ('KW', _('Kuwait')), - ('KG', _('Kyrgyzstan')), - ('LA', _('Lao People\'s Democratic Republic')), - ('LV', _('Latvia')), - ('LB', _('Lebanon')), - ('LS', _('Lesotho')), - ('LR', _('Liberia')), - ('LY', _('Libyan Arab Jamahiriya')), - ('LI', _('Liechtenstein')), - ('LT', _('Lithuania')), - ('LU', _('Luxembourg')), - ('MO', _('Macao')), - ('MK', _('Macedonia, The Former Yugoslav Republic of')), - ('MG', _('Madagascar')), - ('MW', _('Malawi')), - ('MY', _('Malaysia')), - ('MV', _('Maldives')), - ('ML', _('Mali')), - ('MT', _('Malta')), - ('MH', _('Marshall Islands')), - ('MQ', _('Martinique')), - ('MR', _('Mauritania')), - ('MU', _('Mauritius')), - ('YT', _('Mayotte')), - ('MX', _('Mexico')), - ('FM', _('Micronesia, Federated States of')), - ('MD', _('Moldova')), - ('MC', _('Monaco')), - ('MN', _('Mongolia')), - ('ME', _('Montenegro')), - ('MS', _('Montserrat')), - ('MA', _('Morocco')), - ('MZ', _('Mozambique')), - ('MM', _('Myanmar')), - ('NA', _('Namibia')), - ('NR', _('Nauru')), - ('NP', _('Nepal')), - ('NL', _('Netherlands')), - ('AN', _('Netherlands Antilles')), - ('NC', _('New Caledonia')), - ('NZ', _('New Zealand')), - ('NI', _('Nicaragua')), - ('NE', _('Niger')), - ('NG', _('Nigeria')), - ('NU', _('Niue')), - ('NF', _('Norfolk Island')), - ('MP', _('Northern Mariana Islands')), - ('NO', _('Norway')), - ('OM', _('Oman')), - ('PK', _('Pakistan')), - ('PW', _('Palau')), - ('PS', _('Palestinian Territory, Occupied')), - ('PA', _('Panama')), - ('PG', _('Papua New Guinea')), - ('PY', _('Paraguay')), - ('PE', _('Peru')), - ('PH', _('Philippines')), - ('PN', _('Pitcairn')), - ('PL', _('Poland')), - ('PT', _('Portugal')), - ('PR', _('Puerto Rico')), - ('QA', _('Qatar')), - ('RE', _('Reunion')), - ('RO', _('Romania')), - ('RU', _('Russian Federation')), - ('RW', _('Rwanda')), - ('BL', _('Saint Barthelemy')), - ('SH', _('Saint Helena')), - ('KN', _('Saint Kitts and Nevis')), - ('LC', _('Saint Lucia')), - ('MF', _('Saint Martin')), - ('PM', _('Saint Pierre and Miquelon')), - ('VC', _('Saint Vincent and the Grenadines')), - ('WS', _('Samoa')), - ('SM', _('San Marino')), - ('ST', _('Sao Tome and Principe')), - ('SA', _('Saudi Arabia')), - ('SN', _('Senegal')), - ('RS', _('Serbia')), - ('SC', _('Seychelles')), - ('SL', _('Sierra Leone')), - ('SG', _('Singapore')), - ('SK', _('Slovakia')), - ('SI', _('Slovenia')), - ('SB', _('Solomon Islands')), - ('SO', _('Somalia')), - ('ZA', _('South Africa')), - ('GS', _('South Georgia and the South Sandwich Islands')), - ('ES', _('Spain')), - ('LK', _('Sri Lanka')), - ('SD', _('Sudan')), - ('SR', _('Suriname')), - ('SJ', _('Svalbard and Jan Mayen')), - ('SZ', _('Swaziland')), - ('SE', _('Sweden')), - ('CH', _('Switzerland')), - ('SY', _('Syrian Arab Republic')), - ('TW', _('Taiwan, Province of China')), - ('TJ', _('Tajikistan')), - ('TZ', _('Tanzania, United Republic of')), - ('TH', _('Thailand')), - ('TL', _('Timor-Leste')), - ('TG', _('Togo')), - ('TK', _('Tokelau')), - ('TO', _('Tonga')), - ('TT', _('Trinidad and Tobago')), - ('TN', _('Tunisia')), - ('TR', _('Turkey')), - ('TM', _('Turkmenistan')), - ('TC', _('Turks and Caicos Islands')), - ('TV', _('Tuvalu')), - ('UG', _('Uganda')), - ('UA', _('Ukraine')), - ('AE', _('United Arab Emirates')), - ('US', _('United States')), - ('UM', _('United States Minor Outlying Islands')), - ('UY', _('Uruguay')), - ('UZ', _('Uzbekistan')), - ('VU', _('Vanuatu')), - ('VE', _('Venezuela')), - ('VN', _('Viet Nam')), - ('VG', _('Virgin Islands, British')), - ('VI', _('Virgin Islands, U.S.')), - ('WF', _('Wallis and Futuna')), - ('EH', _('Western Sahara')), - ('YE', _('Yemen')), - ('ZM', _('Zambia')), - ('ZW', _('Zimbabwe')), + ("GB", _("United Kingdom")), + ("AF", _("Afghanistan")), + ("AX", _("Aland Islands")), + ("AL", _("Albania")), + ("DZ", _("Algeria")), + ("AS", _("American Samoa")), + ("AD", _("Andorra")), + ("AO", _("Angola")), + ("AI", _("Anguilla")), + ("AQ", _("Antarctica")), + ("AG", _("Antigua and Barbuda")), + ("AR", _("Argentina")), + ("AM", _("Armenia")), + ("AW", _("Aruba")), + ("AU", _("Australia")), + ("AT", _("Austria")), + ("AZ", _("Azerbaijan")), + ("BS", _("Bahamas")), + ("BH", _("Bahrain")), + ("BD", _("Bangladesh")), + ("BB", _("Barbados")), + ("BY", _("Belarus")), + ("BE", _("Belgium")), + ("BZ", _("Belize")), + ("BJ", _("Benin")), + ("BM", _("Bermuda")), + ("BT", _("Bhutan")), + ("BO", _("Bolivia")), + ("BA", _("Bosnia and Herzegovina")), + ("BW", _("Botswana")), + ("BV", _("Bouvet Island")), + ("BR", _("Brazil")), + ("IO", _("British Indian Ocean Territory")), + ("BN", _("Brunei Darussalam")), + ("BG", _("Bulgaria")), + ("BF", _("Burkina Faso")), + ("BI", _("Burundi")), + ("KH", _("Cambodia")), + ("CM", _("Cameroon")), + ("CA", _("Canada")), + ("CV", _("Cape Verde")), + ("KY", _("Cayman Islands")), + ("CF", _("Central African Republic")), + ("TD", _("Chad")), + ("CL", _("Chile")), + ("CN", _("China")), + ("CX", _("Christmas Island")), + ("CC", _("Cocos (Keeling) Islands")), + ("CO", _("Colombia")), + ("KM", _("Comoros")), + ("CG", _("Congo")), + ("CD", _("Congo, The Democratic Republic of the")), + ("CK", _("Cook Islands")), + ("CR", _("Costa Rica")), + ("CI", _("Cote d'Ivoire")), + ("HR", _("Croatia")), + ("CU", _("Cuba")), + ("CY", _("Cyprus")), + ("CZ", _("Czech Republic")), + ("DK", _("Denmark")), + ("DJ", _("Djibouti")), + ("DM", _("Dominica")), + ("DO", _("Dominican Republic")), + ("EC", _("Ecuador")), + ("EG", _("Egypt")), + ("SV", _("El Salvador")), + ("GQ", _("Equatorial Guinea")), + ("ER", _("Eritrea")), + ("EE", _("Estonia")), + ("ET", _("Ethiopia")), + ("FK", _("Falkland Islands (Malvinas)")), + ("FO", _("Faroe Islands")), + ("FJ", _("Fiji")), + ("FI", _("Finland")), + ("FR", _("France")), + ("GF", _("French Guiana")), + ("PF", _("French Polynesia")), + ("TF", _("French Southern Territories")), + ("GA", _("Gabon")), + ("GM", _("Gambia")), + ("GE", _("Georgia")), + ("DE", _("Germany")), + ("GH", _("Ghana")), + ("GI", _("Gibraltar")), + ("GR", _("Greece")), + ("GL", _("Greenland")), + ("GD", _("Grenada")), + ("GP", _("Guadeloupe")), + ("GU", _("Guam")), + ("GT", _("Guatemala")), + ("GG", _("Guernsey")), + ("GN", _("Guinea")), + ("GW", _("Guinea-Bissau")), + ("GY", _("Guyana")), + ("HT", _("Haiti")), + ("HM", _("Heard Island and McDonald Islands")), + ("VA", _("Holy See (Vatican City State)")), + ("HN", _("Honduras")), + ("HK", _("Hong Kong")), + ("HU", _("Hungary")), + ("IS", _("Iceland")), + ("IN", _("India")), + ("ID", _("Indonesia")), + ("IR", _("Iran, Islamic Republic of")), + ("IQ", _("Iraq")), + ("IE", _("Ireland")), + ("IM", _("Isle of Man")), + ("IL", _("Israel")), + ("IT", _("Italy")), + ("JM", _("Jamaica")), + ("JP", _("Japan")), + ("JE", _("Jersey")), + ("JO", _("Jordan")), + ("KZ", _("Kazakhstan")), + ("KE", _("Kenya")), + ("KI", _("Kiribati")), + ("KP", _("Korea, Democratic People's Republic of")), + ("KR", _("Korea, Republic of")), + ("KW", _("Kuwait")), + ("KG", _("Kyrgyzstan")), + ("LA", _("Lao People's Democratic Republic")), + ("LV", _("Latvia")), + ("LB", _("Lebanon")), + ("LS", _("Lesotho")), + ("LR", _("Liberia")), + ("LY", _("Libyan Arab Jamahiriya")), + ("LI", _("Liechtenstein")), + ("LT", _("Lithuania")), + ("LU", _("Luxembourg")), + ("MO", _("Macao")), + ("MK", _("Macedonia, The Former Yugoslav Republic of")), + ("MG", _("Madagascar")), + ("MW", _("Malawi")), + ("MY", _("Malaysia")), + ("MV", _("Maldives")), + ("ML", _("Mali")), + ("MT", _("Malta")), + ("MH", _("Marshall Islands")), + ("MQ", _("Martinique")), + ("MR", _("Mauritania")), + ("MU", _("Mauritius")), + ("YT", _("Mayotte")), + ("MX", _("Mexico")), + ("FM", _("Micronesia, Federated States of")), + ("MD", _("Moldova")), + ("MC", _("Monaco")), + ("MN", _("Mongolia")), + ("ME", _("Montenegro")), + ("MS", _("Montserrat")), + ("MA", _("Morocco")), + ("MZ", _("Mozambique")), + ("MM", _("Myanmar")), + ("NA", _("Namibia")), + ("NR", _("Nauru")), + ("NP", _("Nepal")), + ("NL", _("Netherlands")), + ("AN", _("Netherlands Antilles")), + ("NC", _("New Caledonia")), + ("NZ", _("New Zealand")), + ("NI", _("Nicaragua")), + ("NE", _("Niger")), + ("NG", _("Nigeria")), + ("NU", _("Niue")), + ("NF", _("Norfolk Island")), + ("MP", _("Northern Mariana Islands")), + ("NO", _("Norway")), + ("OM", _("Oman")), + ("PK", _("Pakistan")), + ("PW", _("Palau")), + ("PS", _("Palestinian Territory, Occupied")), + ("PA", _("Panama")), + ("PG", _("Papua New Guinea")), + ("PY", _("Paraguay")), + ("PE", _("Peru")), + ("PH", _("Philippines")), + ("PN", _("Pitcairn")), + ("PL", _("Poland")), + ("PT", _("Portugal")), + ("PR", _("Puerto Rico")), + ("QA", _("Qatar")), + ("RE", _("Reunion")), + ("RO", _("Romania")), + ("RU", _("Russian Federation")), + ("RW", _("Rwanda")), + ("BL", _("Saint Barthelemy")), + ("SH", _("Saint Helena")), + ("KN", _("Saint Kitts and Nevis")), + ("LC", _("Saint Lucia")), + ("MF", _("Saint Martin")), + ("PM", _("Saint Pierre and Miquelon")), + ("VC", _("Saint Vincent and the Grenadines")), + ("WS", _("Samoa")), + ("SM", _("San Marino")), + ("ST", _("Sao Tome and Principe")), + ("SA", _("Saudi Arabia")), + ("SN", _("Senegal")), + ("RS", _("Serbia")), + ("SC", _("Seychelles")), + ("SL", _("Sierra Leone")), + ("SG", _("Singapore")), + ("SK", _("Slovakia")), + ("SI", _("Slovenia")), + ("SB", _("Solomon Islands")), + ("SO", _("Somalia")), + ("ZA", _("South Africa")), + ("GS", _("South Georgia and the South Sandwich Islands")), + ("ES", _("Spain")), + ("LK", _("Sri Lanka")), + ("SD", _("Sudan")), + ("SR", _("Suriname")), + ("SJ", _("Svalbard and Jan Mayen")), + ("SZ", _("Swaziland")), + ("SE", _("Sweden")), + ("CH", _("Switzerland")), + ("SY", _("Syrian Arab Republic")), + ("TW", _("Taiwan, Province of China")), + ("TJ", _("Tajikistan")), + ("TZ", _("Tanzania, United Republic of")), + ("TH", _("Thailand")), + ("TL", _("Timor-Leste")), + ("TG", _("Togo")), + ("TK", _("Tokelau")), + ("TO", _("Tonga")), + ("TT", _("Trinidad and Tobago")), + ("TN", _("Tunisia")), + ("TR", _("Turkey")), + ("TM", _("Turkmenistan")), + ("TC", _("Turks and Caicos Islands")), + ("TV", _("Tuvalu")), + ("UG", _("Uganda")), + ("UA", _("Ukraine")), + ("AE", _("United Arab Emirates")), + ("US", _("United States")), + ("UM", _("United States Minor Outlying Islands")), + ("UY", _("Uruguay")), + ("UZ", _("Uzbekistan")), + ("VU", _("Vanuatu")), + ("VE", _("Venezuela")), + ("VN", _("Viet Nam")), + ("VG", _("Virgin Islands, British")), + ("VI", _("Virgin Islands, U.S.")), + ("WF", _("Wallis and Futuna")), + ("EH", _("Western Sahara")), + ("YE", _("Yemen")), + ("ZM", _("Zambia")), + ("ZW", _("Zimbabwe")), ) CURRENCY_CODES = ( - ('AED', _('AED, Dirham')), - ('AFN', _('AFN, Afghani')), - ('ALL', _('ALL, Lek')), - ('AMD', _('AMD, Dram')), - ('ANG', _('ANG, Guilder')), - ('AOA', _('AOA, Kwanza')), - ('ARS', _('ARS, Peso')), - ('AUD', _('AUD, Dollar')), - ('AWG', _('AWG, Guilder')), - ('AZN', _('AZN, Manat')), - ('BAM', _('BAM, Marka')), - ('BBD', _('BBD, Dollar')), - ('BDT', _('BDT, Taka')), - ('BGN', _('BGN, Lev')), - ('BHD', _('BHD, Dinar')), - ('BIF', _('BIF, Franc')), - ('BMD', _('BMD, Dollar')), - ('BND', _('BND, Dollar')), - ('BOB', _('BOB, Boliviano')), - ('BRL', _('BRL, Real')), - ('BSD', _('BSD, Dollar')), - ('BTN', _('BTN, Ngultrum')), - ('BWP', _('BWP, Pula')), - ('BYR', _('BYR, Ruble')), - ('BZD', _('BZD, Dollar')), - ('CAD', _('CAD, Dollar')), - ('CDF', _('CDF, Franc')), - ('CHF', _('CHF, Franc')), - ('CLP', _('CLP, Peso')), - ('CNY', _('CNY, Yuan Renminbi')), - ('COP', _('COP, Peso')), - ('CRC', _('CRC, Colon')), - ('CUP', _('CUP, Peso')), - ('CVE', _('CVE, Escudo')), - ('CZK', _('CZK, Koruna')), - ('DJF', _('DJF, Franc')), - ('DKK', _('DKK, Krone')), - ('DOP', _('DOP, Peso')), - ('DZD', _('DZD, Dinar')), - ('EGP', _('EGP, Pound')), - ('ERN', _('ERN, Nakfa')), - ('ETB', _('ETB, Birr')), - ('EUR', _('EUR, Euro')), - ('FJD', _('FJD, Dollar')), - ('FKP', _('FKP, Pound')), - ('GBP', _('GBP, Pound')), - ('GEL', _('GEL, Lari')), - ('GHS', _('GHS, Cedi')), - ('GIP', _('GIP, Pound')), - ('GMD', _('GMD, Dalasi')), - ('GNF', _('GNF, Franc')), - ('GTQ', _('GTQ, Quetzal')), - ('GYD', _('GYD, Dollar')), - ('HKD', _('HKD, Dollar')), - ('HNL', _('HNL, Lempira')), - ('HRK', _('HRK, Kuna')), - ('HTG', _('HTG, Gourde')), - ('HUF', _('HUF, Forint')), - ('IDR', _('IDR, Rupiah')), - ('ILS', _('ILS, Shekel')), - ('INR', _('INR, Rupee')), - ('IQD', _('IQD, Dinar')), - ('IRR', _('IRR, Rial')), - ('ISK', _('ISK, Krona')), - ('JMD', _('JMD, Dollar')), - ('JOD', _('JOD, Dinar')), - ('JPY', _('JPY, Yen')), - ('KES', _('KES, Shilling')), - ('KGS', _('KGS, Som')), - ('KHR', _('KHR, Riels')), - ('KMF', _('KMF, Franc')), - ('KPW', _('KPW, Won')), - ('KRW', _('KRW, Won')), - ('KWD', _('KWD, Dinar')), - ('KYD', _('KYD, Dollar')), - ('KZT', _('KZT, Tenge')), - ('LAK', _('LAK, Kip')), - ('LBP', _('LBP, Pound')), - ('LKR', _('LKR, Rupee')), - ('LRD', _('LRD, Dollar')), - ('LSL', _('LSL, Loti')), - ('LTL', _('LTL, Litas')), - ('LVL', _('LVL, Lat')), - ('LYD', _('LYD, Dinar')), - ('MAD', _('MAD, Dirham')), - ('MDL', _('MDL, Leu')), - ('MGA', _('MGA, Ariary')), - ('MKD', _('MKD, Denar')), - ('MMK', _('MMK, Kyat')), - ('MNT', _('MNT, Tugrik')), - ('MOP', _('MOP, Pataca')), - ('MRO', _('MRO, Ouguiya')), - ('MUR', _('MUR, Rupee')), - ('MVR', _('MVR, Rufiyaa')), - ('MWK', _('MWK, Kwacha')), - ('MXN', _('MXN, Peso')), - ('MYR', _('MYR, Ringgit')), - ('MZN', _('MZN, Metical')), - ('NAD', _('NAD, Dollar')), - ('NGN', _('NGN, Naira')), - ('NIO', _('NIO, Cordoba')), - ('NOK', _('NOK, Krone')), - ('NPR', _('NPR, Rupee')), - ('NZD', _('NZD, Dollar')), - ('OMR', _('OMR, Rial')), - ('PAB', _('PAB, Balboa')), - ('PEN', _('PEN, Sol')), - ('PGK', _('PGK, Kina')), - ('PHP', _('PHP, Peso')), - ('PKR', _('PKR, Rupee')), - ('PLN', _('PLN, Zloty')), - ('PYG', _('PYG, Guarani')), - ('QAR', _('QAR, Rial')), - ('RON', _('RON, Leu')), - ('RSD', _('RSD, Dinar')), - ('RUB', _('RUB, Ruble')), - ('RWF', _('RWF, Franc')), - ('SAR', _('SAR, Rial')), - ('SBD', _('SBD, Dollar')), - ('SCR', _('SCR, Rupee')), - ('SDG', _('SDG, Pound')), - ('SEK', _('SEK, Krona')), - ('SGD', _('SGD, Dollar')), - ('SHP', _('SHP, Pound')), - ('SLL', _('SLL, Leone')), - ('SOS', _('SOS, Shilling')), - ('SRD', _('SRD, Dollar')), - ('SSP', _('SSP, Pound')), - ('STD', _('STD, Dobra')), - ('SYP', _('SYP, Pound')), - ('SZL', _('SZL, Lilangeni')), - ('THB', _('THB, Baht')), - ('TJS', _('TJS, Somoni')), - ('TMT', _('TMT, Manat')), - ('TND', _('TND, Dinar')), - ('TOP', _('TOP, Paanga')), - ('TRY', _('TRY, Lira')), - ('TTD', _('TTD, Dollar')), - ('TWD', _('TWD, Dollar')), - ('TZS', _('TZS, Shilling')), - ('UAH', _('UAH, Hryvnia')), - ('UGX', _('UGX, Shilling')), - ('USD', _('$, Dollar')), - ('UYU', _('UYU, Peso')), - ('UZS', _('UZS, Som')), - ('VEF', _('VEF, Bolivar')), - ('VND', _('VND, Dong')), - ('VUV', _('VUV, Vatu')), - ('WST', _('WST, Tala')), - ('XAF', _('XAF, Franc')), - ('XCD', _('XCD, Dollar')), - ('XOF', _('XOF, Franc')), - ('XPF', _('XPF, Franc')), - ('YER', _('YER, Rial')), - ('ZAR', _('ZAR, Rand')), - ('ZMK', _('ZMK, Kwacha')), - ('ZWL', _('ZWL, Dollar')), + ("AED", _("AED, Dirham")), + ("AFN", _("AFN, Afghani")), + ("ALL", _("ALL, Lek")), + ("AMD", _("AMD, Dram")), + ("ANG", _("ANG, Guilder")), + ("AOA", _("AOA, Kwanza")), + ("ARS", _("ARS, Peso")), + ("AUD", _("AUD, Dollar")), + ("AWG", _("AWG, Guilder")), + ("AZN", _("AZN, Manat")), + ("BAM", _("BAM, Marka")), + ("BBD", _("BBD, Dollar")), + ("BDT", _("BDT, Taka")), + ("BGN", _("BGN, Lev")), + ("BHD", _("BHD, Dinar")), + ("BIF", _("BIF, Franc")), + ("BMD", _("BMD, Dollar")), + ("BND", _("BND, Dollar")), + ("BOB", _("BOB, Boliviano")), + ("BRL", _("BRL, Real")), + ("BSD", _("BSD, Dollar")), + ("BTN", _("BTN, Ngultrum")), + ("BWP", _("BWP, Pula")), + ("BYR", _("BYR, Ruble")), + ("BZD", _("BZD, Dollar")), + ("CAD", _("CAD, Dollar")), + ("CDF", _("CDF, Franc")), + ("CHF", _("CHF, Franc")), + ("CLP", _("CLP, Peso")), + ("CNY", _("CNY, Yuan Renminbi")), + ("COP", _("COP, Peso")), + ("CRC", _("CRC, Colon")), + ("CUP", _("CUP, Peso")), + ("CVE", _("CVE, Escudo")), + ("CZK", _("CZK, Koruna")), + ("DJF", _("DJF, Franc")), + ("DKK", _("DKK, Krone")), + ("DOP", _("DOP, Peso")), + ("DZD", _("DZD, Dinar")), + ("EGP", _("EGP, Pound")), + ("ERN", _("ERN, Nakfa")), + ("ETB", _("ETB, Birr")), + ("EUR", _("EUR, Euro")), + ("FJD", _("FJD, Dollar")), + ("FKP", _("FKP, Pound")), + ("GBP", _("GBP, Pound")), + ("GEL", _("GEL, Lari")), + ("GHS", _("GHS, Cedi")), + ("GIP", _("GIP, Pound")), + ("GMD", _("GMD, Dalasi")), + ("GNF", _("GNF, Franc")), + ("GTQ", _("GTQ, Quetzal")), + ("GYD", _("GYD, Dollar")), + ("HKD", _("HKD, Dollar")), + ("HNL", _("HNL, Lempira")), + ("HRK", _("HRK, Kuna")), + ("HTG", _("HTG, Gourde")), + ("HUF", _("HUF, Forint")), + ("IDR", _("IDR, Rupiah")), + ("ILS", _("ILS, Shekel")), + ("INR", _("INR, Rupee")), + ("IQD", _("IQD, Dinar")), + ("IRR", _("IRR, Rial")), + ("ISK", _("ISK, Krona")), + ("JMD", _("JMD, Dollar")), + ("JOD", _("JOD, Dinar")), + ("JPY", _("JPY, Yen")), + ("KES", _("KES, Shilling")), + ("KGS", _("KGS, Som")), + ("KHR", _("KHR, Riels")), + ("KMF", _("KMF, Franc")), + ("KPW", _("KPW, Won")), + ("KRW", _("KRW, Won")), + ("KWD", _("KWD, Dinar")), + ("KYD", _("KYD, Dollar")), + ("KZT", _("KZT, Tenge")), + ("LAK", _("LAK, Kip")), + ("LBP", _("LBP, Pound")), + ("LKR", _("LKR, Rupee")), + ("LRD", _("LRD, Dollar")), + ("LSL", _("LSL, Loti")), + ("LTL", _("LTL, Litas")), + ("LVL", _("LVL, Lat")), + ("LYD", _("LYD, Dinar")), + ("MAD", _("MAD, Dirham")), + ("MDL", _("MDL, Leu")), + ("MGA", _("MGA, Ariary")), + ("MKD", _("MKD, Denar")), + ("MMK", _("MMK, Kyat")), + ("MNT", _("MNT, Tugrik")), + ("MOP", _("MOP, Pataca")), + ("MRO", _("MRO, Ouguiya")), + ("MUR", _("MUR, Rupee")), + ("MVR", _("MVR, Rufiyaa")), + ("MWK", _("MWK, Kwacha")), + ("MXN", _("MXN, Peso")), + ("MYR", _("MYR, Ringgit")), + ("MZN", _("MZN, Metical")), + ("NAD", _("NAD, Dollar")), + ("NGN", _("NGN, Naira")), + ("NIO", _("NIO, Cordoba")), + ("NOK", _("NOK, Krone")), + ("NPR", _("NPR, Rupee")), + ("NZD", _("NZD, Dollar")), + ("OMR", _("OMR, Rial")), + ("PAB", _("PAB, Balboa")), + ("PEN", _("PEN, Sol")), + ("PGK", _("PGK, Kina")), + ("PHP", _("PHP, Peso")), + ("PKR", _("PKR, Rupee")), + ("PLN", _("PLN, Zloty")), + ("PYG", _("PYG, Guarani")), + ("QAR", _("QAR, Rial")), + ("RON", _("RON, Leu")), + ("RSD", _("RSD, Dinar")), + ("RUB", _("RUB, Ruble")), + ("RWF", _("RWF, Franc")), + ("SAR", _("SAR, Rial")), + ("SBD", _("SBD, Dollar")), + ("SCR", _("SCR, Rupee")), + ("SDG", _("SDG, Pound")), + ("SEK", _("SEK, Krona")), + ("SGD", _("SGD, Dollar")), + ("SHP", _("SHP, Pound")), + ("SLL", _("SLL, Leone")), + ("SOS", _("SOS, Shilling")), + ("SRD", _("SRD, Dollar")), + ("SSP", _("SSP, Pound")), + ("STD", _("STD, Dobra")), + ("SYP", _("SYP, Pound")), + ("SZL", _("SZL, Lilangeni")), + ("THB", _("THB, Baht")), + ("TJS", _("TJS, Somoni")), + ("TMT", _("TMT, Manat")), + ("TND", _("TND, Dinar")), + ("TOP", _("TOP, Paanga")), + ("TRY", _("TRY, Lira")), + ("TTD", _("TTD, Dollar")), + ("TWD", _("TWD, Dollar")), + ("TZS", _("TZS, Shilling")), + ("UAH", _("UAH, Hryvnia")), + ("UGX", _("UGX, Shilling")), + ("USD", _("$, Dollar")), + ("UYU", _("UYU, Peso")), + ("UZS", _("UZS, Som")), + ("VEF", _("VEF, Bolivar")), + ("VND", _("VND, Dong")), + ("VUV", _("VUV, Vatu")), + ("WST", _("WST, Tala")), + ("XAF", _("XAF, Franc")), + ("XCD", _("XCD, Dollar")), + ("XOF", _("XOF, Franc")), + ("XPF", _("XPF, Franc")), + ("YER", _("YER, Rial")), + ("ZAR", _("ZAR, Rand")), + ("ZMK", _("ZMK, Kwacha")), + ("ZWL", _("ZWL, Dollar")), ) @@ -574,11 +565,11 @@ def return_complete_address(self): def get_client_ip(request): - x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') + x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") if x_forwarded_for: - ip = x_forwarded_for.split(',')[0] + ip = x_forwarded_for.split(",")[0] else: - ip = request.META.get('REMOTE_ADDR') + ip = request.META.get("REMOTE_ADDR") return ip diff --git a/common/views.py b/common/views.py index 572a8b3..5b447b2 100644 --- a/common/views.py +++ b/common/views.py @@ -95,15 +95,11 @@ def landing_page(request): def check_sub_domain(request): if request.method == "GET": -<<<<<<< HEAD try: if request.company: return redirect("common:login") except: return render(request, "check_subdomain.html", {}) -======= - return render(request, "check_subdomain.html", {}) ->>>>>>> 13b750edba53c119df91e8ca10557731d6d555f7 if request.method == "POST": sub_domain = request.POST.get("sub_domain", "") company = Company.objects.filter(sub_domain=sub_domain).first() @@ -225,7 +221,6 @@ def get_context_data(self, **kwargs): return context -<<<<<<< HEAD # class LoginView(TemplateView): # template_name = "login.html" @@ -308,90 +303,6 @@ def get_context_data(self, **kwargs): # "form": form, # }, # ) -======= -class LoginView(TemplateView): - template_name = "login.html" - - def get_context_data(self, **kwargs): - context = super(LoginView, self).get_context_data(**kwargs) - context["ENABLE_GOOGLE_LOGIN"] = settings.ENABLE_GOOGLE_LOGIN - context["GP_CLIENT_SECRET"] = settings.GP_CLIENT_SECRET - context["GP_CLIENT_ID"] = settings.GP_CLIENT_ID - return context - - def dispatch(self, request, *args, **kwargs): - if request.user.is_authenticated: - return redirect("common:dashboard") - return super(LoginView, self).dispatch(request, *args, **kwargs) - - def post(self, request, *args, **kwargs): - form = LoginForm(request.POST, request=request) - if form.is_valid(): - - user = User.objects.filter(email=request.POST.get("email")).first() - # user = authenticate(username=request.POST.get('email'), password=request.POST.get('password')) - if user is not None: - if user.is_active: - user = authenticate( - username=request.POST.get("email"), - password=request.POST.get("password"), - ) - if user is not None: - login(request, user) - if user.has_sales_access: - return redirect("common:dashboard") - elif user.has_marketing_access: - return redirect("marketing:dashboard") - else: - return redirect("common:dashboard") - return render( - request, - "login.html", - { - "ENABLE_GOOGLE_LOGIN": settings.ENABLE_GOOGLE_LOGIN, - "GP_CLIENT_SECRET": settings.GP_CLIENT_SECRET, - "GP_CLIENT_ID": settings.GP_CLIENT_ID, - "error": True, - "message": "Your username and password didn't match. \ - Please try again.", - }, - ) - return render( - request, - "login.html", - { - "ENABLE_GOOGLE_LOGIN": settings.ENABLE_GOOGLE_LOGIN, - "GP_CLIENT_SECRET": settings.GP_CLIENT_SECRET, - "GP_CLIENT_ID": settings.GP_CLIENT_ID, - "error": True, - "message": "Your Account is inactive. Please Contact Administrator", - }, - ) - return render( - request, - "login.html", - { - "ENABLE_GOOGLE_LOGIN": settings.ENABLE_GOOGLE_LOGIN, - "GP_CLIENT_SECRET": settings.GP_CLIENT_SECRET, - "GP_CLIENT_ID": settings.GP_CLIENT_ID, - "error": True, - "message": "Your Account is not Found. Please Contact Administrator", - }, - ) - - return render( - request, - "login.html", - { - "ENABLE_GOOGLE_LOGIN": settings.ENABLE_GOOGLE_LOGIN, - "GP_CLIENT_SECRET": settings.GP_CLIENT_SECRET, - "GP_CLIENT_ID": settings.GP_CLIENT_ID, - # "error": True, - # "message": "Your username and password didn't match. Please try again." - "form": form, - }, - ) ->>>>>>> 13b750edba53c119df91e8ca10557731d6d555f7 class ForgotPasswordView(TemplateView): @@ -482,32 +393,21 @@ def post(self, request, *args, **kwargs): if form.is_valid(): email = form.cleaned_data.get("email", "") password = form.cleaned_data.get("password", "") -<<<<<<< HEAD # company = get_object_or_404(Company, pk=form.data.get("company", "")) if request.company: user = User.objects.filter(email=email, company=request.company).first() if user is not None: if user.is_active: - user = authenticate(email=email, password=password, company=request.company) -======= - company = get_object_or_404(Company, pk=form.data.get("company", "")) - if company: - user = User.objects.filter(email=email, company=company).first() - if user is not None: - if user.is_active: - user = authenticate(email=email, password=password,) ->>>>>>> 13b750edba53c119df91e8ca10557731d6d555f7 + user = authenticate( + email=email, password=password, company=request.company + ) if user is not None: login(request, user) request.session["company"] = ( user.company.id if user.company else None ) if user.has_sales_access: -<<<<<<< HEAD - return redirect(reverse('common:dashboard')) -======= - return redirect("common:dashboard") ->>>>>>> 13b750edba53c119df91e8ca10557731d6d555f7 + return redirect(reverse("common:dashboard")) elif user.has_marketing_access: return redirect("marketing:dashboard") else: diff --git a/contacts/apps.py b/contacts/apps.py index ad6d0c8..a2d242d 100644 --- a/contacts/apps.py +++ b/contacts/apps.py @@ -2,4 +2,4 @@ class ContactsConfig(AppConfig): - name = 'contacts' + name = "contacts" diff --git a/contacts/forms.py b/contacts/forms.py index 31dc9ee..04dc4d6 100644 --- a/contacts/forms.py +++ b/contacts/forms.py @@ -9,30 +9,36 @@ class ContactForm(forms.ModelForm): teams = forms.MultipleChoiceField(choices=teams_queryset) def __init__(self, *args, **kwargs): - assigned_users = kwargs.pop('assigned_to', []) + assigned_users = kwargs.pop("assigned_to", []) super(ContactForm, self).__init__(*args, **kwargs) for field in self.fields.values(): field.widget.attrs = {"class": "form-control"} - self.fields['description'].widget.attrs.update({ - 'rows': '6'}) + self.fields["description"].widget.attrs.update({"rows": "6"}) if assigned_users: - self.fields['assigned_to'].queryset = assigned_users - self.fields['assigned_to'].required = False + self.fields["assigned_to"].queryset = assigned_users + self.fields["assigned_to"].required = False for key, value in self.fields.items(): - if key == 'phone': - value.widget.attrs['placeholder'] = "+911234567890" + if key == "phone": + value.widget.attrs["placeholder"] = "+911234567890" else: - value.widget.attrs['placeholder'] = value.label - self.fields["teams"].choices = [(team.get('id'), team.get('name')) for team in Teams.objects.all().values('id', 'name')] + value.widget.attrs["placeholder"] = value.label + self.fields["teams"].choices = [ + (team.get("id"), team.get("name")) + for team in Teams.objects.all().values("id", "name") + ] self.fields["teams"].required = False class Meta: model = Contact fields = ( - 'assigned_to', 'first_name', - 'last_name', 'email', - 'phone', 'address', 'description' + "assigned_to", + "first_name", + "last_name", + "email", + "phone", + "address", + "description", ) @@ -41,7 +47,7 @@ class ContactCommentForm(forms.ModelForm): class Meta: model = Comment - fields = ('comment', 'contact', 'commented_by') + fields = ("comment", "contact", "commented_by") class ContactAttachmentForm(forms.ModelForm): @@ -49,4 +55,4 @@ class ContactAttachmentForm(forms.ModelForm): class Meta: model = Attachments - fields = ('attachment', 'contact') + fields = ("attachment", "contact") diff --git a/contacts/migrations/0001_initial.py b/contacts/migrations/0001_initial.py index 4c5adfe..65474df 100644 --- a/contacts/migrations/0001_initial.py +++ b/contacts/migrations/0001_initial.py @@ -11,31 +11,83 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('accounts', '0001_initial'), - ('common', '0001_initial'), + ("accounts", "0001_initial"), + ("common", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Contact', + name="Contact", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('first_name', models.CharField(max_length=255, verbose_name='First name')), - ('last_name', models.CharField(max_length=255, verbose_name='Last name')), - ('email', models.EmailField(max_length=254, unique=True)), - ('phone', phonenumber_field.modelfields.PhoneNumberField(max_length=128, null=True, unique=True)), - ('description', models.TextField(blank=True, null=True)), - ('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Created on')), - ('is_active', models.BooleanField(default=False)), - ('account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lead_account_contacts', to='accounts.Account')), - ('address', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='adress_contacts', to='common.Address')), - ('assigned_to', models.ManyToManyField(related_name='contact_assigned_users', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contact_created_by', to=settings.AUTH_USER_MODEL)), - ('teams', models.ManyToManyField(to='common.Team')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "first_name", + models.CharField(max_length=255, verbose_name="First name"), + ), + ( + "last_name", + models.CharField(max_length=255, verbose_name="Last name"), + ), + ("email", models.EmailField(max_length=254, unique=True)), + ( + "phone", + phonenumber_field.modelfields.PhoneNumberField( + max_length=128, null=True, unique=True + ), + ), + ("description", models.TextField(blank=True, null=True)), + ( + "created_on", + models.DateTimeField(auto_now_add=True, verbose_name="Created on"), + ), + ("is_active", models.BooleanField(default=False)), + ( + "account", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="lead_account_contacts", + to="accounts.Account", + ), + ), + ( + "address", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="adress_contacts", + to="common.Address", + ), + ), + ( + "assigned_to", + models.ManyToManyField( + related_name="contact_assigned_users", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "created_by", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="contact_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), + ("teams", models.ManyToManyField(to="common.Team")), ], ), - # migrations.AlterUniqueTogether( # name='contact', # unique_together={('email',)}, diff --git a/contacts/migrations/0002_auto_20190210_1810.py b/contacts/migrations/0002_auto_20190210_1810.py index 1958fc2..436dca3 100644 --- a/contacts/migrations/0002_auto_20190210_1810.py +++ b/contacts/migrations/0002_auto_20190210_1810.py @@ -6,12 +6,11 @@ class Migration(migrations.Migration): dependencies = [ - ('contacts', '0001_initial'), + ("contacts", "0001_initial"), ] operations = [ migrations.AlterModelOptions( - name='contact', - options={'ordering': ['-created_on']}, + name="contact", options={"ordering": ["-created_on"]}, ), - ] \ No newline at end of file + ] diff --git a/contacts/migrations/0002_auto_20190212_1334.py b/contacts/migrations/0002_auto_20190212_1334.py index 5d5fb2a..ba5242f 100644 --- a/contacts/migrations/0002_auto_20190212_1334.py +++ b/contacts/migrations/0002_auto_20190212_1334.py @@ -8,25 +8,23 @@ class Migration(migrations.Migration): dependencies = [ - ('contacts', '0001_initial'), + ("contacts", "0001_initial"), ] operations = [ migrations.AlterModelOptions( - name='contact', - options={'ordering': ['-created_on']}, - ), - migrations.RemoveField( - model_name='contact', - name='account', - ), - migrations.RemoveField( - model_name='contact', - name='teams', + name="contact", options={"ordering": ["-created_on"]}, ), + migrations.RemoveField(model_name="contact", name="account",), + migrations.RemoveField(model_name="contact", name="teams",), migrations.AlterField( - model_name='contact', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contact_created_by', to=settings.AUTH_USER_MODEL), + model_name="contact", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="contact_created_by", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/contacts/migrations/0003_merge_20190214_1427.py b/contacts/migrations/0003_merge_20190214_1427.py index 9c6d5af..5c05469 100644 --- a/contacts/migrations/0003_merge_20190214_1427.py +++ b/contacts/migrations/0003_merge_20190214_1427.py @@ -6,9 +6,8 @@ class Migration(migrations.Migration): dependencies = [ - ('contacts', '0002_auto_20190210_1810'), - ('contacts', '0002_auto_20190212_1334'), + ("contacts", "0002_auto_20190210_1810"), + ("contacts", "0002_auto_20190212_1334"), ] - operations = [ - ] + operations = [] diff --git a/contacts/migrations/0004_contact_teams.py b/contacts/migrations/0004_contact_teams.py index fcda81c..5733dea 100644 --- a/contacts/migrations/0004_contact_teams.py +++ b/contacts/migrations/0004_contact_teams.py @@ -6,14 +6,16 @@ class Migration(migrations.Migration): dependencies = [ - ('teams', '0003_auto_20190909_1621'), - ('contacts', '0003_merge_20190214_1427'), + ("teams", "0003_auto_20190909_1621"), + ("contacts", "0003_merge_20190214_1427"), ] operations = [ migrations.AddField( - model_name='contact', - name='teams', - field=models.ManyToManyField(related_name='contact_teams', to='teams.Teams'), + model_name="contact", + name="teams", + field=models.ManyToManyField( + related_name="contact_teams", to="teams.Teams" + ), ), ] diff --git a/contacts/migrations/0005_contact_company.py b/contacts/migrations/0005_contact_company.py index b8f29f6..f748e1e 100644 --- a/contacts/migrations/0005_contact_company.py +++ b/contacts/migrations/0005_contact_company.py @@ -7,14 +7,19 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0020_auto_20200409_1653'), - ('contacts', '0004_contact_teams'), + ("common", "0020_auto_20200409_1653"), + ("contacts", "0004_contact_teams"), ] operations = [ migrations.AddField( - model_name='contact', - name='company', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Company'), + model_name="contact", + name="company", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="common.Company", + ), ), ] diff --git a/contacts/tasks.py b/contacts/tasks.py index dd028fd..c8e896d 100644 --- a/contacts/tasks.py +++ b/contacts/tasks.py @@ -9,32 +9,37 @@ @task -def send_email_to_assigned_user(recipients, contact_id, domain='demo.django-crm.io', protocol='http'): +def send_email_to_assigned_user( + recipients, contact_id, domain="demo.django-crm.io", protocol="http" +): """ Send Mail To Users When they are assigned to a contact """ contact = Contact.objects.get(id=contact_id) - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) created_by = contact.created_by for user in recipients: recipients_list = [] user = User.objects.filter(id=user, is_active=True).first() if user: - if (user.email not in blocked_emails) and (user.email.split('@')[-1] not in blocked_domains): + if (user.email not in blocked_emails) and ( + user.email.split("@")[-1] not in blocked_domains + ): recipients_list.append(user.email) context = {} - context["url"] = protocol + '://' + domain + \ - reverse('contacts:view_contact', args=(contact.id,)) + context["url"] = ( + protocol + + "://" + + domain + + reverse("contacts:view_contact", args=(contact.id,)) + ) context["user"] = user context["contact"] = contact context["created_by"] = created_by - subject = 'Assigned a contact for you.' + subject = "Assigned a contact for you." html_content = render_to_string( - 'assigned_to/contact_assigned.html', context=context) - - msg = EmailMessage( - subject, - html_content, - to=recipients_list + "assigned_to/contact_assigned.html", context=context ) + + msg = EmailMessage(subject, html_content, to=recipients_list) msg.content_subtype = "html" msg.send() diff --git a/contacts/tests_celery_tasks.py b/contacts/tests_celery_tasks.py index a902310..2c4f470 100644 --- a/contacts/tests_celery_tasks.py +++ b/contacts/tests_celery_tasks.py @@ -8,11 +8,13 @@ class TestCeleryTasks(ContactObjectsCreation, TestCase): - - @override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - CELERY_ALWAYS_EAGER=True, - BROKER_BACKEND='memory') + @override_settings( + CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, + CELERY_ALWAYS_EAGER=True, + BROKER_BACKEND="memory", + ) def test_celery_tasks(self): task = send_email_to_assigned_user.apply( - ([self.user.id, self.user_contacts_mp.id, ], self.contact.id,),) - self.assertEqual('SUCCESS', task.state) + ([self.user.id, self.user_contacts_mp.id,], self.contact.id,), + ) + self.assertEqual("SUCCESS", task.state) diff --git a/contacts/urls.py b/contacts/urls.py index 50daa0a..a803e55 100644 --- a/contacts/urls.py +++ b/contacts/urls.py @@ -1,34 +1,35 @@ from django.urls import path from contacts.views import ( - ContactsListView, CreateContactView, ContactDetailView, - UpdateContactView, RemoveContactView, get_teams_and_users, - GetContactsView, AddCommentView, UpdateCommentView, - DeleteCommentView, AddAttachmentsView, DeleteAttachmentsView) + ContactsListView, + CreateContactView, + ContactDetailView, + UpdateContactView, + RemoveContactView, + get_teams_and_users, + GetContactsView, + AddCommentView, + UpdateCommentView, + DeleteCommentView, + AddAttachmentsView, + DeleteAttachmentsView, +) -app_name = 'contacts' +app_name = "contacts" urlpatterns = [ - path('', ContactsListView.as_view(), name='list'), - path('create/', CreateContactView.as_view(), name='add_contact'), - path('/view/', ContactDetailView.as_view(), name="view_contact"), - path('/edit/', UpdateContactView.as_view(), name="edit_contact"), - path('/delete/', - RemoveContactView.as_view(), - name="remove_contact"), - - path('get/list/', GetContactsView.as_view(), name="get_contacts"), - - path('comment/add/', AddCommentView.as_view(), name="add_comment"), - path('comment/edit/', UpdateCommentView.as_view(), name="edit_comment"), - path('comment/remove/', - DeleteCommentView.as_view(), - name="remove_comment"), - - path('attachment/add/', - AddAttachmentsView.as_view(), - name="add_attachment"), - path('attachment/remove/', DeleteAttachmentsView.as_view(), - name="remove_attachment"), - path('get_teams_and_users/', get_teams_and_users, name="get_teams_and_users") + path("", ContactsListView.as_view(), name="list"), + path("create/", CreateContactView.as_view(), name="add_contact"), + path("/view/", ContactDetailView.as_view(), name="view_contact"), + path("/edit/", UpdateContactView.as_view(), name="edit_contact"), + path("/delete/", RemoveContactView.as_view(), name="remove_contact"), + path("get/list/", GetContactsView.as_view(), name="get_contacts"), + path("comment/add/", AddCommentView.as_view(), name="add_comment"), + path("comment/edit/", UpdateCommentView.as_view(), name="edit_comment"), + path("comment/remove/", DeleteCommentView.as_view(), name="remove_comment"), + path("attachment/add/", AddAttachmentsView.as_view(), name="add_attachment"), + path( + "attachment/remove/", DeleteAttachmentsView.as_view(), name="remove_attachment" + ), + path("get_teams_and_users/", get_teams_and_users, name="get_teams_and_users"), ] diff --git a/crm/__init__.py b/crm/__init__.py index 3b91b07..85d095e 100644 --- a/crm/__init__.py +++ b/crm/__init__.py @@ -4,4 +4,4 @@ # Django starts so that shared_task will use this app. from .celery import app as celery_app -__all__ = ['celery_app'] +__all__ = ["celery_app"] diff --git a/crm/celery.py b/crm/celery.py index 2fb5283..b8dd306 100644 --- a/crm/celery.py +++ b/crm/celery.py @@ -3,17 +3,17 @@ from celery import Celery # set the default Django settings module for the 'celery' program. -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'crm.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "crm.settings") # os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'crm.dev_settings') # os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'crm.server_settings') -app = Celery('crm') +app = Celery("crm") # Using a string here means the worker don't have to serialize # the configuration object to child processes. # - namespace='CELERY' means all celery-related configuration keys # should have a `CELERY_` prefix. -app.config_from_object('django.conf:settings', namespace='CELERY') +app.config_from_object("django.conf:settings", namespace="CELERY") # Load task modules from all registered Django app configs. app.autodiscover_tasks() diff --git a/crm/helper.py b/crm/helper.py index 4b2cf60..3a9ffe7 100644 --- a/crm/helper.py +++ b/crm/helper.py @@ -15,25 +15,21 @@ def send_mail(mto, mfrom, msubject, mbody, user_active): mail_sender = settings.MAIL_SENDER else: mail_sender = settings.INACTIVE_MAIL_SENDER - if mail_sender == 'AMAZON': + if mail_sender == "AMAZON": # conn=SESConnection(settings.AM_ACCESS_KEY, settings.AM_PASS_KEY) conn = boto.ses.connect_to_region( settings.AWS_REGION, aws_access_key_id=settings.AM_ACCESS_KEY, - aws_secret_access_key=settings.AM_PASS_KEY + aws_secret_access_key=settings.AM_PASS_KEY, ) - response = conn.send_email(mfrom, msubject, mbody, mto, format='html') - elif mail_sender == 'MAILGUN': + response = conn.send_email(mfrom, msubject, mbody, mto, format="html") + elif mail_sender == "MAILGUN": response = requests.post( settings.MGUN_API_URL, - auth=('api', settings.MGUN_API_KEY), - data={ - 'from': mfrom, - 'to': mto, - 'subject': msubject, - 'html': mbody, - }) - elif mail_sender == 'SENDGRID': + auth=("api", settings.MGUN_API_KEY), + data={"from": mfrom, "to": mto, "subject": msubject, "html": mbody,}, + ) + elif mail_sender == "SENDGRID": sg = sendgrid.SendGridClient(settings.SG_USER, settings.SG_PWD) sending_msg = sendgrid.Mail() sending_msg.set_subject(msubject) @@ -42,7 +38,7 @@ def send_mail(mto, mfrom, msubject, mbody, user_active): sending_msg.set_from(mfrom) sending_msg.add_to(mto) response = sg.send(sending_msg) - elif mail_sender == 'MANDRILL': + elif mail_sender == "MANDRILL": api_key = settings.MANDRILL_API_KEY mandrill_client = mandrill.Mandrill(api_key) @@ -51,7 +47,7 @@ def send_mail(mto, mfrom, msubject, mbody, user_active): "subject": msubject, "from_email": mfrom, "from_name": "Django CRM", - "to": [{'email': i, 'type': 'to'} for i in mto] + "to": [{"email": i, "type": "to"} for i in mto], } response = mandrill_client.messages.send(message=message) diff --git a/crm/settings.py b/crm/settings.py index ce37e93..852b10e 100644 --- a/crm/settings.py +++ b/crm/settings.py @@ -6,11 +6,11 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -env_path = Path('.') / '.env' +env_path = Path(".") / ".env" load_dotenv(dotenv_path=env_path) # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = os.getenv('SECRETKEY') +SECRET_KEY = os.getenv("SECRETKEY") # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -172,6 +172,7 @@ elif ENV_TYPE == "live": from .server_settings import * + SESSION_COOKIE_DOMAIN = ".bottlecrm.com" @@ -286,8 +287,8 @@ "version": 1, "disable_existing_loggers": False, "filters": { - "require_debug_false": {"()": "django.utils.log.RequireDebugFalse", }, - "require_debug_true": {"()": "django.utils.log.RequireDebugTrue", }, + "require_debug_false": {"()": "django.utils.log.RequireDebugFalse",}, + "require_debug_true": {"()": "django.utils.log.RequireDebugTrue",}, }, "formatters": { "django.server": { @@ -384,11 +385,11 @@ CACHES = { "default": { "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", - "LOCATION": os.getenv('MEMCACHELOCATION') + "LOCATION": os.getenv("MEMCACHELOCATION"), } } -PASSWORD_RESET_MAIL_FROM_USER = os.getenv('PASSWORD_RESET_MAIL_FROM_USER') +PASSWORD_RESET_MAIL_FROM_USER = os.getenv("PASSWORD_RESET_MAIL_FROM_USER") SETTINGS_EXPORT = ["APPLICATION_NAME"] diff --git a/docs/source/conf.py b/docs/source/conf.py index 2231248..32ced01 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,8 +13,9 @@ # serve to show the default. from recommonmark.parser import CommonMarkParser + source_parsers = { - '.md': CommonMarkParser, + ".md": CommonMarkParser, } @@ -28,50 +29,48 @@ # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ - 'sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.napoleon' -] +extensions = ["sphinx.ext.autodoc", "sphinx.ext.coverage", "sphinx.ext.napoleon"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['ntemplates'] +templates_path = ["ntemplates"] # The suffix of source filenames. # source_suffix = '.rst' -source_suffix = ['.rst', '.md'] +source_suffix = [".rst", ".md"] # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'Django-CRM' -copyright = u'2017, MicroPyramid' +project = u"Django-CRM" +copyright = u"2017, MicroPyramid" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.7.0' +version = "0.7.0" # The full version, including alpha/beta/rc tags. -release = '0.7.0' +release = "0.7.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -79,167 +78,161 @@ # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['nstatic'] +html_static_path = ["nstatic"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'Django-CRM-doc' +htmlhelp_basename = "Django-CRM-doc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'Django-CRM.tex', u'Django-CRM Documentation', - u'Django-CRM', 'manual'), + ("index", "Django-CRM.tex", u"Django-CRM Documentation", u"Django-CRM", "manual"), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'Django-CRM', u'Django-CRM Documentation', - [u'Django-CRM'], 1) -] +man_pages = [("index", "Django-CRM", u"Django-CRM Documentation", [u"Django-CRM"], 1)] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -248,19 +241,25 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'Django-CRM', u'Django-CRM Documentation', - u'Django-CRM', 'Django-CRM', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "Django-CRM", + u"Django-CRM Documentation", + u"Django-CRM", + "Django-CRM", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/emails/admin.py b/emails/admin.py index fa5e257..c168f93 100644 --- a/emails/admin.py +++ b/emails/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin from emails.models import Email + # Register your models here. admin.site.register(Email) diff --git a/emails/apps.py b/emails/apps.py index 12605a0..4290ce3 100644 --- a/emails/apps.py +++ b/emails/apps.py @@ -2,4 +2,4 @@ class EmailsConfig(AppConfig): - name = 'emails' + name = "emails" diff --git a/emails/forms.py b/emails/forms.py index 2cad990..e4854cf 100644 --- a/emails/forms.py +++ b/emails/forms.py @@ -11,4 +11,4 @@ class EmailForm(forms.ModelForm): class Meta: model = Email - fields = ('from_email', 'to_email', 'subject', 'message') + fields = ("from_email", "to_email", "subject", "message") diff --git a/emails/migrations/0001_initial.py b/emails/migrations/0001_initial.py index c151477..f710f13 100644 --- a/emails/migrations/0001_initial.py +++ b/emails/migrations/0001_initial.py @@ -7,25 +7,30 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Email', + name="Email", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('from_email', models.EmailField(max_length=200)), - ('to_email', models.EmailField(max_length=200)), - ('subject', models.CharField(max_length=200)), - ('message', models.CharField(max_length=200)), - ('file', models.FileField(null=True, upload_to='files/')), - ('send_time', models.DateTimeField(auto_now_add=True)), - ('status', models.CharField(default='sent', max_length=200)), - ('important', models.BooleanField(default=False, max_length=10)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("from_email", models.EmailField(max_length=200)), + ("to_email", models.EmailField(max_length=200)), + ("subject", models.CharField(max_length=200)), + ("message", models.CharField(max_length=200)), + ("file", models.FileField(null=True, upload_to="files/")), + ("send_time", models.DateTimeField(auto_now_add=True)), + ("status", models.CharField(default="sent", max_length=200)), + ("important", models.BooleanField(default=False, max_length=10)), ], - options={ - 'ordering': ['-id'], - }, + options={"ordering": ["-id"],}, ), ] diff --git a/emails/models.py b/emails/models.py index 20832a2..50df818 100644 --- a/emails/models.py +++ b/emails/models.py @@ -1,4 +1,5 @@ from django.db import models + # Create your models here. @@ -18,4 +19,4 @@ def __str__(self): return self.subject class Meta: - ordering = ['-id'] + ordering = ["-id"] diff --git a/emails/urls.py b/emails/urls.py index 301abed..f4c8fc0 100644 --- a/emails/urls.py +++ b/emails/urls.py @@ -3,28 +3,36 @@ from django.conf.urls.static import static from django.conf import settings -app_name = 'emails' +app_name = "emails" urlpatterns = [ - url(r'^list/', views.emails_list, name="list"), - url(r'^compose/', views.email, name="compose"), - url(r'^email_sent/', views.email_sent, name="email_sent"), - url(r'^email_move_to_trash/(?P\d+)/$', - views.email_move_to_trash, name='email_move_to_trash'), - url(r'^email_delete/(?P\d+)/$', - views.email_delete, name='email_delete'), - url(r'^email_trash/', views.email_trash, name="email_trash"), - url(r'^email_trash_delete/(?P\d+)/$', - views.email_trash_delete, name="email_trash_delete"), - url(r'^email_draft/', views.email_draft, name="email_draft"), - url(r'^email_draft_delete/(?P\d+)/$', - views.email_draft_delete, name="email_draft_delete"), - url(r'^email_imp/(?P\d+)/$', views.email_imp, name="email_imp"), - url(r'^email_imp_list/', views.email_imp_list, name="email_imp_list"), - url(r'^email_sent_edit/(?P\d+)/$', - views.email_sent_edit, name="email_sent_edit"), - url(r'^email_unimp/(?P\d+)/$', views.email_unimp, name="email_unimp"), - url(r'^email_view/(?P\d+)/$', views.email_view, name="email_view"), - + url(r"^list/", views.emails_list, name="list"), + url(r"^compose/", views.email, name="compose"), + url(r"^email_sent/", views.email_sent, name="email_sent"), + url( + r"^email_move_to_trash/(?P\d+)/$", + views.email_move_to_trash, + name="email_move_to_trash", + ), + url(r"^email_delete/(?P\d+)/$", views.email_delete, name="email_delete"), + url(r"^email_trash/", views.email_trash, name="email_trash"), + url( + r"^email_trash_delete/(?P\d+)/$", + views.email_trash_delete, + name="email_trash_delete", + ), + url(r"^email_draft/", views.email_draft, name="email_draft"), + url( + r"^email_draft_delete/(?P\d+)/$", + views.email_draft_delete, + name="email_draft_delete", + ), + url(r"^email_imp/(?P\d+)/$", views.email_imp, name="email_imp"), + url(r"^email_imp_list/", views.email_imp_list, name="email_imp_list"), + url( + r"^email_sent_edit/(?P\d+)/$", views.email_sent_edit, name="email_sent_edit" + ), + url(r"^email_unimp/(?P\d+)/$", views.email_unimp, name="email_unimp"), + url(r"^email_view/(?P\d+)/$", views.email_view, name="email_view"), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/emails/views.py b/emails/views.py index 1a45a02..30035c8 100644 --- a/emails/views.py +++ b/emails/views.py @@ -11,32 +11,31 @@ def emails_list(request): filter_list = Email.objects.all() - if request.GET.get('from_date', ''): - from_date = request.GET.get('from_date', '') + if request.GET.get("from_date", ""): + from_date = request.GET.get("from_date", "") fd = datetime.strptime(from_date, "%Y-%m-%d").date() filter_list = filter_list.filter(send_time__gte=fd) - if request.GET.get('to_date', ''): - to_date = request.GET.get('to_date', '') + if request.GET.get("to_date", ""): + to_date = request.GET.get("to_date", "") td = datetime.strptime(to_date, "%Y-%m-%d") td = td + timedelta(seconds=(24 * 60 * 60 - 1)) filter_list = filter_list.filter(send_time__lte=td) - if request.GET.get('name', ''): - name = request.GET.get('name', '') + if request.GET.get("name", ""): + name = request.GET.get("name", "") filter_list = filter_list.filter(to_email__startswith=name) - return render(request, 'mail_all.html', { - 'filter_list': filter_list}) + return render(request, "mail_all.html", {"filter_list": filter_list}) def email(request): if request.method == "POST": form = EmailForm(request.POST, request.FILES) if form.is_valid(): - subject = request.POST.get('subject', '') - message = request.POST.get('message', '') - from_email = request.POST.get('from_email', '') - to_email = request.POST.get('to_email', '') - file = request.FILES.get('files', None) - status = request.POST.get('email_draft', '') + subject = request.POST.get("subject", "") + message = request.POST.get("message", "") + from_email = request.POST.get("from_email", "") + to_email = request.POST.get("to_email", "") + file = request.FILES.get("files", None) + status = request.POST.get("email_draft", "") email = EmailMessage(subject, message, from_email, [to_email]) email.content_subtype = "html" f = form.save() @@ -48,126 +47,123 @@ def email(request): else: email.send(fail_silently=False) f.save() - return HttpResponseRedirect(reverse('emails:list')) + return HttpResponseRedirect(reverse("emails:list")) else: - return render(request, 'create_mail.html', {'form': form}) + return render(request, "create_mail.html", {"form": form}) else: form = EmailForm() - return render(request, 'create_mail.html', {'form': form}) + return render(request, "create_mail.html", {"form": form}) def email_sent(request): filter_list = Email.objects.filter(status="sent") - if request.GET.get('from_date', ''): - from_date = request.GET.get('from_date', '') + if request.GET.get("from_date", ""): + from_date = request.GET.get("from_date", "") fd = datetime.strptime(from_date, "%Y-%m-%d").date() filter_list = filter_list.filter(send_time__gte=fd) - if request.GET.get('to_date', ''): - to_date = request.GET.get('to_date', '') + if request.GET.get("to_date", ""): + to_date = request.GET.get("to_date", "") td = datetime.strptime(to_date, "%Y-%m-%d") td = td + timedelta(seconds=(24 * 60 * 60 - 1)) filter_list = filter_list.filter(send_time__lte=td) - if request.GET.get('name', ''): - name = request.GET.get('name', '') + if request.GET.get("name", ""): + name = request.GET.get("name", "") filter_list = filter_list.filter(to_email__startswith=name) - return render(request, 'mail_sent.html', - {'filter_list': filter_list}) + return render(request, "mail_sent.html", {"filter_list": filter_list}) def email_trash(request): filter_list = Email.objects.filter(status="trash") - if request.GET.get('from_date', ''): - from_date = request.GET.get('from_date', '') + if request.GET.get("from_date", ""): + from_date = request.GET.get("from_date", "") fd = datetime.strptime(from_date, "%Y-%m-%d").date() filter_list = filter_list.filter(send_time__gte=fd) - if request.GET.get('to_date', ''): - to_date = request.GET.get('to_date', '') + if request.GET.get("to_date", ""): + to_date = request.GET.get("to_date", "") td = datetime.strptime(to_date, "%Y-%m-%d") td = td + timedelta(seconds=(24 * 60 * 60 - 1)) filter_list = filter_list.filter(send_time__lte=td) - if request.GET.get('name', ''): - name = request.GET.get('name', '') + if request.GET.get("name", ""): + name = request.GET.get("name", "") filter_list = filter_list.filter(to_email__startswith=name) - return render(request, 'mail_trash.html', - {'filter_list': filter_list}) + return render(request, "mail_trash.html", {"filter_list": filter_list}) def email_trash_delete(request, pk): get_object_or_404(Email, id=pk).delete() - return HttpResponseRedirect(reverse('emails:email_trash')) + return HttpResponseRedirect(reverse("emails:email_trash")) def email_draft(request): filter_list = Email.objects.filter(status="draft") - if request.GET.get('from_date', ''): - from_date = request.GET.get('from_date', '') + if request.GET.get("from_date", ""): + from_date = request.GET.get("from_date", "") fd = datetime.strptime(from_date, "%Y-%m-%d").date() filter_list = filter_list.filter(send_time__gte=fd) - if request.GET.get('to_date', ''): - to_date = request.GET.get('to_date', '') + if request.GET.get("to_date", ""): + to_date = request.GET.get("to_date", "") td = datetime.strptime(to_date, "%Y-%m-%d") td = td + timedelta(seconds=(24 * 60 * 60 - 1)) filter_list = filter_list.filter(send_time__lte=td) - if request.GET.get('name', ''): - name = request.GET.get('name', '') + if request.GET.get("name", ""): + name = request.GET.get("name", "") filter_list = filter_list.filter(to_email__startswith=name) - return render(request, 'mail_drafts.html', - {'filter_list': filter_list}) + return render(request, "mail_drafts.html", {"filter_list": filter_list}) def email_draft_delete(request, pk): get_object_or_404(Email, id=pk).delete() - return HttpResponseRedirect(reverse('emails:email_draft')) + return HttpResponseRedirect(reverse("emails:email_draft")) def email_delete(request, pk): get_object_or_404(Email, id=pk).delete() - return HttpResponseRedirect(reverse('emails:email_sent')) + return HttpResponseRedirect(reverse("emails:email_sent")) def email_move_to_trash(request, pk): trashitem = get_object_or_404(Email, id=pk) trashitem.status = "trash" trashitem.save() - return HttpResponseRedirect(request.META['HTTP_REFERER']) + return HttpResponseRedirect(request.META["HTTP_REFERER"]) def email_imp(request, pk): impitem = get_object_or_404(Email, id=pk) impitem.important = True impitem.save() - return HttpResponseRedirect(request.META['HTTP_REFERER']) + return HttpResponseRedirect(request.META["HTTP_REFERER"]) def email_imp_list(request): filter_list = Email.objects.filter(important="True") - if request.GET.get('from_date', ''): - from_date = request.GET.get('from_date', '') + if request.GET.get("from_date", ""): + from_date = request.GET.get("from_date", "") fd = datetime.strptime(from_date, "%Y-%m-%d").date() filter_list = filter_list.filter(send_time__gte=fd) - if request.GET.get('to_date', ''): - to_date = request.GET.get('to_date', '') + if request.GET.get("to_date", ""): + to_date = request.GET.get("to_date", "") td = datetime.strptime(to_date, "%Y-%m-%d") td = td + timedelta(seconds=(24 * 60 * 60 - 1)) filter_list = filter_list.filter(send_time__lte=td) - if request.GET.get('name', ''): - name = request.GET.get('name', '') + if request.GET.get("name", ""): + name = request.GET.get("name", "") filter_list = filter_list.filter(to_email__startswith=name) - return render(request, 'mail_important.html', {'filter_list': filter_list}) + return render(request, "mail_important.html", {"filter_list": filter_list}) def email_sent_edit(request, pk): em = get_object_or_404(Email, pk=pk) - if request.method == 'POST': + if request.method == "POST": form = EmailForm(request.POST, instance=em) if form.is_valid(): - subject = request.POST.get('subject', '') - message = request.POST.get('message', '') - from_email = request.POST.get('from_email', '') - to_email = request.POST.get('to_email', '') - file = request.FILES.get('files', None) - status = request.POST.get('email_draft', '') + subject = request.POST.get("subject", "") + message = request.POST.get("message", "") + from_email = request.POST.get("from_email", "") + to_email = request.POST.get("to_email", "") + file = request.FILES.get("files", None) + status = request.POST.get("email_draft", "") email = EmailMessage(subject, message, from_email, [to_email]) email.content_subtype = "html" f = form.save() @@ -180,24 +176,22 @@ def email_sent_edit(request, pk): email.send(fail_silently=False) f.status = "sent" f.save() - return HttpResponseRedirect(reverse('emails:list')) + return HttpResponseRedirect(reverse("emails:list")) else: - return render(request, 'create_mail.html', - {'form': form, 'em': em}) + return render(request, "create_mail.html", {"form": form, "em": em}) else: form = EmailForm() - return render(request, 'create_mail.html', - {'form': form, 'em': em}) + return render(request, "create_mail.html", {"form": form, "em": em}) def email_unimp(request, pk): unimpitem = get_object_or_404(Email, id=pk) unimpitem.important = False unimpitem.save() - return HttpResponseRedirect(request.META['HTTP_REFERER']) + return HttpResponseRedirect(request.META["HTTP_REFERER"]) def email_view(request, pk): email_view = get_object_or_404(Email, pk=pk) x = EmailForm(instance=email_view) - return render(request, 'create_mail.html', {'x': x}) + return render(request, "create_mail.html", {"x": x}) diff --git a/events/apps.py b/events/apps.py index 3854644..60b376c 100644 --- a/events/apps.py +++ b/events/apps.py @@ -2,4 +2,4 @@ class EventsConfig(AppConfig): - name = 'events' + name = "events" diff --git a/events/migrations/0001_initial.py b/events/migrations/0001_initial.py index 27593dc..4d6cbfd 100644 --- a/events/migrations/0001_initial.py +++ b/events/migrations/0001_initial.py @@ -10,29 +10,86 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('contacts', '0003_merge_20190214_1427'), + ("contacts", "0003_merge_20190214_1427"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Event', + name="Event", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=64, verbose_name='Event')), - ('event_type', models.CharField(choices=[('Recurring', 'Recurring'), ('Non-Recurring', 'Non-Recurring')], max_length=20)), - ('status', models.CharField(blank=True, choices=[('Planned', 'Planned'), ('Held', 'Held'), ('Not Held', 'Not Held'), ('Not Started', 'Not Started'), ('Started', 'Started'), ('Completed', 'Completed'), ('Canceled', 'Canceled'), ('Deferred', 'Deferred')], max_length=64, null=True)), - ('start_date', models.DateField(default=None)), - ('start_time', models.TimeField(default=None)), - ('end_date', models.DateField(default=None)), - ('end_time', models.TimeField(blank=True, default=None, null=True)), - ('description', models.TextField(blank=True, null=True)), - ('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Created on')), - ('is_active', models.BooleanField(default=True)), - ('disabled', models.BooleanField(default=False)), - ('assigned_to', models.ManyToManyField(blank=True, related_name='event_assigned', to=settings.AUTH_USER_MODEL)), - ('contacts', models.ManyToManyField(blank=True, related_name='event_contact', to='contacts.Contact')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='event_created_by_user', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=64, verbose_name="Event")), + ( + "event_type", + models.CharField( + choices=[ + ("Recurring", "Recurring"), + ("Non-Recurring", "Non-Recurring"), + ], + max_length=20, + ), + ), + ( + "status", + models.CharField( + blank=True, + choices=[ + ("Planned", "Planned"), + ("Held", "Held"), + ("Not Held", "Not Held"), + ("Not Started", "Not Started"), + ("Started", "Started"), + ("Completed", "Completed"), + ("Canceled", "Canceled"), + ("Deferred", "Deferred"), + ], + max_length=64, + null=True, + ), + ), + ("start_date", models.DateField(default=None)), + ("start_time", models.TimeField(default=None)), + ("end_date", models.DateField(default=None)), + ("end_time", models.TimeField(blank=True, default=None, null=True)), + ("description", models.TextField(blank=True, null=True)), + ( + "created_on", + models.DateTimeField(auto_now_add=True, verbose_name="Created on"), + ), + ("is_active", models.BooleanField(default=True)), + ("disabled", models.BooleanField(default=False)), + ( + "assigned_to", + models.ManyToManyField( + blank=True, + related_name="event_assigned", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "contacts", + models.ManyToManyField( + blank=True, related_name="event_contact", to="contacts.Contact" + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="event_created_by_user", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/events/migrations/0002_event_date_of_meeting.py b/events/migrations/0002_event_date_of_meeting.py index e5773de..a96f20e 100644 --- a/events/migrations/0002_event_date_of_meeting.py +++ b/events/migrations/0002_event_date_of_meeting.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('events', '0001_initial'), + ("events", "0001_initial"), ] operations = [ migrations.AddField( - model_name='event', - name='date_of_meeting', + model_name="event", + name="date_of_meeting", field=models.DateField(blank=True, null=True), ), ] diff --git a/events/migrations/0003_event_teams.py b/events/migrations/0003_event_teams.py index 3ce3227..3dc0cc9 100644 --- a/events/migrations/0003_event_teams.py +++ b/events/migrations/0003_event_teams.py @@ -6,14 +6,14 @@ class Migration(migrations.Migration): dependencies = [ - ('teams', '0003_auto_20190909_1621'), - ('events', '0002_event_date_of_meeting'), + ("teams", "0003_auto_20190909_1621"), + ("events", "0002_event_date_of_meeting"), ] operations = [ migrations.AddField( - model_name='event', - name='teams', - field=models.ManyToManyField(related_name='event_teams', to='teams.Teams'), + model_name="event", + name="teams", + field=models.ManyToManyField(related_name="event_teams", to="teams.Teams"), ), ] diff --git a/events/migrations/0004_event_company.py b/events/migrations/0004_event_company.py index dd29534..2d97e71 100644 --- a/events/migrations/0004_event_company.py +++ b/events/migrations/0004_event_company.py @@ -7,14 +7,19 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0020_auto_20200409_1653'), - ('events', '0003_event_teams'), + ("common", "0020_auto_20200409_1653"), + ("events", "0003_event_teams"), ] operations = [ migrations.AddField( - model_name='event', - name='company', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Company'), + model_name="event", + name="company", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="common.Company", + ), ), ] diff --git a/events/tasks.py b/events/tasks.py index 6012d00..2166d5a 100644 --- a/events/tasks.py +++ b/events/tasks.py @@ -11,38 +11,44 @@ @task -def send_email(event_id, recipients, domain='demo.django-crm.io', protocol='http'): +def send_email(event_id, recipients, domain="demo.django-crm.io", protocol="http"): event = Event.objects.filter(id=event_id).first() - subject = ' Invitation for an event.' + subject = " Invitation for an event." context = {} - context['event'] = event.name - context['event_id'] = event_id - context['event_created_by'] = event.created_by - context['event_date_of_meeting'] = event.date_of_meeting - context["url"] = protocol + '://' + domain + \ - reverse('events:detail_view', args=(event.id,)) + context["event"] = event.name + context["event_id"] = event_id + context["event_created_by"] = event.created_by + context["event_date_of_meeting"] = event.date_of_meeting + context["url"] = ( + protocol + "://" + domain + reverse("events:detail_view", args=(event.id,)) + ) # recipients = event.assigned_to.filter(is_active=True) - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) for user in recipients: recipients_list = [] user = User.objects.filter(id=user, is_active=True).first() if user: - if (user.email not in blocked_emails) and (user.email.split('@')[-1] not in blocked_domains): + if (user.email not in blocked_emails) and ( + user.email.split("@")[-1] not in blocked_domains + ): recipients_list.append(user.email) event_members = event.assigned_to.filter(is_active=True) - context['other_members'] = list(event_members.exclude( - id=user.id).values_list('email', flat=True)) - if len(context['other_members']) > 0: - context['other_members'] = ', '.join(context['other_members']) + context["other_members"] = list( + event_members.exclude(id=user.id).values_list("email", flat=True) + ) + if len(context["other_members"]) > 0: + context["other_members"] = ", ".join(context["other_members"]) else: - context['other_members'] = '' - context['user'] = user.email + context["other_members"] = "" + context["user"] = user.email html_content = render_to_string( - 'assigned_to_email_template_event.html', context=context) + "assigned_to_email_template_event.html", context=context + ) msg = EmailMessage( - subject=subject, body=html_content, to=recipients_list) + subject=subject, body=html_content, to=recipients_list + ) msg.content_subtype = "html" msg.send() @@ -78,14 +84,14 @@ def send_email(event_id, recipients, domain='demo.django-crm.io', protocol='http # msg.content_subtype = "html" # msg.send() - # might need to add contacts to TODO - # recipients = event.contacts.all() - # if recipients.count() > 0: - # for recipient in recipients: - # context['user'] = recipient.email - # html_content = render_to_string( - # 'assigned_to_email_template.html', context=context) - # msg = EmailMessage( - # subject=subject, body=html_content, to=[recipient.email, ]) - # msg.content_subtype = "html" - # msg.send + # might need to add contacts to TODO + # recipients = event.contacts.all() + # if recipients.count() > 0: + # for recipient in recipients: + # context['user'] = recipient.email + # html_content = render_to_string( + # 'assigned_to_email_template.html', context=context) + # msg = EmailMessage( + # subject=subject, body=html_content, to=[recipient.email, ]) + # msg.content_subtype = "html" + # msg.send diff --git a/events/tests_celery_tasks.py b/events/tests_celery_tasks.py index dc6d467..5d9b4a9 100644 --- a/events/tests_celery_tasks.py +++ b/events/tests_celery_tasks.py @@ -6,10 +6,11 @@ class TestEventCeleryTasks(EventObjectTest, TestCase): - - @override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - CELERY_ALWAYS_EAGER=True, - BROKER_BACKEND='memory') + @override_settings( + CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, + CELERY_ALWAYS_EAGER=True, + BROKER_BACKEND="memory", + ) def test_event_celery_tasks(self): task = send_email.apply((self.event.id, [self.user.id, self.user1.id])) - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) diff --git a/events/urls.py b/events/urls.py index 89a645d..baed470 100644 --- a/events/urls.py +++ b/events/urls.py @@ -1,22 +1,21 @@ from django.urls import path from events.views import * -app_name = 'events' +app_name = "events" urlpatterns = [ - path('', events_list, name='events_list'), - path('create/', event_create, name='event_create'), - path('detail//', event_detail_view, name='detail_view'), - path('edit//', event_update, name='event_update'), - path('delete//', event_delete, name='event_delete'), - - path('comment/add/', AddCommentView.as_view(), name="add_comment"), - path('comment/edit/', UpdateCommentView.as_view(), name="edit_comment"), - path('comment/remove/', DeleteCommentView.as_view(), name="remove_comment"), - - path('attachment/add/', AddAttachmentView.as_view(), name="add_attachment"), - path('attachment/remove/', DeleteAttachmentsView.as_view(), - name="remove_attachment"), - path('get_teams_and_users/', get_teams_and_users, name="get_teams_and_users") + path("", events_list, name="events_list"), + path("create/", event_create, name="event_create"), + path("detail//", event_detail_view, name="detail_view"), + path("edit//", event_update, name="event_update"), + path("delete//", event_delete, name="event_delete"), + path("comment/add/", AddCommentView.as_view(), name="add_comment"), + path("comment/edit/", UpdateCommentView.as_view(), name="edit_comment"), + path("comment/remove/", DeleteCommentView.as_view(), name="remove_comment"), + path("attachment/add/", AddAttachmentView.as_view(), name="add_attachment"), + path( + "attachment/remove/", DeleteAttachmentsView.as_view(), name="remove_attachment" + ), + path("get_teams_and_users/", get_teams_and_users, name="get_teams_and_users"), ] diff --git a/invoices/apps.py b/invoices/apps.py index c929ba1..9de56fa 100644 --- a/invoices/apps.py +++ b/invoices/apps.py @@ -2,4 +2,4 @@ class InvoicesConfig(AppConfig): - name = 'invoices' + name = "invoices" diff --git a/invoices/migrations/0001_initial.py b/invoices/migrations/0001_initial.py index ad43b58..9785b62 100644 --- a/invoices/migrations/0001_initial.py +++ b/invoices/migrations/0001_initial.py @@ -11,37 +11,279 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('common', '0013_auto_20190508_1540'), + ("common", "0013_auto_20190508_1540"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Invoice', + name="Invoice", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('invoice_title', models.CharField(max_length=50, verbose_name='Invoice Title')), - ('invoice_number', models.CharField(max_length=50, verbose_name='Invoice Number')), - ('name', models.CharField(max_length=100, verbose_name='Name')), - ('email', models.EmailField(max_length=254, verbose_name='Email')), - ('quantity', models.PositiveIntegerField(default=0)), - ('rate', models.DecimalField(decimal_places=2, default=0, max_digits=12)), - ('total_amount', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)), - ('currency', models.CharField(blank=True, choices=[('AED', 'AED, Dirham'), ('AFN', 'AFN, Afghani'), ('ALL', 'ALL, Lek'), ('AMD', 'AMD, Dram'), ('ANG', 'ANG, Guilder'), ('AOA', 'AOA, Kwanza'), ('ARS', 'ARS, Peso'), ('AUD', 'AUD, Dollar'), ('AWG', 'AWG, Guilder'), ('AZN', 'AZN, Manat'), ('BAM', 'BAM, Marka'), ('BBD', 'BBD, Dollar'), ('BDT', 'BDT, Taka'), ('BGN', 'BGN, Lev'), ('BHD', 'BHD, Dinar'), ('BIF', 'BIF, Franc'), ('BMD', 'BMD, Dollar'), ('BND', 'BND, Dollar'), ('BOB', 'BOB, Boliviano'), ('BRL', 'BRL, Real'), ('BSD', 'BSD, Dollar'), ('BTN', 'BTN, Ngultrum'), ('BWP', 'BWP, Pula'), ('BYR', 'BYR, Ruble'), ('BZD', 'BZD, Dollar'), ('CAD', 'CAD, Dollar'), ('CDF', 'CDF, Franc'), ('CHF', 'CHF, Franc'), ('CLP', 'CLP, Peso'), ('CNY', 'CNY, Yuan Renminbi'), ('COP', 'COP, Peso'), ('CRC', 'CRC, Colon'), ('CUP', 'CUP, Peso'), ('CVE', 'CVE, Escudo'), ('CZK', 'CZK, Koruna'), ('DJF', 'DJF, Franc'), ('DKK', 'DKK, Krone'), ('DOP', 'DOP, Peso'), ('DZD', 'DZD, Dinar'), ('EGP', 'EGP, Pound'), ('ERN', 'ERN, Nakfa'), ('ETB', 'ETB, Birr'), ('EUR', 'EUR, Euro'), ('FJD', 'FJD, Dollar'), ('FKP', 'FKP, Pound'), ('GBP', 'GBP, Pound'), ('GEL', 'GEL, Lari'), ('GHS', 'GHS, Cedi'), ('GIP', 'GIP, Pound'), ('GMD', 'GMD, Dalasi'), ('GNF', 'GNF, Franc'), ('GTQ', 'GTQ, Quetzal'), ('GYD', 'GYD, Dollar'), ('HKD', 'HKD, Dollar'), ('HNL', 'HNL, Lempira'), ('HRK', 'HRK, Kuna'), ('HTG', 'HTG, Gourde'), ('HUF', 'HUF, Forint'), ('IDR', 'IDR, Rupiah'), ('ILS', 'ILS, Shekel'), ('INR', 'INR, Rupee'), ('IQD', 'IQD, Dinar'), ('IRR', 'IRR, Rial'), ('ISK', 'ISK, Krona'), ('JMD', 'JMD, Dollar'), ('JOD', 'JOD, Dinar'), ('JPY', 'JPY, Yen'), ('KES', 'KES, Shilling'), ('KGS', 'KGS, Som'), ('KHR', 'KHR, Riels'), ('KMF', 'KMF, Franc'), ('KPW', 'KPW, Won'), ('KRW', 'KRW, Won'), ('KWD', 'KWD, Dinar'), ('KYD', 'KYD, Dollar'), ('KZT', 'KZT, Tenge'), ('LAK', 'LAK, Kip'), ('LBP', 'LBP, Pound'), ('LKR', 'LKR, Rupee'), ('LRD', 'LRD, Dollar'), ('LSL', 'LSL, Loti'), ('LTL', 'LTL, Litas'), ('LVL', 'LVL, Lat'), ('LYD', 'LYD, Dinar'), ('MAD', 'MAD, Dirham'), ('MDL', 'MDL, Leu'), ('MGA', 'MGA, Ariary'), ('MKD', 'MKD, Denar'), ('MMK', 'MMK, Kyat'), ('MNT', 'MNT, Tugrik'), ('MOP', 'MOP, Pataca'), ('MRO', 'MRO, Ouguiya'), ('MUR', 'MUR, Rupee'), ('MVR', 'MVR, Rufiyaa'), ('MWK', 'MWK, Kwacha'), ('MXN', 'MXN, Peso'), ('MYR', 'MYR, Ringgit'), ('MZN', 'MZN, Metical'), ('NAD', 'NAD, Dollar'), ('NGN', 'NGN, Naira'), ('NIO', 'NIO, Cordoba'), ('NOK', 'NOK, Krone'), ('NPR', 'NPR, Rupee'), ('NZD', 'NZD, Dollar'), ('OMR', 'OMR, Rial'), ('PAB', 'PAB, Balboa'), ('PEN', 'PEN, Sol'), ('PGK', 'PGK, Kina'), ('PHP', 'PHP, Peso'), ('PKR', 'PKR, Rupee'), ('PLN', 'PLN, Zloty'), ('PYG', 'PYG, Guarani'), ('QAR', 'QAR, Rial'), ('RON', 'RON, Leu'), ('RSD', 'RSD, Dinar'), ('RUB', 'RUB, Ruble'), ('RWF', 'RWF, Franc'), ('SAR', 'SAR, Rial'), ('SBD', 'SBD, Dollar'), ('SCR', 'SCR, Rupee'), ('SDG', 'SDG, Pound'), ('SEK', 'SEK, Krona'), ('SGD', 'SGD, Dollar'), ('SHP', 'SHP, Pound'), ('SLL', 'SLL, Leone'), ('SOS', 'SOS, Shilling'), ('SRD', 'SRD, Dollar'), ('SSP', 'SSP, Pound'), ('STD', 'STD, Dobra'), ('SYP', 'SYP, Pound'), ('SZL', 'SZL, Lilangeni'), ('THB', 'THB, Baht'), ('TJS', 'TJS, Somoni'), ('TMT', 'TMT, Manat'), ('TND', 'TND, Dinar'), ('TOP', 'TOP, Paanga'), ('TRY', 'TRY, Lira'), ('TTD', 'TTD, Dollar'), ('TWD', 'TWD, Dollar'), ('TZS', 'TZS, Shilling'), ('UAH', 'UAH, Hryvnia'), ('UGX', 'UGX, Shilling'), ('USD', '$, Dollar'), ('UYU', 'UYU, Peso'), ('UZS', 'UZS, Som'), ('VEF', 'VEF, Bolivar'), ('VND', 'VND, Dong'), ('VUV', 'VUV, Vatu'), ('WST', 'WST, Tala'), ('XAF', 'XAF, Franc'), ('XCD', 'XCD, Dollar'), ('XOF', 'XOF, Franc'), ('XPF', 'XPF, Franc'), ('YER', 'YER, Rial'), ('ZAR', 'ZAR, Rand'), ('ZMK', 'ZMK, Kwacha'), ('ZWL', 'ZWL, Dollar')], max_length=3, null=True)), - ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True)), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('amount_due', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)), - ('amount_paid', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)), - ('is_email_sent', models.BooleanField(default=False)), - ('status', models.CharField(choices=[('Draft', 'Draft'), ('Sent', 'Sent'), ('Paid', 'Paid'), ('Cancel', 'Cancel'), ('Pending', 'Pending')], default='Draft', max_length=15)), - ('assigned_to', models.ManyToManyField(related_name='invoice_assigned_to', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_created_by', to=settings.AUTH_USER_MODEL)), - ('from_address', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_from_address', to='common.Address')), - ('to_address', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_to_address', to='common.Address')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "invoice_title", + models.CharField(max_length=50, verbose_name="Invoice Title"), + ), + ( + "invoice_number", + models.CharField(max_length=50, verbose_name="Invoice Number"), + ), + ("name", models.CharField(max_length=100, verbose_name="Name")), + ("email", models.EmailField(max_length=254, verbose_name="Email")), + ("quantity", models.PositiveIntegerField(default=0)), + ( + "rate", + models.DecimalField(decimal_places=2, default=0, max_digits=12), + ), + ( + "total_amount", + models.DecimalField( + blank=True, decimal_places=2, max_digits=12, null=True + ), + ), + ( + "currency", + models.CharField( + blank=True, + choices=[ + ("AED", "AED, Dirham"), + ("AFN", "AFN, Afghani"), + ("ALL", "ALL, Lek"), + ("AMD", "AMD, Dram"), + ("ANG", "ANG, Guilder"), + ("AOA", "AOA, Kwanza"), + ("ARS", "ARS, Peso"), + ("AUD", "AUD, Dollar"), + ("AWG", "AWG, Guilder"), + ("AZN", "AZN, Manat"), + ("BAM", "BAM, Marka"), + ("BBD", "BBD, Dollar"), + ("BDT", "BDT, Taka"), + ("BGN", "BGN, Lev"), + ("BHD", "BHD, Dinar"), + ("BIF", "BIF, Franc"), + ("BMD", "BMD, Dollar"), + ("BND", "BND, Dollar"), + ("BOB", "BOB, Boliviano"), + ("BRL", "BRL, Real"), + ("BSD", "BSD, Dollar"), + ("BTN", "BTN, Ngultrum"), + ("BWP", "BWP, Pula"), + ("BYR", "BYR, Ruble"), + ("BZD", "BZD, Dollar"), + ("CAD", "CAD, Dollar"), + ("CDF", "CDF, Franc"), + ("CHF", "CHF, Franc"), + ("CLP", "CLP, Peso"), + ("CNY", "CNY, Yuan Renminbi"), + ("COP", "COP, Peso"), + ("CRC", "CRC, Colon"), + ("CUP", "CUP, Peso"), + ("CVE", "CVE, Escudo"), + ("CZK", "CZK, Koruna"), + ("DJF", "DJF, Franc"), + ("DKK", "DKK, Krone"), + ("DOP", "DOP, Peso"), + ("DZD", "DZD, Dinar"), + ("EGP", "EGP, Pound"), + ("ERN", "ERN, Nakfa"), + ("ETB", "ETB, Birr"), + ("EUR", "EUR, Euro"), + ("FJD", "FJD, Dollar"), + ("FKP", "FKP, Pound"), + ("GBP", "GBP, Pound"), + ("GEL", "GEL, Lari"), + ("GHS", "GHS, Cedi"), + ("GIP", "GIP, Pound"), + ("GMD", "GMD, Dalasi"), + ("GNF", "GNF, Franc"), + ("GTQ", "GTQ, Quetzal"), + ("GYD", "GYD, Dollar"), + ("HKD", "HKD, Dollar"), + ("HNL", "HNL, Lempira"), + ("HRK", "HRK, Kuna"), + ("HTG", "HTG, Gourde"), + ("HUF", "HUF, Forint"), + ("IDR", "IDR, Rupiah"), + ("ILS", "ILS, Shekel"), + ("INR", "INR, Rupee"), + ("IQD", "IQD, Dinar"), + ("IRR", "IRR, Rial"), + ("ISK", "ISK, Krona"), + ("JMD", "JMD, Dollar"), + ("JOD", "JOD, Dinar"), + ("JPY", "JPY, Yen"), + ("KES", "KES, Shilling"), + ("KGS", "KGS, Som"), + ("KHR", "KHR, Riels"), + ("KMF", "KMF, Franc"), + ("KPW", "KPW, Won"), + ("KRW", "KRW, Won"), + ("KWD", "KWD, Dinar"), + ("KYD", "KYD, Dollar"), + ("KZT", "KZT, Tenge"), + ("LAK", "LAK, Kip"), + ("LBP", "LBP, Pound"), + ("LKR", "LKR, Rupee"), + ("LRD", "LRD, Dollar"), + ("LSL", "LSL, Loti"), + ("LTL", "LTL, Litas"), + ("LVL", "LVL, Lat"), + ("LYD", "LYD, Dinar"), + ("MAD", "MAD, Dirham"), + ("MDL", "MDL, Leu"), + ("MGA", "MGA, Ariary"), + ("MKD", "MKD, Denar"), + ("MMK", "MMK, Kyat"), + ("MNT", "MNT, Tugrik"), + ("MOP", "MOP, Pataca"), + ("MRO", "MRO, Ouguiya"), + ("MUR", "MUR, Rupee"), + ("MVR", "MVR, Rufiyaa"), + ("MWK", "MWK, Kwacha"), + ("MXN", "MXN, Peso"), + ("MYR", "MYR, Ringgit"), + ("MZN", "MZN, Metical"), + ("NAD", "NAD, Dollar"), + ("NGN", "NGN, Naira"), + ("NIO", "NIO, Cordoba"), + ("NOK", "NOK, Krone"), + ("NPR", "NPR, Rupee"), + ("NZD", "NZD, Dollar"), + ("OMR", "OMR, Rial"), + ("PAB", "PAB, Balboa"), + ("PEN", "PEN, Sol"), + ("PGK", "PGK, Kina"), + ("PHP", "PHP, Peso"), + ("PKR", "PKR, Rupee"), + ("PLN", "PLN, Zloty"), + ("PYG", "PYG, Guarani"), + ("QAR", "QAR, Rial"), + ("RON", "RON, Leu"), + ("RSD", "RSD, Dinar"), + ("RUB", "RUB, Ruble"), + ("RWF", "RWF, Franc"), + ("SAR", "SAR, Rial"), + ("SBD", "SBD, Dollar"), + ("SCR", "SCR, Rupee"), + ("SDG", "SDG, Pound"), + ("SEK", "SEK, Krona"), + ("SGD", "SGD, Dollar"), + ("SHP", "SHP, Pound"), + ("SLL", "SLL, Leone"), + ("SOS", "SOS, Shilling"), + ("SRD", "SRD, Dollar"), + ("SSP", "SSP, Pound"), + ("STD", "STD, Dobra"), + ("SYP", "SYP, Pound"), + ("SZL", "SZL, Lilangeni"), + ("THB", "THB, Baht"), + ("TJS", "TJS, Somoni"), + ("TMT", "TMT, Manat"), + ("TND", "TND, Dinar"), + ("TOP", "TOP, Paanga"), + ("TRY", "TRY, Lira"), + ("TTD", "TTD, Dollar"), + ("TWD", "TWD, Dollar"), + ("TZS", "TZS, Shilling"), + ("UAH", "UAH, Hryvnia"), + ("UGX", "UGX, Shilling"), + ("USD", "$, Dollar"), + ("UYU", "UYU, Peso"), + ("UZS", "UZS, Som"), + ("VEF", "VEF, Bolivar"), + ("VND", "VND, Dong"), + ("VUV", "VUV, Vatu"), + ("WST", "WST, Tala"), + ("XAF", "XAF, Franc"), + ("XCD", "XCD, Dollar"), + ("XOF", "XOF, Franc"), + ("XPF", "XPF, Franc"), + ("YER", "YER, Rial"), + ("ZAR", "ZAR, Rand"), + ("ZMK", "ZMK, Kwacha"), + ("ZWL", "ZWL, Dollar"), + ], + max_length=3, + null=True, + ), + ), + ( + "phone", + phonenumber_field.modelfields.PhoneNumberField( + blank=True, max_length=128, null=True + ), + ), + ("created_on", models.DateTimeField(auto_now_add=True)), + ( + "amount_due", + models.DecimalField( + blank=True, decimal_places=2, max_digits=12, null=True + ), + ), + ( + "amount_paid", + models.DecimalField( + blank=True, decimal_places=2, max_digits=12, null=True + ), + ), + ("is_email_sent", models.BooleanField(default=False)), + ( + "status", + models.CharField( + choices=[ + ("Draft", "Draft"), + ("Sent", "Sent"), + ("Paid", "Paid"), + ("Cancel", "Cancel"), + ("Pending", "Pending"), + ], + default="Draft", + max_length=15, + ), + ), + ( + "assigned_to", + models.ManyToManyField( + related_name="invoice_assigned_to", to=settings.AUTH_USER_MODEL + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="invoice_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "from_address", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="invoice_from_address", + to="common.Address", + ), + ), + ( + "to_address", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="invoice_to_address", + to="common.Address", + ), + ), ], - options={ - 'verbose_name': 'Invoice', - 'verbose_name_plural': 'Invoices', - }, + options={"verbose_name": "Invoice", "verbose_name_plural": "Invoices",}, ), ] diff --git a/invoices/migrations/0002_auto_20190524_1113.py b/invoices/migrations/0002_auto_20190524_1113.py index 53f24bc..57f1584 100644 --- a/invoices/migrations/0002_auto_20190524_1113.py +++ b/invoices/migrations/0002_auto_20190524_1113.py @@ -6,13 +6,23 @@ class Migration(migrations.Migration): dependencies = [ - ('invoices', '0001_initial'), + ("invoices", "0001_initial"), ] operations = [ migrations.AlterField( - model_name='invoice', - name='status', - field=models.CharField(choices=[('Draft', 'Draft'), ('Sent', 'Sent'), ('Paid', 'Paid'), ('Pending', 'Pending'), ('Cancel', 'Cancel')], default='Draft', max_length=15), + model_name="invoice", + name="status", + field=models.CharField( + choices=[ + ("Draft", "Draft"), + ("Sent", "Sent"), + ("Paid", "Paid"), + ("Pending", "Pending"), + ("Cancel", "Cancel"), + ], + default="Draft", + max_length=15, + ), ), ] diff --git a/invoices/migrations/0003_auto_20190527_1620.py b/invoices/migrations/0003_auto_20190527_1620.py index 4179d46..8b37ef0 100644 --- a/invoices/migrations/0003_auto_20190527_1620.py +++ b/invoices/migrations/0003_auto_20190527_1620.py @@ -6,18 +6,18 @@ class Migration(migrations.Migration): dependencies = [ - ('invoices', '0002_auto_20190524_1113'), + ("invoices", "0002_auto_20190524_1113"), ] operations = [ migrations.AddField( - model_name='invoice', - name='details', - field=models.TextField(blank=True, null=True, verbose_name='Details'), + model_name="invoice", + name="details", + field=models.TextField(blank=True, null=True, verbose_name="Details"), ), migrations.AddField( - model_name='invoice', - name='due_date', + model_name="invoice", + name="due_date", field=models.DateField(blank=True, null=True), ), ] diff --git a/invoices/migrations/0004_auto_20190603_1844.py b/invoices/migrations/0004_auto_20190603_1844.py index 093f47f..1b1cde1 100644 --- a/invoices/migrations/0004_auto_20190603_1844.py +++ b/invoices/migrations/0004_auto_20190603_1844.py @@ -6,13 +6,23 @@ class Migration(migrations.Migration): dependencies = [ - ('invoices', '0003_auto_20190527_1620'), + ("invoices", "0003_auto_20190527_1620"), ] operations = [ migrations.AlterField( - model_name='invoice', - name='status', - field=models.CharField(choices=[('Draft', 'Draft'), ('Sent', 'Sent'), ('Paid', 'Paid'), ('Pending', 'Pending'), ('Cancelled', 'Cancel')], default='Draft', max_length=15), + model_name="invoice", + name="status", + field=models.CharField( + choices=[ + ("Draft", "Draft"), + ("Sent", "Sent"), + ("Paid", "Paid"), + ("Pending", "Pending"), + ("Cancelled", "Cancel"), + ], + default="Draft", + max_length=15, + ), ), ] diff --git a/invoices/migrations/0005_invoicehistory.py b/invoices/migrations/0005_invoicehistory.py index d64dc98..13b2226 100644 --- a/invoices/migrations/0005_invoicehistory.py +++ b/invoices/migrations/0005_invoicehistory.py @@ -9,37 +9,293 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0017_auto_20190722_1443'), + ("common", "0017_auto_20190722_1443"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('invoices', '0004_auto_20190603_1844'), + ("invoices", "0004_auto_20190603_1844"), ] operations = [ migrations.CreateModel( - name='InvoiceHistory', + name="InvoiceHistory", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('invoice_title', models.CharField(max_length=50, verbose_name='Invoice Title')), - ('invoice_number', models.CharField(max_length=50, verbose_name='Invoice Number')), - ('name', models.CharField(max_length=100, verbose_name='Name')), - ('email', models.EmailField(max_length=254, verbose_name='Email')), - ('quantity', models.PositiveIntegerField(default=0)), - ('rate', models.DecimalField(decimal_places=2, default=0, max_digits=12)), - ('total_amount', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)), - ('currency', models.CharField(blank=True, choices=[('AED', 'AED, Dirham'), ('AFN', 'AFN, Afghani'), ('ALL', 'ALL, Lek'), ('AMD', 'AMD, Dram'), ('ANG', 'ANG, Guilder'), ('AOA', 'AOA, Kwanza'), ('ARS', 'ARS, Peso'), ('AUD', 'AUD, Dollar'), ('AWG', 'AWG, Guilder'), ('AZN', 'AZN, Manat'), ('BAM', 'BAM, Marka'), ('BBD', 'BBD, Dollar'), ('BDT', 'BDT, Taka'), ('BGN', 'BGN, Lev'), ('BHD', 'BHD, Dinar'), ('BIF', 'BIF, Franc'), ('BMD', 'BMD, Dollar'), ('BND', 'BND, Dollar'), ('BOB', 'BOB, Boliviano'), ('BRL', 'BRL, Real'), ('BSD', 'BSD, Dollar'), ('BTN', 'BTN, Ngultrum'), ('BWP', 'BWP, Pula'), ('BYR', 'BYR, Ruble'), ('BZD', 'BZD, Dollar'), ('CAD', 'CAD, Dollar'), ('CDF', 'CDF, Franc'), ('CHF', 'CHF, Franc'), ('CLP', 'CLP, Peso'), ('CNY', 'CNY, Yuan Renminbi'), ('COP', 'COP, Peso'), ('CRC', 'CRC, Colon'), ('CUP', 'CUP, Peso'), ('CVE', 'CVE, Escudo'), ('CZK', 'CZK, Koruna'), ('DJF', 'DJF, Franc'), ('DKK', 'DKK, Krone'), ('DOP', 'DOP, Peso'), ('DZD', 'DZD, Dinar'), ('EGP', 'EGP, Pound'), ('ERN', 'ERN, Nakfa'), ('ETB', 'ETB, Birr'), ('EUR', 'EUR, Euro'), ('FJD', 'FJD, Dollar'), ('FKP', 'FKP, Pound'), ('GBP', 'GBP, Pound'), ('GEL', 'GEL, Lari'), ('GHS', 'GHS, Cedi'), ('GIP', 'GIP, Pound'), ('GMD', 'GMD, Dalasi'), ('GNF', 'GNF, Franc'), ('GTQ', 'GTQ, Quetzal'), ('GYD', 'GYD, Dollar'), ('HKD', 'HKD, Dollar'), ('HNL', 'HNL, Lempira'), ('HRK', 'HRK, Kuna'), ('HTG', 'HTG, Gourde'), ('HUF', 'HUF, Forint'), ('IDR', 'IDR, Rupiah'), ('ILS', 'ILS, Shekel'), ('INR', 'INR, Rupee'), ('IQD', 'IQD, Dinar'), ('IRR', 'IRR, Rial'), ('ISK', 'ISK, Krona'), ('JMD', 'JMD, Dollar'), ('JOD', 'JOD, Dinar'), ('JPY', 'JPY, Yen'), ('KES', 'KES, Shilling'), ('KGS', 'KGS, Som'), ('KHR', 'KHR, Riels'), ('KMF', 'KMF, Franc'), ('KPW', 'KPW, Won'), ('KRW', 'KRW, Won'), ('KWD', 'KWD, Dinar'), ('KYD', 'KYD, Dollar'), ('KZT', 'KZT, Tenge'), ('LAK', 'LAK, Kip'), ('LBP', 'LBP, Pound'), ('LKR', 'LKR, Rupee'), ('LRD', 'LRD, Dollar'), ('LSL', 'LSL, Loti'), ('LTL', 'LTL, Litas'), ('LVL', 'LVL, Lat'), ('LYD', 'LYD, Dinar'), ('MAD', 'MAD, Dirham'), ('MDL', 'MDL, Leu'), ('MGA', 'MGA, Ariary'), ('MKD', 'MKD, Denar'), ('MMK', 'MMK, Kyat'), ('MNT', 'MNT, Tugrik'), ('MOP', 'MOP, Pataca'), ('MRO', 'MRO, Ouguiya'), ('MUR', 'MUR, Rupee'), ('MVR', 'MVR, Rufiyaa'), ('MWK', 'MWK, Kwacha'), ('MXN', 'MXN, Peso'), ('MYR', 'MYR, Ringgit'), ('MZN', 'MZN, Metical'), ('NAD', 'NAD, Dollar'), ('NGN', 'NGN, Naira'), ('NIO', 'NIO, Cordoba'), ('NOK', 'NOK, Krone'), ('NPR', 'NPR, Rupee'), ('NZD', 'NZD, Dollar'), ('OMR', 'OMR, Rial'), ('PAB', 'PAB, Balboa'), ('PEN', 'PEN, Sol'), ('PGK', 'PGK, Kina'), ('PHP', 'PHP, Peso'), ('PKR', 'PKR, Rupee'), ('PLN', 'PLN, Zloty'), ('PYG', 'PYG, Guarani'), ('QAR', 'QAR, Rial'), ('RON', 'RON, Leu'), ('RSD', 'RSD, Dinar'), ('RUB', 'RUB, Ruble'), ('RWF', 'RWF, Franc'), ('SAR', 'SAR, Rial'), ('SBD', 'SBD, Dollar'), ('SCR', 'SCR, Rupee'), ('SDG', 'SDG, Pound'), ('SEK', 'SEK, Krona'), ('SGD', 'SGD, Dollar'), ('SHP', 'SHP, Pound'), ('SLL', 'SLL, Leone'), ('SOS', 'SOS, Shilling'), ('SRD', 'SRD, Dollar'), ('SSP', 'SSP, Pound'), ('STD', 'STD, Dobra'), ('SYP', 'SYP, Pound'), ('SZL', 'SZL, Lilangeni'), ('THB', 'THB, Baht'), ('TJS', 'TJS, Somoni'), ('TMT', 'TMT, Manat'), ('TND', 'TND, Dinar'), ('TOP', 'TOP, Paanga'), ('TRY', 'TRY, Lira'), ('TTD', 'TTD, Dollar'), ('TWD', 'TWD, Dollar'), ('TZS', 'TZS, Shilling'), ('UAH', 'UAH, Hryvnia'), ('UGX', 'UGX, Shilling'), ('USD', '$, Dollar'), ('UYU', 'UYU, Peso'), ('UZS', 'UZS, Som'), ('VEF', 'VEF, Bolivar'), ('VND', 'VND, Dong'), ('VUV', 'VUV, Vatu'), ('WST', 'WST, Tala'), ('XAF', 'XAF, Franc'), ('XCD', 'XCD, Dollar'), ('XOF', 'XOF, Franc'), ('XPF', 'XPF, Franc'), ('YER', 'YER, Rial'), ('ZAR', 'ZAR, Rand'), ('ZMK', 'ZMK, Kwacha'), ('ZWL', 'ZWL, Dollar')], max_length=3, null=True)), - ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True)), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('amount_due', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)), - ('amount_paid', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)), - ('is_email_sent', models.BooleanField(default=False)), - ('status', models.CharField(choices=[('Draft', 'Draft'), ('Sent', 'Sent'), ('Paid', 'Paid'), ('Pending', 'Pending'), ('Cancelled', 'Cancel')], default='Draft', max_length=15)), - ('details', models.TextField(blank=True, null=True, verbose_name='Details')), - ('due_date', models.DateField(blank=True, null=True)), - ('assigned_to', models.ManyToManyField(related_name='invoice_history_assigned_to', to=settings.AUTH_USER_MODEL)), - ('from_address', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_history_from_address', to='common.Address')), - ('invoice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invoice_history', to='invoices.Invoice')), - ('to_address', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_history_to_address', to='common.Address')), - ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_history_created_by', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "invoice_title", + models.CharField(max_length=50, verbose_name="Invoice Title"), + ), + ( + "invoice_number", + models.CharField(max_length=50, verbose_name="Invoice Number"), + ), + ("name", models.CharField(max_length=100, verbose_name="Name")), + ("email", models.EmailField(max_length=254, verbose_name="Email")), + ("quantity", models.PositiveIntegerField(default=0)), + ( + "rate", + models.DecimalField(decimal_places=2, default=0, max_digits=12), + ), + ( + "total_amount", + models.DecimalField( + blank=True, decimal_places=2, max_digits=12, null=True + ), + ), + ( + "currency", + models.CharField( + blank=True, + choices=[ + ("AED", "AED, Dirham"), + ("AFN", "AFN, Afghani"), + ("ALL", "ALL, Lek"), + ("AMD", "AMD, Dram"), + ("ANG", "ANG, Guilder"), + ("AOA", "AOA, Kwanza"), + ("ARS", "ARS, Peso"), + ("AUD", "AUD, Dollar"), + ("AWG", "AWG, Guilder"), + ("AZN", "AZN, Manat"), + ("BAM", "BAM, Marka"), + ("BBD", "BBD, Dollar"), + ("BDT", "BDT, Taka"), + ("BGN", "BGN, Lev"), + ("BHD", "BHD, Dinar"), + ("BIF", "BIF, Franc"), + ("BMD", "BMD, Dollar"), + ("BND", "BND, Dollar"), + ("BOB", "BOB, Boliviano"), + ("BRL", "BRL, Real"), + ("BSD", "BSD, Dollar"), + ("BTN", "BTN, Ngultrum"), + ("BWP", "BWP, Pula"), + ("BYR", "BYR, Ruble"), + ("BZD", "BZD, Dollar"), + ("CAD", "CAD, Dollar"), + ("CDF", "CDF, Franc"), + ("CHF", "CHF, Franc"), + ("CLP", "CLP, Peso"), + ("CNY", "CNY, Yuan Renminbi"), + ("COP", "COP, Peso"), + ("CRC", "CRC, Colon"), + ("CUP", "CUP, Peso"), + ("CVE", "CVE, Escudo"), + ("CZK", "CZK, Koruna"), + ("DJF", "DJF, Franc"), + ("DKK", "DKK, Krone"), + ("DOP", "DOP, Peso"), + ("DZD", "DZD, Dinar"), + ("EGP", "EGP, Pound"), + ("ERN", "ERN, Nakfa"), + ("ETB", "ETB, Birr"), + ("EUR", "EUR, Euro"), + ("FJD", "FJD, Dollar"), + ("FKP", "FKP, Pound"), + ("GBP", "GBP, Pound"), + ("GEL", "GEL, Lari"), + ("GHS", "GHS, Cedi"), + ("GIP", "GIP, Pound"), + ("GMD", "GMD, Dalasi"), + ("GNF", "GNF, Franc"), + ("GTQ", "GTQ, Quetzal"), + ("GYD", "GYD, Dollar"), + ("HKD", "HKD, Dollar"), + ("HNL", "HNL, Lempira"), + ("HRK", "HRK, Kuna"), + ("HTG", "HTG, Gourde"), + ("HUF", "HUF, Forint"), + ("IDR", "IDR, Rupiah"), + ("ILS", "ILS, Shekel"), + ("INR", "INR, Rupee"), + ("IQD", "IQD, Dinar"), + ("IRR", "IRR, Rial"), + ("ISK", "ISK, Krona"), + ("JMD", "JMD, Dollar"), + ("JOD", "JOD, Dinar"), + ("JPY", "JPY, Yen"), + ("KES", "KES, Shilling"), + ("KGS", "KGS, Som"), + ("KHR", "KHR, Riels"), + ("KMF", "KMF, Franc"), + ("KPW", "KPW, Won"), + ("KRW", "KRW, Won"), + ("KWD", "KWD, Dinar"), + ("KYD", "KYD, Dollar"), + ("KZT", "KZT, Tenge"), + ("LAK", "LAK, Kip"), + ("LBP", "LBP, Pound"), + ("LKR", "LKR, Rupee"), + ("LRD", "LRD, Dollar"), + ("LSL", "LSL, Loti"), + ("LTL", "LTL, Litas"), + ("LVL", "LVL, Lat"), + ("LYD", "LYD, Dinar"), + ("MAD", "MAD, Dirham"), + ("MDL", "MDL, Leu"), + ("MGA", "MGA, Ariary"), + ("MKD", "MKD, Denar"), + ("MMK", "MMK, Kyat"), + ("MNT", "MNT, Tugrik"), + ("MOP", "MOP, Pataca"), + ("MRO", "MRO, Ouguiya"), + ("MUR", "MUR, Rupee"), + ("MVR", "MVR, Rufiyaa"), + ("MWK", "MWK, Kwacha"), + ("MXN", "MXN, Peso"), + ("MYR", "MYR, Ringgit"), + ("MZN", "MZN, Metical"), + ("NAD", "NAD, Dollar"), + ("NGN", "NGN, Naira"), + ("NIO", "NIO, Cordoba"), + ("NOK", "NOK, Krone"), + ("NPR", "NPR, Rupee"), + ("NZD", "NZD, Dollar"), + ("OMR", "OMR, Rial"), + ("PAB", "PAB, Balboa"), + ("PEN", "PEN, Sol"), + ("PGK", "PGK, Kina"), + ("PHP", "PHP, Peso"), + ("PKR", "PKR, Rupee"), + ("PLN", "PLN, Zloty"), + ("PYG", "PYG, Guarani"), + ("QAR", "QAR, Rial"), + ("RON", "RON, Leu"), + ("RSD", "RSD, Dinar"), + ("RUB", "RUB, Ruble"), + ("RWF", "RWF, Franc"), + ("SAR", "SAR, Rial"), + ("SBD", "SBD, Dollar"), + ("SCR", "SCR, Rupee"), + ("SDG", "SDG, Pound"), + ("SEK", "SEK, Krona"), + ("SGD", "SGD, Dollar"), + ("SHP", "SHP, Pound"), + ("SLL", "SLL, Leone"), + ("SOS", "SOS, Shilling"), + ("SRD", "SRD, Dollar"), + ("SSP", "SSP, Pound"), + ("STD", "STD, Dobra"), + ("SYP", "SYP, Pound"), + ("SZL", "SZL, Lilangeni"), + ("THB", "THB, Baht"), + ("TJS", "TJS, Somoni"), + ("TMT", "TMT, Manat"), + ("TND", "TND, Dinar"), + ("TOP", "TOP, Paanga"), + ("TRY", "TRY, Lira"), + ("TTD", "TTD, Dollar"), + ("TWD", "TWD, Dollar"), + ("TZS", "TZS, Shilling"), + ("UAH", "UAH, Hryvnia"), + ("UGX", "UGX, Shilling"), + ("USD", "$, Dollar"), + ("UYU", "UYU, Peso"), + ("UZS", "UZS, Som"), + ("VEF", "VEF, Bolivar"), + ("VND", "VND, Dong"), + ("VUV", "VUV, Vatu"), + ("WST", "WST, Tala"), + ("XAF", "XAF, Franc"), + ("XCD", "XCD, Dollar"), + ("XOF", "XOF, Franc"), + ("XPF", "XPF, Franc"), + ("YER", "YER, Rial"), + ("ZAR", "ZAR, Rand"), + ("ZMK", "ZMK, Kwacha"), + ("ZWL", "ZWL, Dollar"), + ], + max_length=3, + null=True, + ), + ), + ( + "phone", + phonenumber_field.modelfields.PhoneNumberField( + blank=True, max_length=128, null=True + ), + ), + ("created_on", models.DateTimeField(auto_now_add=True)), + ( + "amount_due", + models.DecimalField( + blank=True, decimal_places=2, max_digits=12, null=True + ), + ), + ( + "amount_paid", + models.DecimalField( + blank=True, decimal_places=2, max_digits=12, null=True + ), + ), + ("is_email_sent", models.BooleanField(default=False)), + ( + "status", + models.CharField( + choices=[ + ("Draft", "Draft"), + ("Sent", "Sent"), + ("Paid", "Paid"), + ("Pending", "Pending"), + ("Cancelled", "Cancel"), + ], + default="Draft", + max_length=15, + ), + ), + ( + "details", + models.TextField(blank=True, null=True, verbose_name="Details"), + ), + ("due_date", models.DateField(blank=True, null=True)), + ( + "assigned_to", + models.ManyToManyField( + related_name="invoice_history_assigned_to", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "from_address", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="invoice_history_from_address", + to="common.Address", + ), + ), + ( + "invoice", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="invoice_history", + to="invoices.Invoice", + ), + ), + ( + "to_address", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="invoice_history_to_address", + to="common.Address", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="invoice_history_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/invoices/migrations/0006_invoice_account.py b/invoices/migrations/0006_invoice_account.py index 86bf724..0ec2939 100644 --- a/invoices/migrations/0006_invoice_account.py +++ b/invoices/migrations/0006_invoice_account.py @@ -6,14 +6,16 @@ class Migration(migrations.Migration): dependencies = [ - ('accounts', '0008_account_assigned_to'), - ('invoices', '0005_invoicehistory'), + ("accounts", "0008_account_assigned_to"), + ("invoices", "0005_invoicehistory"), ] operations = [ migrations.AddField( - model_name='invoice', - name='accounts', - field=models.ManyToManyField(related_name='accounts_invoices', to='accounts.Account'), + model_name="invoice", + name="accounts", + field=models.ManyToManyField( + related_name="accounts_invoices", to="accounts.Account" + ), ), ] diff --git a/invoices/migrations/0007_auto_20190909_1621.py b/invoices/migrations/0007_auto_20190909_1621.py index 7823ce4..112d03a 100644 --- a/invoices/migrations/0007_auto_20190909_1621.py +++ b/invoices/migrations/0007_auto_20190909_1621.py @@ -6,12 +6,11 @@ class Migration(migrations.Migration): dependencies = [ - ('invoices', '0006_invoice_account'), + ("invoices", "0006_invoice_account"), ] operations = [ migrations.AlterModelOptions( - name='invoicehistory', - options={'ordering': ('created_on',)}, + name="invoicehistory", options={"ordering": ("created_on",)}, ), ] diff --git a/invoices/migrations/0008_invoice_teams.py b/invoices/migrations/0008_invoice_teams.py index 8934891..b812c70 100644 --- a/invoices/migrations/0008_invoice_teams.py +++ b/invoices/migrations/0008_invoice_teams.py @@ -6,14 +6,16 @@ class Migration(migrations.Migration): dependencies = [ - ('teams', '0003_auto_20190909_1621'), - ('invoices', '0007_auto_20190909_1621'), + ("teams", "0003_auto_20190909_1621"), + ("invoices", "0007_auto_20190909_1621"), ] operations = [ migrations.AddField( - model_name='invoice', - name='teams', - field=models.ManyToManyField(related_name='invoices_teams', to='teams.Teams'), + model_name="invoice", + name="teams", + field=models.ManyToManyField( + related_name="invoices_teams", to="teams.Teams" + ), ), ] diff --git a/invoices/migrations/0009_invoice_company.py b/invoices/migrations/0009_invoice_company.py index 07fce8c..ec5fff6 100644 --- a/invoices/migrations/0009_invoice_company.py +++ b/invoices/migrations/0009_invoice_company.py @@ -7,14 +7,19 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0021_document_company'), - ('invoices', '0008_invoice_teams'), + ("common", "0021_document_company"), + ("invoices", "0008_invoice_teams"), ] operations = [ migrations.AddField( - model_name='invoice', - name='company', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Company'), + model_name="invoice", + name="company", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="common.Company", + ), ), ] diff --git a/invoices/models.py b/invoices/models.py index a5f81f6..d2876c7 100644 --- a/invoices/models.py +++ b/invoices/models.py @@ -221,4 +221,3 @@ def created_on_arrow(self): class Meta: ordering = ("created_on",) - diff --git a/invoices/tasks.py b/invoices/tasks.py index bdcb8ea..1e31753 100644 --- a/invoices/tasks.py +++ b/invoices/tasks.py @@ -10,84 +10,101 @@ @task -def send_email(invoice_id, recipients, domain='demo.django-crm.io', protocol='http'): +def send_email(invoice_id, recipients, domain="demo.django-crm.io", protocol="http"): invoice = Invoice.objects.filter(id=invoice_id).first() created_by = invoice.created_by - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) for user in recipients: recipients_list = [] user = User.objects.filter(id=user, is_active=True).first() if user: - if (user.email not in blocked_emails) and (user.email.split('@')[-1] not in blocked_domains): + if (user.email not in blocked_emails) and ( + user.email.split("@")[-1] not in blocked_domains + ): recipients_list.append(user.email) - subject = 'Shared an invoice with you.' + subject = "Shared an invoice with you." context = {} - context['invoice_title'] = invoice.invoice_title - context['invoice_id'] = invoice_id - context['invoice_created_by'] = invoice.created_by - context["url"] = protocol + '://' + domain + \ - reverse('invoices:invoice_details', args=(invoice.id,)) + context["invoice_title"] = invoice.invoice_title + context["invoice_id"] = invoice_id + context["invoice_created_by"] = invoice.created_by + context["url"] = ( + protocol + + "://" + + domain + + reverse("invoices:invoice_details", args=(invoice.id,)) + ) - context['user'] = user + context["user"] = user html_content = render_to_string( - 'assigned_to_email_template.html', context=context) + "assigned_to_email_template.html", context=context + ) msg = EmailMessage( - subject=subject, body=html_content, to=recipients_list) + subject=subject, body=html_content, to=recipients_list + ) msg.content_subtype = "html" msg.send() - recipients = invoice.accounts.filter(status='open') + recipients = invoice.accounts.filter(status="open") if recipients.count() > 0: - subject = 'Shared an invoice with you.' + subject = "Shared an invoice with you." context = {} - context['invoice_title'] = invoice.invoice_title - context['invoice_id'] = invoice_id - context['invoice_created_by'] = invoice.created_by - context["url"] = protocol + '://' + domain + \ - reverse('invoices:invoice_details', args=(invoice.id,)) + context["invoice_title"] = invoice.invoice_title + context["invoice_id"] = invoice_id + context["invoice_created_by"] = invoice.created_by + context["url"] = ( + protocol + + "://" + + domain + + reverse("invoices:invoice_details", args=(invoice.id,)) + ) for recipient in recipients: - context['user'] = recipient.email + context["user"] = recipient.email html_content = render_to_string( - 'assigned_to_email_template.html', context=context) + "assigned_to_email_template.html", context=context + ) msg = EmailMessage( - subject=subject, body=html_content, to=[recipient.email, ]) + subject=subject, body=html_content, to=[recipient.email,] + ) msg.content_subtype = "html" msg.send() - @task -def send_invoice_email(invoice_id, domain='demo.django-crm.io', protocol='http'): +def send_invoice_email(invoice_id, domain="demo.django-crm.io", protocol="http"): invoice = Invoice.objects.filter(id=invoice_id).first() if invoice: - subject = 'CRM Invoice : {0}'.format(invoice.invoice_title) + subject = "CRM Invoice : {0}".format(invoice.invoice_title) recipients = [invoice.email] context = {} - context['invoice'] = invoice - context['url'] = protocol + '://' + domain + \ - reverse('invoices:invoice_details', args=(invoice.id,)) - html_content = render_to_string( - 'invoice_detail_email.html', context=context) - msg = EmailMessage(subject=subject, body=html_content, - to=recipients) + context["invoice"] = invoice + context["url"] = ( + protocol + + "://" + + domain + + reverse("invoices:invoice_details", args=(invoice.id,)) + ) + html_content = render_to_string("invoice_detail_email.html", context=context) + msg = EmailMessage(subject=subject, body=html_content, to=recipients) msg.content_subtype = "html" msg.send() @task -def send_invoice_email_cancel(invoice_id, domain='demo.django-crm.io', protocol='http'): +def send_invoice_email_cancel(invoice_id, domain="demo.django-crm.io", protocol="http"): invoice = Invoice.objects.filter(id=invoice_id).first() if invoice: - subject = 'CRM Invoice : {0}'.format(invoice.invoice_title) + subject = "CRM Invoice : {0}".format(invoice.invoice_title) recipients = [invoice.email] context = {} - context['invoice'] = invoice - context['url'] = protocol + '://' + domain + \ - reverse('invoices:invoice_details', args=(invoice.id,)) - html_content = render_to_string( - 'invoice_cancelled.html', context=context) - msg = EmailMessage(subject=subject, body=html_content, - to=recipients) + context["invoice"] = invoice + context["url"] = ( + protocol + + "://" + + domain + + reverse("invoices:invoice_details", args=(invoice.id,)) + ) + html_content = render_to_string("invoice_cancelled.html", context=context) + msg = EmailMessage(subject=subject, body=html_content, to=recipients) msg.content_subtype = "html" msg.send() @@ -98,16 +115,18 @@ def create_invoice_history(original_invoice_id, updated_by_user_id, changed_fiel original_invoice = Invoice.objects.filter(id=original_invoice_id).first() created_by = original_invoice.created_by updated_by_user = User.objects.get(id=updated_by_user_id) - changed_data = [(' '.join(field.split('_')).title()) for field in changed_fields] + changed_data = [(" ".join(field.split("_")).title()) for field in changed_fields] if len(changed_data) > 1: - changed_data = ', '.join(changed_data[:-1]) + ' and ' + changed_data[-1] + ' have changed.' + changed_data = ( + ", ".join(changed_data[:-1]) + " and " + changed_data[-1] + " have changed." + ) elif len(changed_data) == 1: - changed_data = ', '.join(changed_data) + ' has changed.' + changed_data = ", ".join(changed_data) + " has changed." else: changed_data = None if original_invoice.invoice_history.count() == 0: - changed_data = 'Invoice Created.' + changed_data = "Invoice Created." if original_invoice: invoice_history = InvoiceHistory() invoice_history.invoice = original_invoice diff --git a/invoices/tests_celery_tasks.py b/invoices/tests_celery_tasks.py index 81cb62f..3ea287a 100644 --- a/invoices/tests_celery_tasks.py +++ b/invoices/tests_celery_tasks.py @@ -1,22 +1,22 @@ from django.test import TestCase from django.test.utils import override_settings -from invoices.tasks import (send_email, send_invoice_email, - send_invoice_email_cancel) +from invoices.tasks import send_email, send_invoice_email, send_invoice_email_cancel from invoices.tests import InvoiceCreateTest class TestSendMailOnInvoiceCreationTask(InvoiceCreateTest, TestCase): - - @override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - CELERY_ALWAYS_EAGER=True, - BROKER_BACKEND='memory') + @override_settings( + CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, + CELERY_ALWAYS_EAGER=True, + BROKER_BACKEND="memory", + ) def test_send_mail_on_invoice_creation_task(self): task = send_email.apply((self.invoice.id, [self.user.id, self.user1.id])) - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) task = send_invoice_email.apply((self.invoice.id,)) - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) task = send_invoice_email_cancel.apply((self.invoice.id,)) - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) diff --git a/invoices/urls.py b/invoices/urls.py index d6d8010..e53db5e 100644 --- a/invoices/urls.py +++ b/invoices/urls.py @@ -1,27 +1,33 @@ from django.urls import path from invoices.views import * -app_name = 'invoices' +app_name = "invoices" urlpatterns = [ - path('', invoices_list, name='invoices_list'), - path('create/', invoices_create, name='invoices_create'), - path('detail//', invoice_details, name='invoice_details'), - path('edit//', invoice_edit, name='invoice_edit'), - path('delete//', invoice_delete, name='invoice_delete'), - path('download//', invoice_download, name='invoice_download'), - path('send-mail//', invoice_send_mail, name='invoice_send_mail'), - path('cancelled-mail//', invoice_change_status_cancelled, name='invoice_change_status_cancelled'), - path('paid-mail//', invoice_change_status_paid, name='invoice_change_status_paid'), - - path('comment/add/', AddCommentView.as_view(), name="add_comment"), - path('comment/edit/', UpdateCommentView.as_view(), name="edit_comment"), - path('comment/remove/', DeleteCommentView.as_view(), name="remove_comment"), - - path('attachment/add/', AddAttachmentView.as_view(), name="add_attachment"), - path('attachment/remove/', DeleteAttachmentsView.as_view(), - name="remove_attachment"), - path('get_teams_and_users/', get_teams_and_users, name="get_teams_and_users") - + path("", invoices_list, name="invoices_list"), + path("create/", invoices_create, name="invoices_create"), + path("detail//", invoice_details, name="invoice_details"), + path("edit//", invoice_edit, name="invoice_edit"), + path("delete//", invoice_delete, name="invoice_delete"), + path("download//", invoice_download, name="invoice_download"), + path("send-mail//", invoice_send_mail, name="invoice_send_mail"), + path( + "cancelled-mail//", + invoice_change_status_cancelled, + name="invoice_change_status_cancelled", + ), + path( + "paid-mail//", + invoice_change_status_paid, + name="invoice_change_status_paid", + ), + path("comment/add/", AddCommentView.as_view(), name="add_comment"), + path("comment/edit/", UpdateCommentView.as_view(), name="edit_comment"), + path("comment/remove/", DeleteCommentView.as_view(), name="remove_comment"), + path("attachment/add/", AddAttachmentView.as_view(), name="add_attachment"), + path( + "attachment/remove/", DeleteAttachmentsView.as_view(), name="remove_attachment" + ), + path("get_teams_and_users/", get_teams_and_users, name="get_teams_and_users"), ] diff --git a/leads/apps.py b/leads/apps.py index 1fc2244..64e5ac7 100644 --- a/leads/apps.py +++ b/leads/apps.py @@ -2,4 +2,4 @@ class LeadsConfig(AppConfig): - name = 'leads' + name = "leads" diff --git a/leads/migrations/0001_initial.py b/leads/migrations/0001_initial.py index a32fa4a..556f764 100644 --- a/leads/migrations/0001_initial.py +++ b/leads/migrations/0001_initial.py @@ -11,38 +11,145 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('accounts', '0001_initial'), - ('common', '0001_initial'), + ("accounts", "0001_initial"), + ("common", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Lead', + name="Lead", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(blank=True, max_length=64, null=True, verbose_name='Title')), - ('first_name', models.CharField(max_length=255, verbose_name='First name')), - ('last_name', models.CharField(max_length=255, verbose_name='Last name')), - ('email', models.EmailField(max_length=254)), - ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True)), - ('status', models.CharField(blank=True, choices=[('assigned', 'Assigned'), ('in process', 'In Process'), ('converted', 'Converted'), ('recycled', 'Recycled'), ('dead', 'Dead')], max_length=255, null=True, verbose_name='Status of Lead')), - ('source', models.CharField(blank=True, choices=[('call', 'Call'), ('email', 'Email'), ('existing customer', 'Existing Customer'), ('partner', 'Partner'), ('public relations', 'Public Relations'), ('compaign', 'Campaign'), ('other', 'Other')], max_length=255, null=True, verbose_name='Source of Lead')), - ('website', models.CharField(blank=True, max_length=255, null=True, verbose_name='Website')), - ('description', models.TextField(blank=True, null=True)), - ('account_name', models.CharField(blank=True, max_length=255, null=True)), - ('opportunity_amount', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True, verbose_name='Opportunity Amount')), - ('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Created on')), - ('is_active', models.BooleanField(default=False)), - ('enquery_type', models.CharField(blank=True, max_length=255, null=True)), - ('account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Leads', to='accounts.Account')), - ('address', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='leadaddress', to='common.Address')), - ('assigned_to', models.ManyToManyField(related_name='lead_assigned_users', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lead_created_by', to=settings.AUTH_USER_MODEL)), - ('teams', models.ManyToManyField(to='common.Team')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "title", + models.CharField( + blank=True, max_length=64, null=True, verbose_name="Title" + ), + ), + ( + "first_name", + models.CharField(max_length=255, verbose_name="First name"), + ), + ( + "last_name", + models.CharField(max_length=255, verbose_name="Last name"), + ), + ("email", models.EmailField(max_length=254)), + ( + "phone", + phonenumber_field.modelfields.PhoneNumberField( + blank=True, max_length=128, null=True + ), + ), + ( + "status", + models.CharField( + blank=True, + choices=[ + ("assigned", "Assigned"), + ("in process", "In Process"), + ("converted", "Converted"), + ("recycled", "Recycled"), + ("dead", "Dead"), + ], + max_length=255, + null=True, + verbose_name="Status of Lead", + ), + ), + ( + "source", + models.CharField( + blank=True, + choices=[ + ("call", "Call"), + ("email", "Email"), + ("existing customer", "Existing Customer"), + ("partner", "Partner"), + ("public relations", "Public Relations"), + ("compaign", "Campaign"), + ("other", "Other"), + ], + max_length=255, + null=True, + verbose_name="Source of Lead", + ), + ), + ( + "website", + models.CharField( + blank=True, max_length=255, null=True, verbose_name="Website" + ), + ), + ("description", models.TextField(blank=True, null=True)), + ( + "account_name", + models.CharField(blank=True, max_length=255, null=True), + ), + ( + "opportunity_amount", + models.DecimalField( + blank=True, + decimal_places=2, + max_digits=12, + null=True, + verbose_name="Opportunity Amount", + ), + ), + ( + "created_on", + models.DateTimeField(auto_now_add=True, verbose_name="Created on"), + ), + ("is_active", models.BooleanField(default=False)), + ( + "enquery_type", + models.CharField(blank=True, max_length=255, null=True), + ), + ( + "account", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="Leads", + to="accounts.Account", + ), + ), + ( + "address", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="leadaddress", + to="common.Address", + ), + ), + ( + "assigned_to", + models.ManyToManyField( + related_name="lead_assigned_users", to=settings.AUTH_USER_MODEL + ), + ), + ( + "created_by", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="lead_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), + ("teams", models.ManyToManyField(to="common.Team")), ], - options={ - 'ordering': ['-created_on'], - }, + options={"ordering": ["-created_on"],}, ), ] diff --git a/leads/migrations/0002_lead_tags.py b/leads/migrations/0002_lead_tags.py index df3c747..5afeb9d 100644 --- a/leads/migrations/0002_lead_tags.py +++ b/leads/migrations/0002_lead_tags.py @@ -6,14 +6,14 @@ class Migration(migrations.Migration): dependencies = [ - ('accounts', '0003_auto_20190201_1840'), - ('leads', '0001_initial'), + ("accounts", "0003_auto_20190201_1840"), + ("leads", "0001_initial"), ] operations = [ migrations.AddField( - model_name='lead', - name='tags', - field=models.ManyToManyField(blank=True, to='accounts.Tags'), + model_name="lead", + name="tags", + field=models.ManyToManyField(blank=True, to="accounts.Tags"), ), ] diff --git a/leads/migrations/0003_auto_20190211_1142.py b/leads/migrations/0003_auto_20190211_1142.py index df02489..8dfeb42 100644 --- a/leads/migrations/0003_auto_20190211_1142.py +++ b/leads/migrations/0003_auto_20190211_1142.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('leads', '0002_lead_tags'), + ("leads", "0002_lead_tags"), ] operations = [ migrations.AlterField( - model_name='lead', - name='email', + model_name="lead", + name="email", field=models.EmailField(blank=True, max_length=254, null=True), ), ] diff --git a/leads/migrations/0004_auto_20190212_1334.py b/leads/migrations/0004_auto_20190212_1334.py index 821db88..f8f164b 100644 --- a/leads/migrations/0004_auto_20190212_1334.py +++ b/leads/migrations/0004_auto_20190212_1334.py @@ -8,27 +8,28 @@ class Migration(migrations.Migration): dependencies = [ - ('contacts', '0002_auto_20190212_1334'), - ('leads', '0003_auto_20190211_1142'), + ("contacts", "0002_auto_20190212_1334"), + ("leads", "0003_auto_20190211_1142"), ] operations = [ - migrations.RemoveField( - model_name='lead', - name='account', - ), - migrations.RemoveField( - model_name='lead', - name='teams', - ), + migrations.RemoveField(model_name="lead", name="account",), + migrations.RemoveField(model_name="lead", name="teams",), migrations.AddField( - model_name='lead', - name='contacts', - field=models.ManyToManyField(related_name='lead_contacts', to='contacts.Contact'), + model_name="lead", + name="contacts", + field=models.ManyToManyField( + related_name="lead_contacts", to="contacts.Contact" + ), ), migrations.AlterField( - model_name='lead', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lead_created_by', to=settings.AUTH_USER_MODEL), + model_name="lead", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="lead_created_by", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/leads/migrations/0005_auto_20190212_1708.py b/leads/migrations/0005_auto_20190212_1708.py index 2f81da8..7780de0 100644 --- a/leads/migrations/0005_auto_20190212_1708.py +++ b/leads/migrations/0005_auto_20190212_1708.py @@ -6,42 +6,301 @@ class Migration(migrations.Migration): dependencies = [ - ('leads', '0004_auto_20190212_1334'), + ("leads", "0004_auto_20190212_1334"), ] operations = [ - migrations.RemoveField( - model_name='lead', - name='address', - ), + migrations.RemoveField(model_name="lead", name="address",), migrations.AddField( - model_name='lead', - name='address_line', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Address'), + model_name="lead", + name="address_line", + field=models.CharField( + blank=True, max_length=255, null=True, verbose_name="Address" + ), ), migrations.AddField( - model_name='lead', - name='city', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='City'), + model_name="lead", + name="city", + field=models.CharField( + blank=True, max_length=255, null=True, verbose_name="City" + ), ), migrations.AddField( - model_name='lead', - name='country', - field=models.CharField(blank=True, choices=[('GB', 'United Kingdom'), ('AF', 'Afghanistan'), ('AX', 'Aland Islands'), ('AL', 'Albania'), ('DZ', 'Algeria'), ('AS', 'American Samoa'), ('AD', 'Andorra'), ('AO', 'Angola'), ('AI', 'Anguilla'), ('AQ', 'Antarctica'), ('AG', 'Antigua and Barbuda'), ('AR', 'Argentina'), ('AM', 'Armenia'), ('AW', 'Aruba'), ('AU', 'Australia'), ('AT', 'Austria'), ('AZ', 'Azerbaijan'), ('BS', 'Bahamas'), ('BH', 'Bahrain'), ('BD', 'Bangladesh'), ('BB', 'Barbados'), ('BY', 'Belarus'), ('BE', 'Belgium'), ('BZ', 'Belize'), ('BJ', 'Benin'), ('BM', 'Bermuda'), ('BT', 'Bhutan'), ('BO', 'Bolivia'), ('BA', 'Bosnia and Herzegovina'), ('BW', 'Botswana'), ('BV', 'Bouvet Island'), ('BR', 'Brazil'), ('IO', 'British Indian Ocean Territory'), ('BN', 'Brunei Darussalam'), ('BG', 'Bulgaria'), ('BF', 'Burkina Faso'), ('BI', 'Burundi'), ('KH', 'Cambodia'), ('CM', 'Cameroon'), ('CA', 'Canada'), ('CV', 'Cape Verde'), ('KY', 'Cayman Islands'), ('CF', 'Central African Republic'), ('TD', 'Chad'), ('CL', 'Chile'), ('CN', 'China'), ('CX', 'Christmas Island'), ('CC', 'Cocos (Keeling) Islands'), ('CO', 'Colombia'), ('KM', 'Comoros'), ('CG', 'Congo'), ('CD', 'Congo, The Democratic Republic of the'), ('CK', 'Cook Islands'), ('CR', 'Costa Rica'), ('CI', "Cote d'Ivoire"), ('HR', 'Croatia'), ('CU', 'Cuba'), ('CY', 'Cyprus'), ('CZ', 'Czech Republic'), ('DK', 'Denmark'), ('DJ', 'Djibouti'), ('DM', 'Dominica'), ('DO', 'Dominican Republic'), ('EC', 'Ecuador'), ('EG', 'Egypt'), ('SV', 'El Salvador'), ('GQ', 'Equatorial Guinea'), ('ER', 'Eritrea'), ('EE', 'Estonia'), ('ET', 'Ethiopia'), ('FK', 'Falkland Islands (Malvinas)'), ('FO', 'Faroe Islands'), ('FJ', 'Fiji'), ('FI', 'Finland'), ('FR', 'France'), ('GF', 'French Guiana'), ('PF', 'French Polynesia'), ('TF', 'French Southern Territories'), ('GA', 'Gabon'), ('GM', 'Gambia'), ('GE', 'Georgia'), ('DE', 'Germany'), ('GH', 'Ghana'), ('GI', 'Gibraltar'), ('GR', 'Greece'), ('GL', 'Greenland'), ('GD', 'Grenada'), ('GP', 'Guadeloupe'), ('GU', 'Guam'), ('GT', 'Guatemala'), ('GG', 'Guernsey'), ('GN', 'Guinea'), ('GW', 'Guinea-Bissau'), ('GY', 'Guyana'), ('HT', 'Haiti'), ('HM', 'Heard Island and McDonald Islands'), ('VA', 'Holy See (Vatican City State)'), ('HN', 'Honduras'), ('HK', 'Hong Kong'), ('HU', 'Hungary'), ('IS', 'Iceland'), ('IN', 'India'), ('ID', 'Indonesia'), ('IR', 'Iran, Islamic Republic of'), ('IQ', 'Iraq'), ('IE', 'Ireland'), ('IM', 'Isle of Man'), ('IL', 'Israel'), ('IT', 'Italy'), ('JM', 'Jamaica'), ('JP', 'Japan'), ('JE', 'Jersey'), ('JO', 'Jordan'), ('KZ', 'Kazakhstan'), ('KE', 'Kenya'), ('KI', 'Kiribati'), ('KP', "Korea, Democratic People's Republic of"), ('KR', 'Korea, Republic of'), ('KW', 'Kuwait'), ('KG', 'Kyrgyzstan'), ('LA', "Lao People's Democratic Republic"), ('LV', 'Latvia'), ('LB', 'Lebanon'), ('LS', 'Lesotho'), ('LR', 'Liberia'), ('LY', 'Libyan Arab Jamahiriya'), ('LI', 'Liechtenstein'), ('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('MO', 'Macao'), ('MK', 'Macedonia, The Former Yugoslav Republic of'), ('MG', 'Madagascar'), ('MW', 'Malawi'), ('MY', 'Malaysia'), ('MV', 'Maldives'), ('ML', 'Mali'), ('MT', 'Malta'), ('MH', 'Marshall Islands'), ('MQ', 'Martinique'), ('MR', 'Mauritania'), ('MU', 'Mauritius'), ('YT', 'Mayotte'), ('MX', 'Mexico'), ('FM', 'Micronesia, Federated States of'), ('MD', 'Moldova'), ('MC', 'Monaco'), ('MN', 'Mongolia'), ('ME', 'Montenegro'), ('MS', 'Montserrat'), ('MA', 'Morocco'), ('MZ', 'Mozambique'), ('MM', 'Myanmar'), ('NA', 'Namibia'), ('NR', 'Nauru'), ('NP', 'Nepal'), ('NL', 'Netherlands'), ('AN', 'Netherlands Antilles'), ('NC', 'New Caledonia'), ('NZ', 'New Zealand'), ('NI', 'Nicaragua'), ('NE', 'Niger'), ('NG', 'Nigeria'), ('NU', 'Niue'), ('NF', 'Norfolk Island'), ('MP', 'Northern Mariana Islands'), ('NO', 'Norway'), ('OM', 'Oman'), ('PK', 'Pakistan'), ('PW', 'Palau'), ('PS', 'Palestinian Territory, Occupied'), ('PA', 'Panama'), ('PG', 'Papua New Guinea'), ('PY', 'Paraguay'), ('PE', 'Peru'), ('PH', 'Philippines'), ('PN', 'Pitcairn'), ('PL', 'Poland'), ('PT', 'Portugal'), ('PR', 'Puerto Rico'), ('QA', 'Qatar'), ('RE', 'Reunion'), ('RO', 'Romania'), ('RU', 'Russian Federation'), ('RW', 'Rwanda'), ('BL', 'Saint Barthelemy'), ('SH', 'Saint Helena'), ('KN', 'Saint Kitts and Nevis'), ('LC', 'Saint Lucia'), ('MF', 'Saint Martin'), ('PM', 'Saint Pierre and Miquelon'), ('VC', 'Saint Vincent and the Grenadines'), ('WS', 'Samoa'), ('SM', 'San Marino'), ('ST', 'Sao Tome and Principe'), ('SA', 'Saudi Arabia'), ('SN', 'Senegal'), ('RS', 'Serbia'), ('SC', 'Seychelles'), ('SL', 'Sierra Leone'), ('SG', 'Singapore'), ('SK', 'Slovakia'), ('SI', 'Slovenia'), ('SB', 'Solomon Islands'), ('SO', 'Somalia'), ('ZA', 'South Africa'), ('GS', 'South Georgia and the South Sandwich Islands'), ('ES', 'Spain'), ('LK', 'Sri Lanka'), ('SD', 'Sudan'), ('SR', 'Suriname'), ('SJ', 'Svalbard and Jan Mayen'), ('SZ', 'Swaziland'), ('SE', 'Sweden'), ('CH', 'Switzerland'), ('SY', 'Syrian Arab Republic'), ('TW', 'Taiwan, Province of China'), ('TJ', 'Tajikistan'), ('TZ', 'Tanzania, United Republic of'), ('TH', 'Thailand'), ('TL', 'Timor-Leste'), ('TG', 'Togo'), ('TK', 'Tokelau'), ('TO', 'Tonga'), ('TT', 'Trinidad and Tobago'), ('TN', 'Tunisia'), ('TR', 'Turkey'), ('TM', 'Turkmenistan'), ('TC', 'Turks and Caicos Islands'), ('TV', 'Tuvalu'), ('UG', 'Uganda'), ('UA', 'Ukraine'), ('AE', 'United Arab Emirates'), ('US', 'United States'), ('UM', 'United States Minor Outlying Islands'), ('UY', 'Uruguay'), ('UZ', 'Uzbekistan'), ('VU', 'Vanuatu'), ('VE', 'Venezuela'), ('VN', 'Viet Nam'), ('VG', 'Virgin Islands, British'), ('VI', 'Virgin Islands, U.S.'), ('WF', 'Wallis and Futuna'), ('EH', 'Western Sahara'), ('YE', 'Yemen'), ('ZM', 'Zambia'), ('ZW', 'Zimbabwe')], max_length=3, null=True), + model_name="lead", + name="country", + field=models.CharField( + blank=True, + choices=[ + ("GB", "United Kingdom"), + ("AF", "Afghanistan"), + ("AX", "Aland Islands"), + ("AL", "Albania"), + ("DZ", "Algeria"), + ("AS", "American Samoa"), + ("AD", "Andorra"), + ("AO", "Angola"), + ("AI", "Anguilla"), + ("AQ", "Antarctica"), + ("AG", "Antigua and Barbuda"), + ("AR", "Argentina"), + ("AM", "Armenia"), + ("AW", "Aruba"), + ("AU", "Australia"), + ("AT", "Austria"), + ("AZ", "Azerbaijan"), + ("BS", "Bahamas"), + ("BH", "Bahrain"), + ("BD", "Bangladesh"), + ("BB", "Barbados"), + ("BY", "Belarus"), + ("BE", "Belgium"), + ("BZ", "Belize"), + ("BJ", "Benin"), + ("BM", "Bermuda"), + ("BT", "Bhutan"), + ("BO", "Bolivia"), + ("BA", "Bosnia and Herzegovina"), + ("BW", "Botswana"), + ("BV", "Bouvet Island"), + ("BR", "Brazil"), + ("IO", "British Indian Ocean Territory"), + ("BN", "Brunei Darussalam"), + ("BG", "Bulgaria"), + ("BF", "Burkina Faso"), + ("BI", "Burundi"), + ("KH", "Cambodia"), + ("CM", "Cameroon"), + ("CA", "Canada"), + ("CV", "Cape Verde"), + ("KY", "Cayman Islands"), + ("CF", "Central African Republic"), + ("TD", "Chad"), + ("CL", "Chile"), + ("CN", "China"), + ("CX", "Christmas Island"), + ("CC", "Cocos (Keeling) Islands"), + ("CO", "Colombia"), + ("KM", "Comoros"), + ("CG", "Congo"), + ("CD", "Congo, The Democratic Republic of the"), + ("CK", "Cook Islands"), + ("CR", "Costa Rica"), + ("CI", "Cote d'Ivoire"), + ("HR", "Croatia"), + ("CU", "Cuba"), + ("CY", "Cyprus"), + ("CZ", "Czech Republic"), + ("DK", "Denmark"), + ("DJ", "Djibouti"), + ("DM", "Dominica"), + ("DO", "Dominican Republic"), + ("EC", "Ecuador"), + ("EG", "Egypt"), + ("SV", "El Salvador"), + ("GQ", "Equatorial Guinea"), + ("ER", "Eritrea"), + ("EE", "Estonia"), + ("ET", "Ethiopia"), + ("FK", "Falkland Islands (Malvinas)"), + ("FO", "Faroe Islands"), + ("FJ", "Fiji"), + ("FI", "Finland"), + ("FR", "France"), + ("GF", "French Guiana"), + ("PF", "French Polynesia"), + ("TF", "French Southern Territories"), + ("GA", "Gabon"), + ("GM", "Gambia"), + ("GE", "Georgia"), + ("DE", "Germany"), + ("GH", "Ghana"), + ("GI", "Gibraltar"), + ("GR", "Greece"), + ("GL", "Greenland"), + ("GD", "Grenada"), + ("GP", "Guadeloupe"), + ("GU", "Guam"), + ("GT", "Guatemala"), + ("GG", "Guernsey"), + ("GN", "Guinea"), + ("GW", "Guinea-Bissau"), + ("GY", "Guyana"), + ("HT", "Haiti"), + ("HM", "Heard Island and McDonald Islands"), + ("VA", "Holy See (Vatican City State)"), + ("HN", "Honduras"), + ("HK", "Hong Kong"), + ("HU", "Hungary"), + ("IS", "Iceland"), + ("IN", "India"), + ("ID", "Indonesia"), + ("IR", "Iran, Islamic Republic of"), + ("IQ", "Iraq"), + ("IE", "Ireland"), + ("IM", "Isle of Man"), + ("IL", "Israel"), + ("IT", "Italy"), + ("JM", "Jamaica"), + ("JP", "Japan"), + ("JE", "Jersey"), + ("JO", "Jordan"), + ("KZ", "Kazakhstan"), + ("KE", "Kenya"), + ("KI", "Kiribati"), + ("KP", "Korea, Democratic People's Republic of"), + ("KR", "Korea, Republic of"), + ("KW", "Kuwait"), + ("KG", "Kyrgyzstan"), + ("LA", "Lao People's Democratic Republic"), + ("LV", "Latvia"), + ("LB", "Lebanon"), + ("LS", "Lesotho"), + ("LR", "Liberia"), + ("LY", "Libyan Arab Jamahiriya"), + ("LI", "Liechtenstein"), + ("LT", "Lithuania"), + ("LU", "Luxembourg"), + ("MO", "Macao"), + ("MK", "Macedonia, The Former Yugoslav Republic of"), + ("MG", "Madagascar"), + ("MW", "Malawi"), + ("MY", "Malaysia"), + ("MV", "Maldives"), + ("ML", "Mali"), + ("MT", "Malta"), + ("MH", "Marshall Islands"), + ("MQ", "Martinique"), + ("MR", "Mauritania"), + ("MU", "Mauritius"), + ("YT", "Mayotte"), + ("MX", "Mexico"), + ("FM", "Micronesia, Federated States of"), + ("MD", "Moldova"), + ("MC", "Monaco"), + ("MN", "Mongolia"), + ("ME", "Montenegro"), + ("MS", "Montserrat"), + ("MA", "Morocco"), + ("MZ", "Mozambique"), + ("MM", "Myanmar"), + ("NA", "Namibia"), + ("NR", "Nauru"), + ("NP", "Nepal"), + ("NL", "Netherlands"), + ("AN", "Netherlands Antilles"), + ("NC", "New Caledonia"), + ("NZ", "New Zealand"), + ("NI", "Nicaragua"), + ("NE", "Niger"), + ("NG", "Nigeria"), + ("NU", "Niue"), + ("NF", "Norfolk Island"), + ("MP", "Northern Mariana Islands"), + ("NO", "Norway"), + ("OM", "Oman"), + ("PK", "Pakistan"), + ("PW", "Palau"), + ("PS", "Palestinian Territory, Occupied"), + ("PA", "Panama"), + ("PG", "Papua New Guinea"), + ("PY", "Paraguay"), + ("PE", "Peru"), + ("PH", "Philippines"), + ("PN", "Pitcairn"), + ("PL", "Poland"), + ("PT", "Portugal"), + ("PR", "Puerto Rico"), + ("QA", "Qatar"), + ("RE", "Reunion"), + ("RO", "Romania"), + ("RU", "Russian Federation"), + ("RW", "Rwanda"), + ("BL", "Saint Barthelemy"), + ("SH", "Saint Helena"), + ("KN", "Saint Kitts and Nevis"), + ("LC", "Saint Lucia"), + ("MF", "Saint Martin"), + ("PM", "Saint Pierre and Miquelon"), + ("VC", "Saint Vincent and the Grenadines"), + ("WS", "Samoa"), + ("SM", "San Marino"), + ("ST", "Sao Tome and Principe"), + ("SA", "Saudi Arabia"), + ("SN", "Senegal"), + ("RS", "Serbia"), + ("SC", "Seychelles"), + ("SL", "Sierra Leone"), + ("SG", "Singapore"), + ("SK", "Slovakia"), + ("SI", "Slovenia"), + ("SB", "Solomon Islands"), + ("SO", "Somalia"), + ("ZA", "South Africa"), + ("GS", "South Georgia and the South Sandwich Islands"), + ("ES", "Spain"), + ("LK", "Sri Lanka"), + ("SD", "Sudan"), + ("SR", "Suriname"), + ("SJ", "Svalbard and Jan Mayen"), + ("SZ", "Swaziland"), + ("SE", "Sweden"), + ("CH", "Switzerland"), + ("SY", "Syrian Arab Republic"), + ("TW", "Taiwan, Province of China"), + ("TJ", "Tajikistan"), + ("TZ", "Tanzania, United Republic of"), + ("TH", "Thailand"), + ("TL", "Timor-Leste"), + ("TG", "Togo"), + ("TK", "Tokelau"), + ("TO", "Tonga"), + ("TT", "Trinidad and Tobago"), + ("TN", "Tunisia"), + ("TR", "Turkey"), + ("TM", "Turkmenistan"), + ("TC", "Turks and Caicos Islands"), + ("TV", "Tuvalu"), + ("UG", "Uganda"), + ("UA", "Ukraine"), + ("AE", "United Arab Emirates"), + ("US", "United States"), + ("UM", "United States Minor Outlying Islands"), + ("UY", "Uruguay"), + ("UZ", "Uzbekistan"), + ("VU", "Vanuatu"), + ("VE", "Venezuela"), + ("VN", "Viet Nam"), + ("VG", "Virgin Islands, British"), + ("VI", "Virgin Islands, U.S."), + ("WF", "Wallis and Futuna"), + ("EH", "Western Sahara"), + ("YE", "Yemen"), + ("ZM", "Zambia"), + ("ZW", "Zimbabwe"), + ], + max_length=3, + null=True, + ), ), migrations.AddField( - model_name='lead', - name='postcode', - field=models.CharField(blank=True, max_length=64, null=True, verbose_name='Post/Zip-code'), + model_name="lead", + name="postcode", + field=models.CharField( + blank=True, max_length=64, null=True, verbose_name="Post/Zip-code" + ), ), migrations.AddField( - model_name='lead', - name='state', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='State'), + model_name="lead", + name="state", + field=models.CharField( + blank=True, max_length=255, null=True, verbose_name="State" + ), ), migrations.AddField( - model_name='lead', - name='street', - field=models.CharField(blank=True, max_length=55, null=True, verbose_name='Street'), + model_name="lead", + name="street", + field=models.CharField( + blank=True, max_length=55, null=True, verbose_name="Street" + ), ), ] diff --git a/leads/migrations/0006_auto_20190218_1217.py b/leads/migrations/0006_auto_20190218_1217.py index eec6583..4f04774 100644 --- a/leads/migrations/0006_auto_20190218_1217.py +++ b/leads/migrations/0006_auto_20190218_1217.py @@ -6,13 +6,25 @@ class Migration(migrations.Migration): dependencies = [ - ('leads', '0005_auto_20190212_1708'), + ("leads", "0005_auto_20190212_1708"), ] operations = [ migrations.AlterField( - model_name='lead', - name='status', - field=models.CharField(blank=True, choices=[('assigned', 'Assigned'), ('in process', 'In Process'), ('converted', 'Converted'), ('recycled', 'Recycled'), ('dead', 'Closed')], max_length=255, null=True, verbose_name='Status of Lead'), + model_name="lead", + name="status", + field=models.CharField( + blank=True, + choices=[ + ("assigned", "Assigned"), + ("in process", "In Process"), + ("converted", "Converted"), + ("recycled", "Recycled"), + ("dead", "Closed"), + ], + max_length=255, + null=True, + verbose_name="Status of Lead", + ), ), ] diff --git a/leads/migrations/0007_auto_20190306_1226.py b/leads/migrations/0007_auto_20190306_1226.py index 4885d66..49bcac0 100644 --- a/leads/migrations/0007_auto_20190306_1226.py +++ b/leads/migrations/0007_auto_20190306_1226.py @@ -6,13 +6,25 @@ class Migration(migrations.Migration): dependencies = [ - ('leads', '0006_auto_20190218_1217'), + ("leads", "0006_auto_20190218_1217"), ] operations = [ migrations.AlterField( - model_name='lead', - name='status', - field=models.CharField(blank=True, choices=[('assigned', 'Assigned'), ('in process', 'In Process'), ('converted', 'Converted'), ('recycled', 'Recycled'), ('closed', 'Closed')], max_length=255, null=True, verbose_name='Status of Lead'), + model_name="lead", + name="status", + field=models.CharField( + blank=True, + choices=[ + ("assigned", "Assigned"), + ("in process", "In Process"), + ("converted", "Converted"), + ("recycled", "Recycled"), + ("closed", "Closed"), + ], + max_length=255, + null=True, + verbose_name="Status of Lead", + ), ), ] diff --git a/leads/migrations/0008_auto_20190315_1503.py b/leads/migrations/0008_auto_20190315_1503.py index 7e195a7..7118557 100644 --- a/leads/migrations/0008_auto_20190315_1503.py +++ b/leads/migrations/0008_auto_20190315_1503.py @@ -6,24 +6,28 @@ class Migration(migrations.Migration): dependencies = [ - ('leads', '0007_auto_20190306_1226'), + ("leads", "0007_auto_20190306_1226"), ] operations = [ migrations.AlterField( - model_name='lead', - name='first_name', - field=models.CharField(max_length=255, null=True, verbose_name='First name'), + model_name="lead", + name="first_name", + field=models.CharField( + max_length=255, null=True, verbose_name="First name" + ), ), migrations.AlterField( - model_name='lead', - name='last_name', - field=models.CharField(max_length=255, null=True, verbose_name='Last name'), + model_name="lead", + name="last_name", + field=models.CharField(max_length=255, null=True, verbose_name="Last name"), ), migrations.AlterField( - model_name='lead', - name='title', - field=models.CharField(default='title', max_length=64, verbose_name='Title'), + model_name="lead", + name="title", + field=models.CharField( + default="title", max_length=64, verbose_name="Title" + ), preserve_default=False, ), ] diff --git a/leads/migrations/0009_lead_created_from_site.py b/leads/migrations/0009_lead_created_from_site.py index 970b4f8..50f4247 100644 --- a/leads/migrations/0009_lead_created_from_site.py +++ b/leads/migrations/0009_lead_created_from_site.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('leads', '0008_auto_20190315_1503'), + ("leads", "0008_auto_20190315_1503"), ] operations = [ migrations.AddField( - model_name='lead', - name='created_from_site', + model_name="lead", + name="created_from_site", field=models.BooleanField(default=False), ), ] diff --git a/leads/migrations/0010_lead_teams.py b/leads/migrations/0010_lead_teams.py index fc3f0fe..8c6d1d1 100644 --- a/leads/migrations/0010_lead_teams.py +++ b/leads/migrations/0010_lead_teams.py @@ -6,14 +6,14 @@ class Migration(migrations.Migration): dependencies = [ - ('teams', '0003_auto_20190909_1621'), - ('leads', '0009_lead_created_from_site'), + ("teams", "0003_auto_20190909_1621"), + ("leads", "0009_lead_created_from_site"), ] operations = [ migrations.AddField( - model_name='lead', - name='teams', - field=models.ManyToManyField(related_name='lead_teams', to='teams.Teams'), + model_name="lead", + name="teams", + field=models.ManyToManyField(related_name="lead_teams", to="teams.Teams"), ), ] diff --git a/leads/migrations/0011_auto_20200401_0937.py b/leads/migrations/0011_auto_20200401_0937.py index 9ceaceb..0f55c1b 100644 --- a/leads/migrations/0011_auto_20200401_0937.py +++ b/leads/migrations/0011_auto_20200401_0937.py @@ -6,13 +6,15 @@ class Migration(migrations.Migration): dependencies = [ - ('leads', '0010_lead_teams'), + ("leads", "0010_lead_teams"), ] operations = [ migrations.AlterField( - model_name='lead', - name='source', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Source of Lead'), + model_name="lead", + name="source", + field=models.CharField( + blank=True, max_length=255, null=True, verbose_name="Source of Lead" + ), ), ] diff --git a/leads/migrations/0012_lead_company.py b/leads/migrations/0012_lead_company.py index 72eb51a..87f53cf 100644 --- a/leads/migrations/0012_lead_company.py +++ b/leads/migrations/0012_lead_company.py @@ -7,14 +7,19 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0020_auto_20200409_1653'), - ('leads', '0011_auto_20200401_0937'), + ("common", "0020_auto_20200409_1653"), + ("leads", "0011_auto_20200401_0937"), ] operations = [ migrations.AddField( - model_name='lead', - name='company', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Company'), + model_name="lead", + name="company", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="common.Company", + ), ), ] diff --git a/leads/signals.py b/leads/signals.py index 9fcdcef..e4ec5f9 100644 --- a/leads/signals.py +++ b/leads/signals.py @@ -19,4 +19,4 @@ # open_leads = queryset.exclude(status='closed') # close_leads = queryset.filter(status='closed') # cache.set('admin_leads_open_queryset', open_leads, 60*60) -# cache.set('admin_leads_close_queryset', close_leads, 60*60) \ No newline at end of file +# cache.set('admin_leads_close_queryset', close_leads, 60*60) diff --git a/leads/tasks.py b/leads/tasks.py index 8b65927..12c8349 100644 --- a/leads/tasks.py +++ b/leads/tasks.py @@ -19,14 +19,21 @@ def get_rendered_html(template_name, context={}): @task -def send_email(subject, html_content, - text_content=None, from_email=None, - recipients=[], attachments=[], bcc=[], cc=[]): +def send_email( + subject, + html_content, + text_content=None, + from_email=None, + recipients=[], + attachments=[], + bcc=[], + cc=[], +): # send email to user with attachment if not from_email: from_email = settings.DEFAULT_FROM_EMAIL if not text_content: - text_content = '' + text_content = "" email = EmailMultiAlternatives( subject, text_content, from_email, recipients, bcc=bcc, cc=cc ) @@ -40,7 +47,7 @@ def send_email(subject, html_content, @task def send_lead_assigned_emails(lead_id, new_assigned_to_list, site_address): lead_instance = Lead.objects.filter( - ~Q(status='converted'), pk=lead_id, is_active=True + ~Q(status="converted"), pk=lead_id, is_active=True ).first() if not (lead_instance and new_assigned_to_list): return False @@ -48,10 +55,10 @@ def send_lead_assigned_emails(lead_id, new_assigned_to_list, site_address): users = User.objects.filter(id__in=new_assigned_to_list).distinct() subject = "Lead '%s' has been assigned to you" % lead_instance from_email = settings.DEFAULT_FROM_EMAIL - template_name = 'lead_assigned.html' + template_name = "lead_assigned.html" url = site_address - url += '/leads/' + str(lead_instance.id) + '/view/' + url += "/leads/" + str(lead_instance.id) + "/view/" context = { "lead_instance": lead_instance, @@ -68,33 +75,38 @@ def send_lead_assigned_emails(lead_id, new_assigned_to_list, site_address): @task -def send_email_to_assigned_user(recipients, lead_id, domain='demo.django-crm.io', protocol='http', source=''): +def send_email_to_assigned_user( + recipients, lead_id, domain="demo.django-crm.io", protocol="http", source="" +): """ Send Mail To Users When they are assigned to a lead """ lead = Lead.objects.get(id=lead_id) created_by = lead.created_by - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) for user in recipients: recipients_list = [] user = User.objects.filter(id=user, is_active=True).first() if user: - if (user.email not in blocked_emails) and (user.email.split('@')[-1] not in blocked_domains): + if (user.email not in blocked_emails) and ( + user.email.split("@")[-1] not in blocked_domains + ): recipients_list.append(user.email) context = {} - context["url"] = protocol + '://' + domain + \ - reverse('leads:view_lead', args=(lead.id,)) + context["url"] = ( + protocol + + "://" + + domain + + reverse("leads:view_lead", args=(lead.id,)) + ) context["user"] = user context["lead"] = lead context["created_by"] = created_by context["source"] = source - subject = 'Assigned a lead for you. ' + subject = "Assigned a lead for you. " html_content = render_to_string( - 'assigned_to/leads_assigned.html', context=context) - msg = EmailMessage( - subject, - html_content, - to=recipients_list + "assigned_to/leads_assigned.html", context=context ) + msg = EmailMessage(subject, html_content, to=recipients_list) msg.content_subtype = "html" msg.send() @@ -104,27 +116,27 @@ def create_lead_from_file(validated_rows, invalid_rows, user_id, source): """Parameters : validated_rows, invalid_rows, user_id. This function is used to create leads from a given file. """ - email_regex = '^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,4})$' + email_regex = "^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,4})$" user = User.objects.get(id=user_id) for row in validated_rows: - if not Lead.objects.filter(title=row.get('title')).exists(): - if re.match(email_regex, row.get('email')) is not None: + if not Lead.objects.filter(title=row.get("title")).exists(): + if re.match(email_regex, row.get("email")) is not None: try: lead = Lead() - lead.title = row.get('title', '')[:64] - lead.first_name = row.get('first name', '')[:255] - lead.last_name = row.get('last name', '')[:255] - lead.website = row.get('website', '')[:255] - lead.email = row.get('email', '') - lead.phone = row.get('phone', '') - lead.address_line = row.get('address', '')[:255] - lead.city = row.get('city', '')[:255] - lead.state = row.get('state', '')[:255] - lead.postcode = row.get('postcode', '')[:64] - lead.country = row.get('country', '')[:3] - lead.description = row.get('description', '') - lead.status = row.get('status', '') - lead.account_name = row.get('account_name', '')[:255] + lead.title = row.get("title", "")[:64] + lead.first_name = row.get("first name", "")[:255] + lead.last_name = row.get("last name", "")[:255] + lead.website = row.get("website", "")[:255] + lead.email = row.get("email", "") + lead.phone = row.get("phone", "") + lead.address_line = row.get("address", "")[:255] + lead.city = row.get("city", "")[:255] + lead.state = row.get("state", "")[:255] + lead.postcode = row.get("postcode", "")[:64] + lead.country = row.get("country", "")[:3] + lead.description = row.get("description", "") + lead.status = row.get("status", "") + lead.account_name = row.get("account_name", "")[:255] lead.created_from_site = False lead.created_by = user lead.save() @@ -134,9 +146,13 @@ def create_lead_from_file(validated_rows, invalid_rows, user_id, source): @task def update_leads_cache(): - queryset = Lead.objects.all().exclude(status='converted').select_related('created_by' - ).prefetch_related('tags', 'assigned_to',) - open_leads = queryset.exclude(status='closed') - close_leads = queryset.filter(status='closed') - cache.set('admin_leads_open_queryset', open_leads, 60*60) - cache.set('admin_leads_close_queryset', close_leads, 60*60) + queryset = ( + Lead.objects.all() + .exclude(status="converted") + .select_related("created_by") + .prefetch_related("tags", "assigned_to",) + ) + open_leads = queryset.exclude(status="closed") + close_leads = queryset.filter(status="closed") + cache.set("admin_leads_open_queryset", open_leads, 60 * 60) + cache.set("admin_leads_close_queryset", close_leads, 60 * 60) diff --git a/leads/tests_celery_tasks.py b/leads/tests_celery_tasks.py index 226a219..89cb48e 100644 --- a/leads/tests_celery_tasks.py +++ b/leads/tests_celery_tasks.py @@ -1,50 +1,110 @@ from django.test import TestCase from django.test.utils import override_settings -from leads.tasks import (create_lead_from_file, send_email, - send_email_to_assigned_user, - send_lead_assigned_emails) +from leads.tasks import ( + create_lead_from_file, + send_email, + send_email_to_assigned_user, + send_lead_assigned_emails, +) from leads.tests import TestLeadModel class TestCeleryTasks(TestLeadModel, TestCase): - - @override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - CELERY_ALWAYS_EAGER=True, - BROKER_BACKEND='memory') + @override_settings( + CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, + CELERY_ALWAYS_EAGER=True, + BROKER_BACKEND="memory", + ) def test_celery_tasks(self): task = send_email_to_assigned_user.apply( - ([self.user.id, self.user1.id, ], self.lead.id,),) - self.assertEqual('SUCCESS', task.state) + ([self.user.id, self.user1.id,], self.lead.id,), + ) + self.assertEqual("SUCCESS", task.state) task = send_lead_assigned_emails.apply( - (self.lead.id, [self.user.id, self.user1.id, self.user2.id, ], 'https://www.example.com',),) - self.assertEqual('SUCCESS', task.state) + ( + self.lead.id, + [self.user.id, self.user1.id, self.user2.id,], + "https://www.example.com", + ), + ) + self.assertEqual("SUCCESS", task.state) task = send_email.apply( - ('mail subject', 'html content',), {'recipients': [self.user.id, self.user1.id, self.user2.id, ], },) - self.assertEqual('SUCCESS', task.state) + ("mail subject", "html content",), + {"recipients": [self.user.id, self.user1.id, self.user2.id,],}, + ) + self.assertEqual("SUCCESS", task.state) task = send_lead_assigned_emails.apply( - (self.lead1.id, [self.user.id, self.user1.id, self.user2.id, ], 'https://www.example.com',),) - self.assertEqual('SUCCESS', task.state) + ( + self.lead1.id, + [self.user.id, self.user1.id, self.user2.id,], + "https://www.example.com", + ), + ) + self.assertEqual("SUCCESS", task.state) valid_rows = [ - {'title': 'lead1 csv', 'first name': 'john', 'last name': 'doe', 'website': 'www.example.com', - 'phone': '911234567890', 'email': 'user1@email.com', 'address': 'address for lead1'}, - {'title': 'lead2 csv', 'first name': 'jane', 'last name': 'doe', 'website': 'www.website.com', - 'phone': '911234567891', 'email': 'user2@email.com', 'address': 'address for lead2'}, - {'title': 'lead3 csv', 'first name': 'joe', 'last name': 'doe', 'website': 'www.test.com', - 'phone': '911234567892', 'email': 'user3@email.com', 'address': 'address for lead3'}, - {'title': 'lead4 csv', 'first name': 'john', 'last name': 'doe', 'website': 'www.sample.com', - 'phone': '911234567893', 'email': 'user4@email.com', 'address': 'address for lead4'} + { + "title": "lead1 csv", + "first name": "john", + "last name": "doe", + "website": "www.example.com", + "phone": "911234567890", + "email": "user1@email.com", + "address": "address for lead1", + }, + { + "title": "lead2 csv", + "first name": "jane", + "last name": "doe", + "website": "www.website.com", + "phone": "911234567891", + "email": "user2@email.com", + "address": "address for lead2", + }, + { + "title": "lead3 csv", + "first name": "joe", + "last name": "doe", + "website": "www.test.com", + "phone": "911234567892", + "email": "user3@email.com", + "address": "address for lead3", + }, + { + "title": "lead4 csv", + "first name": "john", + "last name": "doe", + "website": "www.sample.com", + "phone": "911234567893", + "email": "user4@email.com", + "address": "address for lead4", + }, ] invalid_rows = [ - {'title': 'lead5 csv', 'first name': 'joe', 'last name': 'doe', 'website': 'www.test.com', - 'phone': '911234567892', 'email': 'useremail.com', 'address': 'address for lead3'}, - {'title': 'lead6 csv', 'first name': 'john', 'last name': 'doe', 'website': 'www.sample.com', - 'phone': '911234567893', 'email': 'user4@email', 'address': 'address for lead4'} + { + "title": "lead5 csv", + "first name": "joe", + "last name": "doe", + "website": "www.test.com", + "phone": "911234567892", + "email": "useremail.com", + "address": "address for lead3", + }, + { + "title": "lead6 csv", + "first name": "john", + "last name": "doe", + "website": "www.sample.com", + "phone": "911234567893", + "email": "user4@email", + "address": "address for lead4", + }, ] task = create_lead_from_file.apply( - (valid_rows, invalid_rows, self.user.id, 'example.com'),) - self.assertEqual('SUCCESS', task.state) + (valid_rows, invalid_rows, self.user.id, "example.com"), + ) + self.assertEqual("SUCCESS", task.state) diff --git a/leads/urls.py b/leads/urls.py index 345ca82..75b8c2e 100644 --- a/leads/urls.py +++ b/leads/urls.py @@ -1,43 +1,51 @@ from django.urls import path from leads.views import ( - LeadListView, create_lead, LeadDetailView, - update_lead, DeleteLeadView, convert_lead, - GetLeadsView, AddCommentView, UpdateCommentView, DeleteCommentView, - AddAttachmentsView, DeleteAttachmentsView, create_lead_from_site, - update_lead_tags, remove_lead_tag, upload_lead_csv_file, sample_lead_file, - lead_list_view, get_teams_and_users + LeadListView, + create_lead, + LeadDetailView, + update_lead, + DeleteLeadView, + convert_lead, + GetLeadsView, + AddCommentView, + UpdateCommentView, + DeleteCommentView, + AddAttachmentsView, + DeleteAttachmentsView, + create_lead_from_site, + update_lead_tags, + remove_lead_tag, + upload_lead_csv_file, + sample_lead_file, + lead_list_view, + get_teams_and_users, ) -app_name = 'leads' +app_name = "leads" urlpatterns = [ -# path('', LeadListView.as_view(), name='list'), - path('', lead_list_view, name='list'), - path('create/', create_lead, name='add_lead'), + # path('', LeadListView.as_view(), name='list'), + path("", lead_list_view, name="list"), + path("create/", create_lead, name="add_lead"), # create_lead_from_site - path('create/from-site/', create_lead_from_site, - name="create_lead_from_site"), - path('/view/', LeadDetailView.as_view(), name="view_lead"), - path('/edit/', update_lead, name="edit_lead"), - path('/delete/', DeleteLeadView.as_view(), name="remove_lead"), - path('/convert/', convert_lead, name="leads_convert"), - - path('get/list/', GetLeadsView.as_view(), name="get_lead"), - - path('comment/add/', AddCommentView.as_view(), name="add_comment"), - path('comment/edit/', UpdateCommentView.as_view(), name="edit_comment"), - path('comment/remove/', - DeleteCommentView.as_view(), name="remove_comment"), - - path('attachment/add/', - AddAttachmentsView.as_view(), name="add_attachment"), - path('attachment/remove/', DeleteAttachmentsView.as_view(), - name="remove_attachment"), - path('update_lead_tags//', update_lead_tags, name="update_lead_tags"), - path('remove_lead_tag/', remove_lead_tag, name="remove_lead_tag"), - path('upload_lead_csv_file/', upload_lead_csv_file, name="upload_lead_csv_file"), - path('sample_lead_file/', sample_lead_file, name="sample_lead_file"), - path('get_teams_and_users/', get_teams_and_users, name="get_teams_and_users") + path("create/from-site/", create_lead_from_site, name="create_lead_from_site"), + path("/view/", LeadDetailView.as_view(), name="view_lead"), + path("/edit/", update_lead, name="edit_lead"), + path("/delete/", DeleteLeadView.as_view(), name="remove_lead"), + path("/convert/", convert_lead, name="leads_convert"), + path("get/list/", GetLeadsView.as_view(), name="get_lead"), + path("comment/add/", AddCommentView.as_view(), name="add_comment"), + path("comment/edit/", UpdateCommentView.as_view(), name="edit_comment"), + path("comment/remove/", DeleteCommentView.as_view(), name="remove_comment"), + path("attachment/add/", AddAttachmentsView.as_view(), name="add_attachment"), + path( + "attachment/remove/", DeleteAttachmentsView.as_view(), name="remove_attachment" + ), + path("update_lead_tags//", update_lead_tags, name="update_lead_tags"), + path("remove_lead_tag/", remove_lead_tag, name="remove_lead_tag"), + path("upload_lead_csv_file/", upload_lead_csv_file, name="upload_lead_csv_file"), + path("sample_lead_file/", sample_lead_file, name="sample_lead_file"), + path("get_teams_and_users/", get_teams_and_users, name="get_teams_and_users"), ] diff --git a/manage.py b/manage.py index 2876f4b..83ab37d 100755 --- a/manage.py +++ b/manage.py @@ -4,7 +4,7 @@ if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "crm.settings") - os.environ['DJANGO_SETTINGS_MODULE'] = "crm.settings" + os.environ["DJANGO_SETTINGS_MODULE"] = "crm.settings" try: from django.core.management import execute_from_command_line except ImportError: diff --git a/manage_local.py b/manage_local.py index e23b489..96a22bb 100644 --- a/manage_local.py +++ b/manage_local.py @@ -4,7 +4,7 @@ if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "crm.local_settings") - os.environ['DJANGO_SETTINGS_MODULE'] = "crm.local_settings" + os.environ["DJANGO_SETTINGS_MODULE"] = "crm.local_settings" try: from django.core.management import execute_from_command_line except ImportError: diff --git a/marketing/apps.py b/marketing/apps.py index bc0eb06..5d6b252 100644 --- a/marketing/apps.py +++ b/marketing/apps.py @@ -2,4 +2,4 @@ class MarketingConfig(AppConfig): - name = 'marketing' + name = "marketing" diff --git a/marketing/forms.py b/marketing/forms.py index 565d58a..de38ddc 100644 --- a/marketing/forms.py +++ b/marketing/forms.py @@ -1,15 +1,21 @@ import csv import datetime -import json import re import openpyxl import xlrd from django import forms -from common.models import User -from marketing.models import (BlockedDomain, BlockedEmail, Campaign, Contact, - ContactEmailCampaign, ContactList, EmailTemplate, Tag) +from marketing.models import ( + BlockedDomain, + BlockedEmail, + Campaign, + Contact, + ContactEmailCampaign, + ContactList, + EmailTemplate, +) + # from haystack.forms import SearchForm email_regex = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)" @@ -29,16 +35,14 @@ def csv_doc_validate(document): each = {} invalid_each = {} if y_index == 0: - csv_headers = [header_name.lower() - for header_name in row if header_name] - missing_headers = set(required_headers) - \ - set([r.lower() for r in row]) + csv_headers = [header_name.lower() for header_name in row if header_name] + missing_headers = set(required_headers) - set([r.lower() for r in row]) if missing_headers: - missing_headers_str = ', '.join(missing_headers) - message = 'Missing headers: %s' % (missing_headers_str) + missing_headers_str = ", ".join(missing_headers) + message = "Missing headers: %s" % (missing_headers_str) return {"error": True, "message": message} continue - elif not ''.join(str(x) for x in row): + elif not "".join(str(x) for x in row): continue else: for x_index, cell_value in enumerate(row): @@ -62,8 +66,13 @@ def csv_doc_validate(document): failed_contacts_csv.append(list(each.values())) else: temp_row.append(each) - return {"error": False, "validated_rows": temp_row, "invalid_rows": invalid_row, "headers": csv_headers, - "failed_contacts_csv": failed_contacts_csv} + return { + "error": False, + "validated_rows": temp_row, + "invalid_rows": invalid_row, + "headers": csv_headers, + "failed_contacts_csv": failed_contacts_csv, + } # def get_validated_rows(wb, sheet_name, validated_rows, invalid_rows): @@ -107,6 +116,7 @@ def csv_doc_validate(document): # validated_rows.append(temp_row) # return validated_rows, invalid_rows + def get_validated_rows(wb, sheet_name, validated_rows, invalid_rows): wb_sheet = wb[sheet_name] sheet_headers = [cell.value for cell in wb_sheet[1]] @@ -115,8 +125,8 @@ def get_validated_rows(wb, sheet_name, validated_rows, invalid_rows): # this condition will check if the both fields in required_headers exist if not len(set(sheet_headers).intersection(required_headers)) == 2: missing_headers = set(required_headers) - set(sheet_headers) - missing_headers_str = ', '.join(missing_headers) - message = 'Missing headers: {}'.format(missing_headers_str) + missing_headers_str = ", ".join(missing_headers) + message = "Missing headers: {}".format(missing_headers_str) return {"error": True, "message": message}, message else: data = [] @@ -131,8 +141,8 @@ def get_validated_rows(wb, sheet_name, validated_rows, invalid_rows): validated_rows = [] invalid_rows = [] for row in data: - if row.get('email', None): - if re.match(email_regex, row.get('email', None)) is None: + if row.get("email", None): + if re.match(email_regex, row.get("email", None)) is None: invalid_rows.append(row) else: validated_rows.append(row) @@ -146,12 +156,17 @@ def xlsx_doc_validate(document): invalid_rows = [] for sheet_name in sheets: valid_data, invalid_data = get_validated_rows( - wb, sheet_name, validated_rows, invalid_rows) - if type(valid_data) == dict: + wb, sheet_name, validated_rows, invalid_rows + ) + if isinstance(valid_data, dict): return valid_data validated_rows = validated_rows + valid_data invalid_rows = invalid_rows + invalid_data - return {"error": False, "validated_rows": validated_rows, "invalid_rows": invalid_rows} + return { + "error": False, + "validated_rows": validated_rows, + "invalid_rows": invalid_rows, + } def get_validated_rows_xls(wb, sheet_name, validated_rows, invalid_rows): @@ -162,15 +177,17 @@ def get_validated_rows_xls(wb, sheet_name, validated_rows, invalid_rows): # this condition will check if the both fields in required_headers exist if not len(set(sheet_headers).intersection(required_headers)) == 2: missing_headers = set(required_headers) - set(sheet_headers) - missing_headers_str = ', '.join(missing_headers) - message = 'Missing headers: {}'.format(missing_headers_str) + missing_headers_str = ", ".join(missing_headers) + message = "Missing headers: {}".format(missing_headers_str) return {"error": True, "message": message}, message else: no_of_rows = wb_sheet.nrows data = [] for nrow in range(no_of_rows): d = {} - for key, cell_value in zip(sheet_headers, [cell.value for cell in wb_sheet.row(nrow)]): + for key, cell_value in zip( + sheet_headers, [cell.value for cell in wb_sheet.row(nrow)] + ): d[key] = cell_value data.append(d) # remove the first element, it contains no values @@ -179,8 +196,8 @@ def get_validated_rows_xls(wb, sheet_name, validated_rows, invalid_rows): validated_rows = [] invalid_rows = [] for row in data: - if row.get('email', None): - if re.match(email_regex, row.get('email', None)) is None: + if row.get("email", None): + if re.match(email_regex, row.get("email", None)) is None: invalid_rows.append(row) else: validated_rows.append(row) @@ -194,13 +211,18 @@ def xls_doc_validate(document): invalid_rows = [] for sheet_name in sheets: valid_data, invalid_data = get_validated_rows_xls( - wb, sheet_name, validated_rows, invalid_rows) + wb, sheet_name, validated_rows, invalid_rows + ) # if the data is valid data we'll get a list or else a dict if type(valid_data) == dict: return valid_data validated_rows = validated_rows + valid_data invalid_rows = invalid_rows + invalid_data - return {"error": False, "validated_rows": validated_rows, "invalid_rows": invalid_rows} + return { + "error": False, + "validated_rows": validated_rows, + "invalid_rows": invalid_rows, + } def import_document_validator(document): @@ -210,17 +232,17 @@ def import_document_validator(document): return csv_doc_validate(document) except Exception as e: print(e) - print('csv') + print("csv") try: return xlsx_doc_validate(document) # xlsx file except Exception as e: print(e) - print('xlsx') + print("xlsx") try: return xls_doc_validate(document) # xls file except Exception as e: print(e) - print('xls') + print("xls") return {"error": True, "message": "Not a valid CSV/XLS, XLSX file"} @@ -234,23 +256,26 @@ class Meta: def __init__(self, *args, **kwargs): super(ContactListForm, self).__init__(*args, **kwargs) - self.fields['contacts_file'].widget.attrs.update({ - "accept": ".csv,.xls,.xlsx,.xlsm,.xlsb,.xml", - }) + self.fields["contacts_file"].widget.attrs.update( + {"accept": ".csv,.xls,.xlsx,.xlsm,.xlsb,.xml",} + ) if self.instance.id is None: - self.fields['contacts_file'].required = True + self.fields["contacts_file"].required = True else: - self.fields['contacts_file'].required = False - if self.data.get('contacts_file'): - self.fields['contacts_file'].widget.attrs.update({ - "accept": ".csv,.xls,.xlsx,.xlsm,.xlsb,.xml", - }) + self.fields["contacts_file"].required = False + if self.data.get("contacts_file"): + self.fields["contacts_file"].widget.attrs.update( + {"accept": ".csv,.xls,.xlsx,.xlsm,.xlsb,.xml",} + ) def clean_name(self): - name = self.cleaned_data.get('name') - if ContactList.objects.filter(name__iexact=name).exclude(id=self.instance.id).exists(): - raise forms.ValidationError( - 'Contact List with this Name already exists.') + name = self.cleaned_data.get("name") + if ( + ContactList.objects.filter(name__iexact=name) + .exclude(id=self.instance.id) + .exists() + ): + raise forms.ValidationError("Contact List with this Name already exists.") return name def clean_contacts_file(self): @@ -264,7 +289,8 @@ def clean_contacts_file(self): self.invalid_rows = data.get("invalid_rows", []) if len(self.validated_rows) == 0: raise forms.ValidationError( - "All the contacts in the file are invalid.") + "All the contacts in the file are invalid." + ) # self.headers = data.get("headers", []) # self.failed_contacts_csv = data.get("failed_contacts_csv", []) # failed_csv_data = [] @@ -299,30 +325,36 @@ class ContactForm(forms.ModelForm): contact_list = forms.CharField(max_length=5000) def __init__(self, *args, **kwargs): - request_user = kwargs.pop('request_user', None) - self.obj_instance = kwargs.get('instance', None) + # request_user = kwargs.pop("request_user", None) + self.obj_instance = kwargs.get("instance", None) super(ContactForm, self).__init__(*args, **kwargs) for field in self.fields.values(): field.widget.attrs = {"class": "form-control"} - self.fields['name'].required = True - self.fields['email'].required = True - self.fields['last_name'].required = False - self.fields['city'].required = False - self.fields['state'].required = False - self.fields['company_name'].required = False - self.fields['contact_list'].required = False + self.fields["name"].required = True + self.fields["email"].required = True + self.fields["last_name"].required = False + self.fields["city"].required = False + self.fields["state"].required = False + self.fields["company_name"].required = False + self.fields["contact_list"].required = False class Meta: model = Contact - fields = ["name", "email", "contact_number", - "last_name", "city", "state", "company_name"] + fields = [ + "name", + "email", + "contact_number", + "last_name", + "city", + "state", + "company_name", + ] def clean_email(self): - email = self.cleaned_data.get('email') + email = self.cleaned_data.get("email") if Contact.objects.filter(email=email).exclude(id=self.instance.id).exists(): - raise forms.ValidationError( - 'Contact with this Email already exists') + raise forms.ValidationError("Contact with this Email already exists") return email # def clean_contact_list(self): @@ -376,10 +408,9 @@ def clean_email(self): class EmailTemplateForm(forms.ModelForm): - class Meta: model = EmailTemplate - fields = ['title', 'subject', 'html'] + fields = ["title", "subject", "html"] class SendCampaignForm(forms.ModelForm): @@ -395,48 +426,56 @@ class SendCampaignForm(forms.ModelForm): class Meta: model = Campaign - fields = ['title', 'subject', 'html', 'email_template', 'attachment', ] + fields = [ + "title", + "subject", + "html", + "email_template", + "attachment", + ] def __init__(self, *args, **kwargs): - self.contacts_list = kwargs.pop('contacts_list') + self.contacts_list = kwargs.pop("contacts_list") super(SendCampaignForm, self).__init__(*args, **kwargs) - if self.data.get('schedule_later') and self.data['schedule_later'] == 'true': - self.fields['timezone'].required = True - self.fields['schedule_date_time'].required = True - if not self.data.get('reply_to_crm'): - self.fields['reply_to_email'].required = True + if self.data.get("schedule_later") and self.data["schedule_later"] == "true": + self.fields["timezone"].required = True + self.fields["schedule_date_time"].required = True + if not self.data.get("reply_to_crm"): + self.fields["reply_to_email"].required = True def clean_schedule_date_time(self): - schedule_date_time = self.cleaned_data.get('schedule_date_time') - if self.cleaned_data.get('schedule_later') == True: + schedule_date_time = self.cleaned_data.get("schedule_date_time") + if self.cleaned_data.get("schedule_later") == True: schedule_date_time = datetime.datetime.strptime( - schedule_date_time, '%Y-%m-%d %H:%M') + schedule_date_time, "%Y-%m-%d %H:%M" + ) if schedule_date_time < datetime.datetime.now(): raise forms.ValidationError( - 'Schedule Date Time should be greater than current time') + "Schedule Date Time should be greater than current time" + ) return schedule_date_time def clean_contact_list(self): contact_list = self.contacts_list - if not contact_list or contact_list == '[]' or \ - contact_list == []: - raise forms.ValidationError( - "Please choose any of the Contact List") + if not contact_list or contact_list == "[]" or contact_list == []: + raise forms.ValidationError("Please choose any of the Contact List") else: for each in contact_list: contacts_list_obj = ContactList.objects.filter(id=each).first() if not contacts_list_obj: - raise forms.ValidationError( - "Please choose a valid Contact List") + raise forms.ValidationError("Please choose a valid Contact List") if contacts_list_obj.contacts.count() < 1: raise forms.ValidationError( - 'The contact list "{}" does not have any contacts in it .'.format(contacts_list_obj.name)) + 'The contact list "{}" does not have any contacts in it .'.format( + contacts_list_obj.name + ) + ) return contact_list def clean_html(self): - html = self.cleaned_data.get('html') + html = self.cleaned_data.get("html") count = 0 for i in html: if i == "{": @@ -444,70 +483,88 @@ def clean_html(self): elif i == "}": count -= 1 if count < 0: - raise forms.ValidationError( - 'Brackets do not match, Enter valid tags.') + raise forms.ValidationError("Brackets do not match, Enter valid tags.") if count != 0: - raise forms.ValidationError( - 'Brackets do not match, Enter valid tags.') + raise forms.ValidationError("Brackets do not match, Enter valid tags.") return html def clean_title(self): - title = self.cleaned_data.get('title') - if Campaign.objects.filter(title__iexact=title).exclude(id=self.instance.id).exists(): - raise forms.ValidationError( - 'Campaign with this title already exists.') + title = self.cleaned_data.get("title") + if ( + Campaign.objects.filter(title__iexact=title) + .exclude(id=self.instance.id) + .exists() + ): + raise forms.ValidationError("Campaign with this title already exists.") return title class EmailCampaignForm(forms.ModelForm): - def __init__(self, *args, **kwargs): - self.obj_instance = kwargs.get('instance', None) + self.obj_instance = kwargs.get("instance", None) super(EmailCampaignForm, self).__init__(*args, **kwargs) for field in self.fields.values(): field.widget.attrs = {"class": "form-control"} - self.fields['name'].required = True - self.fields['email'].required = True - self.fields['last_name'].required = False + self.fields["name"].required = True + self.fields["email"].required = True + self.fields["last_name"].required = False class Meta: model = ContactEmailCampaign - fields = ["name", "email", "last_name", ] + fields = [ + "name", + "email", + "last_name", + ] def clean_email(self): - email = self.cleaned_data.get('email') - if ContactEmailCampaign.objects.filter(email=email).exclude(id=self.instance.id).exists(): - raise forms.ValidationError( - 'Contact with this Email already exists.') + email = self.cleaned_data.get("email") + if ( + ContactEmailCampaign.objects.filter(email=email) + .exclude(id=self.instance.id) + .exists() + ): + raise forms.ValidationError("Contact with this Email already exists.") return email class BlockedDomainsForm(forms.ModelForm): - class Meta: model = BlockedDomain - fields = ['domain', ] + fields = [ + "domain", + ] def clean_domain(self): - domain = self.cleaned_data.get('domain') - domain_regex = '^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$' + domain = self.cleaned_data.get("domain") + domain_regex = "^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$" if re.match(domain_regex, domain) is None: - raise forms.ValidationError('Enter a valid domain.') - if BlockedDomain.objects.filter(domain__iexact=domain).exclude(id=self.instance.id).exists(): - raise forms.ValidationError('Domain with this name already exists.') + raise forms.ValidationError("Enter a valid domain.") + if ( + BlockedDomain.objects.filter(domain__iexact=domain) + .exclude(id=self.instance.id) + .exists() + ): + raise forms.ValidationError("Domain with this name already exists.") return domain -class BlockedEmailForm(forms.ModelForm): +class BlockedEmailForm(forms.ModelForm): class Meta: model = BlockedEmail - fields = ['email', ] + fields = [ + "email", + ] def clean_email(self): - email = self.cleaned_data.get('email') - if BlockedEmail.objects.filter(email__iexact=email).exclude(id=self.instance.id).exists(): - raise forms.ValidationError('Email already exists.') + email = self.cleaned_data.get("email") + if ( + BlockedEmail.objects.filter(email__iexact=email) + .exclude(id=self.instance.id) + .exists() + ): + raise forms.ValidationError("Email already exists.") return email @@ -533,4 +590,4 @@ def clean_email(self): # domain_regex = '^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$' # if re.match(domain_regex, email_domain) is None: # raise forms.ValidationError('Enter a valid domain.') -# return email_domain \ No newline at end of file +# return email_domain diff --git a/marketing/migrations/0001_initial.py b/marketing/migrations/0001_initial.py index 8ea614e..c258b87 100644 --- a/marketing/migrations/0001_initial.py +++ b/marketing/migrations/0001_initial.py @@ -16,163 +16,375 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Campaign', + name="Campaign", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=5000)), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('schedule_date_time', models.DateTimeField(blank=True, null=True)), - ('reply_to_email', models.EmailField(blank=True, max_length=254, null=True)), - ('subject', models.CharField(max_length=5000)), - ('html', models.TextField()), - ('html_processed', models.TextField(blank=True, default='')), - ('from_email', models.EmailField(blank=True, max_length=254, null=True)), - ('from_name', models.EmailField(blank=True, max_length=254, null=True)), - ('sent', models.IntegerField(blank=True, default='0')), - ('opens', models.IntegerField(blank=True, default='0')), - ('opens_unique', models.IntegerField(blank=True, default='0')), - ('bounced', models.IntegerField(default='0')), - ('status', models.CharField(choices=[('Scheduled', 'Scheduled'), ('Cancelled', 'Cancelled'), ('Sending', 'Sending'), ('Preparing', 'Preparing'), ('Sent', 'Sent')], default='Preparing', max_length=20)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=5000)), + ("created_on", models.DateTimeField(auto_now_add=True)), + ("schedule_date_time", models.DateTimeField(blank=True, null=True)), + ( + "reply_to_email", + models.EmailField(blank=True, max_length=254, null=True), + ), + ("subject", models.CharField(max_length=5000)), + ("html", models.TextField()), + ("html_processed", models.TextField(blank=True, default="")), + ( + "from_email", + models.EmailField(blank=True, max_length=254, null=True), + ), + ("from_name", models.EmailField(blank=True, max_length=254, null=True)), + ("sent", models.IntegerField(blank=True, default="0")), + ("opens", models.IntegerField(blank=True, default="0")), + ("opens_unique", models.IntegerField(blank=True, default="0")), + ("bounced", models.IntegerField(default="0")), + ( + "status", + models.CharField( + choices=[ + ("Scheduled", "Scheduled"), + ("Cancelled", "Cancelled"), + ("Sending", "Sending"), + ("Preparing", "Preparing"), + ("Sent", "Sent"), + ], + default="Preparing", + max_length=20, + ), + ), ], ), migrations.CreateModel( - name='CampaignLinkClick', + name="CampaignLinkClick", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('ip_address', models.GenericIPAddressField()), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('user_agent', models.CharField(blank=True, max_length=2000, null=True)), - ('campaign', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='marketing.Campaign')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("ip_address", models.GenericIPAddressField()), + ("created_on", models.DateTimeField(auto_now_add=True)), + ( + "user_agent", + models.CharField(blank=True, max_length=2000, null=True), + ), + ( + "campaign", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="marketing.Campaign", + ), + ), ], ), migrations.CreateModel( - name='CampaignLog', + name="CampaignLog", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('message_id', models.CharField(blank=True, max_length=1000, null=True)), - ('campaign', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='campaign_contacts', to='marketing.Campaign')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_on", models.DateTimeField(auto_now_add=True)), + ( + "message_id", + models.CharField(blank=True, max_length=1000, null=True), + ), + ( + "campaign", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="campaign_contacts", + to="marketing.Campaign", + ), + ), ], ), migrations.CreateModel( - name='CampaignOpen', + name="CampaignOpen", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('ip_address', models.GenericIPAddressField()), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('user_agent', models.CharField(blank=True, max_length=2000, null=True)), - ('campaign', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='marketing.Campaign')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("ip_address", models.GenericIPAddressField()), + ("created_on", models.DateTimeField(auto_now_add=True)), + ( + "user_agent", + models.CharField(blank=True, max_length=2000, null=True), + ), + ( + "campaign", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="marketing.Campaign", + ), + ), ], ), migrations.CreateModel( - name='Contact', + name="Contact", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('name', models.CharField(max_length=500)), - ('email', models.EmailField(max_length=254)), - ('contact_number', models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(message="Phone number must be entered in the format: '+999999999'. Up to 20 digits allowed.", regex='^\\+?1?\\d{9,15}$')])), - ('is_unsubscribed', models.BooleanField(default=False)), - ('is_bounced', models.BooleanField(default=False)), - ('company_name', models.CharField(blank=True, max_length=500, null=True)), - ('last_name', models.CharField(blank=True, max_length=500, null=True)), - ('city', models.CharField(blank=True, max_length=500, null=True)), - ('state', models.CharField(blank=True, max_length=500, null=True)), - ('contry', models.CharField(blank=True, max_length=500, null=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_on", models.DateTimeField(auto_now_add=True)), + ("name", models.CharField(max_length=500)), + ("email", models.EmailField(max_length=254)), + ( + "contact_number", + models.CharField( + blank=True, + max_length=20, + null=True, + validators=[ + django.core.validators.RegexValidator( + message="Phone number must be entered in the format: '+999999999'. Up to 20 digits allowed.", + regex="^\\+?1?\\d{9,15}$", + ) + ], + ), + ), + ("is_unsubscribed", models.BooleanField(default=False)), + ("is_bounced", models.BooleanField(default=False)), + ( + "company_name", + models.CharField(blank=True, max_length=500, null=True), + ), + ("last_name", models.CharField(blank=True, max_length=500, null=True)), + ("city", models.CharField(blank=True, max_length=500, null=True)), + ("state", models.CharField(blank=True, max_length=500, null=True)), + ("contry", models.CharField(blank=True, max_length=500, null=True)), ], ), migrations.CreateModel( - name='ContactList', + name="ContactList", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('name', models.CharField(max_length=500)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='marketing_contactlist', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_on", models.DateTimeField(auto_now_add=True)), + ("name", models.CharField(max_length=500)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="marketing_contactlist", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='EmailTemplate', + name="EmailTemplate", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('title', models.CharField(max_length=5000)), - ('subject', models.CharField(max_length=5000)), - ('html', models.TextField()), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='marketing_emailtemplates', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_on", models.DateTimeField(auto_now_add=True)), + ("title", models.CharField(max_length=5000)), + ("subject", models.CharField(max_length=5000)), + ("html", models.TextField()), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="marketing_emailtemplates", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='Link', + name="Link", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('original', models.URLField(max_length=2100)), - ('clicks', models.IntegerField(default='0')), - ('unique', models.IntegerField(default='0')), - ('campaign', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='marketing_links', to='marketing.Campaign')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("original", models.URLField(max_length=2100)), + ("clicks", models.IntegerField(default="0")), + ("unique", models.IntegerField(default="0")), + ( + "campaign", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="marketing_links", + to="marketing.Campaign", + ), + ), ], ), migrations.CreateModel( - name='Tag', + name="Tag", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=500)), - ('color', models.CharField(default='#999999', max_length=20, verbose_name='color')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='marketing_tags', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=500)), + ( + "color", + models.CharField( + default="#999999", max_length=20, verbose_name="color" + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="marketing_tags", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.AddField( - model_name='contactlist', - name='tags', - field=models.ManyToManyField(to='marketing.Tag'), + model_name="contactlist", + name="tags", + field=models.ManyToManyField(to="marketing.Tag"), ), migrations.AddField( - model_name='contactlist', - name='visible_to', - field=models.ManyToManyField(related_name='contact_lists_visible_to', to=settings.AUTH_USER_MODEL), + model_name="contactlist", + name="visible_to", + field=models.ManyToManyField( + related_name="contact_lists_visible_to", to=settings.AUTH_USER_MODEL + ), ), migrations.AddField( - model_name='contact', - name='contact_list', - field=models.ManyToManyField(related_name='contacts', to='marketing.ContactList'), + model_name="contact", + name="contact_list", + field=models.ManyToManyField( + related_name="contacts", to="marketing.ContactList" + ), ), migrations.AddField( - model_name='contact', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='marketing_contacts_created_by', to=settings.AUTH_USER_MODEL), + model_name="contact", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="marketing_contacts_created_by", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='campaignopen', - name='contact', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='marketing.Contact'), + model_name="campaignopen", + name="contact", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="marketing.Contact", + ), ), migrations.AddField( - model_name='campaignlog', - name='contact', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='marketing_campaign_logs', to='marketing.Contact'), + model_name="campaignlog", + name="contact", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="marketing_campaign_logs", + to="marketing.Contact", + ), ), migrations.AddField( - model_name='campaignlinkclick', - name='contact', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='marketing.Contact'), + model_name="campaignlinkclick", + name="contact", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="marketing.Contact", + ), ), migrations.AddField( - model_name='campaignlinkclick', - name='link', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='marketing.Link'), + model_name="campaignlinkclick", + name="link", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="marketing.Link", + ), ), migrations.AddField( - model_name='campaign', - name='contact_lists', - field=models.ManyToManyField(related_name='campaigns', to='marketing.ContactList'), + model_name="campaign", + name="contact_lists", + field=models.ManyToManyField( + related_name="campaigns", to="marketing.ContactList" + ), ), migrations.AddField( - model_name='campaign', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='marketing_campaigns_created_by', to=settings.AUTH_USER_MODEL), + model_name="campaign", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="marketing_campaigns_created_by", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='campaign', - name='email_template', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='marketing.EmailTemplate'), + model_name="campaign", + name="email_template", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="marketing.EmailTemplate", + ), ), ] diff --git a/marketing/migrations/0002_auto_20190307_1227.py b/marketing/migrations/0002_auto_20190307_1227.py index df674c6..980b8ef 100644 --- a/marketing/migrations/0002_auto_20190307_1227.py +++ b/marketing/migrations/0002_auto_20190307_1227.py @@ -7,34 +7,36 @@ class Migration(migrations.Migration): dependencies = [ - ('marketing', '0001_initial'), + ("marketing", "0001_initial"), ] operations = [ migrations.AddField( - model_name='campaign', - name='updated_on', + model_name="campaign", + name="updated_on", field=models.DateTimeField(auto_now=True), ), migrations.AddField( - model_name='contact', - name='updated_on', + model_name="contact", + name="updated_on", field=models.DateTimeField(auto_now=True), ), migrations.AddField( - model_name='contactlist', - name='updated_on', + model_name="contactlist", + name="updated_on", field=models.DateTimeField(auto_now=True), ), migrations.AddField( - model_name='emailtemplate', - name='updated_on', + model_name="emailtemplate", + name="updated_on", field=models.DateTimeField(auto_now=True), ), migrations.AddField( - model_name='tag', - name='created_on', - field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + model_name="tag", + name="created_on", + field=models.DateTimeField( + auto_now_add=True, default=django.utils.timezone.now + ), preserve_default=False, ), ] diff --git a/marketing/migrations/0003_failedcontact.py b/marketing/migrations/0003_failedcontact.py index b950679..f541556 100644 --- a/marketing/migrations/0003_failedcontact.py +++ b/marketing/migrations/0003_failedcontact.py @@ -10,25 +10,62 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('marketing', '0002_auto_20190307_1227'), + ("marketing", "0002_auto_20190307_1227"), ] operations = [ migrations.CreateModel( - name='FailedContact', + name="FailedContact", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('name', models.CharField(blank=True, max_length=500, null=True)), - ('email', models.EmailField(blank=True, max_length=254, null=True)), - ('contact_number', models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(message="Phone number must be entered in the format: '+999999999'. Up to 20 digits allowed.", regex='^\\+?1?\\d{9,15}$')])), - ('company_name', models.CharField(blank=True, max_length=500, null=True)), - ('last_name', models.CharField(blank=True, max_length=500, null=True)), - ('city', models.CharField(blank=True, max_length=500, null=True)), - ('state', models.CharField(blank=True, max_length=500, null=True)), - ('contry', models.CharField(blank=True, max_length=500, null=True)), - ('contact_list', models.ManyToManyField(related_name='failed_contacts', to='marketing.ContactList')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='marketing_failed_contacts_created_by', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_on", models.DateTimeField(auto_now_add=True)), + ("name", models.CharField(blank=True, max_length=500, null=True)), + ("email", models.EmailField(blank=True, max_length=254, null=True)), + ( + "contact_number", + models.CharField( + blank=True, + max_length=20, + null=True, + validators=[ + django.core.validators.RegexValidator( + message="Phone number must be entered in the format: '+999999999'. Up to 20 digits allowed.", + regex="^\\+?1?\\d{9,15}$", + ) + ], + ), + ), + ( + "company_name", + models.CharField(blank=True, max_length=500, null=True), + ), + ("last_name", models.CharField(blank=True, max_length=500, null=True)), + ("city", models.CharField(blank=True, max_length=500, null=True)), + ("state", models.CharField(blank=True, max_length=500, null=True)), + ("contry", models.CharField(blank=True, max_length=500, null=True)), + ( + "contact_list", + models.ManyToManyField( + related_name="failed_contacts", to="marketing.ContactList" + ), + ), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="marketing_failed_contacts_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/marketing/migrations/0004_auto_20190315_1443.py b/marketing/migrations/0004_auto_20190315_1443.py index afdbea8..bedc779 100644 --- a/marketing/migrations/0004_auto_20190315_1443.py +++ b/marketing/migrations/0004_auto_20190315_1443.py @@ -7,18 +7,38 @@ class Migration(migrations.Migration): dependencies = [ - ('marketing', '0003_failedcontact'), + ("marketing", "0003_failedcontact"), ] operations = [ migrations.AlterField( - model_name='contact', - name='contact_number', - field=models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(message="Phone number must be entered in the format: '+999999999'. Up to 20 digits allowed.", regex='^\\+?1?\\d{9,15}$')]), + model_name="contact", + name="contact_number", + field=models.CharField( + blank=True, + max_length=20, + null=True, + validators=[ + django.core.validators.RegexValidator( + message="Phone number must be entered in the format: '+999999999'. Up to 20 digits allowed.", + regex="^\\+?1?\\d{9,15}$", + ) + ], + ), ), migrations.AlterField( - model_name='failedcontact', - name='contact_number', - field=models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(message="Phone number must be entered in the format: '+999999999'. Up to 20 digits allowed.", regex='^\\+?1?\\d{9,15}$')]), + model_name="failedcontact", + name="contact_number", + field=models.CharField( + blank=True, + max_length=20, + null=True, + validators=[ + django.core.validators.RegexValidator( + message="Phone number must be entered in the format: '+999999999'. Up to 20 digits allowed.", + regex="^\\+?1?\\d{9,15}$", + ) + ], + ), ), ] diff --git a/marketing/migrations/0005_campaign_timezone.py b/marketing/migrations/0005_campaign_timezone.py index 31de46d..0b1625b 100644 --- a/marketing/migrations/0005_campaign_timezone.py +++ b/marketing/migrations/0005_campaign_timezone.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('marketing', '0004_auto_20190315_1443'), + ("marketing", "0004_auto_20190315_1443"), ] operations = [ migrations.AddField( - model_name='campaign', - name='timezone', - field=models.CharField(default='UTC', max_length=100), + model_name="campaign", + name="timezone", + field=models.CharField(default="UTC", max_length=100), ), ] diff --git a/marketing/migrations/0006_campaign_attachment.py b/marketing/migrations/0006_campaign_attachment.py index d8cad5c..9273c77 100644 --- a/marketing/migrations/0006_campaign_attachment.py +++ b/marketing/migrations/0006_campaign_attachment.py @@ -7,13 +7,18 @@ class Migration(migrations.Migration): dependencies = [ - ('marketing', '0005_campaign_timezone'), + ("marketing", "0005_campaign_timezone"), ] operations = [ migrations.AddField( - model_name='campaign', - name='attachment', - field=models.FileField(blank=True, max_length=1000, null=True, upload_to=marketing.models.get_campaign_attachment_path), + model_name="campaign", + name="attachment", + field=models.FileField( + blank=True, + max_length=1000, + null=True, + upload_to=marketing.models.get_campaign_attachment_path, + ), ), ] diff --git a/marketing/migrations/0007_auto_20190611_1226.py b/marketing/migrations/0007_auto_20190611_1226.py index 871436d..7b8a427 100644 --- a/marketing/migrations/0007_auto_20190611_1226.py +++ b/marketing/migrations/0007_auto_20190611_1226.py @@ -7,21 +7,42 @@ class Migration(migrations.Migration): dependencies = [ - ('marketing', '0006_campaign_attachment'), + ("marketing", "0006_campaign_attachment"), ] operations = [ migrations.CreateModel( - name='CampaignCompleted', + name="CampaignCompleted", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('is_completed', models.BooleanField(default=False)), - ('campaign', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='campaign_is_completed', to='marketing.Campaign')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("is_completed", models.BooleanField(default=False)), + ( + "campaign", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="campaign_is_completed", + to="marketing.Campaign", + ), + ), ], ), migrations.AlterField( - model_name='campaignopen', - name='contact', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contact_campaign', to='marketing.Contact'), + model_name="campaignopen", + name="contact", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="contact_campaign", + to="marketing.Contact", + ), ), ] diff --git a/marketing/migrations/0008_auto_20190612_1905.py b/marketing/migrations/0008_auto_20190612_1905.py index da0c38a..1a918eb 100644 --- a/marketing/migrations/0008_auto_20190612_1905.py +++ b/marketing/migrations/0008_auto_20190612_1905.py @@ -7,39 +7,43 @@ class Migration(migrations.Migration): dependencies = [ - ('marketing', '0007_auto_20190611_1226'), + ("marketing", "0007_auto_20190611_1226"), ] operations = [ migrations.AlterModelOptions( - name='campaign', - options={'ordering': ('created_on',)}, + name="campaign", options={"ordering": ("created_on",)}, ), + migrations.AlterModelOptions(name="contact", options={"ordering": ["id"]},), migrations.AlterModelOptions( - name='contact', - options={'ordering': ['id']}, + name="contactlist", options={"ordering": ("id",)}, ), migrations.AlterModelOptions( - name='contactlist', - options={'ordering': ('id',)}, - ), - migrations.AlterModelOptions( - name='emailtemplate', - options={'ordering': ['id']}, + name="emailtemplate", options={"ordering": ["id"]}, ), migrations.AddField( - model_name='campaign', - name='tags', - field=models.ManyToManyField(to='marketing.Tag'), + model_name="campaign", + name="tags", + field=models.ManyToManyField(to="marketing.Tag"), ), migrations.AlterField( - model_name='campaignopen', - name='campaign', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='campaign_open', to='marketing.Campaign'), + model_name="campaignopen", + name="campaign", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="campaign_open", + to="marketing.Campaign", + ), ), migrations.AlterField( - model_name='campaignopen', - name='contact', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contact_campaign_open', to='marketing.Contact'), + model_name="campaignopen", + name="contact", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="contact_campaign_open", + to="marketing.Contact", + ), ), ] diff --git a/marketing/migrations/0009_auto_20190618_1144.py b/marketing/migrations/0009_auto_20190618_1144.py index 60c9d6c..ebb2074 100644 --- a/marketing/migrations/0009_auto_20190618_1144.py +++ b/marketing/migrations/0009_auto_20190618_1144.py @@ -8,29 +8,44 @@ class Migration(migrations.Migration): dependencies = [ - ('marketing', '0008_auto_20190612_1905'), + ("marketing", "0008_auto_20190612_1905"), ] operations = [ migrations.CreateModel( - name='ContactUnsubscribedCampaign', + name="ContactUnsubscribedCampaign", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('is_unsubscribed', models.BooleanField(default=False)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("is_unsubscribed", models.BooleanField(default=False)), ], ), migrations.AlterModelOptions( - name='campaign', - options={'ordering': ('-created_on',)}, + name="campaign", options={"ordering": ("-created_on",)}, ), migrations.AddField( - model_name='contactunsubscribedcampaign', - name='campaigns', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='campaign_is_unsubscribed', to='marketing.Campaign'), + model_name="contactunsubscribedcampaign", + name="campaigns", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="campaign_is_unsubscribed", + to="marketing.Campaign", + ), ), migrations.AddField( - model_name='contactunsubscribedcampaign', - name='contacts', - field=models.ForeignKey(on_delete=django.db.models.expressions.Case, related_name='contact_is_unsubscribed', to='marketing.Contact'), + model_name="contactunsubscribedcampaign", + name="contacts", + field=models.ForeignKey( + on_delete=django.db.models.expressions.Case, + related_name="contact_is_unsubscribed", + to="marketing.Contact", + ), ), ] diff --git a/marketing/migrations/0010_auto_20190805_1038.py b/marketing/migrations/0010_auto_20190805_1038.py index f124a31..f98a6d6 100644 --- a/marketing/migrations/0010_auto_20190805_1038.py +++ b/marketing/migrations/0010_auto_20190805_1038.py @@ -9,37 +9,57 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('marketing', '0009_auto_20190618_1144'), + ("marketing", "0009_auto_20190618_1144"), ] operations = [ migrations.AlterModelOptions( - name='contactlist', - options={'ordering': ('-created_on',)}, - ), - migrations.AlterModelOptions( - name='link', - options={'ordering': ('id',)}, + name="contactlist", options={"ordering": ("-created_on",)}, ), + migrations.AlterModelOptions(name="link", options={"ordering": ("id",)},), migrations.AlterField( - model_name='campaignlinkclick', - name='campaign', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='campaign_link_click', to='marketing.Campaign'), + model_name="campaignlinkclick", + name="campaign", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="campaign_link_click", + to="marketing.Campaign", + ), ), migrations.AlterField( - model_name='campaignlog', - name='campaign', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='campaign_log_contacts', to='marketing.Campaign'), + model_name="campaignlog", + name="campaign", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="campaign_log_contacts", + to="marketing.Campaign", + ), ), migrations.CreateModel( - name='ContactEmailCampaign', + name="ContactEmailCampaign", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=500)), - ('email', models.EmailField(max_length=254)), - ('last_name', models.CharField(blank=True, max_length=500, null=True)), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='marketing_contacts_emails_campaign_created_by', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=500)), + ("email", models.EmailField(max_length=254)), + ("last_name", models.CharField(blank=True, max_length=500, null=True)), + ("created_on", models.DateTimeField(auto_now_add=True)), + ( + "created_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="marketing_contacts_emails_campaign_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/marketing/migrations/0011_auto_20190904_1143.py b/marketing/migrations/0011_auto_20190904_1143.py index 2df3157..d7f7b29 100644 --- a/marketing/migrations/0011_auto_20190904_1143.py +++ b/marketing/migrations/0011_auto_20190904_1143.py @@ -7,20 +7,43 @@ class Migration(migrations.Migration): dependencies = [ - ('marketing', '0010_auto_20190805_1038'), + ("marketing", "0010_auto_20190805_1038"), ] operations = [ migrations.AlterModelOptions( - name='contactemailcampaign', - options={'ordering': ('created_on',)}, + name="contactemailcampaign", options={"ordering": ("created_on",)}, ), migrations.CreateModel( - name='DuplicateContacts', + name="DuplicateContacts", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('contact_list', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='duplicate_contact_contact_list', to='marketing.ContactList')), - ('contacts', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='duplicate_contact', to='marketing.Contact')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "contact_list", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="duplicate_contact_contact_list", + to="marketing.ContactList", + ), + ), + ( + "contacts", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="duplicate_contact", + to="marketing.Contact", + ), + ), ], ), ] diff --git a/marketing/migrations/0012_auto_20190909_1621.py b/marketing/migrations/0012_auto_20190909_1621.py index fa857f2..05f0365 100644 --- a/marketing/migrations/0012_auto_20190909_1621.py +++ b/marketing/migrations/0012_auto_20190909_1621.py @@ -6,12 +6,11 @@ class Migration(migrations.Migration): dependencies = [ - ('marketing', '0011_auto_20190904_1143'), + ("marketing", "0011_auto_20190904_1143"), ] operations = [ migrations.AlterModelOptions( - name='duplicatecontacts', - options={'ordering': ('id',)}, + name="duplicatecontacts", options={"ordering": ("id",)}, ), ] diff --git a/marketing/migrations/0013_blockeddomain_blockedemail.py b/marketing/migrations/0013_blockeddomain_blockedemail.py index 7b59a62..765172c 100644 --- a/marketing/migrations/0013_blockeddomain_blockedemail.py +++ b/marketing/migrations/0013_blockeddomain_blockedemail.py @@ -9,32 +9,60 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('marketing', '0012_auto_20190909_1621'), + ("marketing", "0012_auto_20190909_1621"), ] operations = [ migrations.CreateModel( - name='BlockedEmail', + name="BlockedEmail", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('email', models.EmailField(max_length=254)), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("email", models.EmailField(max_length=254)), + ("created_on", models.DateTimeField(auto_now_add=True)), + ( + "created_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), ], - options={ - 'ordering': ('created_on',), - }, + options={"ordering": ("created_on",),}, ), migrations.CreateModel( - name='BlockedDomain', + name="BlockedDomain", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('domain', models.CharField(max_length=200)), - ('created_on', models.DateTimeField(auto_now_add=True)), - ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("domain", models.CharField(max_length=200)), + ("created_on", models.DateTimeField(auto_now_add=True)), + ( + "created_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), ], - options={ - 'ordering': ('created_on',), - }, + options={"ordering": ("created_on",),}, ), ] diff --git a/marketing/models.py b/marketing/models.py index 2b212ff..581f01a 100644 --- a/marketing/models.py +++ b/marketing/models.py @@ -14,11 +14,10 @@ class Tag(models.Model): name = models.CharField(max_length=500) - color = models.CharField(max_length=20, - default="#999999", verbose_name=_("color")) - created_by = models.ForeignKey(User, - related_name="marketing_tags", - null=True, on_delete=models.SET_NULL) + color = models.CharField(max_length=20, default="#999999", verbose_name=_("color")) + created_by = models.ForeignKey( + User, related_name="marketing_tags", null=True, on_delete=models.SET_NULL + ) created_on = models.DateTimeField(auto_now_add=True) @property @@ -28,8 +27,11 @@ def created_by_user(self): class EmailTemplate(models.Model): created_by = models.ForeignKey( - User, related_name="marketing_emailtemplates", - null=True, on_delete=models.SET_NULL) + User, + related_name="marketing_emailtemplates", + null=True, + on_delete=models.SET_NULL, + ) created_on = models.DateTimeField(auto_now_add=True) updated_on = models.DateTimeField(auto_now=True) title = models.CharField(max_length=5000) @@ -37,7 +39,9 @@ class EmailTemplate(models.Model): html = models.TextField() class Meta: - ordering = ['id', ] + ordering = [ + "id", + ] @property def created_by_user(self): @@ -50,18 +54,17 @@ def created_on_arrow(self): class ContactList(models.Model): created_by = models.ForeignKey( - User, related_name="marketing_contactlist", - null=True, on_delete=models.SET_NULL) + User, related_name="marketing_contactlist", null=True, on_delete=models.SET_NULL + ) created_on = models.DateTimeField(auto_now_add=True) updated_on = models.DateTimeField(auto_now=True) name = models.CharField(max_length=500) tags = models.ManyToManyField(Tag) # is_public = models.BooleanField(default=False) - visible_to = models.ManyToManyField( - User, related_name="contact_lists_visible_to") + visible_to = models.ManyToManyField(User, related_name="contact_lists_visible_to") class Meta: - ordering = ('-created_on',) + ordering = ("-created_on",) @property def created_by_user(self): @@ -69,7 +72,7 @@ def created_by_user(self): @property def created_on_format(self): - return self.created_on.strftime('%b %d, %Y %I:%M %p') + return self.created_on.strftime("%b %d, %Y %I:%M %p") # @property # def created_on_since(self): @@ -120,20 +123,24 @@ def updated_on_arrow(self): class Contact(models.Model): phone_regex = RegexValidator( - regex=r'^\+?1?\d{9,15}$', + regex=r"^\+?1?\d{9,15}$", message="Phone number must be entered in the format: '+999999999'. \ - Up to 20 digits allowed." + Up to 20 digits allowed.", ) created_by = models.ForeignKey( - User, related_name="marketing_contacts_created_by", - null=True, on_delete=models.SET_NULL) + User, + related_name="marketing_contacts_created_by", + null=True, + on_delete=models.SET_NULL, + ) created_on = models.DateTimeField(auto_now_add=True) updated_on = models.DateTimeField(auto_now=True) contact_list = models.ManyToManyField(ContactList, related_name="contacts") name = models.CharField(max_length=500) email = models.EmailField() contact_number = models.CharField( - validators=[phone_regex], max_length=20, blank=True, null=True) + validators=[phone_regex], max_length=20, blank=True, null=True + ) is_unsubscribed = models.BooleanField(default=False) is_bounced = models.BooleanField(default=False) company_name = models.CharField(max_length=500, null=True, blank=True) @@ -150,25 +157,30 @@ def created_on_arrow(self): return arrow.get(self.created_on).humanize() class Meta: - ordering = ['id', ] + ordering = [ + "id", + ] class FailedContact(models.Model): phone_regex = RegexValidator( - regex=r'^\+?1?\d{9,15}$', + regex=r"^\+?1?\d{9,15}$", message="Phone number must be entered in the format: '+999999999'.\ - Up to 20 digits allowed." + Up to 20 digits allowed.", ) created_by = models.ForeignKey( - User, related_name="marketing_failed_contacts_created_by", null=True, - on_delete=models.SET_NULL) + User, + related_name="marketing_failed_contacts_created_by", + null=True, + on_delete=models.SET_NULL, + ) created_on = models.DateTimeField(auto_now_add=True) - contact_list = models.ManyToManyField( - ContactList, related_name="failed_contacts") + contact_list = models.ManyToManyField(ContactList, related_name="failed_contacts") name = models.CharField(max_length=500, null=True, blank=True) email = models.EmailField(null=True, blank=True) contact_number = models.CharField( - validators=[phone_regex], max_length=20, blank=True, null=True) + validators=[phone_regex], max_length=20, blank=True, null=True + ) company_name = models.CharField(max_length=500, null=True, blank=True) last_name = models.CharField(max_length=500, null=True, blank=True) city = models.CharField(max_length=500, null=True, blank=True) @@ -184,7 +196,7 @@ def created_on_arrow(self): def get_campaign_attachment_path(self, filename): - file_split = filename.split('.') + file_split = filename.split(".") file_extension = file_split[-1] path = "%s_%s" % (file_split[0], str(datetime.now())) return "campaigns/attachment/" + slugify(path) + "." + file_extension @@ -192,43 +204,48 @@ def get_campaign_attachment_path(self, filename): class Campaign(models.Model): STATUS_CHOICES = ( - ('Scheduled', 'Scheduled'), - ('Cancelled', 'Cancelled'), - ('Sending', 'Sending'), - ('Preparing', 'Preparing'), - ('Sent', 'Sent'), + ("Scheduled", "Scheduled"), + ("Cancelled", "Cancelled"), + ("Sending", "Sending"), + ("Preparing", "Preparing"), + ("Sent", "Sent"), ) title = models.CharField(max_length=5000) created_by = models.ForeignKey( - User, related_name="marketing_campaigns_created_by", - null=True, on_delete=models.SET_NULL) + User, + related_name="marketing_campaigns_created_by", + null=True, + on_delete=models.SET_NULL, + ) created_on = models.DateTimeField(auto_now_add=True) updated_on = models.DateTimeField(auto_now=True) - contact_lists = models.ManyToManyField( - ContactList, related_name="campaigns") + contact_lists = models.ManyToManyField(ContactList, related_name="campaigns") email_template = models.ForeignKey( - EmailTemplate, blank=True, null=True, on_delete=models.SET_NULL) + EmailTemplate, blank=True, null=True, on_delete=models.SET_NULL + ) schedule_date_time = models.DateTimeField(blank=True, null=True) - timezone = models.CharField(max_length=100, default='UTC') + timezone = models.CharField(max_length=100, default="UTC") reply_to_email = models.EmailField(blank=True, null=True) subject = models.CharField(max_length=5000) html = models.TextField() html_processed = models.TextField(default="", blank=True) from_email = models.EmailField(blank=True, null=True) from_name = models.EmailField(blank=True, null=True) - sent = models.IntegerField(default='0', blank=True) - opens = models.IntegerField(default='0', blank=True) - opens_unique = models.IntegerField(default='0', blank=True) - bounced = models.IntegerField(default='0') + sent = models.IntegerField(default="0", blank=True) + opens = models.IntegerField(default="0", blank=True) + opens_unique = models.IntegerField(default="0", blank=True) + bounced = models.IntegerField(default="0") tags = models.ManyToManyField(Tag) status = models.CharField( - default="Preparing", choices=STATUS_CHOICES, max_length=20) + default="Preparing", choices=STATUS_CHOICES, max_length=20 + ) attachment = models.FileField( - max_length=1000, upload_to=get_campaign_attachment_path, blank=True, null=True) + max_length=1000, upload_to=get_campaign_attachment_path, blank=True, null=True + ) class Meta: - ordering = ('-created_on', ) + ordering = ("-created_on",) # @property # def no_of_unsubscribers(self): @@ -244,7 +261,7 @@ class Meta: @property def no_of_clicks(self): - clicks = self.marketing_links.aggregate(Sum('clicks'))['clicks__sum'] + clicks = self.marketing_links.aggregate(Sum("clicks"))["clicks__sum"] return clicks # @property @@ -260,12 +277,12 @@ def no_of_clicks(self): def sent_on_format(self): if self.schedule_date_time: c_schedule_date_time = convert_to_custom_timezone( - self.schedule_date_time, self.timezone) - return c_schedule_date_time.strftime('%b %d, %Y %I:%M %p') + self.schedule_date_time, self.timezone + ) + return c_schedule_date_time.strftime("%b %d, %Y %I:%M %p") else: - c_created_on = convert_to_custom_timezone( - self.created_on, self.timezone) - return c_created_on.strftime('%b %d, %Y %I:%M %p') + c_created_on = convert_to_custom_timezone(self.created_on, self.timezone) + return c_created_on.strftime("%b %d, %Y %I:%M %p") @property def get_all_emails_count(self): @@ -278,7 +295,8 @@ def get_all_email_bounces_count(self): # return self.contact_lists.filter(contacts__is_bounced=True # ).exclude(contacts__email=None).values_list('contacts__email').count() email_count = CampaignLog.objects.filter( - campaign=self, contact__is_bounced=True).count() + campaign=self, contact__is_bounced=True + ).count() return email_count @property @@ -286,17 +304,23 @@ def get_all_emails_unsubscribed_count(self): # return self.contact_lists.filter(contacts__is_unsubscribed=True # ).exclude(contacts__email=None).values_list('contacts__email').count() email_count = CampaignLog.objects.filter( - campaign=self, contact__is_unsubscribed=True).count() + campaign=self, contact__is_unsubscribed=True + ).count() return email_count @property def get_all_emails_subscribed_count(self): - return self.get_all_emails_count - self.get_all_email_bounces_count - self.get_all_emails_unsubscribed_count + return ( + self.get_all_emails_count + - self.get_all_email_bounces_count + - self.get_all_emails_unsubscribed_count + ) @property def get_all_emails_contacts_opened(self): - contact_ids = CampaignOpen.objects.filter( - campaign=self).values_list('contact_id', flat=True) + contact_ids = CampaignOpen.objects.filter(campaign=self).values_list( + "contact_id", flat=True + ) # opened_contacts = Contact.objects.filter(id__in=contact_ids) # return opened_contacts return contact_ids.count() @@ -305,12 +329,12 @@ def get_all_emails_contacts_opened(self): def sent_on_arrow(self): if self.schedule_date_time: c_schedule_date_time = convert_to_custom_timezone( - self.schedule_date_time, self.timezone) + self.schedule_date_time, self.timezone + ) # return c_schedule_date_time.strftime('%b %d, %Y %I:%M %p') return arrow.get(c_schedule_date_time).humanize() else: - c_created_on = convert_to_custom_timezone( - self.created_on, self.timezone) + c_created_on = convert_to_custom_timezone(self.created_on, self.timezone) # return c_created_on.strftime('%b %d, %Y %I:%M %p') return arrow.get(self.created_on).humanize() @@ -329,53 +353,67 @@ def comment_attachments_delete(sender, instance, **kwargs): class Link(models.Model): campaign = models.ForeignKey( - Campaign, related_name="marketing_links", on_delete=models.CASCADE) + Campaign, related_name="marketing_links", on_delete=models.CASCADE + ) original = models.URLField(max_length=2100) - clicks = models.IntegerField(default='0') - unique = models.IntegerField(default='0') + clicks = models.IntegerField(default="0") + unique = models.IntegerField(default="0") class Meta: - ordering = ('id',) + ordering = ("id",) class CampaignLog(models.Model): created_on = models.DateTimeField(auto_now_add=True) campaign = models.ForeignKey( - Campaign, related_name='campaign_log_contacts', on_delete=models.CASCADE) + Campaign, related_name="campaign_log_contacts", on_delete=models.CASCADE + ) contact = models.ForeignKey( - Contact, related_name="marketing_campaign_logs", - null=True, on_delete=models.SET_NULL) + Contact, + related_name="marketing_campaign_logs", + null=True, + on_delete=models.SET_NULL, + ) message_id = models.CharField(max_length=1000, null=True, blank=True) class CampaignLinkClick(models.Model): campaign = models.ForeignKey( - Campaign, on_delete=models.CASCADE, related_name="campaign_link_click") - link = models.ForeignKey( - Link, blank=True, null=True, on_delete=models.CASCADE) + Campaign, on_delete=models.CASCADE, related_name="campaign_link_click" + ) + link = models.ForeignKey(Link, blank=True, null=True, on_delete=models.CASCADE) ip_address = models.GenericIPAddressField() created_on = models.DateTimeField(auto_now_add=True) user_agent = models.CharField(max_length=2000, blank=True, null=True) contact = models.ForeignKey( - Contact, blank=True, null=True, on_delete=models.CASCADE) + Contact, blank=True, null=True, on_delete=models.CASCADE + ) class CampaignOpen(models.Model): campaign = models.ForeignKey( - Campaign, on_delete=models.CASCADE, related_name='campaign_open') + Campaign, on_delete=models.CASCADE, related_name="campaign_open" + ) ip_address = models.GenericIPAddressField() created_on = models.DateTimeField(auto_now_add=True) user_agent = models.CharField(max_length=2000, blank=True, null=True) contact = models.ForeignKey( - Contact, blank=True, null=True, on_delete=models.CASCADE, related_name='contact_campaign_open') + Contact, + blank=True, + null=True, + on_delete=models.CASCADE, + related_name="contact_campaign_open", + ) class CampaignCompleted(models.Model): """ This Model Is Used To Check If The Scheduled Later Emails Have Been Sent related name : campaign_is_completed """ + campaign = models.OneToOneField( - Campaign, on_delete=models.CASCADE, related_name='campaign_is_completed') + Campaign, on_delete=models.CASCADE, related_name="campaign_is_completed" + ) is_completed = models.BooleanField(default=False) @@ -383,10 +421,13 @@ class ContactUnsubscribedCampaign(models.Model): """ This Model Is Used To Check If The Contact has Unsubscribed To a Particular Campaign related name : contact_is_unsubscribed """ + campaigns = models.ForeignKey( - Campaign, on_delete=models.CASCADE, related_name='campaign_is_unsubscribed') + Campaign, on_delete=models.CASCADE, related_name="campaign_is_unsubscribed" + ) contacts = models.ForeignKey( - Contact, on_delete=models.Case, related_name='contact_is_unsubscribed') + Contact, on_delete=models.Case, related_name="contact_is_unsubscribed" + ) is_unsubscribed = models.BooleanField(default=False) @@ -394,42 +435,54 @@ class ContactEmailCampaign(models.Model): """ send all campaign emails to this contact """ + name = models.CharField(max_length=500) email = models.EmailField() last_name = models.CharField(max_length=500, null=True, blank=True) created_by = models.ForeignKey( - User, related_name="marketing_contacts_emails_campaign_created_by", - null=True, on_delete=models.SET_NULL) + User, + related_name="marketing_contacts_emails_campaign_created_by", + null=True, + on_delete=models.SET_NULL, + ) created_on = models.DateTimeField(auto_now_add=True) def created_on_arrow(self): return arrow.get(self.created_on).humanize() class Meta: - ordering = ('created_on',) + ordering = ("created_on",) class DuplicateContacts(models.Model): """ this model is used to store duplicate contacts """ + contacts = models.ForeignKey( - Contact, related_name='duplicate_contact', on_delete=models.SET_NULL, null=True) + Contact, related_name="duplicate_contact", on_delete=models.SET_NULL, null=True + ) contact_list = models.ForeignKey( - ContactList, related_name='duplicate_contact_contact_list', on_delete=models.SET_NULL, null=True) + ContactList, + related_name="duplicate_contact_contact_list", + on_delete=models.SET_NULL, + null=True, + ) class Meta: - ordering = ('id', ) + ordering = ("id",) class BlockedDomain(models.Model): """ this model is used to block the domain """ + domain = models.CharField(max_length=200) created_on = models.DateTimeField(auto_now_add=True) created_by = models.ForeignKey( - User, on_delete=models.SET_NULL, null=True, blank=True) + User, on_delete=models.SET_NULL, null=True, blank=True + ) def __str__(self): return self.domain @@ -438,22 +491,25 @@ def created_on_arrow(self): return arrow.get(self.created_on).humanize() class Meta: - ordering = ('created_on',) + ordering = ("created_on",) class BlockedEmail(models.Model): """ this model is used to block the email """ + email = models.EmailField() created_on = models.DateTimeField(auto_now_add=True) created_by = models.ForeignKey( - User, on_delete=models.SET_NULL, null=True, blank=True) + User, on_delete=models.SET_NULL, null=True, blank=True + ) def __str__(self): return self.email def created_on_arrow(self): return arrow.get(self.created_on).humanize() + class Meta: - ordering = ('created_on',) + ordering = ("created_on",) diff --git a/marketing/search_backends.py b/marketing/search_backends.py index b95ef64..b93949e 100644 --- a/marketing/search_backends.py +++ b/marketing/search_backends.py @@ -7,14 +7,14 @@ class CustomElasticsearchSearchBackend(ElasticsearchSearchBackend): - def __init__(self, connection_alias, **connection_options): super(CustomElasticsearchSearchBackend, self).__init__( - connection_alias, **connection_options) + connection_alias, **connection_options + ) - setattr(self, 'DEFAULT_SETTINGS', settings.ELASTICSEARCH_INDEX_SETTINGS) + setattr(self, "DEFAULT_SETTINGS", settings.ELASTICSEARCH_INDEX_SETTINGS) class CustomElasticsearchSearchEngine(ElasticsearchSearchEngine): - backend = CustomElasticsearchSearchBackend \ No newline at end of file + backend = CustomElasticsearchSearchBackend diff --git a/marketing/search_indexes.py b/marketing/search_indexes.py index 4f595b9..78e7426 100644 --- a/marketing/search_indexes.py +++ b/marketing/search_indexes.py @@ -4,15 +4,16 @@ class MarketingContactIndex(indexes.SearchIndex, indexes.Indexable): text = indexes.CharField( - document=True, use_template=True, template_name='search/contact_emails.txt') + document=True, use_template=True, template_name="search/contact_emails.txt" + ) - id = indexes.CharField(model_attr='id') - email = indexes.EdgeNgramField(model_attr='email') + id = indexes.CharField(model_attr="id") + email = indexes.EdgeNgramField(model_attr="email") email_domain = indexes.EdgeNgramField() - name = indexes.CharField(model_attr='name') + name = indexes.CharField(model_attr="name") company_name = indexes.CharField() - created_on = indexes.CharField(model_attr='created_on') - created_on_arrow = indexes.CharField(model_attr='created_on_arrow') + created_on = indexes.CharField(model_attr="created_on") + created_on_arrow = indexes.CharField(model_attr="created_on_arrow") created_by = indexes.CharField() created_by_id = indexes.CharField() contact_lists = indexes.MultiValueField() @@ -24,25 +25,32 @@ def get_model(self): return Contact def prepare_email_domain(self, obj): - return obj.email.split('@')[-1] + return obj.email.split("@")[-1] def prepare_contact_lists(self, obj): - return [[contact_list.id, contact_list.name if contact_list.name else ''] for contact_list in obj.contact_list.all()] + return [ + [contact_list.id, contact_list.name if contact_list.name else ""] + for contact_list in obj.contact_list.all() + ] def prepare_contact_lists_id(self, obj): - return [contact_list.id for contact_list in obj.contact_list.all().order_by('id')] + return [ + contact_list.id for contact_list in obj.contact_list.all().order_by("id") + ] def prepare_contact_lists_name(self, obj): - return [contact_list.name for contact_list in obj.contact_list.all().order_by('id')] + return [ + contact_list.name for contact_list in obj.contact_list.all().order_by("id") + ] def prepare_company_name(self, obj): - return obj.company_name if obj.company_name else '' + return obj.company_name if obj.company_name else "" def prepare_created_by(self, obj): - return obj.created_by.email if obj.created_by else '' + return obj.created_by.email if obj.created_by else "" def prepare_created_by_id(self, obj): - return obj.created_by.id if obj.created_by else '' + return obj.created_by.id if obj.created_by else "" def prepare_is_bounced(self, obj): return obj.is_bounced @@ -51,48 +59,56 @@ def index_queryset(self, using=None): return self.get_model().objects.all() - class MarketingFailedContactIndex(indexes.SearchIndex, indexes.Indexable): text = indexes.CharField( - document=True, use_template=True, template_name='search/failed_contact_emails.txt') + document=True, + use_template=True, + template_name="search/failed_contact_emails.txt", + ) - id = indexes.CharField(model_attr='id') - email = indexes.EdgeNgramField(model_attr='email') + id = indexes.CharField(model_attr="id") + email = indexes.EdgeNgramField(model_attr="email") email = indexes.EdgeNgramField() - name = indexes.CharField(model_attr='name') + name = indexes.CharField(model_attr="name") company_name = indexes.CharField() - created_on = indexes.CharField(model_attr='created_on') - created_on_arrow = indexes.CharField(model_attr='created_on_arrow') + created_on = indexes.CharField(model_attr="created_on") + created_on_arrow = indexes.CharField(model_attr="created_on_arrow") created_by = indexes.CharField() created_by_id = indexes.CharField() contact_lists = indexes.MultiValueField() contact_lists_id = indexes.MultiValueField() contact_lists_name = indexes.MultiValueField() - def get_model(self): return FailedContact def prepare_email_domain(self, obj): - return obj.email.split('@')[-1] + return obj.email.split("@")[-1] def prepare_contact_lists(self, obj): - return [[contact_list.id, contact_list.name if contact_list.name else ''] for contact_list in obj.contact_list.all()] + return [ + [contact_list.id, contact_list.name if contact_list.name else ""] + for contact_list in obj.contact_list.all() + ] def prepare_contact_lists_id(self, obj): - return [contact_list.id for contact_list in obj.contact_list.all().order_by('id')] + return [ + contact_list.id for contact_list in obj.contact_list.all().order_by("id") + ] def prepare_contact_lists_name(self, obj): - return [contact_list.name for contact_list in obj.contact_list.all().order_by('id')] + return [ + contact_list.name for contact_list in obj.contact_list.all().order_by("id") + ] def prepare_company_name(self, obj): - return obj.company_name if obj.company_name else '' + return obj.company_name if obj.company_name else "" def prepare_created_by(self, obj): - return obj.created_by.email if obj.created_by else '' + return obj.created_by.email if obj.created_by else "" def prepare_created_by_id(self, obj): - return obj.created_by.id if obj.created_by else '' + return obj.created_by.id if obj.created_by else "" def index_queryset(self, using=None): return self.get_model().objects.all() diff --git a/marketing/tasks.py b/marketing/tasks.py index 03a8dd3..9e8994a 100644 --- a/marketing/tasks.py +++ b/marketing/tasks.py @@ -12,10 +12,18 @@ from django.template import Context, Template from common.utils import convert_to_custom_timezone -from marketing.models import (BlockedDomain, BlockedEmail, Campaign, - CampaignCompleted, CampaignLog, Contact, - ContactEmailCampaign, ContactList, - DuplicateContacts, FailedContact) +from marketing.models import ( + BlockedDomain, + BlockedEmail, + Campaign, + CampaignCompleted, + CampaignLog, + Contact, + ContactEmailCampaign, + ContactList, + DuplicateContacts, + FailedContact, +) @task @@ -36,60 +44,55 @@ def campaign_click(request): @task def upload_csv_file(data, invalid_data, user, contact_lists): for each in data: - contact = Contact.objects.filter(email=each['email']).first() + contact = Contact.objects.filter(email=each["email"]).first() if not contact: contact = Contact.objects.create( - email=each['email'], created_by_id=user, - name=each['first name']) - if each.get('company name', None): - contact.company_name = each['company name'] - if each.get('last name', None): - contact.last_name = each['last name'] - if each.get('city', None): - contact.city = each['city'] + email=each["email"], created_by_id=user, name=each["first name"] + ) + if each.get("company name", None): + contact.company_name = each["company name"] + if each.get("last name", None): + contact.last_name = each["last name"] + if each.get("city", None): + contact.city = each["city"] if each.get("state", None): - contact.state = each['state'] + contact.state = each["state"] contact.save() else: if not DuplicateContacts.objects.filter( contacts=contact, - contact_list=ContactList.objects.get(id=int(contact_lists[0]))).exists(): + contact_list=ContactList.objects.get(id=int(contact_lists[0])), + ).exists(): DuplicateContacts.objects.create( contacts=contact, - contact_list=ContactList.objects.get(id=int(contact_lists[0]))) + contact_list=ContactList.objects.get(id=int(contact_lists[0])), + ) for contact_list in contact_lists: - contact.contact_list.add( - ContactList.objects.get(id=int(contact_list))) + contact.contact_list.add(ContactList.objects.get(id=int(contact_list))) for each in invalid_data: - contact = FailedContact.objects.filter(email=each['email']).first() + contact = FailedContact.objects.filter(email=each["email"]).first() if not contact: contact = FailedContact.objects.create( - email=each['email'], created_by_id=user, - name=each['first name']) - if each.get('company name', None): - contact.company_name = each['company name'] - if each.get('last name', None): - contact.last_name = each['last name'] - if each.get('city', None): - contact.city = each['city'] + email=each["email"], created_by_id=user, name=each["first name"] + ) + if each.get("company name", None): + contact.company_name = each["company name"] + if each.get("last name", None): + contact.last_name = each["last name"] + if each.get("city", None): + contact.city = each["city"] if each.get("state", None): - contact.state = each['state'] + contact.state = each["state"] contact.save() for contact_list in contact_lists: - contact.contact_list.add( - ContactList.objects.get(id=int(contact_list))) - - -def send_campaign_mail(subject, content, from_email, to_email, bcc, reply_to, attachments): - msg = EmailMessage( - subject, - content, - from_email, - to_email, - bcc, - reply_to=reply_to, - ) + contact.contact_list.add(ContactList.objects.get(id=int(contact_list))) + + +def send_campaign_mail( + subject, content, from_email, to_email, bcc, reply_to, attachments +): + msg = EmailMessage(subject, content, from_email, to_email, bcc, reply_to=reply_to,) for attachment in attachments: msg.attach(*attachment) msg.content_subtype = "html" @@ -100,84 +103,120 @@ def send_campaign_mail(subject, content, from_email, to_email, bcc, reply_to, at def get_campaign_message_id(campaign): hash_ = hashlib.md5() hash_.update( - str(str(campaign.id) + str(campaign.campaign.created_by.id)).encode('utf-8') + - str(datetime.datetime.now()).encode('utf-8') + str(str(campaign.id) + str(campaign.campaign.created_by.id)).encode("utf-8") + + str(datetime.datetime.now()).encode("utf-8") ) file_hash = hash_.hexdigest() return file_hash @task -def run_campaign(campaign, domain='demo.django-crm.io', protocol='https'): - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) +def run_campaign(campaign, domain="demo.django-crm.io", protocol="https"): + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) try: campaign = Campaign.objects.get(id=campaign) attachments = [] if campaign.attachment: file_path = campaign.attachment.path file_name = file_path.split("/")[-1] - content = open(file_path, 'rb').read() + content = open(file_path, "rb").read() mime = MimeTypes() mime_type = mime.guess_type(file_path) attachments.append((file_name, content, mime_type[0])) subject = campaign.subject contacts = Contact.objects.filter( - contact_list__in=[each_list for each_list in campaign.contact_lists.all()]) + contact_list__in=[each_list for each_list in campaign.contact_lists.all()] + ) default_html = campaign.html_processed for each_contact in contacts: html = default_html - campaign_log = CampaignLog.objects.create(contact=each_contact, - campaign=campaign) + campaign_log = CampaignLog.objects.create( + contact=each_contact, campaign=campaign + ) if campaign.reply_to_email: reply_to_email = campaign.reply_to_email else: message_id = get_campaign_message_id(campaign_log) campaign_log.message_id = message_id campaign_log.save() - domain_name = 'django-crm.com' + domain_name = "django-crm.com" if campaign.from_email is not None: from_email = campaign.from_email else: from_email = campaign.created_by.email - reply_to_email = str(from_email) + ' <' + \ - str(message_id + '@' + domain_name + '') + '>' + reply_to_email = ( + str(from_email) + + " <" + + str(message_id + "@" + domain_name + "") + + ">" + ) if not (each_contact.is_bounced or each_contact.is_unsubscribed): - if ((each_contact.email not in blocked_emails) and (each_contact.email.split('@')[-1] not in blocked_domains)): + if (each_contact.email not in blocked_emails) and ( + each_contact.email.split("@")[-1] not in blocked_domains + ): # domain_url = settings.URL_FOR_LINKS - domain_url = protocol + '://' + domain - img_src_url = domain_url + reverse('marketing:campaign_open', kwargs={ - 'campaign_log_id': campaign_log.id, 'email_id': each_contact.id}) + domain_url = protocol + "://" + domain + img_src_url = domain_url + reverse( + "marketing:campaign_open", + kwargs={ + "campaign_log_id": campaign_log.id, + "email_id": each_contact.id, + }, + ) # images can only be accessed over https link = 'company_logo'.format( - img_src_url=img_src_url) + img_src_url=img_src_url + ) # link = 'company_logo' unsubscribe_from_campaign_url = reverse( - 'marketing:unsubscribe_from_campaign', kwargs={'contact_id': each_contact.id, - 'campaign_id': campaign.id}) + "marketing:unsubscribe_from_campaign", + kwargs={ + "contact_id": each_contact.id, + "campaign_id": campaign.id, + }, + ) unsubscribe_from_campaign_html = "

Unsubscribe".format( - domain_url + unsubscribe_from_campaign_url) - names_dict = {'company_name': each_contact.company_name if each_contact.company_name else '', - 'last_name': each_contact.last_name if each_contact.last_name else '', - 'city': each_contact.city if each_contact.city else '', - 'state': each_contact.state if each_contact.state else '', - 'first_name': each_contact.name, - 'email': each_contact.email, 'email_id': each_contact.id, - 'name': each_contact.name + ' ' + each_contact.last_name if each_contact.last_name else '', - 'unsubscribe_from_campaign_url': unsubscribe_from_campaign_url} + domain_url + unsubscribe_from_campaign_url + ) + names_dict = { + "company_name": each_contact.company_name + if each_contact.company_name + else "", + "last_name": each_contact.last_name + if each_contact.last_name + else "", + "city": each_contact.city if each_contact.city else "", + "state": each_contact.state if each_contact.state else "", + "first_name": each_contact.name, + "email": each_contact.email, + "email_id": each_contact.id, + "name": each_contact.name + " " + each_contact.last_name + if each_contact.last_name + else "", + "unsubscribe_from_campaign_url": unsubscribe_from_campaign_url, + } html = Template(html).render(Context(names_dict)) mail_html = html + link + unsubscribe_from_campaign_html - from_email = str(campaign.from_name) + "<" + \ - str(campaign.from_email) + '>' + from_email = ( + str(campaign.from_name) + "<" + str(campaign.from_email) + ">" + ) to_email = [each_contact.email] send_campaign_mail( - subject, mail_html, from_email, to_email, [], [reply_to_email], attachments) + subject, + mail_html, + from_email, + to_email, + [], + [reply_to_email], + attachments, + ) except Exception as e: print(e) pass @@ -193,20 +232,28 @@ def run_all_campaigns(): @task def list_all_bounces_unsubscribes(): - bounces = requests.get('https://api.sendgrid.com/api/bounces.get.json?api_user=' + - settings.EMAIL_HOST_USER + '&api_key=' + settings.EMAIL_HOST_PASSWORD) + bounces = requests.get( + "https://api.sendgrid.com/api/bounces.get.json?api_user=" + + settings.EMAIL_HOST_USER + + "&api_key=" + + settings.EMAIL_HOST_PASSWORD + ) for each in bounces.json(): if type(each) == dict: - contact = Contact.objects.filter(email=each.get('email')).first() + contact = Contact.objects.filter(email=each.get("email")).first() if contact: contact.is_bounced = True contact.save() - bounces = requests.get('https://api.sendgrid.com/api/unsubscribes.get.json?api_user=' + - settings.EMAIL_HOST_USER + '&api_key=' + settings.EMAIL_HOST_PASSWORD) + bounces = requests.get( + "https://api.sendgrid.com/api/unsubscribes.get.json?api_user=" + + settings.EMAIL_HOST_USER + + "&api_key=" + + settings.EMAIL_HOST_PASSWORD + ) for each in bounces.json(): if type(each) == dict: - contact = Contact.objects.filter(email=each.get('email')).first() + contact = Contact.objects.filter(email=each.get("email")).first() if contact: contact.is_unsubscribed = True contact.save() @@ -215,28 +262,29 @@ def list_all_bounces_unsubscribes(): @task def send_scheduled_campaigns(): from datetime import datetime + campaigns = Campaign.objects.filter(schedule_date_time__isnull=False) for each in campaigns: - completed = CampaignCompleted.objects.filter( - is_completed=True).values_list('campaign_id', flat=True) + completed = CampaignCompleted.objects.filter(is_completed=True).values_list( + "campaign_id", flat=True + ) if each.id not in completed: schedule_date_time = each.schedule_date_time - sent_time = datetime.now().strftime('%Y-%m-%d %H:%M') - sent_time = datetime.strptime(sent_time, '%Y-%m-%d %H:%M') + sent_time = datetime.now().strftime("%Y-%m-%d %H:%M") + sent_time = datetime.strptime(sent_time, "%Y-%m-%d %H:%M") local_tz = pytz.timezone(settings.TIME_ZONE) sent_time = local_tz.localize(sent_time) sent_time = convert_to_custom_timezone( - sent_time, each.timezone, to_utc=True) + sent_time, each.timezone, to_utc=True + ) - if ( - str(each.schedule_date_time.date()) == str(sent_time.date()) and - str(schedule_date_time.hour) == str(sent_time.hour) - ): + if str(each.schedule_date_time.date()) == str(sent_time.date()) and str( + schedule_date_time.hour + ) == str(sent_time.hour): run_campaign.delay(each.id) - CampaignCompleted.objects.create( - campaign=each, is_completed=True) + CampaignCompleted.objects.create(campaign=each, is_completed=True) @task @@ -254,38 +302,46 @@ def delete_multiple_contacts_tasks(contact_list_id, bounced=True): @task -def send_campaign_email_to_admin_contact(campaign, domain='demo.django-crm.io', protocol='https'): +def send_campaign_email_to_admin_contact( + campaign, domain="demo.django-crm.io", protocol="https" +): try: campaign = Campaign.objects.get(id=campaign) attachments = [] if campaign.attachment: file_path = campaign.attachment.path file_name = file_path.split("/")[-1] - content = open(file_path, 'rb').read() + content = open(file_path, "rb").read() mime = MimeTypes() mime_type = mime.guess_type(file_path) attachments.append((file_name, content, mime_type[0])) subject = campaign.subject contacts = ContactEmailCampaign.objects.all() default_html = campaign.html_processed - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) for each_contact in contacts: - if ((each_contact.email not in blocked_emails) and (each_contact.email.split('@')[-1] not in blocked_domains)): + if (each_contact.email not in blocked_emails) and ( + each_contact.email.split("@")[-1] not in blocked_domains + ): html = default_html if campaign.reply_to_email: reply_to_email = campaign.reply_to_email else: - domain_name = 'django-crm.com' + domain_name = "django-crm.com" if campaign.from_email is not None: from_email = campaign.from_email else: from_email = campaign.created_by.email - reply_to_email = str(from_email) + ' <' + \ - str(settings.EMAIL_HOST_USER + '@' + domain_name + '') + '>' + reply_to_email = ( + str(from_email) + + " <" + + str(settings.EMAIL_HOST_USER + "@" + domain_name + "") + + ">" + ) # domain_url = settings.URL_FOR_LINKS - domain_url = protocol + '://' + domain + domain_url = protocol + "://" + domain # img_src_url = domain_url + reverse('marketing:campaign_open', kwargs={ # 'campaign_log_id': campaign_log.id, 'email_id': each_contact.id}) # # images can only be accessed over https @@ -309,17 +365,26 @@ def send_campaign_email_to_admin_contact(campaign, domain='demo.django-crm.io', # } # mail_html = html + link + unsubscribe_from_campaign_html - html = Template(html).render(Context({'email_id': each_contact.id})) + html = Template(html).render(Context({"email_id": each_contact.id})) mail_html = html - from_email = str(campaign.from_name) + "<" + \ - str(campaign.from_email) + '>' + from_email = ( + str(campaign.from_name) + "<" + str(campaign.from_email) + ">" + ) to_email = [each_contact.email] send_campaign_mail( - subject, mail_html, from_email, to_email, [], [reply_to_email], attachments) + subject, + mail_html, + from_email, + to_email, + [], + [reply_to_email], + attachments, + ) except Exception as e: print(e) pass + @task def update_elastic_search_index(): - call_command('update_index --age=1', interactive=False) \ No newline at end of file + call_command("update_index --age=1", interactive=False) diff --git a/marketing/templatetags/digg_paginator.py b/marketing/templatetags/digg_paginator.py index 72a8683..684bfe9 100644 --- a/marketing/templatetags/digg_paginator.py +++ b/marketing/templatetags/digg_paginator.py @@ -8,17 +8,17 @@ ADJACENT_PAGES = 2 -def digg_paginator(context): # pragma: no cover - ''' +def digg_paginator(context): # pragma: no cover + """ To be used in conjunction with the object_list generic view. Adds pagination context variables for use in displaying leading, adjacent and trailing page links in addition to those created by the object_list generic view. - ''' + """ - paginator = context['paginator'] - page_obj = context['page_obj'] + paginator = context["paginator"] + page_obj = context["page_obj"] pages = paginator.num_pages page = page_obj.number in_leading_range = in_trailing_range = False @@ -30,31 +30,41 @@ def digg_paginator(context): # pragma: no cover in_leading_range = True page_range = [n for n in range(1, LEADING_PAGE_RANGE_DISPLAYED + 1)] pages_outside_leading_range = [ - n + pages for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)] + n + pages for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1) + ] elif page > pages - TRAILING_PAGE_RANGE: in_trailing_range = True - page_range = [n for n in range( - pages - TRAILING_PAGE_RANGE_DISPLAYED + 1, pages + 1) if n > 0 and n <= pages] + page_range = [ + n + for n in range(pages - TRAILING_PAGE_RANGE_DISPLAYED + 1, pages + 1) + if n > 0 and n <= pages + ] pages_outside_trailing_range = [ - n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)] + n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE) + ] else: - page_range = [n for n in range( - page - ADJACENT_PAGES, page + ADJACENT_PAGES + 1) if n > 0 and n <= pages] + page_range = [ + n + for n in range(page - ADJACENT_PAGES, page + ADJACENT_PAGES + 1) + if n > 0 and n <= pages + ] pages_outside_leading_range = [ - n + pages for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)] + n + pages for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1) + ] pages_outside_trailing_range = [ - n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)] + n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE) + ] # Now try to retain GET params, except for 'page' # Add 'django.core.context_processors.request' to settings.TEMPLATE_CONTEXT_PROCESSORS beforehand - request = context['request'] + request = context["request"] params = request.GET.copy() # if 'page' in params: # del(params['page']) # get_params = params.urlencode() - if 'bounced_contacts_page' in params: - del(params['bounced_contacts_page']) + if "bounced_contacts_page" in params: + del params["bounced_contacts_page"] get_params = params.urlencode() if page_obj.has_previous(): @@ -68,20 +78,21 @@ def digg_paginator(context): # pragma: no cover next_page = pages return { - 'pages': pages, - 'bounced_contacts_page': page, - 'previous': previous_page, - 'next': next_page, - 'has_previous': page_obj.has_previous(), - 'has_next': page_obj.has_next(), - 'page_range': page_range, - 'in_leading_range': in_leading_range, - 'in_trailing_range': in_trailing_range, - 'pages_outside_leading_range': pages_outside_leading_range, - 'pages_outside_trailing_range': pages_outside_trailing_range, - 'get_params': get_params, + "pages": pages, + "bounced_contacts_page": page, + "previous": previous_page, + "next": next_page, + "has_previous": page_obj.has_previous(), + "has_next": page_obj.has_next(), + "page_range": page_range, + "in_leading_range": in_leading_range, + "in_trailing_range": in_trailing_range, + "pages_outside_leading_range": pages_outside_leading_range, + "pages_outside_trailing_range": pages_outside_trailing_range, + "get_params": get_params, } -register.inclusion_tag("contact_list_digg_paginator.html", - takes_context=True)(digg_paginator) +register.inclusion_tag("contact_list_digg_paginator.html", takes_context=True)( + digg_paginator +) diff --git a/marketing/tests_celery_tasks.py b/marketing/tests_celery_tasks.py index d6342e4..1b6c7d2 100644 --- a/marketing/tests_celery_tasks.py +++ b/marketing/tests_celery_tasks.py @@ -3,64 +3,106 @@ from django.test import TestCase from django.test.utils import override_settings -from marketing.tasks import (delete_multiple_contacts_tasks, - list_all_bounces_unsubscribes, run_all_campaigns, - run_campaign, - send_campaign_email_to_admin_contact, - send_scheduled_campaigns, upload_csv_file) +from marketing.tasks import ( + delete_multiple_contacts_tasks, + list_all_bounces_unsubscribes, + run_all_campaigns, + run_campaign, + send_campaign_email_to_admin_contact, + send_scheduled_campaigns, + upload_csv_file, +) from marketing.tests import TestMarketingModel class TestCeleryTasks(TestMarketingModel, TestCase): - - @override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - CELERY_ALWAYS_EAGER=True, - BROKER_BACKEND='memory') + @override_settings( + CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, + CELERY_ALWAYS_EAGER=True, + BROKER_BACKEND="memory", + ) def test_celery_tasks(self): task = run_campaign.apply((self.campaign.id,),) - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) self.campaign.reply_to_email = None self.campaign.save() task = run_campaign.apply((self.campaign.id,),) - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) self.campaign.schedule_date_time = datetime.now() self.campaign.save() task = run_all_campaigns.apply() - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) task = list_all_bounces_unsubscribes.apply() - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) task = send_scheduled_campaigns.apply() - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) task = delete_multiple_contacts_tasks.apply((self.contact_list.id,),) - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) task = send_campaign_email_to_admin_contact.apply((self.campaign.id,),) - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) valid_rows = [ - {'company name': 'company_name_1', 'email': 'user1@email.com', 'first name': 'first_name', - 'last name': 'last_name', 'city': 'Hyderabad', 'state': 'Telangana'}, - {'company name': 'company_name_2', 'email': 'user2@email.com', 'first name': 'first_name', - 'last name': 'last_name', 'city': 'Hyderabad', 'state': 'Telangana'}, - {'company name': 'company_name_3', 'email': 'user3@email.com', 'first name': 'first_name', - 'last name': 'last_name', 'city': 'Hyderabad', 'state': 'Telangana'}, - {'company name': 'company_name_4', 'email': 'user4@email.com', 'first name': 'first_name', - 'last name': 'last_name', 'city': 'Hyderabad', 'state': 'Telangana'} + { + "company name": "company_name_1", + "email": "user1@email.com", + "first name": "first_name", + "last name": "last_name", + "city": "Hyderabad", + "state": "Telangana", + }, + { + "company name": "company_name_2", + "email": "user2@email.com", + "first name": "first_name", + "last name": "last_name", + "city": "Hyderabad", + "state": "Telangana", + }, + { + "company name": "company_name_3", + "email": "user3@email.com", + "first name": "first_name", + "last name": "last_name", + "city": "Hyderabad", + "state": "Telangana", + }, + { + "company name": "company_name_4", + "email": "user4@email.com", + "first name": "first_name", + "last name": "last_name", + "city": "Hyderabad", + "state": "Telangana", + }, ] invalid_rows = [ - {'company name': 'company_name_1', 'email': 'useremail.com', 'first name': 'first_name', - 'last name': 'last_name', 'city': 'Hyderabad', 'state': 'Telangana'}, - {'company name': 'company_name_2', 'email': 'user2@email', 'first name': 'first_name', - 'last name': 'last_name', 'city': 'Hyderabad', 'state': 'Telangana'}, + { + "company name": "company_name_1", + "email": "useremail.com", + "first name": "first_name", + "last name": "last_name", + "city": "Hyderabad", + "state": "Telangana", + }, + { + "company name": "company_name_2", + "email": "user2@email", + "first name": "first_name", + "last name": "last_name", + "city": "Hyderabad", + "state": "Telangana", + }, ] task = upload_csv_file.apply( - (valid_rows, invalid_rows, self.user.id, [self.contact_list.id, ],),) - self.assertEqual('SUCCESS', task.state) + (valid_rows, invalid_rows, self.user.id, [self.contact_list.id,],), + ) + self.assertEqual("SUCCESS", task.state) diff --git a/marketing/urls.py b/marketing/urls.py index 4aefaaf..9f8b671 100644 --- a/marketing/urls.py +++ b/marketing/urls.py @@ -1,15 +1,46 @@ from django.urls import path, include from .views import ( - dashboard, contact_lists, contacts_list, contact_list_new, contact_list_detail, edit_contact, - email_template_list, email_template_new, email_template_edit, email_template_delete, - email_template_detail, campaign_list, campaign_new, campaign_details, campaign_delete, - edit_contact_list, failed_contact_list_download_delete, - campaign_link_click, campaign_open, demo_file_download, delete_contact, unsubscribe_from_campaign, contact_detail, - download_contacts_for_campaign, create_campaign_from_template, - download_links_clicked, delete_multiple_contacts, download_failed_contacts, delete_all_contacts, - add_email_for_campaigns, list_all_emails_for_campaigns, edit_email_for_campaigns, delete_email_for_campaigns, - add_blocked_domain, blocked_domain_list, edit_blocked_domain, delete_blocked_domain, - add_blocked_email, blocked_email_list, edit_blocked_email, delete_blocked_email, + dashboard, + contact_lists, + contacts_list, + contact_list_new, + contact_list_detail, + edit_contact, + email_template_list, + email_template_new, + email_template_edit, + email_template_delete, + email_template_detail, + campaign_list, + campaign_new, + campaign_details, + campaign_delete, + edit_contact_list, + failed_contact_list_download_delete, + campaign_link_click, + campaign_open, + demo_file_download, + delete_contact, + unsubscribe_from_campaign, + contact_detail, + download_contacts_for_campaign, + create_campaign_from_template, + download_links_clicked, + delete_multiple_contacts, + download_failed_contacts, + delete_all_contacts, + add_email_for_campaigns, + list_all_emails_for_campaigns, + edit_email_for_campaigns, + delete_email_for_campaigns, + add_blocked_domain, + blocked_domain_list, + edit_blocked_domain, + delete_blocked_domain, + add_blocked_email, + blocked_email_list, + edit_blocked_email, + delete_blocked_email, contacts_list_elastic_search, # MarketingContactEmailSearch, # contacts_list_new @@ -20,63 +51,146 @@ # failed_contact_list_detail ) -app_name = 'marketing' +app_name = "marketing" urlpatterns = [ - path('', dashboard, name='dashboard'), - - path('contact-list/', contact_lists, name='contact_lists'), - path('contacts//edit/', edit_contact, name='edit_contact'), - path('contacts//delete/', delete_contact, name='delete_contact'), - path('contacts/', contacts_list_elastic_search, name='contacts_list'), - path('contact-list/create/', contact_list_new, name='contact_list_new'), + path("", dashboard, name="dashboard"), + path("contact-list/", contact_lists, name="contact_lists"), + path("contacts//edit/", edit_contact, name="edit_contact"), + path("contacts//delete/", delete_contact, name="delete_contact"), + path("contacts/", contacts_list_elastic_search, name="contacts_list"), + path("contact-list/create/", contact_list_new, name="contact_list_new"), # path('cl/list/cnew/', contacts_list_new, name='contacts_list_new'), - path('contact-list//detail/', contact_list_detail, name='contact_list_detail'), + path( + "contact-list//detail/", contact_list_detail, name="contact_list_detail" + ), # path('contact-list//failed/', failed_contact_list_detail, name='failed_contact_list_detail'), # path('contact-list//failed/edit/', edit_failed_contact, name='edit_failed_contact'), # path('contact-list//failed/delete/', delete_failed_contact, name='delete_failed_contact'), - path('contact-list//failed/download/', - failed_contact_list_download_delete, name='failed_contact_list_download_delete'), - path('contact-list//edit/', edit_contact_list, name='edit_contact_list'), + path( + "contact-list//failed/download/", + failed_contact_list_download_delete, + name="failed_contact_list_download_delete", + ), + path("contact-list//edit/", edit_contact_list, name="edit_contact_list"), # path('cl/list//delete/', delete_contact_list, name='delete_contact_list'), - - path('email-templates/', email_template_list, name='email_template_list'), - path('email-templates/create/', email_template_new, name='email_template_new'), - path('email-templates//edit/', email_template_edit, name='email_template_edit'), - path('email-templates//detail/', email_template_detail, name='email_template_detail'), - path('email-templates//delete/', email_template_delete, name='email_template_delete'), - - path('campaigns/', campaign_list, name='campaign_list'), - path('campaigns/create/', campaign_new, name='campaign_new'), + path("email-templates/", email_template_list, name="email_template_list"), + path("email-templates/create/", email_template_new, name="email_template_new"), + path( + "email-templates//edit/", + email_template_edit, + name="email_template_edit", + ), + path( + "email-templates//detail/", + email_template_detail, + name="email_template_detail", + ), + path( + "email-templates//delete/", + email_template_delete, + name="email_template_delete", + ), + path("campaigns/", campaign_list, name="campaign_list"), + path("campaigns/create/", campaign_new, name="campaign_new"), # path('cm//edit/', campaign_edit, name='campaign_edit'), - path('campaigns//details/', campaign_details, name='campaign_details'), - path('campaigns//delete/', campaign_delete, name='campaign_delete'), - path('cm/link//e//', campaign_link_click, name='campaign_link_click'), - path('cm/track-email//contact//', campaign_open, name='campaign_open'), - path('demo-file-download-for-contacts-list/', demo_file_download, name='demo_file_download'), - path('unsubscribe-from-campaign///', unsubscribe_from_campaign, name="unsubscribe_from_campaign"), - path('contacts//view/', contact_detail, name="contact_detail"), - path('download-contacts-for-campaign//', download_contacts_for_campaign, name="download_contacts_for_campaign"), - path('create-campaign-from-template//', create_campaign_from_template, name="create_campaign_from_template"), - path('download-links-clicked//', download_links_clicked, name="download_links_clicked"), - path('delete_multiple_contacts/', delete_multiple_contacts, name="delete_multiple_contacts"), - path('download-failed-contacts//', download_failed_contacts, name="download_failed_contacts"), - path('delete_all_contacts//', delete_all_contacts, name="delete_all_contacts"), - path('add-email-for-campaigns/', add_email_for_campaigns, name="add_email_for_campaigns"), - path('list-all-emails-for-campaigns/', list_all_emails_for_campaigns, name="list_all_emails_for_campaigns"), - path('edit-email-for-campaigns//', edit_email_for_campaigns, name="edit_email_for_campaigns"), - path('delete-email-for-campaigns//', delete_email_for_campaigns, name="delete_email_for_campaigns"), - - path('add-blocked-domain/', add_blocked_domain, name="add_blocked_domain"), - path('blocked-domain-list/', blocked_domain_list, name="blocked_domain_list"), - path('edit-blocked-domain//', edit_blocked_domain, name="edit_blocked_domain"), - path('delete-blocked-domain//', delete_blocked_domain, name="delete_blocked_domain"), - - path('add-blocked-email/', add_blocked_email, name="add_blocked_email"), - path('blocked-email-list/', blocked_email_list, name="blocked_email_list"), - path('edit-blocked-email//', edit_blocked_email, name="edit_blocked_email"), - path('delete-blocked-email//', delete_blocked_email, name="delete_blocked_email"), - + path("campaigns//details/", campaign_details, name="campaign_details"), + path("campaigns//delete/", campaign_delete, name="campaign_delete"), + path( + "cm/link//e//", + campaign_link_click, + name="campaign_link_click", + ), + path( + "cm/track-email//contact//", + campaign_open, + name="campaign_open", + ), + path( + "demo-file-download-for-contacts-list/", + demo_file_download, + name="demo_file_download", + ), + path( + "unsubscribe-from-campaign///", + unsubscribe_from_campaign, + name="unsubscribe_from_campaign", + ), + path("contacts//view/", contact_detail, name="contact_detail"), + path( + "download-contacts-for-campaign//", + download_contacts_for_campaign, + name="download_contacts_for_campaign", + ), + path( + "create-campaign-from-template//", + create_campaign_from_template, + name="create_campaign_from_template", + ), + path( + "download-links-clicked//", + download_links_clicked, + name="download_links_clicked", + ), + path( + "delete_multiple_contacts/", + delete_multiple_contacts, + name="delete_multiple_contacts", + ), + path( + "download-failed-contacts//", + download_failed_contacts, + name="download_failed_contacts", + ), + path( + "delete_all_contacts//", + delete_all_contacts, + name="delete_all_contacts", + ), + path( + "add-email-for-campaigns/", + add_email_for_campaigns, + name="add_email_for_campaigns", + ), + path( + "list-all-emails-for-campaigns/", + list_all_emails_for_campaigns, + name="list_all_emails_for_campaigns", + ), + path( + "edit-email-for-campaigns//", + edit_email_for_campaigns, + name="edit_email_for_campaigns", + ), + path( + "delete-email-for-campaigns//", + delete_email_for_campaigns, + name="delete_email_for_campaigns", + ), + path("add-blocked-domain/", add_blocked_domain, name="add_blocked_domain"), + path("blocked-domain-list/", blocked_domain_list, name="blocked_domain_list"), + path( + "edit-blocked-domain//", + edit_blocked_domain, + name="edit_blocked_domain", + ), + path( + "delete-blocked-domain//", + delete_blocked_domain, + name="delete_blocked_domain", + ), + path("add-blocked-email/", add_blocked_email, name="add_blocked_email"), + path("blocked-email-list/", blocked_email_list, name="blocked_email_list"), + path( + "edit-blocked-email//", + edit_blocked_email, + name="edit_blocked_email", + ), + path( + "delete-blocked-email//", + delete_blocked_email, + name="delete_blocked_email", + ), # path('search-marketing-contact-emails/', include('haystack.urls')), # path('elastic-search/', contacts_list_elastic_search), ] diff --git a/marketing/views.py b/marketing/views.py index 961b4cd..45eb212 100644 --- a/marketing/views.py +++ b/marketing/views.py @@ -10,8 +10,7 @@ from django.core.exceptions import PermissionDenied from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator from django.db.models import Q -from django.http.response import (HttpResponse, HttpResponseRedirect, - JsonResponse) +from django.http.response import HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse, reverse_lazy from lxml.cssselect import CSSSelector @@ -19,18 +18,44 @@ from common import status from common.utils import convert_to_custom_timezone from common.models import User -from marketing.forms import (ContactForm, ContactListForm, EmailTemplateForm, - SendCampaignForm, EmailCampaignForm, BlockedDomainsForm, - BlockedEmailForm) -from marketing.models import (Campaign, CampaignLinkClick, CampaignLog, - CampaignOpen, Contact, ContactList, - EmailTemplate, Link, Tag, FailedContact, ContactUnsubscribedCampaign, - ContactEmailCampaign, BlockedDomain, BlockedEmail) -from marketing.tasks import (run_campaign, upload_csv_file, - delete_multiple_contacts_tasks, - send_campaign_email_to_admin_contact, - update_elastic_search_index) -from common.access_decorators_mixins import marketing_access_required, MarketingAccessRequiredMixin, admin_login_required +from marketing.forms import ( + ContactForm, + ContactListForm, + EmailTemplateForm, + SendCampaignForm, + EmailCampaignForm, + BlockedDomainsForm, + BlockedEmailForm, +) +from marketing.models import ( + Campaign, + CampaignLinkClick, + CampaignLog, + CampaignOpen, + Contact, + ContactList, + EmailTemplate, + Link, + Tag, + FailedContact, + ContactUnsubscribedCampaign, + ContactEmailCampaign, + BlockedDomain, + BlockedEmail, +) +from marketing.tasks import ( + run_campaign, + upload_csv_file, + delete_multiple_contacts_tasks, + send_campaign_email_to_admin_contact, + update_elastic_search_index, +) +from common.access_decorators_mixins import ( + marketing_access_required, + MarketingAccessRequiredMixin, + admin_login_required, +) + # from haystack.generic_views import SearchView # from haystack.query import SearchQuerySet @@ -46,10 +71,10 @@ # return query -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def dashboard(request): - if request.user.role == 'ADMIN' or request.user.is_superuser: + if request.user.role == "ADMIN" or request.user.is_superuser: email_templates = EmailTemplate.objects.all() contacts = Contact.objects.all() campaign = Campaign.objects.all() @@ -63,112 +88,126 @@ def dashboard(request): # x_axis_titles = [ # campaign_obj.title for campaign_obj in campaign[:5]] x_axis_titles = [ - campaign_obj.title[0:10] + '...' if len(campaign_obj.title) > 15 else campaign_obj.title for campaign_obj in campaign[:5]] + campaign_obj.title[0:10] + "..." + if len(campaign_obj.title) > 15 + else campaign_obj.title + for campaign_obj in campaign[:5] + ] y_axis_bounces = [ - campaign_obj.get_all_email_bounces_count for campaign_obj in campaign[:5]] + campaign_obj.get_all_email_bounces_count for campaign_obj in campaign[:5] + ] y_axis_unsubscribed = [ - campaign_obj.get_all_emails_unsubscribed_count for campaign_obj in campaign[:5]] + campaign_obj.get_all_emails_unsubscribed_count for campaign_obj in campaign[:5] + ] y_axis_subscribed = [ - campaign_obj.get_all_emails_subscribed_count for campaign_obj in campaign[:5]] + campaign_obj.get_all_emails_subscribed_count for campaign_obj in campaign[:5] + ] y_axis_opened = [ - campaign_obj.get_all_emails_contacts_opened for campaign_obj in campaign[:5]] - + campaign_obj.get_all_emails_contacts_opened for campaign_obj in campaign[:5] + ] context = { - 'email_templates': email_templates, - 'contacts': contacts, - 'campaigns': campaign, - 'contacts_list': contacts_list, - 'y_axis_subscribed': y_axis_subscribed, - 'y_axis_unsubscribed': y_axis_unsubscribed, - 'y_axis_bounces': y_axis_bounces, - 'y_axis_opened': y_axis_opened, - 'x_axis_titles': x_axis_titles, + "email_templates": email_templates, + "contacts": contacts, + "campaigns": campaign, + "contacts_list": contacts_list, + "y_axis_subscribed": y_axis_subscribed, + "y_axis_unsubscribed": y_axis_unsubscribed, + "y_axis_bounces": y_axis_bounces, + "y_axis_opened": y_axis_opened, + "x_axis_titles": x_axis_titles, } - return render(request, 'marketing/dashboard.html', context) + return render(request, "marketing/dashboard.html", context) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def contact_lists(request): tags = Tag.objects.all() - if (request.user.role == "ADMIN"): - queryset = ContactList.objects.all().order_by('-created_on') + if request.user.role == "ADMIN": + queryset = ContactList.objects.all().order_by("-created_on") else: queryset = ContactList.objects.filter( - Q(created_by=request.user) | Q(visible_to=request.user)).order_by('-created_on') + Q(created_by=request.user) | Q(visible_to=request.user) + ).order_by("-created_on") - users = User.objects.filter( - id__in=queryset.values_list('created_by_id', flat=True)) - if request.GET.get('tag'): - queryset = queryset.filter(tags=request.GET.get('tag')) + users = User.objects.filter(id__in=queryset.values_list("created_by_id", flat=True)) + if request.GET.get("tag"): + queryset = queryset.filter(tags=request.GET.get("tag")) search = False - if request.method == 'POST': - post_tags = request.POST.getlist('tag') + if request.method == "POST": + post_tags = request.POST.getlist("tag") - if request.POST.get('contact_list_name'): + if request.POST.get("contact_list_name"): queryset = queryset.filter( - name__icontains=request.POST.get('contact_list_name')) + name__icontains=request.POST.get("contact_list_name") + ) - if request.POST.get('created_by'): - queryset = queryset.filter( - created_by=request.POST.get('created_by')) + if request.POST.get("created_by"): + queryset = queryset.filter(created_by=request.POST.get("created_by")) - if request.POST.get('tag'): + if request.POST.get("tag"): queryset = queryset.filter(tags__id__in=post_tags) - request_tags = request.POST.getlist('tag') + request_tags = request.POST.getlist("tag") if ( - request.POST.get('contact_list_name')or - request.POST.get('created_by')or - request.POST.get('created_by') - ): - search = True #for filter form to show in contactlist - - data = {'contact_lists': queryset, 'tags': tags, 'users': users, - 'request_tags': request.POST.getlist('tag'),'search':search } - return render(request, 'marketing/lists/index.html', data) + request.POST.get("contact_list_name") + or request.POST.get("created_by") + or request.POST.get("created_by") + ): + search = True # for filter form to show in contactlist + + data = { + "contact_lists": queryset, + "tags": tags, + "users": users, + "request_tags": request.POST.getlist("tag"), + "search": search, + } + return render(request, "marketing/lists/index.html", data) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def contacts_list(request): - if (request.user.role == "ADMIN"): + if request.user.role == "ADMIN": contacts = Contact.objects.all() contact_lists = ContactList.objects.all() else: - contact_ids = request.user.marketing_contactlist.all().values_list('contacts', - flat=True) + contact_ids = request.user.marketing_contactlist.all().values_list( + "contacts", flat=True + ) contacts = Contact.objects.filter(id__in=contact_ids) contact_lists = ContactList.objects.filter(created_by=request.user) # contacts = Contact.objects.filter(created_by=request.user) - users = User.objects.filter( - id__in=contacts.values_list('created_by_id', flat=True)) + users = User.objects.filter(id__in=contacts.values_list("created_by_id", flat=True)) - if request.method == 'GET': - context = {'contacts': contacts, 'users': users, 'contact_lists': contact_lists} - return render(request, 'marketing/lists/all.html', context) + if request.method == "GET": + context = {"contacts": contacts, "users": users, "contact_lists": contact_lists} + return render(request, "marketing/lists/all.html", context) - if request.method == 'POST': + if request.method == "POST": data = request.POST - if data.get('contact_name'): - contacts = contacts.filter( - name__icontains=data.get('contact_name')) + if data.get("contact_name"): + contacts = contacts.filter(name__icontains=data.get("contact_name")) - if data.get('contact_email'): - contacts = contacts.filter(email__icontains=data.get('contact_email')) + if data.get("contact_email"): + contacts = contacts.filter(email__icontains=data.get("contact_email")) - if data.get('created_by'): - contacts = contacts.filter(created_by=data.get('created_by')) + if data.get("created_by"): + contacts = contacts.filter(created_by=data.get("created_by")) - if data.get('contact_list'): - contacts = contacts.filter(contact_list=data.get('contact_list')) + if data.get("contact_list"): + contacts = contacts.filter(contact_list=data.get("contact_list")) - context = {'contacts': contacts, 'users': User.objects.all(), - 'contact_lists': contact_lists} - return render(request, 'marketing/lists/all.html', context) + context = { + "contacts": contacts, + "users": User.objects.all(), + "contact_lists": contact_lists, + } + return render(request, "marketing/lists/all.html", context) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def contact_list_new(request): data = {} @@ -178,63 +217,68 @@ def contact_list_new(request): instance = form.save(commit=False) instance.created_by = request.user instance.save() - tags = request.POST['tags'].split( - ',') if request.POST['tags'] else [] + tags = request.POST["tags"].split(",") if request.POST["tags"] else [] for each in tags: - tag, _ = Tag.objects.get_or_create( - name=each, created_by=request.user) + tag, _ = Tag.objects.get_or_create(name=each, created_by=request.user) instance.tags.add(tag) - if request.FILES.get('contacts_file'): + if request.FILES.get("contacts_file"): upload_csv_file.delay( - form.validated_rows, form.invalid_rows, request.user.id, [instance.id]) + form.validated_rows, + form.invalid_rows, + request.user.id, + [instance.id], + ) - return JsonResponse({'error': False, - 'data': form.data}, - status=status.HTTP_201_CREATED) + return JsonResponse( + {"error": False, "data": form.data}, status=status.HTTP_201_CREATED + ) else: # return JsonResponse({'error': True, 'errors': form.errors}, # status=status.HTTP_400_BAD_REQUEST) return JsonResponse( - {'error': True, 'errors': form.errors}, - status=status.HTTP_200_OK) + {"error": True, "errors": form.errors}, status=status.HTTP_200_OK + ) else: - return render(request, 'marketing/lists/new.html', data) + return render(request, "marketing/lists/new.html", data) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def edit_contact_list(request, pk): user = request.user contact_list = get_object_or_404(ContactList, pk=pk) - if request.method == 'GET': + if request.method == "GET": # contact_lists = ContactList.objects.filter(company=request.company) - if (user.is_superuser or request.user.role == 'ADMIN'): + if user.is_superuser or request.user.role == "ADMIN": contact_lists = ContactList.objects.all() else: contact_lists = ContactList.objects.filter(created_by=request.user) - data = {'contact_list': contact_list, 'contact_lists': contact_lists} - return render(request, 'marketing/lists/new.html', data) + data = {"contact_list": contact_list, "contact_lists": contact_lists} + return render(request, "marketing/lists/new.html", data) else: - form = ContactListForm( - request.POST, request.FILES, instance=contact_list) + form = ContactListForm(request.POST, request.FILES, instance=contact_list) if form.is_valid(): instance = form.save() instance.tags.clear() - tags = request.POST['tags'].split( - ',') if request.POST['tags'] else [] + tags = request.POST["tags"].split(",") if request.POST["tags"] else [] for each in tags: - tag, _ = Tag.objects.get_or_create( - name=each, created_by=request.user) + tag, _ = Tag.objects.get_or_create(name=each, created_by=request.user) instance.tags.add(tag) - if request.FILES.get('contacts_file'): + if request.FILES.get("contacts_file"): upload_csv_file.delay( - form.validated_rows, form.invalid_rows, request.user.id, [instance.id]) + form.validated_rows, + form.invalid_rows, + request.user.id, + [instance.id], + ) - return JsonResponse({'error': False, - 'data': form.data}, status=status.HTTP_200_OK) - return JsonResponse({'error': True, - 'errors': form.errors}, status=status.HTTP_200_OK) + return JsonResponse( + {"error": False, "data": form.data}, status=status.HTTP_200_OK + ) + return JsonResponse( + {"error": True, "errors": form.errors}, status=status.HTTP_200_OK + ) # @login_required(login_url='/login') @@ -298,25 +342,35 @@ def edit_contact_list(request, pk): # return render(request, 'marketing/lists/cnew.html', data) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def edit_contact(request, pk): url_redirect_to = None contact_obj = get_object_or_404(Contact, pk=pk) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or contact_obj.created_by == request.user or - (contact_obj.id in request.user.marketing_contactlist.all().values_list('contacts', flat=True))): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or contact_obj.created_by == request.user + or ( + contact_obj.id + in request.user.marketing_contactlist.all().values_list( + "contacts", flat=True + ) + ) + ): raise PermissionDenied - if request.method == 'GET': + if request.method == "GET": form = ContactForm(instance=contact_obj) - return render(request, 'marketing/lists/edit_contact.html', {'form': form}) + return render(request, "marketing/lists/edit_contact.html", {"form": form}) - if request.method == 'POST': + if request.method == "POST": form = ContactForm(request.POST, instance=contact_obj) if form.is_valid(): if form.has_changed(): if contact_obj.contact_list.count() > 1: contact_list_obj = ContactList.objects.filter( - id=request.POST.get('from_url')).first() + id=request.POST.get("from_url") + ).first() if contact_list_obj: contact_obj.contact_list.remove(contact_list_obj) updated_contact = ContactForm(request.POST) @@ -335,43 +389,77 @@ def edit_contact(request, pk): if form.is_valid(): contact.save() form.save_m2m() - if request.POST.get('from_url'): - return JsonResponse({'error': False, - 'success_url': reverse('marketing:contact_list_detail', args=(request.POST.get('from_url'),))}) - return JsonResponse({'error': False, 'success_url': reverse('marketing:contacts_list')}) + if request.POST.get("from_url"): + return JsonResponse( + { + "error": False, + "success_url": reverse( + "marketing:contact_list_detail", + args=(request.POST.get("from_url"),), + ), + } + ) + return JsonResponse( + {"error": False, "success_url": reverse("marketing:contacts_list")} + ) else: - return JsonResponse({'error': True, 'errors': form.errors, }) + return JsonResponse({"error": True, "errors": form.errors,}) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def delete_contact(request, pk): contact_obj = get_object_or_404(Contact, pk=pk) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or contact_obj.created_by == request.user or - (contact_obj.id in request.user.marketing_contactlist.all().values_list('contacts', flat=True))): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or contact_obj.created_by == request.user + or ( + contact_obj.id + in request.user.marketing_contactlist.all().values_list( + "contacts", flat=True + ) + ) + ): raise PermissionDenied - if request.GET.get('from_contact'): + if request.GET.get("from_contact"): if contact_obj.contact_list.count() == 1: contact_obj.delete() # update_elastic_search_index.delay() - return redirect(reverse('marketing:contact_list_detail', args=(request.GET.get('from_contact'),))) + return redirect( + reverse( + "marketing:contact_list_detail", + args=(request.GET.get("from_contact"),), + ) + ) else: - contact_list_obj = get_object_or_404(ContactList, pk=request.GET.get('from_contact')) + contact_list_obj = get_object_or_404( + ContactList, pk=request.GET.get("from_contact") + ) contact_obj.contact_list.remove(contact_list_obj) # update_elastic_search_index.delay() - return redirect(reverse('marketing:contact_list_detail', args=(request.GET.get('from_contact'),))) + return redirect( + reverse( + "marketing:contact_list_detail", + args=(request.GET.get("from_contact"),), + ) + ) else: contact_obj.delete() # update_elastic_search_index.delay() - return redirect('marketing:contacts_list') + return redirect("marketing:contacts_list") -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def contact_list_detail(request, pk): contact_list = get_object_or_404(ContactList, pk=pk) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or contact_list.created_by == request.user): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or contact_list.created_by == request.user + ): raise PermissionDenied contacts_list = contact_list.contacts.filter(is_bounced=False) contacts_list_count = contact_list.contacts.filter(is_bounced=False).count() @@ -379,41 +467,51 @@ def contact_list_detail(request, pk): bounced_contacts_list_count = contact_list.contacts.filter(is_bounced=True).count() duplicate_contacts = contact_list.duplicate_contact_contact_list.all() if request.POST: - if request.POST.get('name'): + if request.POST.get("name"): contacts_list = contacts_list.filter( - name__icontains=request.POST.get('name')) - if request.POST.get('email'): + name__icontains=request.POST.get("name") + ) + if request.POST.get("email"): contacts_list = contacts_list.filter( - email__icontains=request.POST.get('email')) - if request.POST.get('company_name'): + email__icontains=request.POST.get("email") + ) + if request.POST.get("company_name"): contacts_list = contacts_list.filter( - company_name=request.POST.get('company_name')) + company_name=request.POST.get("company_name") + ) - if request.POST.get('name'): + if request.POST.get("name"): bounced_contacts_list = bounced_contacts_list.filter( - name__icontains=request.POST.get('name')) - if request.POST.get('email'): + name__icontains=request.POST.get("name") + ) + if request.POST.get("email"): bounced_contacts_list = bounced_contacts_list.filter( - email__icontains=request.POST.get('email')) - if request.POST.get('company_name'): + email__icontains=request.POST.get("email") + ) + if request.POST.get("company_name"): bounced_contacts_list = bounced_contacts_list.filter( - company_name=request.POST.get('company_name')) + company_name=request.POST.get("company_name") + ) - page = request.GET.get('bounced_contacts_page', 1) + page = request.GET.get("bounced_contacts_page", 1) paginator = Paginator(bounced_contacts_list, 10) try: bounced_contacts_list = paginator.page(page) except: bounced_contacts_list = paginator.page(paginator.num_pages) - data = {'contact_list': contact_list, "contacts_list": contacts_list, - "bounced_contacts_list":bounced_contacts_list, - 'bounced_contacts_list_count': bounced_contacts_list_count, - 'contacts_list_count': contacts_list_count, - 'duplicate_contacts': duplicate_contacts, + data = { + "contact_list": contact_list, + "contacts_list": contacts_list, + "bounced_contacts_list": bounced_contacts_list, + "bounced_contacts_list_count": bounced_contacts_list_count, + "contacts_list_count": contacts_list_count, + "duplicate_contacts": duplicate_contacts, # these two are added for digg pagintor - 'paginator':paginator,'page_obj': bounced_contacts_list,} - return render(request, 'marketing/lists/detail.html', data) + "paginator": paginator, + "page_obj": bounced_contacts_list, + } + return render(request, "marketing/lists/detail.html", data) # @login_required(login_url='/login') @@ -426,56 +524,65 @@ def contact_list_detail(request, pk): # return render(request, 'marketing/lists/failed_detail.html', data) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def failed_contact_list_download_delete(request, pk): contact_list = get_object_or_404(ContactList, pk=pk) failed_contacts_list = contact_list.failed_contacts.all() if failed_contacts_list.count() > 0: - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="failed_contacts.csv"' + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = 'attachment; filename="failed_contacts.csv"' writer = csv.writer(response) - writer.writerow(["company name", "email", "first name", - "last name", "city", "state"]) + writer.writerow( + ["company name", "email", "first name", "last name", "city", "state"] + ) for contact in failed_contacts_list: - writer.writerow([ - contact.company_name, contact.email, contact.name, contact.last_name, contact.city, contact.state]) + writer.writerow( + [ + contact.company_name, + contact.email, + contact.name, + contact.last_name, + contact.city, + contact.state, + ] + ) failed_contacts_list.delete() return response else: - return HttpResponseRedirect(reverse('marketing:contact_list_detail', kwargs={"pk": pk})) + return HttpResponseRedirect( + reverse("marketing:contact_list_detail", kwargs={"pk": pk}) + ) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def email_template_list(request): # users = User.objects.all() - if (request.user.role == 'ADMIN' or request.user.is_superuser): - queryset = EmailTemplate.objects.all().order_by('-created_on') + if request.user.role == "ADMIN" or request.user.is_superuser: + queryset = EmailTemplate.objects.all().order_by("-created_on") else: - queryset = EmailTemplate.objects.filter( - created_by=request.user).order_by('-created_on') - users = User.objects.filter( - id__in=queryset.values_list('created_by_id', flat=True)) + queryset = EmailTemplate.objects.filter(created_by=request.user).order_by( + "-created_on" + ) + users = User.objects.filter(id__in=queryset.values_list("created_by_id", flat=True)) search = False - if request.method == 'POST': - if request.POST.get('template_name'): + if request.method == "POST": + if request.POST.get("template_name"): queryset = queryset.filter( - title__icontains=request.POST.get('template_name')) + title__icontains=request.POST.get("template_name") + ) - if request.POST.get('created_by'): - queryset = queryset.filter( - created_by=request.POST.get('created_by')) - if (request.POST.get('template_name')or - request.POST.get('created_by') - ): - search =True - data = {'email_templates': queryset, 'users': users, 'search':search} - return render(request, 'marketing/email_template/index.html', data) + if request.POST.get("created_by"): + queryset = queryset.filter(created_by=request.POST.get("created_by")) + if request.POST.get("template_name") or request.POST.get("created_by"): + search = True + data = {"email_templates": queryset, "users": users, "search": search} + return render(request, "marketing/email_template/index.html", data) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def email_template_new(request): if request.POST: @@ -484,189 +591,226 @@ def email_template_new(request): instance = form.save(commit=False) instance.created_by = request.user instance.save() - return JsonResponse({'error': False, 'data': form.data}, status=status.HTTP_201_CREATED) + return JsonResponse( + {"error": False, "data": form.data}, status=status.HTTP_201_CREATED + ) else: - return JsonResponse({'error': True, 'errors': form.errors}, status=status.HTTP_200_OK) + return JsonResponse( + {"error": True, "errors": form.errors}, status=status.HTTP_200_OK + ) else: - return render(request, 'marketing/email_template/new.html') + return render(request, "marketing/email_template/new.html") -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def email_template_edit(request, pk): email_template = get_object_or_404(EmailTemplate, pk=pk) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or request.user == email_template.created_by): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or request.user == email_template.created_by + ): raise PermissionDenied - if request.method == 'GET': - data = {'email_template': email_template} - return render(request, 'marketing/email_template/new.html', data) + if request.method == "GET": + data = {"email_template": email_template} + return render(request, "marketing/email_template/new.html", data) else: form = EmailTemplateForm(request.POST, instance=email_template) if form.is_valid(): instance = form.save(commit=False) # instance.created_by = request.user instance.save() - return JsonResponse({'error': False, 'data': form.data}, status=status.HTTP_201_CREATED) - return JsonResponse({'error': True, 'errors': form.errors}, status=status.HTTP_200_OK) + return JsonResponse( + {"error": False, "data": form.data}, status=status.HTTP_201_CREATED + ) + return JsonResponse( + {"error": True, "errors": form.errors}, status=status.HTTP_200_OK + ) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def email_template_detail(request, pk): queryset = get_object_or_404(EmailTemplate, id=pk) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or request.user == queryset.created_by): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or request.user == queryset.created_by + ): raise PermissionDenied - data = {'email_template': queryset} - return render(request, 'marketing/email_template/details.html', data) + data = {"email_template": queryset} + return render(request, "marketing/email_template/details.html", data) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def email_template_delete(request, pk): email_template_obj = get_object_or_404(EmailTemplate, pk=pk) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or request.user == email_template_obj.created_by): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or request.user == email_template_obj.created_by + ): raise PermissionDenied email_template_obj.delete() - redirect_to = reverse('marketing:email_template_list') + redirect_to = reverse("marketing:email_template_list") return HttpResponseRedirect(redirect_to) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def campaign_list(request): # users = User.objects.all() - if (request.user.role == "ADMIN"): + if request.user.role == "ADMIN": queryset = Campaign.objects.all() else: queryset = Campaign.objects.all().filter(created_by=request.user) - users = User.objects.filter( - id__in=queryset.values_list('created_by_id', flat=True)) - if request.GET.get('tag'): - queryset = queryset.filter(tags=request.GET.get('tag')) + users = User.objects.filter(id__in=queryset.values_list("created_by_id", flat=True)) + if request.GET.get("tag"): + queryset = queryset.filter(tags=request.GET.get("tag")) search = False - if request.method == 'POST': - if request.POST.get('campaign_name'): + if request.method == "POST": + if request.POST.get("campaign_name"): queryset = queryset.filter( - title__icontains=request.POST.get('campaign_name')) + title__icontains=request.POST.get("campaign_name") + ) - if request.POST.get('created_by'): - queryset = queryset.filter( - created_by=request.POST.get('created_by')) - #shows the filter form in marketing-Campaigns after any search is requested - if (request.POST.get('campaign_name')or - request.POST.get('created_by') - ): + if request.POST.get("created_by"): + queryset = queryset.filter(created_by=request.POST.get("created_by")) + # shows the filter form in marketing-Campaigns after any search is requested + if request.POST.get("campaign_name") or request.POST.get("created_by"): search = True - data = {'campaigns_list': queryset,'search':search ,'users': users} - return render(request, 'marketing/campaign/index.html', data) + data = {"campaigns_list": queryset, "search": search, "users": users} + return render(request, "marketing/campaign/index.html", data) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def campaign_new(request): - if request.method == 'GET': - if request.user.role == 'ADMIN' or request.user.is_superuser: + if request.method == "GET": + if request.user.role == "ADMIN" or request.user.is_superuser: email_templates = EmailTemplate.objects.all() else: - email_templates = EmailTemplate.objects.filter( - created_by=request.user) - if request.user.role == 'ADMIN' or request.user.is_superuser: + email_templates = EmailTemplate.objects.filter(created_by=request.user) + if request.user.role == "ADMIN" or request.user.is_superuser: contact_lists = ContactList.objects.all() else: - contact_lists = ContactList.objects.filter( - created_by=request.user) + contact_lists = ContactList.objects.filter(created_by=request.user) # if request.GET.get('contact_list'): # contacts = Contact.objects.filter( # contact_list__id__in=json.loads(request.GET.get('contact_list'))).distinct() data = { - 'contact_lists': contact_lists, 'email_templates': email_templates, - "timezones": TIMEZONE_CHOICES, "settings_timezone": settings.TIME_ZONE} + "contact_lists": contact_lists, + "email_templates": email_templates, + "timezones": TIMEZONE_CHOICES, + "settings_timezone": settings.TIME_ZONE, + } # return JsonResponse(data, status=status.HTTP_200_OK) - return render(request, 'marketing/campaign/new.html', data) + return render(request, "marketing/campaign/new.html", data) else: - form = SendCampaignForm(request.POST, request.FILES, - contacts_list=request.POST.getlist('contact_list')) + form = SendCampaignForm( + request.POST, + request.FILES, + contacts_list=request.POST.getlist("contact_list"), + ) if form.is_valid(): instance = form.save(commit=False) instance.created_by = request.user - if request.POST.get('from_email'): - instance.from_email = request.POST['from_email'] - if request.POST.get('from_name'): - instance.from_name = request.POST['from_name'] - if request.POST.get('reply_to_crm') == 'true': + if request.POST.get("from_email"): + instance.from_email = request.POST["from_email"] + if request.POST.get("from_name"): + instance.from_name = request.POST["from_name"] + if request.POST.get("reply_to_crm") == "true": instance.reply_to_email = settings.MARKETING_REPLY_EMAIL else: - if request.POST.get('reply_to_email'): - instance.reply_to_email = request.POST['reply_to_email'] + if request.POST.get("reply_to_email"): + instance.reply_to_email = request.POST["reply_to_email"] # if request.FILES.get('attachment'): # instance.attachment = request.FILES.get('attachment') instance.save() - for each in request.POST.getlist('contact_list'): + for each in request.POST.getlist("contact_list"): instance.contact_lists.add(ContactList.objects.get(id=each)) # contacts = Contact.objects.filter(contact_list__in=json.loads(request.POST['contact_list'])).distinct() # for each_contact in contacts: # instance.contacts.add(each_contact) - instance.original_contact_count = instance.contact_lists.exclude( - contacts__email=None).values_list('contacts__email').count() + instance.original_contact_count = ( + instance.contact_lists.exclude(contacts__email=None) + .values_list("contacts__email") + .count() + ) instance.save() - tags = request.POST['tags'].split( - ',') if request.POST['tags'] else [] + tags = request.POST["tags"].split(",") if request.POST["tags"] else [] for each in tags: - tag, _ = Tag.objects.get_or_create( - name=each, created_by=request.user) + tag, _ = Tag.objects.get_or_create(name=each, created_by=request.user) instance.tags.add(tag) camp = instance html = camp.html dom = lxml.html.document_fromstring(html) - selAnchor = CSSSelector('a') + selAnchor = CSSSelector("a") links = selAnchor(dom) llist = [] # Extract links and unique links for e in links: - llist.append(e.get('href')) + llist.append(e.get("href")) # get unique link links = set(llist) # Replace Links with new One # domain_url = '%s://%s' % (request.scheme, request.META['HTTP_HOST']) - domain_url = request.scheme + '://' + request.get_host() + domain_url = request.scheme + "://" + request.get_host() if Link.objects.filter(campaign_id=camp.id): Link.objects.filter(campaign_id=camp.id).delete() for l in links: link = Link.objects.create(campaign_id=camp.id, original=l) html = html.replace( - 'href="' + l + '"', 'href="' + domain_url + '/marketing/cm/link/' + str(link.id) + '/e/{{email_id}}"') + 'href="' + l + '"', + 'href="' + + domain_url + + "/marketing/cm/link/" + + str(link.id) + + '/e/{{email_id}}"', + ) camp.html_processed = html camp.sent_status = "scheduled" camp.save() - if request.POST.get('schedule_later', None) == 'true': - schedule_date_time = request.POST.get('schedule_date_time', '') - user_timezone = request.POST.get('timezone', '') + if request.POST.get("schedule_later", None) == "true": + schedule_date_time = request.POST.get("schedule_date_time", "") + user_timezone = request.POST.get("timezone", "") if user_timezone and schedule_date_time: start_time_object = ddatetime.strptime( - schedule_date_time, '%Y-%m-%d %H:%M') + schedule_date_time, "%Y-%m-%d %H:%M" + ) schedule_date_time = convert_to_custom_timezone( - start_time_object, user_timezone, to_utc=True) + start_time_object, user_timezone, to_utc=True + ) instance.schedule_date_time = schedule_date_time instance.timezone = user_timezone instance.save() send_campaign_email_to_admin_contact.delay( - instance.id, domain=request.get_host(), protocol=request.scheme) + instance.id, domain=request.get_host(), protocol=request.scheme + ) else: run_campaign.delay( - instance.id, domain=request.get_host(), protocol=request.scheme) + instance.id, domain=request.get_host(), protocol=request.scheme + ) send_campaign_email_to_admin_contact.delay( - instance.id, domain=request.get_host(), protocol=request.scheme) - return JsonResponse({'error': False, 'data': form.data}, status=status.HTTP_201_CREATED) - return JsonResponse({'error': True, 'errors': form.errors}, status=status.HTTP_200_OK) + instance.id, domain=request.get_host(), protocol=request.scheme + ) + return JsonResponse( + {"error": False, "data": form.data}, status=status.HTTP_201_CREATED + ) + return JsonResponse( + {"error": True, "errors": form.errors}, status=status.HTTP_200_OK + ) # @login_required(login_url='/login') @@ -675,14 +819,18 @@ def campaign_new(request): # return render(request, 'marketing/campaign/edit.html') -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def campaign_details(request, pk): campaign = get_object_or_404(Campaign, pk=pk) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or request.user == campaign.created_by): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or request.user == campaign.created_by + ): raise PermissionDenied contact_lists = campaign.contact_lists.all() - if (request.user.role == 'ADMIN' or request.user.is_superuser): + if request.user.role == "ADMIN" or request.user.is_superuser: contact_lists = contact_lists else: # contact_lists = contact_lists.filter( @@ -690,30 +838,29 @@ def campaign_details(request, pk): contact_lists = contact_lists.filter(created_by=request.user) contacts = Contact.objects.filter(contact_list__in=contact_lists) - contact_list_ids = campaign.contact_lists.all().values_list('id', flat=True) + contact_list_ids = campaign.contact_lists.all().values_list("id", flat=True) contacts = Contact.objects.filter(contact_list__id__in=contact_list_ids) all_contacts = contacts bounced_contacts = contacts.filter(is_bounced=True).distinct() - unsubscribe_contacts = contacts.filter( - is_unsubscribed=True).distinct() + unsubscribe_contacts = contacts.filter(is_unsubscribed=True).distinct() # unsubscribe_contacts_ids = ContactUnsubscribedCampaign.objects.filter( # campaigns=campaign, is_unsubscribed=True).values_list('contacts_id', flat=True) # unsubscribe_contacts = Contact.objects.filter( # id__in=unsubscribe_contacts_ids) # read_contacts = campaign.marketing_links.filter(Q(clicks__gt=0)).distinct() - contact_ids = CampaignOpen.objects.filter( - campaign=campaign).values_list('contact_id', flat=True) + contact_ids = CampaignOpen.objects.filter(campaign=campaign).values_list( + "contact_id", flat=True + ) read_contacts = Contact.objects.filter(id__in=contact_ids) - sent_contacts = contacts.exclude( - Q(is_bounced=True) | Q(is_unsubscribed=True)) + sent_contacts = contacts.exclude(Q(is_bounced=True) | Q(is_unsubscribed=True)) # if request.POST: # contacts = contacts.filter(Q(name__icontains=request.POST['search'])) - tab = request.GET.get('tab', 'contacts') + tab = request.GET.get("tab", "contacts") per_page = request.GET.get("per_page", 10) - page = request.GET.get('page', 1) + page = request.GET.get("page", 1) # contacts_paginator = Paginator(contacts.order_by('id'), per_page) # if (tab == "contacts"): @@ -799,41 +946,54 @@ def campaign_details(request, pk): # read_contacts = read_contacts_paginator.page( # read_contacts_paginator.num_pages) - data = {'contact_lists': contact_lists, "campaign": campaign, - "contacts": contacts, 'unsubscribe_contacts': unsubscribe_contacts, - 'bounced_contacts': bounced_contacts, 'read_contacts': read_contacts, - 'sent_contacts': sent_contacts, 'all_contacts': all_contacts, 'tab': tab} + data = { + "contact_lists": contact_lists, + "campaign": campaign, + "contacts": contacts, + "unsubscribe_contacts": unsubscribe_contacts, + "bounced_contacts": bounced_contacts, + "read_contacts": read_contacts, + "sent_contacts": sent_contacts, + "all_contacts": all_contacts, + "tab": tab, + } - return render(request, 'marketing/campaign/details.html', data) + return render(request, "marketing/campaign/details.html", data) -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def campaign_delete(request, pk): campaign = get_object_or_404(Campaign, pk=pk) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or request.user == campaign.created_by): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or request.user == campaign.created_by + ): raise PermissionDenied campaign.delete() - redirect_url = reverse('marketing:campaign_list') + redirect_url = reverse("marketing:campaign_list") return redirect(redirect_url) def campaign_link_click(request, link_id, email_id): - x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') + x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") if x_forwarded_for: - ipaddress = x_forwarded_for.split(',')[-1].strip() + ipaddress = x_forwarded_for.split(",")[-1].strip() else: - ipaddress = request.META.get('REMOTE_ADDR') + ipaddress = request.META.get("REMOTE_ADDR") link = Link.objects.filter(id=link_id).first() contact = Contact.objects.filter(id=email_id).first() if link and contact: campaign_link_click = CampaignLinkClick.objects.filter( - link=link, campaign=link.campaign, contact=contact).first() + link=link, campaign=link.campaign, contact=contact + ).first() if not campaign_link_click: campaign_link_click = CampaignLinkClick.objects.create( - link=link, campaign=link.campaign, contact=contact, ip_address=ipaddress) + link=link, campaign=link.campaign, contact=contact, ip_address=ipaddress + ) link.unique += 1 else: campaign_link_click.ip_address = ipaddress @@ -847,23 +1007,25 @@ def campaign_link_click(request, link_id, email_id): return redirect(url) -def campaign_open(request, campaign_log_id, email_id): # pragma: no cover - x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') +def campaign_open(request, campaign_log_id, email_id): # pragma: no cover + x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") if x_forwarded_for: - ipaddress = x_forwarded_for.split(',')[-1].strip() + ipaddress = x_forwarded_for.split(",")[-1].strip() else: - ipaddress = request.META.get('REMOTE_ADDR') + ipaddress = request.META.get("REMOTE_ADDR") campaign_log = CampaignLog.objects.filter(id=campaign_log_id).first() contact = Contact.objects.filter(id=email_id).first() - image_data = open('static/images/company.png', 'rb').read() + image_data = open("static/images/company.png", "rb").read() if campaign_log and contact: campaign_open = CampaignOpen.objects.filter( - campaign=campaign_log.campaign, contact=contact).first() + campaign=campaign_log.campaign, contact=contact + ).first() if not campaign_open: campaign_open = CampaignOpen.objects.create( - campaign=campaign_log.campaign, contact=contact, ip_address=ipaddress) + campaign=campaign_log.campaign, contact=contact, ip_address=ipaddress + ) campaign_log.campaign.opens_unique += 1 else: campaign_open.ip_address = ipaddress @@ -876,16 +1038,16 @@ def campaign_open(request, campaign_log_id, email_id): # pragma: no cover def demo_file_download(request): sample_data = [ - 'company name,email,first name,last name,city,state\n', - 'company_name_1,user1@email.com,first_name,last_name,Hyderabad,Telangana\n', - 'company_name_2,user2@email.com,first_name,last_name,Hyderabad,Telangana\n', - 'company_name_3,user3@email.com,first_name,last_name,Hyderabad,Telangana\n', - 'company_name_4,user4@email.com,first_name,last_name,Hyderabad,Telangana\n' + "company name,email,first name,last name,city,state\n", + "company_name_1,user1@email.com,first_name,last_name,Hyderabad,Telangana\n", + "company_name_2,user2@email.com,first_name,last_name,Hyderabad,Telangana\n", + "company_name_3,user3@email.com,first_name,last_name,Hyderabad,Telangana\n", + "company_name_4,user4@email.com,first_name,last_name,Hyderabad,Telangana\n", ] - response = HttpResponse( - sample_data, content_type='text/plain') - response['Content-Disposition'] = 'attachment; filename={}'.format( - 'sample_data.csv') + response = HttpResponse(sample_data, content_type="text/plain") + response["Content-Disposition"] = "attachment; filename={}".format( + "sample_data.csv" + ) return response @@ -897,21 +1059,31 @@ def unsubscribe_from_campaign(request, contact_id, campaign_id): # campaign_obj = get_object_or_404(Campaign, pk=campaign_id) if campaign_obj: ContactUnsubscribedCampaign.objects.create( - campaigns=campaign_obj, contacts=contact_obj, is_unsubscribed=True) - return render(request, 'unsubscribe_from_campaign_template.html') + campaigns=campaign_obj, contacts=contact_obj, is_unsubscribed=True + ) + return render(request, "unsubscribe_from_campaign_template.html") @login_required @marketing_access_required def contact_detail(request, contact_id): contact_obj = get_object_or_404(Contact, pk=contact_id) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or contact_obj.created_by == request.user or - (contact_obj.id in request.user.marketing_contactlist.all().values_list('contacts', flat=True))): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or contact_obj.created_by == request.user + or ( + contact_obj.id + in request.user.marketing_contactlist.all().values_list( + "contacts", flat=True + ) + ) + ): # the above query is for: a contact may be common to multiple contact lists, so query from # a contact list can be created by multiple users raise PermissionDenied - if request.method == 'GET': - return render(request, 'contact_detail.html', {'contact_obj': contact_obj}) + if request.method == "GET": + return render(request, "contact_detail.html", {"contact_obj": contact_obj}) # @login_required(login_url='/login') @@ -950,46 +1122,62 @@ def contact_detail(request, contact_id): @marketing_access_required def download_contacts_for_campaign(request, compaign_id): campaign_obj = get_object_or_404(Campaign, pk=compaign_id) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or campaign_obj.created_by == request.user): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or campaign_obj.created_by == request.user + ): raise PermissionDenied - if request.method == 'GET': - if request.GET.get('is_bounced') == 'true': - contact_ids = campaign_obj.campaign_log_contacts.filter(contact__is_bounced=True).values_list( - 'contact__id', flat=True) + if request.method == "GET": + if request.GET.get("is_bounced") == "true": + contact_ids = campaign_obj.campaign_log_contacts.filter( + contact__is_bounced=True + ).values_list("contact__id", flat=True) contacts = Contact.objects.filter(id__in=contact_ids).values( - 'company_name', 'email', 'name', 'last_name', 'city', 'state') + "company_name", "email", "name", "last_name", "city", "state" + ) - if request.GET.get('is_unsubscribed') == 'true': + if request.GET.get("is_unsubscribed") == "true": # unsubscribe_contacts_ids = ContactUnsubscribedCampaign.objects.filter( # campaigns=campaign_obj, is_unsubscribed=True).values_list('contacts_id', flat=True) - contact_ids = campaign_obj.campaign_log_contacts.filter(contact__is_unsubscribed=True).values_list( - 'contact__id', flat=True) + contact_ids = campaign_obj.campaign_log_contacts.filter( + contact__is_unsubscribed=True + ).values_list("contact__id", flat=True) contacts = Contact.objects.filter(id__in=contact_ids).values( - 'company_name', 'email', 'name', 'last_name', 'city', 'state') + "company_name", "email", "name", "last_name", "city", "state" + ) - if request.GET.get('is_opened') == 'true': + if request.GET.get("is_opened") == "true": contact_ids = CampaignOpen.objects.filter( - campaign=campaign_obj).values_list('contact_id', flat=True) + campaign=campaign_obj + ).values_list("contact_id", flat=True) contacts = Contact.objects.filter(id__in=contact_ids).values( - 'company_name', 'email', 'name', 'last_name', 'city', 'state') + "company_name", "email", "name", "last_name", "city", "state" + ) data = [ - 'company name,email,first name,last name,city,state\n', + "company name,email,first name,last name,city,state\n", ] for contact in contacts: data.append( - str(contact.get('company_name')) + ',' + - str(contact.get('email')) + ',' + - str(contact.get('name')) + ',' + - str(contact.get('last_name')) + ',' + - str(contact.get('city')) + ',' + - str(contact.get('state')) + '\n' + str(contact.get("company_name")) + + "," + + str(contact.get("email")) + + "," + + str(contact.get("name")) + + "," + + str(contact.get("last_name")) + + "," + + str(contact.get("city")) + + "," + + str(contact.get("state")) + + "\n" ) - response = HttpResponse( - data, content_type='text/plain') - response['Content-Disposition'] = 'attachment; filename={}'.format( - 'data_downloads.csv') + response = HttpResponse(data, content_type="text/plain") + response["Content-Disposition"] = "attachment; filename={}".format( + "data_downloads.csv" + ) return response @@ -997,86 +1185,133 @@ def download_contacts_for_campaign(request, compaign_id): @marketing_access_required def create_campaign_from_template(request, template_id): email_template_obj = get_object_or_404(EmailTemplate, pk=template_id) - if request.method == 'GET': - url_location = reverse('marketing:campaign_new') - url_query_params = '?email_template={}'.format(template_id) + if request.method == "GET": + url_location = reverse("marketing:campaign_new") + url_query_params = "?email_template={}".format(template_id) url = url_location + url_query_params return HttpResponseRedirect(url) def download_links_clicked(request, campaign_id): campaign_obj = get_object_or_404(Campaign, pk=campaign_id) - campaign_link_click_objects = campaign_obj.campaign_link_click.all().select_related('link', 'campaign', 'contact') - csv_row_headers = ['email', 'company', 'first name', 'last name', 'city', 'state', 'original link', 'unique clicks', 'total clicks', 'campaign title'] + campaign_link_click_objects = campaign_obj.campaign_link_click.all().select_related( + "link", "campaign", "contact" + ) + csv_row_headers = [ + "email", + "company", + "first name", + "last name", + "city", + "state", + "original link", + "unique clicks", + "total clicks", + "campaign title", + ] data = [] - data.append(','.join(csv_row_headers) + '\n') + data.append(",".join(csv_row_headers) + "\n") if campaign_link_click_objects: for campaign_link_click_object in campaign_link_click_objects: data.append( - str(campaign_link_click_object.contact.email) + ',' + - str(campaign_link_click_object.contact.company_name) + ',' + - str(campaign_link_click_object.contact.name) + ',' + - str(campaign_link_click_object.contact.last_name) + ',' + - str(campaign_link_click_object.contact.city) + ',' + - str(campaign_link_click_object.contact.state) + ',' + - str(campaign_link_click_object.link.original) + ',' + - str(campaign_link_click_object.link.unique) + ',' + - str(campaign_link_click_object.link.clicks) + ',' + - str(campaign_link_click_object.link.campaign.title) + '\n' + str(campaign_link_click_object.contact.email) + + "," + + str(campaign_link_click_object.contact.company_name) + + "," + + str(campaign_link_click_object.contact.name) + + "," + + str(campaign_link_click_object.contact.last_name) + + "," + + str(campaign_link_click_object.contact.city) + + "," + + str(campaign_link_click_object.contact.state) + + "," + + str(campaign_link_click_object.link.original) + + "," + + str(campaign_link_click_object.link.unique) + + "," + + str(campaign_link_click_object.link.clicks) + + "," + + str(campaign_link_click_object.link.campaign.title) + + "\n" ) - response = HttpResponse(data, content_type='text/plain') - response['Content-Disposition'] = 'attachment; filename={}'.format('data_downloads.csv') + response = HttpResponse(data, content_type="text/plain") + response["Content-Disposition"] = "attachment; filename={}".format( + "data_downloads.csv" + ) return response -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def delete_multiple_contacts(request): - contacts_list = Contact.objects.filter(id__in=request.POST.getlist('selected_list[]')) - shared_contact_lists = request.user.marketing_contactlist.all().values_list('contacts', flat=True) + contacts_list = Contact.objects.filter( + id__in=request.POST.getlist("selected_list[]") + ) + shared_contact_lists = request.user.marketing_contactlist.all().values_list( + "contacts", flat=True + ) cannot_be_deleted = [] error = False for contact_obj in contacts_list: - if not (request.user.role == 'ADMIN' or request.user.is_superuser or contact_obj.created_by == request.user or (contact_obj.id in shared_contact_lists)): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or contact_obj.created_by == request.user + or (contact_obj.id in shared_contact_lists) + ): cannot_be_deleted.append(contact_obj.email) error = True if len(cannot_be_deleted) == 0: error = False for contact_obj in contacts_list: - if request.POST.get('from_contact'): + if request.POST.get("from_contact"): if contact_obj.contact_list.count() == 1: contact_obj.delete() else: - if request.POST.get('duplicate_contacts', None) == 'true': - contact_list_obj = get_object_or_404(ContactList, pk=request.POST.get('from_contact')) + if request.POST.get("duplicate_contacts", None) == "true": + contact_list_obj = get_object_or_404( + ContactList, pk=request.POST.get("from_contact") + ) contact_obj.contact_list.remove(contact_list_obj) - contact_list_duplicates = contact_list_obj.duplicate_contact_contact_list.all() - contacts_list_ids = contacts_list.values_list('id', flat=True) - contact_list_duplicates.filter(contacts__id__in=contacts_list_ids).delete() + contact_list_duplicates = ( + contact_list_obj.duplicate_contact_contact_list.all() + ) + contacts_list_ids = contacts_list.values_list("id", flat=True) + contact_list_duplicates.filter( + contacts__id__in=contacts_list_ids + ).delete() else: - contact_list_obj = get_object_or_404(ContactList, pk=request.POST.get('from_contact')) + contact_list_obj = get_object_or_404( + ContactList, pk=request.POST.get("from_contact") + ) contact_obj.contact_list.remove(contact_list_obj) - return JsonResponse({'error':False, 'refresh': True}) + return JsonResponse({"error": False, "refresh": True}) else: - message = "You don't have permission to delete {}".format(', '.join(cannot_be_deleted)) - return JsonResponse({'error': True, 'message' : message }) + message = "You don't have permission to delete {}".format( + ", ".join(cannot_be_deleted) + ) + return JsonResponse({"error": True, "message": message}) - -@login_required(login_url='/login') +@login_required(login_url="/login") @marketing_access_required def delete_all_contacts(request, contact_list_id): contacts_list_obj = get_object_or_404(ContactList, pk=contact_list_id) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or contacts_list_obj.created_by == request.user): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or contacts_list_obj.created_by == request.user + ): raise PermissionDenied - if request.GET.get('bounced','') == 'true': + if request.GET.get("bounced", "") == "true": bounced = True else: bounced = False - if request.GET.get('duplicate_contacts', '') == 'true': + if request.GET.get("duplicate_contacts", "") == "true": contact_list_duplicates = contacts_list_obj.duplicate_contact_contact_list.all() if contact_list_duplicates: for dup_contact_obj in contact_list_duplicates: @@ -1097,7 +1332,7 @@ def delete_all_contacts(request, contact_list_id): contact_obj.delete() # delete_multiple_contacts_tasks.delay(contact_list_id, bounced) - redirect_to = reverse('marketing:contact_list_detail', args=(contact_list_id,)) + redirect_to = reverse("marketing:contact_list_detail", args=(contact_list_id,)) return HttpResponseRedirect(redirect_to) @@ -1106,60 +1341,74 @@ def delete_all_contacts(request, contact_list_id): def download_failed_contacts(request, contact_list_id): contact_list_obj = get_object_or_404(ContactList, pk=contact_list_id) - if not (request.user.role == 'ADMIN' or request.user.is_superuser or contact_list_obj.created_by == request.user): + if not ( + request.user.role == "ADMIN" + or request.user.is_superuser + or contact_list_obj.created_by == request.user + ): raise PermissionDenied - failed_contacts = contact_list_obj.failed_contacts.values('company_name', 'email', 'name', 'last_name', 'city', 'state') + failed_contacts = contact_list_obj.failed_contacts.values( + "company_name", "email", "name", "last_name", "city", "state" + ) if failed_contacts: data = [ - 'company name,email,first name,last name,city,state\n', + "company name,email,first name,last name,city,state\n", ] for contact in failed_contacts: data.append( - str(contact.get('company_name')) + ',' + - str(contact.get('email')) + ',' + - str(contact.get('name')) + ',' + - str(contact.get('last_name')) + ',' + - str(contact.get('city')) + ',' + - str(contact.get('state')) + '\n' + str(contact.get("company_name")) + + "," + + str(contact.get("email")) + + "," + + str(contact.get("name")) + + "," + + str(contact.get("last_name")) + + "," + + str(contact.get("city")) + + "," + + str(contact.get("state")) + + "\n" ) - response = HttpResponse( - data, content_type='text/plain') - response['Content-Disposition'] = 'attachment; filename={}'.format( - 'failed_contacts.csv') + response = HttpResponse(data, content_type="text/plain") + response["Content-Disposition"] = "attachment; filename={}".format( + "failed_contacts.csv" + ) return response else: - return HttpResponse('No Data') + return HttpResponse("No Data") @login_required @admin_login_required def list_all_emails_for_campaigns(request): context = {} - if request.method == 'GET': + if request.method == "GET": queryset = ContactEmailCampaign.objects.all() - context['contacts'] = queryset - return render(request, 'email_for_campaigns_list.html', context) + context["contacts"] = queryset + return render(request, "email_for_campaigns_list.html", context) @login_required @admin_login_required def add_email_for_campaigns(request): context = {} - if request.method == 'GET': + if request.method == "GET": form = EmailCampaignForm() - context['form'] = form - return render(request, 'add_email_for_campaign.html', context) - if request.method == 'POST': + context["form"] = form + return render(request, "add_email_for_campaign.html", context) + if request.method == "POST": form = EmailCampaignForm(request.POST) if form.is_valid(): obj = form.save(commit=False) obj.created_by = request.user obj.save() - return JsonResponse({'error':False, 'success_url':reverse('common:api_settings')}) + return JsonResponse( + {"error": False, "success_url": reverse("common:api_settings")} + ) else: - return JsonResponse({'error':True, 'errors':form.errors}) + return JsonResponse({"error": True, "errors": form.errors}) @login_required @@ -1167,19 +1416,21 @@ def add_email_for_campaigns(request): def edit_email_for_campaigns(request, pk): edit_contact_obj = get_object_or_404(ContactEmailCampaign, pk=pk) context = {} - if request.method == 'GET': + if request.method == "GET": form = EmailCampaignForm(instance=edit_contact_obj) - context['form'] = form - context['edit_obj'] = edit_contact_obj - return render(request, 'add_email_for_campaign.html', context) - if request.method == 'POST': + context["form"] = form + context["edit_obj"] = edit_contact_obj + return render(request, "add_email_for_campaign.html", context) + if request.method == "POST": form = EmailCampaignForm(request.POST, instance=edit_contact_obj) if form.is_valid(): obj = form.save(commit=False) obj.save() - return JsonResponse({'error':False, 'success_url':reverse('common:api_settings')}) + return JsonResponse( + {"error": False, "success_url": reverse("common:api_settings")} + ) else: - return JsonResponse({'error':True, 'errors':form.errors}) + return JsonResponse({"error": True, "errors": form.errors}) @login_required @@ -1187,122 +1438,163 @@ def edit_email_for_campaigns(request, pk): def delete_email_for_campaigns(request, pk): contact_obj = get_object_or_404(ContactEmailCampaign, pk=pk) context = {} - if request.method == 'GET': + if request.method == "GET": contact_obj.delete() - return HttpResponseRedirect(reverse('common:api_settings')) + return HttpResponseRedirect(reverse("common:api_settings")) + # the below views should be in settings page + @login_required @admin_login_required def add_blocked_domain(request): - if request.method == 'GET': + if request.method == "GET": form = BlockedDomainsForm() - return render(request, 'add_blocked_domain.html', {'form': form}) - if request.method == 'POST': + return render(request, "add_blocked_domain.html", {"form": form}) + if request.method == "POST": form = BlockedDomainsForm(request.POST) if form.is_valid(): domain = form.save(commit=False) domain.created_by = request.user domain.save() - return JsonResponse({'error':False, 'success_url':reverse('common:api_settings')}) + return JsonResponse( + {"error": False, "success_url": reverse("common:api_settings")} + ) else: - return JsonResponse({'error':True, 'errors':form.errors}) + return JsonResponse({"error": True, "errors": form.errors}) + @login_required @admin_login_required def blocked_domain_list(request): - if request.method == 'GET': + if request.method == "GET": blocked_domains = BlockedDomain.objects.all() - return render(request, 'blocked_domain_list.html', {'blocked_domains': blocked_domains}) - if request.method == 'POST': + return render( + request, "blocked_domain_list.html", {"blocked_domains": blocked_domains} + ) + if request.method == "POST": blocked_domains = BlockedDomain.objects.all() - if request.POST.get('domain', ''): - blocked_domains = blocked_domains.filter(domain__icontains=request.POST.get('domain', '')) - if request.POST.get('created_by', ''): - blocked_domains = blocked_domains.filter(created_by_id=request.POST.get('created_by', '')) - return render(request, 'blocked_domain_list.html', {'blocked_domains': blocked_domains}) + if request.POST.get("domain", ""): + blocked_domains = blocked_domains.filter( + domain__icontains=request.POST.get("domain", "") + ) + if request.POST.get("created_by", ""): + blocked_domains = blocked_domains.filter( + created_by_id=request.POST.get("created_by", "") + ) + return render( + request, "blocked_domain_list.html", {"blocked_domains": blocked_domains} + ) + @login_required @admin_login_required def edit_blocked_domain(request, blocked_domain_id): block_domain_obj = get_object_or_404(BlockedDomain, pk=blocked_domain_id) - if request.method == 'GET': + if request.method == "GET": form = BlockedDomainsForm(instance=block_domain_obj) - return render(request, 'add_blocked_domain.html', {'form': form, 'block_domain_obj': block_domain_obj}) - if request.method == 'POST': + return render( + request, + "add_blocked_domain.html", + {"form": form, "block_domain_obj": block_domain_obj}, + ) + if request.method == "POST": form = BlockedDomainsForm(request.POST, instance=block_domain_obj) if form.is_valid(): form.save() - return JsonResponse({'error':False, 'success_url':reverse('common:api_settings')}) + return JsonResponse( + {"error": False, "success_url": reverse("common:api_settings")} + ) else: - return JsonResponse({'error':True, 'errors':form.errors}) + return JsonResponse({"error": True, "errors": form.errors}) + @login_required @admin_login_required def delete_blocked_domain(request, blocked_domain_id): block_domain_obj = get_object_or_404(BlockedDomain, pk=blocked_domain_id) block_domain_obj.delete() - return redirect(reverse('common:api_settings') + '?blocked_domains') + return redirect(reverse("common:api_settings") + "?blocked_domains") @login_required @admin_login_required def add_blocked_email(request): - if request.method == 'GET': + if request.method == "GET": form = BlockedEmailForm() - return render(request, 'add_blocked_email.html', {'form': form}) - if request.method == 'POST': + return render(request, "add_blocked_email.html", {"form": form}) + if request.method == "POST": form = BlockedEmailForm(request.POST) if form.is_valid(): email = form.save(commit=False) email.created_by = request.user email.save() - return JsonResponse({'error':False, 'success_url':reverse('common:api_settings')}) + return JsonResponse( + {"error": False, "success_url": reverse("common:api_settings")} + ) else: - return JsonResponse({'error':True, 'errors':form.errors}) + return JsonResponse({"error": True, "errors": form.errors}) + @login_required @admin_login_required def blocked_email_list(request): - if request.method == 'GET': + if request.method == "GET": blocked_emails = BlockedEmail.objects.all() - return render(request, 'blocked_email_list.html', {'blocked_emails': blocked_emails}) - if request.method == 'POST': + return render( + request, "blocked_email_list.html", {"blocked_emails": blocked_emails} + ) + if request.method == "POST": blocked_emails = BlockedEmail.objects.all() - if request.POST.get('email', ''): - blocked_emails = blocked_emails.filter(email__icontains=request.POST.get('email', '')) - if request.POST.get('created_by', ''): - blocked_emails = blocked_emails.filter(created_by_id=request.POST.get('created_by', '')) - return render(request, 'blocked_email_list.html', {'blocked_emails': blocked_emails}) + if request.POST.get("email", ""): + blocked_emails = blocked_emails.filter( + email__icontains=request.POST.get("email", "") + ) + if request.POST.get("created_by", ""): + blocked_emails = blocked_emails.filter( + created_by_id=request.POST.get("created_by", "") + ) + return render( + request, "blocked_email_list.html", {"blocked_emails": blocked_emails} + ) + @login_required @admin_login_required def edit_blocked_email(request, blocked_email_id): block_email_obj = get_object_or_404(BlockedEmail, pk=blocked_email_id) - if request.method == 'GET': + if request.method == "GET": form = BlockedEmailForm(instance=block_email_obj) - return render(request, 'add_blocked_email.html', {'form': form, 'block_email_obj': block_email_obj}) - if request.method == 'POST': + return render( + request, + "add_blocked_email.html", + {"form": form, "block_email_obj": block_email_obj}, + ) + if request.method == "POST": form = BlockedEmailForm(request.POST, instance=block_email_obj) if form.is_valid(): form.save() - return JsonResponse({'error':False, 'success_url':reverse('common:api_settings')}) + return JsonResponse( + {"error": False, "success_url": reverse("common:api_settings")} + ) else: - return JsonResponse({'error':True, 'errors':form.errors}) + return JsonResponse({"error": True, "errors": form.errors}) + @login_required @admin_login_required def delete_blocked_email(request, blocked_email_id): block_email_obj = get_object_or_404(BlockedEmail, pk=blocked_email_id) block_email_obj.delete() - return redirect(reverse('common:api_settings') + '?blocked_emails') + return redirect(reverse("common:api_settings") + "?blocked_emails") -@login_required(login_url='/login') + +@login_required(login_url="/login") @marketing_access_required def contacts_list_elastic_search(request): search = False - if (request.user.role == "ADMIN"): + if request.user.role == "ADMIN": contacts = Contact.objects.filter(is_bounced=False) # contacts = SearchQuerySet().filter(is_bounced='false').models(Contact) # bounced_contacts = SearchQuerySet().filter(is_bounced='true').models(Contact) @@ -1311,8 +1603,9 @@ def contacts_list_elastic_search(request): failed_contacts = FailedContact.objects.all() contact_lists = ContactList.objects.all() else: - contact_ids = request.user.marketing_contactlist.all().values_list('contacts', - flat=True) + contact_ids = request.user.marketing_contactlist.all().values_list( + "contacts", flat=True + ) # contacts = SearchQuerySet().filter(is_bounced='false', id__in=contact_ids).models(Contact) # bounced_contacts = SearchQuerySet().filter(is_bounced='true', id__in=contact_ids).models(Contact) # failed_contacts = SearchQuerySet().models(FailedContact).filter(created_by_id=str(request.user.id)) @@ -1323,57 +1616,75 @@ def contacts_list_elastic_search(request): # contacts = Contact.objects.filter(created_by=request.user) users = User.objects.filter( - id__in=[_id for _id in contacts.values_list('created_by_id', flat=True) if _id != '']) - - if request.method == 'GET': - context = {'contacts': contacts, 'users': users, 'contact_lists': contact_lists, - 'bounced_contacts': bounced_contacts, 'failed_contacts': failed_contacts, - 'search':search, + id__in=[ + _id for _id in contacts.values_list("created_by_id", flat=True) if _id != "" + ] + ) + + if request.method == "GET": + context = { + "contacts": contacts, + "users": users, + "contact_lists": contact_lists, + "bounced_contacts": bounced_contacts, + "failed_contacts": failed_contacts, + "search": search, } - return render(request, 'search_contact_emails.html', context) + return render(request, "search_contact_emails.html", context) - if request.method == 'POST': + if request.method == "POST": data = request.POST - if data.get('name'): - contacts = contacts.filter( - name__icontains=data.get('name')) + if data.get("name"): + contacts = contacts.filter(name__icontains=data.get("name")) + bounced_contacts = bounced_contacts.filter(name__icontains=data.get("name")) + failed_contacts = failed_contacts.filter(name__icontains=data.get("name")) + if data.get("email"): + contacts = contacts.filter(email__icontains=data.get("email")) bounced_contacts = bounced_contacts.filter( - name__icontains=data.get('name')) - failed_contacts = failed_contacts.filter( - name__icontains=data.get('name')) - if data.get('email'): - contacts = contacts.filter(email__icontains=data.get('email')) - bounced_contacts = bounced_contacts.filter(email__icontains=data.get('email')) - failed_contacts = failed_contacts.filter(email__icontains=data.get('email')) + email__icontains=data.get("email") + ) + failed_contacts = failed_contacts.filter(email__icontains=data.get("email")) # if data.get('domain_name'): # contacts = contacts.filter(email_domain__icontains=data.get('domain_name')) # bounced_contacts = bounced_contacts.filter(email_domain__icontains=data.get('domain_name')) # failed_contacts = failed_contacts.filter(email_domain__icontains=data.get('domain_name')) - if data.get('created_by'): - contacts = contacts.filter(created_by_id=data.get('created_by')) - bounced_contacts = bounced_contacts.filter(created_by_id=data.get('created_by')) - failed_contacts = failed_contacts.filter(created_by_id=data.get('created_by')) + if data.get("created_by"): + contacts = contacts.filter(created_by_id=data.get("created_by")) + bounced_contacts = bounced_contacts.filter( + created_by_id=data.get("created_by") + ) + failed_contacts = failed_contacts.filter( + created_by_id=data.get("created_by") + ) - if data.get('contact_list'): - contacts = contacts.filter(contact_list__id=data.get('contact_list')) - bounced_contacts = bounced_contacts.filter(contact_list__id=data.get('contact_list')) - failed_contacts = failed_contacts.filter(contact_list__id=data.get('contact_list')) + if data.get("contact_list"): + contacts = contacts.filter(contact_list__id=data.get("contact_list")) + bounced_contacts = bounced_contacts.filter( + contact_list__id=data.get("contact_list") + ) + failed_contacts = failed_contacts.filter( + contact_list__id=data.get("contact_list") + ) - # shows filter form in marketing-contact if any search + # shows filter form in marketing-contact if any search if ( - data.get('name')or - data.get('email')or - data.get('created_by')or - data.get('contact_list') - ): + data.get("name") + or data.get("email") + or data.get("created_by") + or data.get("contact_list") + ): search = True - context = {'contacts': contacts, 'users': users, 'contact_lists': contact_lists, - 'bounced_contacts': bounced_contacts, 'failed_contacts': failed_contacts, - 'search':search, + context = { + "contacts": contacts, + "users": users, + "contact_lists": contact_lists, + "bounced_contacts": bounced_contacts, + "failed_contacts": failed_contacts, + "search": search, } - return render(request, 'search_contact_emails.html', context) + return render(request, "search_contact_emails.html", context) # class MarketingContactEmailSearch(SearchView): diff --git a/opportunity/apps.py b/opportunity/apps.py index 167fd61..c16ad4b 100644 --- a/opportunity/apps.py +++ b/opportunity/apps.py @@ -2,4 +2,4 @@ class OpportunityConfig(AppConfig): - name = 'opportunity' + name = "opportunity" diff --git a/opportunity/migrations/0001_initial.py b/opportunity/migrations/0001_initial.py index 7b5da17..ee556c5 100644 --- a/opportunity/migrations/0001_initial.py +++ b/opportunity/migrations/0001_initial.py @@ -10,36 +10,285 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('accounts', '0001_initial'), - ('common', '0001_initial'), - ('contacts', '0001_initial'), + ("accounts", "0001_initial"), + ("common", "0001_initial"), + ("contacts", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Opportunity', + name="Opportunity", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=64, verbose_name='Name')), - ('stage', models.CharField(choices=[('QUALIFICATION', 'QUALIFICATION'), ('NEEDS ANALYSIS', 'NEEDS ANALYSIS'), ('VALUE PROPOSITION', 'VALUE PROPOSITION'), ('ID.DECISION MAKERS', 'ID.DECISION MAKERS'), ('PERCEPTION ANALYSIS', 'PERCEPTION ANALYSIS'), ('PROPOSAL/PRICE QUOTE', 'PROPOSAL/PRICE QUOTE'), ('NEGOTIATION/REVIEW', 'NEGOTIATION/REVIEW'), ('CLOSED WON', 'CLOSED WON'), ('CLOSED LOST', 'CLOSED LOST')], max_length=64, verbose_name='Stage')), - ('currency', models.CharField(blank=True, choices=[('AED', 'AED, Dirham'), ('AFN', 'AFN, Afghani'), ('ALL', 'ALL, Lek'), ('AMD', 'AMD, Dram'), ('ANG', 'ANG, Guilder'), ('AOA', 'AOA, Kwanza'), ('ARS', 'ARS, Peso'), ('AUD', 'AUD, Dollar'), ('AWG', 'AWG, Guilder'), ('AZN', 'AZN, Manat'), ('BAM', 'BAM, Marka'), ('BBD', 'BBD, Dollar'), ('BDT', 'BDT, Taka'), ('BGN', 'BGN, Lev'), ('BHD', 'BHD, Dinar'), ('BIF', 'BIF, Franc'), ('BMD', 'BMD, Dollar'), ('BND', 'BND, Dollar'), ('BOB', 'BOB, Boliviano'), ('BRL', 'BRL, Real'), ('BSD', 'BSD, Dollar'), ('BTN', 'BTN, Ngultrum'), ('BWP', 'BWP, Pula'), ('BYR', 'BYR, Ruble'), ('BZD', 'BZD, Dollar'), ('CAD', 'CAD, Dollar'), ('CDF', 'CDF, Franc'), ('CHF', 'CHF, Franc'), ('CLP', 'CLP, Peso'), ('CNY', 'CNY, Yuan Renminbi'), ('COP', 'COP, Peso'), ('CRC', 'CRC, Colon'), ('CUP', 'CUP, Peso'), ('CVE', 'CVE, Escudo'), ('CZK', 'CZK, Koruna'), ('DJF', 'DJF, Franc'), ('DKK', 'DKK, Krone'), ('DOP', 'DOP, Peso'), ('DZD', 'DZD, Dinar'), ('EGP', 'EGP, Pound'), ('ERN', 'ERN, Nakfa'), ('ETB', 'ETB, Birr'), ('EUR', 'EUR, Euro'), ('FJD', 'FJD, Dollar'), ('FKP', 'FKP, Pound'), ('GBP', 'GBP, Pound'), ('GEL', 'GEL, Lari'), ('GHS', 'GHS, Cedi'), ('GIP', 'GIP, Pound'), ('GMD', 'GMD, Dalasi'), ('GNF', 'GNF, Franc'), ('GTQ', 'GTQ, Quetzal'), ('GYD', 'GYD, Dollar'), ('HKD', 'HKD, Dollar'), ('HNL', 'HNL, Lempira'), ('HRK', 'HRK, Kuna'), ('HTG', 'HTG, Gourde'), ('HUF', 'HUF, Forint'), ('IDR', 'IDR, Rupiah'), ('ILS', 'ILS, Shekel'), ('INR', 'INR, Rupee'), ('IQD', 'IQD, Dinar'), ('IRR', 'IRR, Rial'), ('ISK', 'ISK, Krona'), ('JMD', 'JMD, Dollar'), ('JOD', 'JOD, Dinar'), ('JPY', 'JPY, Yen'), ('KES', 'KES, Shilling'), ('KGS', 'KGS, Som'), ('KHR', 'KHR, Riels'), ('KMF', 'KMF, Franc'), ('KPW', 'KPW, Won'), ('KRW', 'KRW, Won'), ('KWD', 'KWD, Dinar'), ('KYD', 'KYD, Dollar'), ('KZT', 'KZT, Tenge'), ('LAK', 'LAK, Kip'), ('LBP', 'LBP, Pound'), ('LKR', 'LKR, Rupee'), ('LRD', 'LRD, Dollar'), ('LSL', 'LSL, Loti'), ('LTL', 'LTL, Litas'), ('LVL', 'LVL, Lat'), ('LYD', 'LYD, Dinar'), ('MAD', 'MAD, Dirham'), ('MDL', 'MDL, Leu'), ('MGA', 'MGA, Ariary'), ('MKD', 'MKD, Denar'), ('MMK', 'MMK, Kyat'), ('MNT', 'MNT, Tugrik'), ('MOP', 'MOP, Pataca'), ('MRO', 'MRO, Ouguiya'), ('MUR', 'MUR, Rupee'), ('MVR', 'MVR, Rufiyaa'), ('MWK', 'MWK, Kwacha'), ('MXN', 'MXN, Peso'), ('MYR', 'MYR, Ringgit'), ('MZN', 'MZN, Metical'), ('NAD', 'NAD, Dollar'), ('NGN', 'NGN, Naira'), ('NIO', 'NIO, Cordoba'), ('NOK', 'NOK, Krone'), ('NPR', 'NPR, Rupee'), ('NZD', 'NZD, Dollar'), ('OMR', 'OMR, Rial'), ('PAB', 'PAB, Balboa'), ('PEN', 'PEN, Sol'), ('PGK', 'PGK, Kina'), ('PHP', 'PHP, Peso'), ('PKR', 'PKR, Rupee'), ('PLN', 'PLN, Zloty'), ('PYG', 'PYG, Guarani'), ('QAR', 'QAR, Rial'), ('RON', 'RON, Leu'), ('RSD', 'RSD, Dinar'), ('RUB', 'RUB, Ruble'), ('RWF', 'RWF, Franc'), ('SAR', 'SAR, Rial'), ('SBD', 'SBD, Dollar'), ('SCR', 'SCR, Rupee'), ('SDG', 'SDG, Pound'), ('SEK', 'SEK, Krona'), ('SGD', 'SGD, Dollar'), ('SHP', 'SHP, Pound'), ('SLL', 'SLL, Leone'), ('SOS', 'SOS, Shilling'), ('SRD', 'SRD, Dollar'), ('SSP', 'SSP, Pound'), ('STD', 'STD, Dobra'), ('SYP', 'SYP, Pound'), ('SZL', 'SZL, Lilangeni'), ('THB', 'THB, Baht'), ('TJS', 'TJS, Somoni'), ('TMT', 'TMT, Manat'), ('TND', 'TND, Dinar'), ('TOP', 'TOP, Paanga'), ('TRY', 'TRY, Lira'), ('TTD', 'TTD, Dollar'), ('TWD', 'TWD, Dollar'), ('TZS', 'TZS, Shilling'), ('UAH', 'UAH, Hryvnia'), ('UGX', 'UGX, Shilling'), ('USD', '$, Dollar'), ('UYU', 'UYU, Peso'), ('UZS', 'UZS, Som'), ('VEF', 'VEF, Bolivar'), ('VND', 'VND, Dong'), ('VUV', 'VUV, Vatu'), ('WST', 'WST, Tala'), ('XAF', 'XAF, Franc'), ('XCD', 'XCD, Dollar'), ('XOF', 'XOF, Franc'), ('XPF', 'XPF, Franc'), ('YER', 'YER, Rial'), ('ZAR', 'ZAR, Rand'), ('ZMK', 'ZMK, Kwacha'), ('ZWL', 'ZWL, Dollar')], max_length=3, null=True)), - ('amount', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True, verbose_name='Opportunity Amount')), - ('lead_source', models.CharField(blank=True, choices=[('NONE', 'NONE'), ('CALL', 'CALL'), ('EMAIL', ' EMAIL'), ('EXISTING CUSTOMER', 'EXISTING CUSTOMER'), ('PARTNER', 'PARTNER'), ('PUBLIC RELATIONS', 'PUBLIC RELATIONS'), ('CAMPAIGN', 'CAMPAIGN'), ('WEBSITE', 'WEBSITE'), ('OTHER', 'OTHER')], max_length=255, null=True, verbose_name='Source of Lead')), - ('probability', models.IntegerField(blank=True, default=0, null=True)), - ('closed_on', models.DateField(blank=True, null=True)), - ('description', models.TextField(blank=True, null=True)), - ('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Created on')), - ('is_active', models.BooleanField(default=False)), - ('account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to='accounts.Account')), - ('assigned_to', models.ManyToManyField(related_name='opportunity_assigned_to', to=settings.AUTH_USER_MODEL)), - ('closed_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ('contacts', models.ManyToManyField(to='contacts.Contact')), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opportunity_created_by', to=settings.AUTH_USER_MODEL)), - ('teams', models.ManyToManyField(to='common.Team')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=64, verbose_name="Name")), + ( + "stage", + models.CharField( + choices=[ + ("QUALIFICATION", "QUALIFICATION"), + ("NEEDS ANALYSIS", "NEEDS ANALYSIS"), + ("VALUE PROPOSITION", "VALUE PROPOSITION"), + ("ID.DECISION MAKERS", "ID.DECISION MAKERS"), + ("PERCEPTION ANALYSIS", "PERCEPTION ANALYSIS"), + ("PROPOSAL/PRICE QUOTE", "PROPOSAL/PRICE QUOTE"), + ("NEGOTIATION/REVIEW", "NEGOTIATION/REVIEW"), + ("CLOSED WON", "CLOSED WON"), + ("CLOSED LOST", "CLOSED LOST"), + ], + max_length=64, + verbose_name="Stage", + ), + ), + ( + "currency", + models.CharField( + blank=True, + choices=[ + ("AED", "AED, Dirham"), + ("AFN", "AFN, Afghani"), + ("ALL", "ALL, Lek"), + ("AMD", "AMD, Dram"), + ("ANG", "ANG, Guilder"), + ("AOA", "AOA, Kwanza"), + ("ARS", "ARS, Peso"), + ("AUD", "AUD, Dollar"), + ("AWG", "AWG, Guilder"), + ("AZN", "AZN, Manat"), + ("BAM", "BAM, Marka"), + ("BBD", "BBD, Dollar"), + ("BDT", "BDT, Taka"), + ("BGN", "BGN, Lev"), + ("BHD", "BHD, Dinar"), + ("BIF", "BIF, Franc"), + ("BMD", "BMD, Dollar"), + ("BND", "BND, Dollar"), + ("BOB", "BOB, Boliviano"), + ("BRL", "BRL, Real"), + ("BSD", "BSD, Dollar"), + ("BTN", "BTN, Ngultrum"), + ("BWP", "BWP, Pula"), + ("BYR", "BYR, Ruble"), + ("BZD", "BZD, Dollar"), + ("CAD", "CAD, Dollar"), + ("CDF", "CDF, Franc"), + ("CHF", "CHF, Franc"), + ("CLP", "CLP, Peso"), + ("CNY", "CNY, Yuan Renminbi"), + ("COP", "COP, Peso"), + ("CRC", "CRC, Colon"), + ("CUP", "CUP, Peso"), + ("CVE", "CVE, Escudo"), + ("CZK", "CZK, Koruna"), + ("DJF", "DJF, Franc"), + ("DKK", "DKK, Krone"), + ("DOP", "DOP, Peso"), + ("DZD", "DZD, Dinar"), + ("EGP", "EGP, Pound"), + ("ERN", "ERN, Nakfa"), + ("ETB", "ETB, Birr"), + ("EUR", "EUR, Euro"), + ("FJD", "FJD, Dollar"), + ("FKP", "FKP, Pound"), + ("GBP", "GBP, Pound"), + ("GEL", "GEL, Lari"), + ("GHS", "GHS, Cedi"), + ("GIP", "GIP, Pound"), + ("GMD", "GMD, Dalasi"), + ("GNF", "GNF, Franc"), + ("GTQ", "GTQ, Quetzal"), + ("GYD", "GYD, Dollar"), + ("HKD", "HKD, Dollar"), + ("HNL", "HNL, Lempira"), + ("HRK", "HRK, Kuna"), + ("HTG", "HTG, Gourde"), + ("HUF", "HUF, Forint"), + ("IDR", "IDR, Rupiah"), + ("ILS", "ILS, Shekel"), + ("INR", "INR, Rupee"), + ("IQD", "IQD, Dinar"), + ("IRR", "IRR, Rial"), + ("ISK", "ISK, Krona"), + ("JMD", "JMD, Dollar"), + ("JOD", "JOD, Dinar"), + ("JPY", "JPY, Yen"), + ("KES", "KES, Shilling"), + ("KGS", "KGS, Som"), + ("KHR", "KHR, Riels"), + ("KMF", "KMF, Franc"), + ("KPW", "KPW, Won"), + ("KRW", "KRW, Won"), + ("KWD", "KWD, Dinar"), + ("KYD", "KYD, Dollar"), + ("KZT", "KZT, Tenge"), + ("LAK", "LAK, Kip"), + ("LBP", "LBP, Pound"), + ("LKR", "LKR, Rupee"), + ("LRD", "LRD, Dollar"), + ("LSL", "LSL, Loti"), + ("LTL", "LTL, Litas"), + ("LVL", "LVL, Lat"), + ("LYD", "LYD, Dinar"), + ("MAD", "MAD, Dirham"), + ("MDL", "MDL, Leu"), + ("MGA", "MGA, Ariary"), + ("MKD", "MKD, Denar"), + ("MMK", "MMK, Kyat"), + ("MNT", "MNT, Tugrik"), + ("MOP", "MOP, Pataca"), + ("MRO", "MRO, Ouguiya"), + ("MUR", "MUR, Rupee"), + ("MVR", "MVR, Rufiyaa"), + ("MWK", "MWK, Kwacha"), + ("MXN", "MXN, Peso"), + ("MYR", "MYR, Ringgit"), + ("MZN", "MZN, Metical"), + ("NAD", "NAD, Dollar"), + ("NGN", "NGN, Naira"), + ("NIO", "NIO, Cordoba"), + ("NOK", "NOK, Krone"), + ("NPR", "NPR, Rupee"), + ("NZD", "NZD, Dollar"), + ("OMR", "OMR, Rial"), + ("PAB", "PAB, Balboa"), + ("PEN", "PEN, Sol"), + ("PGK", "PGK, Kina"), + ("PHP", "PHP, Peso"), + ("PKR", "PKR, Rupee"), + ("PLN", "PLN, Zloty"), + ("PYG", "PYG, Guarani"), + ("QAR", "QAR, Rial"), + ("RON", "RON, Leu"), + ("RSD", "RSD, Dinar"), + ("RUB", "RUB, Ruble"), + ("RWF", "RWF, Franc"), + ("SAR", "SAR, Rial"), + ("SBD", "SBD, Dollar"), + ("SCR", "SCR, Rupee"), + ("SDG", "SDG, Pound"), + ("SEK", "SEK, Krona"), + ("SGD", "SGD, Dollar"), + ("SHP", "SHP, Pound"), + ("SLL", "SLL, Leone"), + ("SOS", "SOS, Shilling"), + ("SRD", "SRD, Dollar"), + ("SSP", "SSP, Pound"), + ("STD", "STD, Dobra"), + ("SYP", "SYP, Pound"), + ("SZL", "SZL, Lilangeni"), + ("THB", "THB, Baht"), + ("TJS", "TJS, Somoni"), + ("TMT", "TMT, Manat"), + ("TND", "TND, Dinar"), + ("TOP", "TOP, Paanga"), + ("TRY", "TRY, Lira"), + ("TTD", "TTD, Dollar"), + ("TWD", "TWD, Dollar"), + ("TZS", "TZS, Shilling"), + ("UAH", "UAH, Hryvnia"), + ("UGX", "UGX, Shilling"), + ("USD", "$, Dollar"), + ("UYU", "UYU, Peso"), + ("UZS", "UZS, Som"), + ("VEF", "VEF, Bolivar"), + ("VND", "VND, Dong"), + ("VUV", "VUV, Vatu"), + ("WST", "WST, Tala"), + ("XAF", "XAF, Franc"), + ("XCD", "XCD, Dollar"), + ("XOF", "XOF, Franc"), + ("XPF", "XPF, Franc"), + ("YER", "YER, Rial"), + ("ZAR", "ZAR, Rand"), + ("ZMK", "ZMK, Kwacha"), + ("ZWL", "ZWL, Dollar"), + ], + max_length=3, + null=True, + ), + ), + ( + "amount", + models.DecimalField( + blank=True, + decimal_places=2, + max_digits=12, + null=True, + verbose_name="Opportunity Amount", + ), + ), + ( + "lead_source", + models.CharField( + blank=True, + choices=[ + ("NONE", "NONE"), + ("CALL", "CALL"), + ("EMAIL", " EMAIL"), + ("EXISTING CUSTOMER", "EXISTING CUSTOMER"), + ("PARTNER", "PARTNER"), + ("PUBLIC RELATIONS", "PUBLIC RELATIONS"), + ("CAMPAIGN", "CAMPAIGN"), + ("WEBSITE", "WEBSITE"), + ("OTHER", "OTHER"), + ], + max_length=255, + null=True, + verbose_name="Source of Lead", + ), + ), + ("probability", models.IntegerField(blank=True, default=0, null=True)), + ("closed_on", models.DateField(blank=True, null=True)), + ("description", models.TextField(blank=True, null=True)), + ( + "created_on", + models.DateTimeField(auto_now_add=True, verbose_name="Created on"), + ), + ("is_active", models.BooleanField(default=False)), + ( + "account", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="opportunities", + to="accounts.Account", + ), + ), + ( + "assigned_to", + models.ManyToManyField( + related_name="opportunity_assigned_to", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "closed_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ("contacts", models.ManyToManyField(to="contacts.Contact")), + ( + "created_by", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="opportunity_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), + ("teams", models.ManyToManyField(to="common.Team")), ], - options={ - 'ordering': ['-created_on'], - }, + options={"ordering": ["-created_on"],}, ), ] diff --git a/opportunity/migrations/0002_opportunity_tags.py b/opportunity/migrations/0002_opportunity_tags.py index 828f6c8..4fae672 100644 --- a/opportunity/migrations/0002_opportunity_tags.py +++ b/opportunity/migrations/0002_opportunity_tags.py @@ -6,14 +6,14 @@ class Migration(migrations.Migration): dependencies = [ - ('accounts', '0004_account_status'), - ('opportunity', '0001_initial'), + ("accounts", "0004_account_status"), + ("opportunity", "0001_initial"), ] operations = [ migrations.AddField( - model_name='opportunity', - name='tags', - field=models.ManyToManyField(blank=True, to='accounts.Tags'), + model_name="opportunity", + name="tags", + field=models.ManyToManyField(blank=True, to="accounts.Tags"), ), ] diff --git a/opportunity/migrations/0003_auto_20190212_1334.py b/opportunity/migrations/0003_auto_20190212_1334.py index 23ce3a7..30496c2 100644 --- a/opportunity/migrations/0003_auto_20190212_1334.py +++ b/opportunity/migrations/0003_auto_20190212_1334.py @@ -8,22 +8,28 @@ class Migration(migrations.Migration): dependencies = [ - ('opportunity', '0002_opportunity_tags'), + ("opportunity", "0002_opportunity_tags"), ] operations = [ - migrations.RemoveField( - model_name='opportunity', - name='teams', - ), + migrations.RemoveField(model_name="opportunity", name="teams",), migrations.AlterField( - model_name='opportunity', - name='closed_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + model_name="opportunity", + name="closed_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='opportunity', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='opportunity_created_by', to=settings.AUTH_USER_MODEL), + model_name="opportunity", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="opportunity_created_by", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/opportunity/migrations/0004_opportunity_teams.py b/opportunity/migrations/0004_opportunity_teams.py index d160c10..629ad44 100644 --- a/opportunity/migrations/0004_opportunity_teams.py +++ b/opportunity/migrations/0004_opportunity_teams.py @@ -6,14 +6,16 @@ class Migration(migrations.Migration): dependencies = [ - ('teams', '0003_auto_20190909_1621'), - ('opportunity', '0003_auto_20190212_1334'), + ("teams", "0003_auto_20190909_1621"), + ("opportunity", "0003_auto_20190212_1334"), ] operations = [ migrations.AddField( - model_name='opportunity', - name='teams', - field=models.ManyToManyField(related_name='oppurtunity_teams', to='teams.Teams'), + model_name="opportunity", + name="teams", + field=models.ManyToManyField( + related_name="oppurtunity_teams", to="teams.Teams" + ), ), ] diff --git a/opportunity/migrations/0005_opportunity_company.py b/opportunity/migrations/0005_opportunity_company.py index b70e416..a7d05df 100644 --- a/opportunity/migrations/0005_opportunity_company.py +++ b/opportunity/migrations/0005_opportunity_company.py @@ -7,14 +7,19 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0021_document_company'), - ('opportunity', '0004_opportunity_teams'), + ("common", "0021_document_company"), + ("opportunity", "0004_opportunity_teams"), ] operations = [ migrations.AddField( - model_name='opportunity', - name='company', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Company'), + model_name="opportunity", + name="company", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="common.Company", + ), ), ] diff --git a/opportunity/tasks.py b/opportunity/tasks.py index 57415de..95ad4cb 100644 --- a/opportunity/tasks.py +++ b/opportunity/tasks.py @@ -11,32 +11,37 @@ @task -def send_email_to_assigned_user(recipients, opportunity_id, domain='demo.django-crm.io', protocol='http'): +def send_email_to_assigned_user( + recipients, opportunity_id, domain="demo.django-crm.io", protocol="http" +): """ Send Mail To Users When they are assigned to a opportunity """ opportunity = Opportunity.objects.get(id=opportunity_id) created_by = opportunity.created_by - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) for user in recipients: recipients_list = [] user = User.objects.filter(id=user, is_active=True).first() if user: - if (user.email not in blocked_emails) and (user.email.split('@')[-1] not in blocked_domains): + if (user.email not in blocked_emails) and ( + user.email.split("@")[-1] not in blocked_domains + ): recipients_list.append(user.email) context = {} - context["url"] = protocol + '://' + domain + \ - reverse('opportunity:opp_view', args=(opportunity.id,)) + context["url"] = ( + protocol + + "://" + + domain + + reverse("opportunity:opp_view", args=(opportunity.id,)) + ) context["user"] = user context["opportunity"] = opportunity context["created_by"] = created_by - subject = 'Assigned an opportunity for you.' + subject = "Assigned an opportunity for you." html_content = render_to_string( - 'assigned_to/opportunity_assigned.html', context=context) - - msg = EmailMessage( - subject, - html_content, - to=recipients_list + "assigned_to/opportunity_assigned.html", context=context ) + + msg = EmailMessage(subject, html_content, to=recipients_list) msg.content_subtype = "html" msg.send() diff --git a/opportunity/tests_celery_tasks.py b/opportunity/tests_celery_tasks.py index 06ba33c..a28ed40 100644 --- a/opportunity/tests_celery_tasks.py +++ b/opportunity/tests_celery_tasks.py @@ -6,11 +6,13 @@ class TestCeleryTasks(OpportunityModel, TestCase): - - @override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - CELERY_ALWAYS_EAGER=True, - BROKER_BACKEND='memory') + @override_settings( + CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, + CELERY_ALWAYS_EAGER=True, + BROKER_BACKEND="memory", + ) def test_celery_tasks(self): task = send_email_to_assigned_user.apply( - ([self.user.id, self.user1.id, ], self.opportunity.id,),) - self.assertEqual('SUCCESS', task.state) + ([self.user.id, self.user1.id,], self.opportunity.id,), + ) + self.assertEqual("SUCCESS", task.state) diff --git a/planner/admin.py b/planner/admin.py index 98c0f35..74e37dc 100644 --- a/planner/admin.py +++ b/planner/admin.py @@ -1,6 +1,7 @@ from django.contrib import admin from planner.models import Event, Reminder + # Register your models here. admin.site.register(Event) diff --git a/planner/apps.py b/planner/apps.py index aa9adee..a52d5f9 100644 --- a/planner/apps.py +++ b/planner/apps.py @@ -2,4 +2,4 @@ class PlannerConfig(AppConfig): - name = 'planner' + name = "planner" diff --git a/planner/forms.py b/planner/forms.py index 9bfeb77..851747e 100644 --- a/planner/forms.py +++ b/planner/forms.py @@ -55,27 +55,29 @@ class ReminderForm(forms.ModelForm): class Meta: model = Reminder - fields = '__all__' + fields = "__all__" - reminder_type = forms.ChoiceField(choices=( - ('Email', 'Email'), ('Popup', 'Popup')), - widget=forms.Select(attrs={ - 'class': 'form-control input-sm'})) - reminder_time = forms.ChoiceField(choices=(("0", "on time"), - ("60", "1m before"), - ("120", "2m before"), - ("300", "5m before"), - ("600", "10m before"), - ("900", "15m before"), - ("1800", "30m before"), - ("3600", "1h before"), - ("7200", "2h before"), - ("10800", "3h before"), - ("18000", "5h before"), - ("86400", "1d before") - ), - widget=forms.Select(attrs={ - 'class': 'form-control input-sm'})) + reminder_type = forms.ChoiceField( + choices=(("Email", "Email"), ("Popup", "Popup")), + widget=forms.Select(attrs={"class": "form-control input-sm"}), + ) + reminder_time = forms.ChoiceField( + choices=( + ("0", "on time"), + ("60", "1m before"), + ("120", "2m before"), + ("300", "5m before"), + ("600", "10m before"), + ("900", "15m before"), + ("1800", "30m before"), + ("3600", "1h before"), + ("7200", "2h before"), + ("10800", "3h before"), + ("18000", "5h before"), + ("86400", "1d before"), + ), + widget=forms.Select(attrs={"class": "form-control input-sm"}), + ) # class MeetingEventForm(EventForm): diff --git a/planner/migrations/0001_initial.py b/planner/migrations/0001_initial.py index 251fda3..93652be 100644 --- a/planner/migrations/0001_initial.py +++ b/planner/migrations/0001_initial.py @@ -10,60 +10,182 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('common', '0002_auto_20190128_1237'), - ('contenttypes', '0002_remove_content_type_name'), - ('leads', '0001_initial'), - ('contacts', '0001_initial'), + ("common", "0002_auto_20190128_1237"), + ("contenttypes", "0002_remove_content_type_name"), + ("leads", "0001_initial"), + ("contacts", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Event', + name="Event", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=64, verbose_name='Event')), - ('event_type', models.CharField(max_length=7, verbose_name='Type of the event')), - ('object_id', models.PositiveIntegerField(blank=True, null=True)), - ('status', models.CharField(blank=True, choices=[('Planned', 'Planned'), ('Held', 'Held'), ('Not Held', 'Not Held'), ('Not Started', 'Not Started'), ('Started', 'Started'), ('Completed', 'Completed'), ('Canceled', 'Canceled'), ('Deferred', 'Deferred')], max_length=64, verbose_name='Status')), - ('direction', models.CharField(blank=True, max_length=20)), - ('start_date', models.DateField(default=None)), - ('close_date', models.DateField(default=None, null=True)), - ('duration', models.IntegerField(default=None, null=True, verbose_name='Durations')), - ('priority', models.CharField(blank=True, max_length=10)), - ('updated_on', models.DateTimeField(auto_now=True)), - ('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Created on')), - ('description', models.TextField(blank=True, null=True)), - ('is_active', models.BooleanField(default=False)), - ('assigned_to', models.ManyToManyField(blank=True, related_name='event_assigned_users', to=settings.AUTH_USER_MODEL)), - ('attendees_contacts', models.ManyToManyField(blank=True, related_name='attendees_contact', to='contacts.Contact')), - ('attendees_leads', models.ManyToManyField(blank=True, related_name='attendees_lead', to='leads.Lead')), - ('attendees_user', models.ManyToManyField(blank=True, related_name='attendees_user', to=settings.AUTH_USER_MODEL)), - ('content_type', models.ForeignKey(blank=True, choices=[(10, 'Account'), (13, 'Lead'), (14, 'Opportunity'), (11, 'Case')], limit_choices_to=models.Q(models.Q(('app_label', 'account'), ('id', 10), ('model', 'Account')), models.Q(('app_label', 'leads'), ('id', 13), ('model', 'Lead')), models.Q(('app_label', 'opportunity'), ('id', 14), ('model', 'Opportunity')), models.Q(('app_label', 'cases'), ('id', 11), ('model', 'Case')), _connector='OR'), null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='event_created_by', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=64, verbose_name="Event")), + ( + "event_type", + models.CharField(max_length=7, verbose_name="Type of the event"), + ), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ( + "status", + models.CharField( + blank=True, + choices=[ + ("Planned", "Planned"), + ("Held", "Held"), + ("Not Held", "Not Held"), + ("Not Started", "Not Started"), + ("Started", "Started"), + ("Completed", "Completed"), + ("Canceled", "Canceled"), + ("Deferred", "Deferred"), + ], + max_length=64, + verbose_name="Status", + ), + ), + ("direction", models.CharField(blank=True, max_length=20)), + ("start_date", models.DateField(default=None)), + ("close_date", models.DateField(default=None, null=True)), + ( + "duration", + models.IntegerField( + default=None, null=True, verbose_name="Durations" + ), + ), + ("priority", models.CharField(blank=True, max_length=10)), + ("updated_on", models.DateTimeField(auto_now=True)), + ( + "created_on", + models.DateTimeField(auto_now_add=True, verbose_name="Created on"), + ), + ("description", models.TextField(blank=True, null=True)), + ("is_active", models.BooleanField(default=False)), + ( + "assigned_to", + models.ManyToManyField( + blank=True, + related_name="event_assigned_users", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "attendees_contacts", + models.ManyToManyField( + blank=True, + related_name="attendees_contact", + to="contacts.Contact", + ), + ), + ( + "attendees_leads", + models.ManyToManyField( + blank=True, related_name="attendees_lead", to="leads.Lead" + ), + ), + ( + "attendees_user", + models.ManyToManyField( + blank=True, + related_name="attendees_user", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "content_type", + models.ForeignKey( + blank=True, + choices=[ + (10, "Account"), + (13, "Lead"), + (14, "Opportunity"), + (11, "Case"), + ], + limit_choices_to=models.Q( + models.Q( + ("app_label", "account"), + ("id", 10), + ("model", "Account"), + ), + models.Q( + ("app_label", "leads"), ("id", 13), ("model", "Lead") + ), + models.Q( + ("app_label", "opportunity"), + ("id", 14), + ("model", "Opportunity"), + ), + models.Q( + ("app_label", "cases"), ("id", 11), ("model", "Case") + ), + _connector="OR", + ), + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="contenttypes.ContentType", + ), + ), + ( + "created_by", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="event_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='Reminder', + name="Reminder", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reminder_type', models.CharField(blank=True, max_length=5, null=True)), - ('reminder_time', models.IntegerField(blank=True, null=True, verbose_name='Reminder')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "reminder_type", + models.CharField(blank=True, max_length=5, null=True), + ), + ( + "reminder_time", + models.IntegerField(blank=True, null=True, verbose_name="Reminder"), + ), ], ), migrations.AddField( - model_name='event', - name='reminders', - field=models.ManyToManyField(blank=True, to='planner.Reminder'), + model_name="event", + name="reminders", + field=models.ManyToManyField(blank=True, to="planner.Reminder"), ), migrations.AddField( - model_name='event', - name='teams', - field=models.ManyToManyField(blank=True, to='common.Team'), + model_name="event", + name="teams", + field=models.ManyToManyField(blank=True, to="common.Team"), ), migrations.AddField( - model_name='event', - name='updated_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='updated_user', to=settings.AUTH_USER_MODEL), + model_name="event", + name="updated_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="updated_user", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/planner/migrations/0002_auto_20190212_1334.py b/planner/migrations/0002_auto_20190212_1334.py index 8b189ea..75b268e 100644 --- a/planner/migrations/0002_auto_20190212_1334.py +++ b/planner/migrations/0002_auto_20190212_1334.py @@ -8,22 +8,30 @@ class Migration(migrations.Migration): dependencies = [ - ('planner', '0001_initial'), + ("planner", "0001_initial"), ] operations = [ - migrations.RemoveField( - model_name='event', - name='teams', - ), + migrations.RemoveField(model_name="event", name="teams",), migrations.AlterField( - model_name='event', - name='created_by', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='event_created_by', to=settings.AUTH_USER_MODEL), + model_name="event", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="event_created_by", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AlterField( - model_name='event', - name='updated_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='updated_user', to=settings.AUTH_USER_MODEL), + model_name="event", + name="updated_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="updated_user", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/planner/models.py b/planner/models.py index 6dfe828..72f2532 100644 --- a/planner/models.py +++ b/planner/models.py @@ -11,64 +11,81 @@ class Reminder(models.Model): - reminder_type = models.CharField(max_length=5, blank=True, - null=True) - reminder_time = models.IntegerField(pgettext_lazy( - "time of the reminder to event in Seconds", "Reminder"), - blank=True, null=True) + reminder_type = models.CharField(max_length=5, blank=True, null=True) + reminder_time = models.IntegerField( + pgettext_lazy("time of the reminder to event in Seconds", "Reminder"), + blank=True, + null=True, + ) def __str__(self): return self.reminder_type class Event(models.Model): - limit = models.Q(app_label='account', model='Account', id=10) | \ - models.Q(app_label='leads', model='Lead', id=13) | \ - models.Q(app_label='opportunity', model='Opportunity', id=14) | \ - models.Q(app_label='cases', model='Case', id=11) - name = models.CharField( - pgettext_lazy("Name of the Event", "Event"), - max_length=64) + limit = ( + models.Q(app_label="account", model="Account", id=10) + | models.Q(app_label="leads", model="Lead", id=13) + | models.Q(app_label="opportunity", model="Opportunity", id=14) + | models.Q(app_label="cases", model="Case", id=11) + ) + name = models.CharField(pgettext_lazy("Name of the Event", "Event"), max_length=64) event_type = models.CharField( - _("Type of the event"), max_length=7) # call meeting task - content_type = models.ForeignKey(ContentType, - on_delete=models.CASCADE, - blank=True, null=True, - limit_choices_to=limit, - choices=EVENT_PARENT_TYPE) + _("Type of the event"), max_length=7 + ) # call meeting task + content_type = models.ForeignKey( + ContentType, + on_delete=models.CASCADE, + blank=True, + null=True, + limit_choices_to=limit, + choices=EVENT_PARENT_TYPE, + ) object_id = models.PositiveIntegerField(blank=True, null=True) - parent = GenericForeignKey('content_type', 'object_id') + parent = GenericForeignKey("content_type", "object_id") status = models.CharField( - pgettext_lazy("status of the Event", "Status"), choices=EVENT_STATUS, - max_length=64, blank=True) + pgettext_lazy("status of the Event", "Status"), + choices=EVENT_STATUS, + max_length=64, + blank=True, + ) direction = models.CharField(max_length=20, blank=True) # only for calls # start_date = models.DateTimeField(default=None) # close_date = models.DateTimeField(default=None, null=True) start_date = models.DateField(default=None) close_date = models.DateField(default=None, null=True) - duration = models.IntegerField(pgettext_lazy( - "Duration of the Event in Seconds", "Durations"), default=None, - null=True) # not for tasks + duration = models.IntegerField( + pgettext_lazy("Duration of the Event in Seconds", "Durations"), + default=None, + null=True, + ) # not for tasks reminders = models.ManyToManyField(Reminder, blank=True) priority = models.CharField(max_length=10, blank=True) # only for task updated_on = models.DateTimeField(auto_now=True) updated_by = models.ForeignKey( - User, on_delete=models.SET_NULL, null=True, - blank=True, related_name='updated_user') - attendees_user = models.ManyToManyField(User, blank=True, - related_name='attendees_user') - attendees_contacts = models.ManyToManyField(Contact, blank=True, - related_name='attendees_contact' - ) - attendees_leads = models.ManyToManyField(Lead, blank=True, - related_name='attendees_lead') + User, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="updated_user", + ) + attendees_user = models.ManyToManyField( + User, blank=True, related_name="attendees_user" + ) + attendees_contacts = models.ManyToManyField( + Contact, blank=True, related_name="attendees_contact" + ) + attendees_leads = models.ManyToManyField( + Lead, blank=True, related_name="attendees_lead" + ) created_on = models.DateTimeField(_("Created on"), auto_now_add=True) created_by = models.ForeignKey( - User, related_name='event_created_by', - on_delete=models.SET_NULL, null=True) + User, related_name="event_created_by", on_delete=models.SET_NULL, null=True + ) assigned_to = models.ManyToManyField( - User, blank=True, related_name='event_assigned_users') + User, blank=True, related_name="event_assigned_users" + ) description = models.TextField(blank=True, null=True) is_active = models.BooleanField(default=False) diff --git a/setup.py b/setup.py index a18f5b4..d4fc063 100644 --- a/setup.py +++ b/setup.py @@ -1,47 +1,45 @@ import os from setuptools import setup, find_packages -with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme: +with open(os.path.join(os.path.dirname(__file__), "README.rst")) as readme: README = readme.read() # allow setup.py to be run from any path os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) -PROJECT_NAME = 'django_crm' +PROJECT_NAME = "django_crm" data_files = [] for dirpath, dirnames, filenames in os.walk(PROJECT_NAME): for i, dirname in enumerate(dirnames): - if dirname.startswith('.'): + if dirname.startswith("."): del dirnames[i] - if '__init__.py' in filenames: + if "__init__.py" in filenames: continue elif filenames: for f in filenames: - data_files.append(os.path.join( - dirpath[len(PROJECT_NAME) + 1:], f)) + data_files.append(os.path.join(dirpath[len(PROJECT_NAME) + 1 :], f)) setup( - name='django-crm', - version='0.7.0', - packages=find_packages(exclude=['tests', 'tests.*']), + name="django-crm", + version="0.7.0", + packages=find_packages(exclude=["tests", "tests.*"]), include_package_data=True, - description='An opensourse CRM developed on django framework', + description="An opensourse CRM developed on django framework", long_description=README, - url='https://github.com/MicroPyramid/Django-CRM.git', - author='Micropyramid', - author_email='hello@micropyramid.com', + url="https://github.com/MicroPyramid/Django-CRM.git", + author="Micropyramid", + author_email="hello@micropyramid.com", classifiers=[ - 'Development Status :: 4 - Beta', - 'Framework :: Django', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - ], - install_requires=[ + "Development Status :: 4 - Beta", + "Framework :: Django", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.2", + "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", ], + install_requires=[], ) diff --git a/tasks/admin.py b/tasks/admin.py index b886bac..01cb3ca 100644 --- a/tasks/admin.py +++ b/tasks/admin.py @@ -1,5 +1,6 @@ from django.contrib import admin from tasks.models import Task + # Register your models here. -admin.site.register(Task) \ No newline at end of file +admin.site.register(Task) diff --git a/tasks/apps.py b/tasks/apps.py index 2054722..5aadae0 100644 --- a/tasks/apps.py +++ b/tasks/apps.py @@ -2,4 +2,4 @@ class TasksConfig(AppConfig): - name = 'tasks' + name = "tasks" diff --git a/tasks/celery_tasks.py b/tasks/celery_tasks.py index c99e2ba..ebcd92e 100644 --- a/tasks/celery_tasks.py +++ b/tasks/celery_tasks.py @@ -12,29 +12,37 @@ @task -def send_email(task_id, recipients, domain='demo.django-crm.io', protocol='http'): +def send_email(task_id, recipients, domain="demo.django-crm.io", protocol="http"): task = Task.objects.filter(id=task_id).first() created_by = task.created_by - blocked_domains = BlockedDomain.objects.values_list('domain', flat=True) - blocked_emails = BlockedEmail.objects.values_list('email', flat=True) + blocked_domains = BlockedDomain.objects.values_list("domain", flat=True) + blocked_emails = BlockedEmail.objects.values_list("email", flat=True) for user in recipients: recipients_list = [] user = User.objects.filter(id=user, is_active=True).first() if user: - if (user.email not in blocked_emails) and (user.email.split('@')[-1] not in blocked_domains): + if (user.email not in blocked_emails) and ( + user.email.split("@")[-1] not in blocked_domains + ): recipients_list.append(user.email) - subject = ' Assigned a task for you .' + subject = " Assigned a task for you ." context = {} - context['task_title'] = task.title - context['task_id'] = task.id - context['task_created_by'] = task.created_by - context["url"] = protocol + '://' + domain + \ - reverse('tasks:task_detail', args=(task.id,)) - context['user'] = user + context["task_title"] = task.title + context["task_id"] = task.id + context["task_created_by"] = task.created_by + context["url"] = ( + protocol + + "://" + + domain + + reverse("tasks:task_detail", args=(task.id,)) + ) + context["user"] = user html_content = render_to_string( - 'tasks_email_template.html', context=context) + "tasks_email_template.html", context=context + ) msg = EmailMessage( - subject=subject, body=html_content, to=recipients_list) + subject=subject, body=html_content, to=recipients_list + ) msg.content_subtype = "html" msg.send() diff --git a/tasks/migrations/0001_initial.py b/tasks/migrations/0001_initial.py index 16639c8..fa040d1 100644 --- a/tasks/migrations/0001_initial.py +++ b/tasks/migrations/0001_initial.py @@ -10,26 +10,73 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('accounts', '0007_email'), + ("accounts", "0007_email"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('contacts', '0003_merge_20190214_1427'), + ("contacts", "0003_merge_20190214_1427"), ] operations = [ migrations.CreateModel( - name='Task', + name="Task", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=200, verbose_name='title')), - ('status', models.CharField(choices=[('New', 'New'), ('In Progress', 'In Progress'), ('Completed', 'Completed')], max_length=50, verbose_name='status')), - ('priority', models.CharField(choices=[('Low', 'Low'), ('Medium', 'Medium'), ('High', 'High')], max_length=50, verbose_name='priority')), - ('due_date', models.DateField(blank=True, null=True)), - ('account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='accounts_tasks', to='accounts.Account')), - ('assigned_to', models.ManyToManyField(related_name='users_tasks', to=settings.AUTH_USER_MODEL)), - ('contacts', models.ManyToManyField(related_name='contacts_tasks', to='contacts.Contact')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=200, verbose_name="title")), + ( + "status", + models.CharField( + choices=[ + ("New", "New"), + ("In Progress", "In Progress"), + ("Completed", "Completed"), + ], + max_length=50, + verbose_name="status", + ), + ), + ( + "priority", + models.CharField( + choices=[ + ("Low", "Low"), + ("Medium", "Medium"), + ("High", "High"), + ], + max_length=50, + verbose_name="priority", + ), + ), + ("due_date", models.DateField(blank=True, null=True)), + ( + "account", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="accounts_tasks", + to="accounts.Account", + ), + ), + ( + "assigned_to", + models.ManyToManyField( + related_name="users_tasks", to=settings.AUTH_USER_MODEL + ), + ), + ( + "contacts", + models.ManyToManyField( + related_name="contacts_tasks", to="contacts.Contact" + ), + ), ], - options={ - 'ordering': ['-due_date'], - }, + options={"ordering": ["-due_date"],}, ), ] diff --git a/tasks/migrations/0002_task_created_by.py b/tasks/migrations/0002_task_created_by.py index 482b2dc..35de810 100644 --- a/tasks/migrations/0002_task_created_by.py +++ b/tasks/migrations/0002_task_created_by.py @@ -9,13 +9,19 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('tasks', '0001_initial'), + ("tasks", "0001_initial"), ] operations = [ migrations.AddField( - model_name='task', - name='created_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task_created', to=settings.AUTH_USER_MODEL), + model_name="task", + name="created_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="task_created", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/tasks/migrations/0003_task_created_on.py b/tasks/migrations/0003_task_created_on.py index cca5339..a2e3521 100644 --- a/tasks/migrations/0003_task_created_on.py +++ b/tasks/migrations/0003_task_created_on.py @@ -7,14 +7,18 @@ class Migration(migrations.Migration): dependencies = [ - ('tasks', '0002_task_created_by'), + ("tasks", "0002_task_created_by"), ] operations = [ migrations.AddField( - model_name='task', - name='created_on', - field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created on'), + model_name="task", + name="created_on", + field=models.DateTimeField( + auto_now_add=True, + default=django.utils.timezone.now, + verbose_name="Created on", + ), preserve_default=False, ), ] diff --git a/tasks/migrations/0004_task_teams.py b/tasks/migrations/0004_task_teams.py index 1fd66eb..f0e9fec 100644 --- a/tasks/migrations/0004_task_teams.py +++ b/tasks/migrations/0004_task_teams.py @@ -6,14 +6,14 @@ class Migration(migrations.Migration): dependencies = [ - ('teams', '0003_auto_20190909_1621'), - ('tasks', '0003_task_created_on'), + ("teams", "0003_auto_20190909_1621"), + ("tasks", "0003_task_created_on"), ] operations = [ migrations.AddField( - model_name='task', - name='teams', - field=models.ManyToManyField(related_name='tasks_teams', to='teams.Teams'), + model_name="task", + name="teams", + field=models.ManyToManyField(related_name="tasks_teams", to="teams.Teams"), ), ] diff --git a/tasks/migrations/0005_task_company.py b/tasks/migrations/0005_task_company.py index f6e92ff..ea5671c 100644 --- a/tasks/migrations/0005_task_company.py +++ b/tasks/migrations/0005_task_company.py @@ -7,14 +7,19 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0021_document_company'), - ('tasks', '0004_task_teams'), + ("common", "0021_document_company"), + ("tasks", "0004_task_teams"), ] operations = [ migrations.AddField( - model_name='task', - name='company', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Company'), + model_name="task", + name="company", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="common.Company", + ), ), ] diff --git a/tasks/tests_celery_tasks.py b/tasks/tests_celery_tasks.py index 50111f5..4b0a4c0 100644 --- a/tasks/tests_celery_tasks.py +++ b/tasks/tests_celery_tasks.py @@ -6,10 +6,11 @@ class TestEventCeleryTasks(TaskCreateTest, TestCase): - - @override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - CELERY_ALWAYS_EAGER=True, - BROKER_BACKEND='memory') + @override_settings( + CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, + CELERY_ALWAYS_EAGER=True, + BROKER_BACKEND="memory", + ) def test_event_celery_tasks(self): task = send_email.apply((self.task.id, [self.user.id, self.user1.id,])) - self.assertEqual('SUCCESS', task.state) + self.assertEqual("SUCCESS", task.state) diff --git a/tasks/urls.py b/tasks/urls.py index 834780f..3808f50 100644 --- a/tasks/urls.py +++ b/tasks/urls.py @@ -1,21 +1,20 @@ from django.urls import path from tasks.views import * -app_name = 'tasks' +app_name = "tasks" urlpatterns = [ - path('', tasks_list, name='tasks_list'), - path('create/', task_create, name='task_create'), - path('detail//', task_detail, name='task_detail'), - path('edit//', task_edit, name='task_edit'), - path('delete//', task_delete, name='task_delete'), - - path('comment/add/', AddCommentView.as_view(), name="add_comment"), - path('comment/edit/', UpdateCommentView.as_view(), name="edit_comment"), - path('comment/remove/', DeleteCommentView.as_view(), name="remove_comment"), - - path('attachment/add/', AddAttachmentView.as_view(), name="add_attachment"), - path('attachment/remove/', DeleteAttachmentsView.as_view(), - name="remove_attachment"), + path("", tasks_list, name="tasks_list"), + path("create/", task_create, name="task_create"), + path("detail//", task_detail, name="task_detail"), + path("edit//", task_edit, name="task_edit"), + path("delete//", task_delete, name="task_delete"), + path("comment/add/", AddCommentView.as_view(), name="add_comment"), + path("comment/edit/", UpdateCommentView.as_view(), name="edit_comment"), + path("comment/remove/", DeleteCommentView.as_view(), name="remove_comment"), + path("attachment/add/", AddAttachmentView.as_view(), name="add_attachment"), + path( + "attachment/remove/", DeleteAttachmentsView.as_view(), name="remove_attachment" + ), ] diff --git a/tasks/utils.py b/tasks/utils.py index 2be9aca..8c0f3fe 100644 --- a/tasks/utils.py +++ b/tasks/utils.py @@ -1,11 +1,7 @@ STATUS_CHOICES = ( ("New", "New"), ("In Progress", "In Progress"), - ("Completed", "Completed") + ("Completed", "Completed"), ) -PRIORITY_CHOICES = ( - ("Low", "Low"), - ("Medium", "Medium"), - ("High", "High") -) +PRIORITY_CHOICES = (("Low", "Low"), ("Medium", "Medium"), ("High", "High")) diff --git a/teams/apps.py b/teams/apps.py index 17954d6..2cbcff7 100644 --- a/teams/apps.py +++ b/teams/apps.py @@ -2,4 +2,4 @@ class TeamsConfig(AppConfig): - name = 'teams' + name = "teams" diff --git a/teams/migrations/0001_initial.py b/teams/migrations/0001_initial.py index 3795824..950c505 100644 --- a/teams/migrations/0001_initial.py +++ b/teams/migrations/0001_initial.py @@ -14,12 +14,25 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Teams', + name="Teams", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('description', models.TextField()), - ('users', models.ManyToManyField(related_name='user_teams', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=100)), + ("description", models.TextField()), + ( + "users", + models.ManyToManyField( + related_name="user_teams", to=settings.AUTH_USER_MODEL + ), + ), ], ), ] diff --git a/teams/migrations/0002_auto_20190624_1250.py b/teams/migrations/0002_auto_20190624_1250.py index cc2cb79..4506fd6 100644 --- a/teams/migrations/0002_auto_20190624_1250.py +++ b/teams/migrations/0002_auto_20190624_1250.py @@ -10,19 +10,29 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('teams', '0001_initial'), + ("teams", "0001_initial"), ] operations = [ migrations.AddField( - model_name='teams', - name='created_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='teams_created', to=settings.AUTH_USER_MODEL), + model_name="teams", + name="created_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="teams_created", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='teams', - name='created_on', - field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created on'), + model_name="teams", + name="created_on", + field=models.DateTimeField( + auto_now_add=True, + default=django.utils.timezone.now, + verbose_name="Created on", + ), preserve_default=False, ), ] diff --git a/teams/migrations/0003_auto_20190909_1621.py b/teams/migrations/0003_auto_20190909_1621.py index df76daa..20668cc 100644 --- a/teams/migrations/0003_auto_20190909_1621.py +++ b/teams/migrations/0003_auto_20190909_1621.py @@ -6,12 +6,9 @@ class Migration(migrations.Migration): dependencies = [ - ('teams', '0002_auto_20190624_1250'), + ("teams", "0002_auto_20190624_1250"), ] operations = [ - migrations.AlterModelOptions( - name='teams', - options={'ordering': ('id',)}, - ), + migrations.AlterModelOptions(name="teams", options={"ordering": ("id",)},), ] diff --git a/teams/migrations/0004_teams_company.py b/teams/migrations/0004_teams_company.py index a07c95e..2728c33 100644 --- a/teams/migrations/0004_teams_company.py +++ b/teams/migrations/0004_teams_company.py @@ -7,14 +7,19 @@ class Migration(migrations.Migration): dependencies = [ - ('common', '0020_auto_20200409_1653'), - ('teams', '0003_auto_20190909_1621'), + ("common", "0020_auto_20200409_1653"), + ("teams", "0003_auto_20190909_1621"), ] operations = [ migrations.AddField( - model_name='teams', - name='company', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Company'), + model_name="teams", + name="company", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="common.Company", + ), ), ] diff --git a/teams/urls.py b/teams/urls.py index 7ac2b3d..d90d2d3 100644 --- a/teams/urls.py +++ b/teams/urls.py @@ -1,17 +1,14 @@ from django.urls import path -from teams.views import ( - teams_list, team_create, team_edit, team_delete, team_detail -) +from teams.views import teams_list, team_create, team_edit, team_delete, team_detail -app_name = 'teams' +app_name = "teams" urlpatterns = [ - path('', teams_list, name='teams_list'), - path('create/', team_create, name='team_create'), - path('edit//', team_edit, name='team_edit'), - path('delete//', team_delete, name='team_delete'), - path('detail//', team_detail, name='team_detail'), - + path("", teams_list, name="teams_list"), + path("create/", team_create, name="team_create"), + path("edit//", team_edit, name="team_edit"), + path("delete//", team_delete, name="team_delete"), + path("detail//", team_detail, name="team_detail"), ]