New changes (#218)

* added models contacts, contactlists, campaigns for marketing module & completed crud operations for email templates model in marketing, added contact list crud operations, sample csv file added for reference, updated actions icons in all list pages, fixed issue of getting closed leads in accounts create/edit pages

* completed crud operations to use email templates model, displaying contact list contacts and faile/invalid contacts, completed changes for new campaign, removed firstname and lastname as required and added title as required in leads create page, completed changes for Campaign email send with attachment
This commit is contained in:
nikhila05 2019-03-22 15:15:24 +05:30 committed by GitHub
parent 88ae667245
commit e2772eca8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1821 additions and 1157 deletions

View file

@ -1,23 +1,36 @@
from django.test import TestCase
from cases.models import Case
from accounts.models import Account
from common.models import User, Comment, Attachments
from accounts.models import Account, Tags
from common.models import User, Comment, Attachments, Address
from django.urls import reverse
from leads.models import Lead
from contacts.models import Contact
from django.core.files.uploadedfile import SimpleUploadedFile
class AccountCreateTest(object):
def setUp(self):
self.user = User.objects.create(
first_name="mike", username='mike', email='u@mp.com', role='ADMIN')
self.user.set_password('mike2293')
self.user.save()
self.user1 = User.objects.create(
first_name="mp",
username='mp',
email='mp@micropyramid.com',
role="USER")
self.user1.set_password('mp')
self.user1.save()
self.account = Account.objects.create(
name="mike", email="mike@micropyramid.com", phone="8333855552",
billing_address_line="", billing_street="KPHB",
billing_city="New York",
billing_state="usa", billing_postcode="500073",
billing_country="IN",
website="www.mike.com", created_by=self.user,
website="www.mike.com", created_by=self.user, status="open",
industry="SOFTWARE", description="Yes.. Testing Done")
self.case = Case.objects.create(
name="raghu", case_type="Problem",
@ -33,9 +46,40 @@ class AccountCreateTest(object):
created_by=self.user, account=self.account
)
self.client.login(email='u@mp.com', password='mike2293')
self.lead = Lead.objects.create(title="LeadCreation",
first_name="Alisa",
last_name="k",
email="Alisak1993@gmail.com",
address_line="",
street="Arcade enclave colony",
city="NewTown",
state="California",
postcode="5079",
country="AD",
website="www.gmail.com",
status="assigned",
source="Call",
opportunity_amount="700",
description="Iam an Lead",
created_by=self.user)
self.lead.assigned_to.add(self.user)
self.address = Address.objects.create(
street="5th phase",
city="Orlando",
state="Florida",
postcode=502279, country="AD")
self.contact = Contact.objects.create(
first_name="contact",
email="contact@gmail.com",
phone="12345",
address=self.address,
description="contact",
created_by=self.user)
class AccountsCreateTestCase(AccountCreateTest, TestCase):
def test_account_create_url(self):
response = self.client.get('/accounts/create/', {
'name': "mike", 'email': "mike@micropyramid.com",
@ -87,12 +131,14 @@ class AccountsListTestCase(AccountCreateTest, TestCase):
class AccountsCountTestCase(AccountCreateTest, TestCase):
def test_accounts_list_count(self):
count = Account.objects.all().count()
self.assertEqual(count, 1)
class AccountsViewTestCase(AccountCreateTest, TestCase):
def test_accounts_view(self):
self.accounts = Account.objects.all()
response = self.client.get(
@ -102,6 +148,7 @@ class AccountsViewTestCase(AccountCreateTest, TestCase):
class AccountsRemoveTestCase(AccountCreateTest, TestCase):
def test_accounts_remove(self):
response = self.client.get(
'/accounts/' + str(self.account.id) + '/delete/')
@ -114,6 +161,7 @@ class AccountsRemoveTestCase(AccountCreateTest, TestCase):
class AccountsUpdateUrlTestCase(AccountCreateTest, TestCase):
def test_accounts_update(self):
response = self.client.get(
'/accounts/' + str(self.account.id) + '/edit/', {
@ -187,14 +235,25 @@ class AccountModelTest(AccountCreateTest, TestCase):
class CommentTestCase(AccountCreateTest, TestCase):
def test_comment_add(self):
response = self.client.post(
'/accounts/comment/add/', {'accountid': self.account.id})
'/accounts/comment/add/', {'accountid': self.account.id,
'comment': 'comment'})
self.assertEqual(response.status_code, 200)
def test_comment_create(self):
response = self.client.post(
'/accounts/comment/add/', {'accountid': self.account.id,
'comment': 'comment'})
self.assertEqual(response.status_code, 200)
def test_comment_edit(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/accounts/comment/edit/', {'commentid': self.comment.id})
'/accounts/comment/edit/', {'commentid': self.comment.id,
'comment': 'comment'})
self.assertEqual(response.status_code, 200)
# def test_comment_valid(self):
@ -210,16 +269,27 @@ class CommentTestCase(AccountCreateTest, TestCase):
'/accounts/comment/remove/', {'comment_id': self.comment.id})
self.assertEqual(response.status_code, 200)
def test_comment_deletion(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/accounts/comment/remove/', {'comment_id': self.comment.id})
self.assertEqual(response.status_code, 200)
class AttachmentTestCase(AccountCreateTest, TestCase):
def test_attachment_add(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/accounts/attachment/add/', {'accountid': self.account.id})
self.assertEqual(response.status_code, 200)
def test_attachment_valid(self):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post(
'/accounts/attachment/add/', {'accountid': self.account.id})
'/accounts/attachment/add/', {'accountid': self.account.id,
'attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())})
self.assertEqual(response.status_code, 200)
def test_attachment_delete(self):
@ -227,3 +297,52 @@ class AttachmentTestCase(AccountCreateTest, TestCase):
'/accounts/attachment/remove/',
{'attachment_id': self.attachment.id})
self.assertEqual(response.status_code, 200)
def test_attachment_deletion(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/accounts/attachment/remove/',
{'attachment_id': self.attachment.id})
self.assertEqual(response.status_code, 200)
class TagCreateTest(object):
def setUp(self):
self.tag = Tags.objects.create(
name="tag", slug="tag1")
class TagModelTest(TagCreateTest, TestCase):
def test_string_representation(self):
tag = Tags(name="tag", slug="tag1")
self.assertEqual(str(self.tag.name), tag.name)
class TestCreateLeadPostView(AccountCreateTest, TestCase):
def test_create_lead_post_status(self):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post(reverse(
'accounts:new_account'), {"name": "mike",
"email": "mike@micropyramid.com",
"phone": "+91-833-385-5552",
"billing_address_line": "sddsv",
"billing_street": "KPHB",
"billing_city": "New York",
"billing_state": "usa",
"billing_postcode": "500073",
"billing_country": "IN",
"website": "www.mike.com",
"created_by": self.user,
"status": "open",
"industry": "SOFTWARE",
"description": "Yes.. Testing Done",
"lead": str(self.lead.id),
'contacts': str(self.contact.id),
'tags': 'tag1',
'account_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())
})
self.assertEqual(response.status_code, 302)

View file

@ -4,9 +4,12 @@ from contacts.models import Contact
from accounts.models import Account
from common.models import Address, Comment, Attachments
from common.models import User
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile
class CaseCreation(object):
def setUp(self):
self.address = Address.objects.create(
street="6th phase",
@ -21,6 +24,13 @@ class CaseCreation(object):
role="ADMIN")
self.user.set_password('robert')
self.user.save()
self.user1 = User.objects.create(
first_name="mp",
username='mp',
email='mp@micropyramid.com',
role="USER")
self.user1.set_password('mp')
self.user1.save()
self.client.login(email='r@mp.com', password='robert')
self.client.login(email='r@mp.com', password='robert')
@ -56,9 +66,12 @@ class CaseCreation(object):
attachment='image.png', case=self.case,
created_by=self.user, account=self.account
)
self.content_type = ContentType.objects.create(
app_label="cases1", model="case")
class CaseViewTestCase(CaseCreation, TestCase):
def test_list_cases(self):
self.cases = Case.objects.all()
response = self.client.get('/cases/list/')
@ -78,12 +91,16 @@ class CaseViewTestCase(CaseCreation, TestCase):
class CaseCreationUrlTestCase(CaseCreation, TestCase):
def test_create_cases(self):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post('/cases/create/', {
'name': 'new case', 'case_type': 'Problem',
'status': 'New',
'account': self.account, 'contacts': [self.contacts.id],
'priority': "Low", 'description': "something"
'name': 'new case', 'closed_on': '2019-03-14',
'status': 'New', 'priority': "Low",
'created_by': self.user, "assigned_to": str(self.user.id),
'contacts': str(self.contacts.id),
'case_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())
})
self.assertEqual(response.status_code, 200)
@ -99,8 +116,13 @@ class CaseCreationUrlTestCase(CaseCreation, TestCase):
# self.assertTemplateUsed(response, 'create_cases.html')
self.assertEqual(response.status_code, 200)
def test_create_case_get_request(self):
response = self.client.get('/cases/create/')
self.assertEqual(response.status_code, 200)
class CaseShowTestCase(CaseCreation, TestCase):
def test_show_case(self):
response = self.client.get('/cases/' + str(self.case.id) + '/view/')
self.assertEqual(response.status_code, 200)
@ -116,6 +138,7 @@ class CaseShowTestCase(CaseCreation, TestCase):
class CaseRemoveTestCase(CaseCreation, TestCase):
def test_case_deletion_show_case(self):
response = self.client.get('/cases/' + str(self.case.id) + '/remove/')
self.assertEqual(response['location'], '/cases/list/')
@ -128,15 +151,22 @@ class CaseRemoveTestCase(CaseCreation, TestCase):
class CaseUpdateTestCase(CaseCreation, TestCase):
def test_update_case_view(self):
response = self.client.get(
'/cases/' + str(self.case.id) + '/edit_case/')
self.assertEqual(response.status_code, 200)
def test_case_update(self):
response = self.client.post(
'/cases/' + str(self.case.id) + '/edit_case/',
{'hiddenval': self.case.id})
upload_file = open('static/images/user.png', 'rb')
response = self.client.post('/cases/' + str(self.case.id) + '/edit_case/', {
'name': 'new case', 'closed_on': '2019-03-14',
'status': 'New', 'priority': "Low",
'created_by': self.user, "assigned_to": str(self.user.id),
'contacts': str(self.contacts.id),
'case_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())
})
self.assertEqual(response.status_code, 200)
def test_case_update_html(self):
@ -185,10 +215,17 @@ class CaseFormTestCase(CaseCreation, TestCase):
self.assertEqual(response.status_code, 200)
def test_comment_add(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/cases/comment/add/', {'caseid': self.case.id})
self.assertEqual(response.status_code, 200)
def test_comment_form_valid(self):
response = self.client.post(
'/cases/comment/add/', {'caseid': self.case.id, 'comment': 'comment'})
self.assertEqual(response.status_code, 200)
def test_comment_edit(self):
response = self.client.post(
'/cases/comment/edit/', {'commentid': self.comment.id})
@ -201,7 +238,18 @@ class CaseFormTestCase(CaseCreation, TestCase):
class AttachmentTestCase(CaseCreation, TestCase):
def test_attachment_add(self):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post(
'/cases/attachment/add/', {'caseid': self.case.id,
'attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())
})
self.assertEqual(response.status_code, 200)
def test_attachment_creation(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/cases/attachment/add/', {'caseid': self.case.id})
self.assertEqual(response.status_code, 200)
@ -211,8 +259,15 @@ class AttachmentTestCase(CaseCreation, TestCase):
'/cases/attachment/remove/', {'attachment_id': self.attachment.id})
self.assertEqual(response.status_code, 200)
def test_attachment_deletion(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/cases/attachment/remove/', {'attachment_id': self.attachment.id})
self.assertEqual(response.status_code, 200)
class SelectViewTestCase(CaseCreation, TestCase):
def test_select_contact(self):
response = self.client.get('/cases/select_contacts/')
self.assertEqual(response.status_code, 200)

View file

@ -1,11 +1,13 @@
from django.test import TestCase
from django.test import Client
from django.urls import reverse
from common.models import User, Document
from common.forms import *
from django.core.files.uploadedfile import SimpleUploadedFile
class ObjectsCreation(object):
def setUp(self):
self.client = Client()
# self.user = User.objects.create(first_name="admin",
@ -33,13 +35,19 @@ class ObjectsCreation(object):
self.user.set_password('paul123')
self.user.save()
# self.user
user_login = self.client.login(
self.client.login(
username='admin@micropyramid.com', password='admin123')
self.document = Document.objects.create(
title="abc", document_file="1.png", created_by=self.user)
self.comment = Comment.objects.create(
comment='testikd', user=self.user,
commented_by=self.user
)
class TestHomePage(ObjectsCreation, TestCase):
def test_home_page(self):
response = self.client.get('/')
self.assertEqual(response.status_code, 200)
@ -80,6 +88,7 @@ class UserCreateTestCase(ObjectsCreation, TestCase):
class PasswordChangeTestCase(ObjectsCreation, TestCase):
def test_password_change(self):
self.client.login(username="admin@micropyramid.com",
password="admin123")
@ -98,6 +107,14 @@ class PasswordChangeTestCase(ObjectsCreation, TestCase):
response = self.client.post(url, data)
self.assertEqual(response.status_code, 200)
def change_passsword_by_admin(self):
print(self.user, "madhu", self.user.role)
response = self.client.post(reverse('common:change_passsword_by_admin'), {
'useer_id': self.user.id,
'new_passwoord': "admin123"
})
self.assertEqual(response.status_code, 302)
class ForgotPasswordViewTestCase(ObjectsCreation, TestCase):
@ -109,7 +126,8 @@ class ForgotPasswordViewTestCase(ObjectsCreation, TestCase):
class LoginViewTestCase(ObjectsCreation, TestCase):
# user2 = User.objects.create(first_name="paul", username='paul', email='paul@micropyramid.com',
# is_staff=True, is_admin=True, is_superuser=True, is_active=False)
# is_staff=True, is_admin=True, is_superuser=True, is_active=False)
def test_login_post(self):
self.client.logout()
data = {"email": "admin@micropyramid.com", "password": "admin123"}
@ -147,6 +165,7 @@ class LoginViewTestCase(ObjectsCreation, TestCase):
class UserTestCase(ObjectsCreation, TestCase):
def test_user_create_url(self):
response = self.client.get('/users/create/', {
'first_name': 'micheal',
@ -188,6 +207,7 @@ class UserListTestCase(ObjectsCreation, TestCase):
class UserRemoveTestCase(ObjectsCreation, TestCase):
def test_users_remove(self):
response = self.client.get('/users/' + str(self.user.id) + '/delete/')
self.assertEqual(response['location'], '/users/list/')
@ -199,6 +219,7 @@ class UserRemoveTestCase(ObjectsCreation, TestCase):
class UserUpdateTestCase(ObjectsCreation, TestCase):
def test_users_update(self):
response = self.client.get('/users/' + str(self.user.id) + '/edit/', {
'first_name': "admin",
@ -239,6 +260,7 @@ class UserUpdateTestCase(ObjectsCreation, TestCase):
class ProfileViewTestCase(ObjectsCreation, TestCase):
def test_profile_view(self):
url = "/profile/"
response = self.client.get(url)
@ -254,6 +276,7 @@ class ProfileViewTestCase(ObjectsCreation, TestCase):
class UserDetailView(ObjectsCreation, TestCase):
def test_user_detail(self):
url = "/users/" + str(self.user.id) + "/view/"
response = self.client.get(url)
@ -261,6 +284,7 @@ class UserDetailView(ObjectsCreation, TestCase):
class DocumentDetailView(ObjectsCreation, TestCase):
def test_document_detail(self):
url = "/documents/" + str(self.document.id) + "/view/"
repsonse = self.client.get(url)
@ -271,12 +295,8 @@ class DocumentDetailView(ObjectsCreation, TestCase):
class CreateCommentFile(TestCase):
def test_invalid_user_form(self):
fields = ['email',
'first_name',
'last_name',
'username', 'role',
'profile_pic']
user1 = User.objects.create(username='robert',
first_name='robert',
last_name='clark',
@ -295,3 +315,81 @@ class CreateCommentFile(TestCase):
self.assertEqual(len(userr.password), 3)
self.assertFalse(form.is_valid())
# self.assertTrue(form.)
class CommentTestCase(ObjectsCreation, TestCase):
def test_comment_add(self):
response = self.client.post(
'/comment/add/', {'userid': self.user.id})
self.assertEqual(response.status_code, 200)
def test_comment_edit(self):
response = self.client.post(reverse('common:edit_comment',
kwargs={'pk': self.comment.id}))
self.assertEqual(response.status_code, 200)
resp = self.client.post(reverse('common:edit_comment',
kwargs={'pk': self.comment.id}), {'comment': 'hello123'})
self.assertEqual(resp.status_code, 200)
def test_comment_delete(self):
response = self.client.post(
'/comment/remove/', {'comment_id': self.comment.id})
self.assertEqual(response.status_code, 200)
def test_form_valid(self):
response = self.client.post(
'/comment/add/', {'userid': self.user.id,
'comment': 'hello'})
self.assertEqual(response.status_code, 200)
class DocumentTestCase(ObjectsCreation, TestCase):
def test_document_add(self):
response = self.client.post(
'/documents/create/')
self.assertEqual(response.status_code, 200)
def test_document_create(self):
response = self.client.post(
'/documents/create/')
self.assertEqual(response.status_code, 200)
def test_document_edit(self):
upload_file = open('static/images/user.png', 'rb')
data = {'title': "doc",
'created_by': self.user,
'document_file': SimpleUploadedFile(
upload_file.name, upload_file.read()),
'status': 'active',
'shared_to': str(self.user.id)}
response = self.client.get(reverse('common:edit_doc',
kwargs={'pk': self.document.id}), data)
self.assertEqual(response.status_code, 200)
def test_document_valid(self):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post(
'/documents/create/', {'title': "doc",
'created_by': self.user,
'document_file': SimpleUploadedFile(
upload_file.name, upload_file.read()),
'status': 'active',
'shared_to': str(self.user.id)})
self.assertEqual(response.status_code, 200)
def test_document_delete(self):
response = self.client.get(reverse('common:remove_doc',
kwargs={'pk': self.document.id}))
self.assertEqual(response.status_code, 302)
def test_document_list_view(self):
response = self.client.get(reverse('common:doc_list'))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse('common:doc_list'), {
'doc_name': 'doc',
'status': 'active',
"shared_to": str(self.user.id)
})
self.assertEqual(response.status_code, 200)

View file

@ -4,9 +4,13 @@ from common.models import Address, User, Comment, Attachments
from cases.models import Case
from django.test import Client
from django.urls import reverse
from django.core.files.uploadedfile import SimpleUploadedFile
from contacts.forms import ContactAttachmentForm
from accounts.models import Account
class ContactObjectsCreation(object):
def setUp(self):
self.client = Client()
self.user = User.objects.create(
@ -50,6 +54,7 @@ class ContactObjectsCreation(object):
class ContactObjectsCreation_Count(ContactObjectsCreation, TestCase):
def test_contact_object_creation(self):
c = Contact.objects.count()
con = Contact.objects.filter(id=self.contact.id)
@ -77,6 +82,7 @@ class ContactViewsTestCase(ContactObjectsCreation, TestCase):
self.assertTemplateUsed(response, 'contacts.html')
def test_contacts_create(self):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post('/contacts/create/', {
'first_name': 'contact',
'last_name': 'george',
@ -84,7 +90,45 @@ class ContactViewsTestCase(ContactObjectsCreation, TestCase):
'phone': '+917898901234',
'address': self.address.id,
'description': 'contact',
'created_by': self.user})
'created_by': self.user,
'assigned_to': str(self.user.id),
'contact_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())
})
self.assertEqual(response.status_code, 302)
def test_contact_create(self):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post('/contacts/create/', {
'first_name': 'contact',
'last_name': 'george',
'email': 'meg@gmail.com',
'phone': '+917898901234',
'address': self.address.id,
'description': 'contact',
'created_by': self.user,
'assigned_to': str(self.user.id),
'contact_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())
})
self.assertEqual(response.status_code, 302)
def test_update_contact(self):
upload_file = open('static/images/user.png', 'rb')
url = '/contacts/' + str(self.contact.id) + '/edit/'
data = {
'first_name': 'contact',
'last_name': 'george',
'email': 'meg@gmail.com',
'phone': '+917898901234',
'address': self.address.id,
'description': 'contact',
'created_by': self.user,
'assigned_to': str(self.user.id),
'contact_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())
}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 302)
def test_contacts_create_html(self):
@ -166,6 +210,7 @@ class ContactsListTestCase(ContactObjectsCreation, TestCase):
class CommentTestCase(ContactObjectsCreation, TestCase):
def test_comment_add(self):
response = self.client.post(
'/contacts/comment/add/', {'contactid': self.contact.id})
@ -199,14 +244,18 @@ class CommentTestCase(ContactObjectsCreation, TestCase):
class AttachmentTestCase(ContactObjectsCreation, TestCase):
def test_attachment_add(self):
response = self.client.post(
'/contacts/attachment/add/', {'contactid': self.contact.id})
self.assertEqual(response.status_code, 200)
def test_attachment_valid(self):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post(
'/contacts/attachment/add/', {'contactid': self.contact.id})
'/contacts/attachment/add/', {'contactid': self.contact.id,
'attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())})
self.assertEqual(response.status_code, 200)
def test_attachment_delete(self):

View file

@ -1,4 +1,5 @@
import os
from celery.schedules import crontab
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -107,7 +108,7 @@ AUTH_PASSWORD_VALIDATORS = [
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Calcutta'
TIME_ZONE = 'Asia/Kolkata'
USE_I18N = True
@ -219,6 +220,21 @@ DEFAULT_FROM_EMAIL = 'no-reply@django-crm.micropyramid.com'
CELERY_BROKER_URL = 'redis://localhost:6379'
CELERY_RESULT_BACKEND = 'redis://localhost:6379'
CELERY_BEAT_SCHEDULE = {
"runs-campaign-for-every-thiry-minutes": {
"task": "marketing.tasks.run_all_campaigns",
"schedule": crontab(minute=30, hour='*')
},
"runs-campaign-for-every-five-minutes": {
"task": "marketing.tasks.list_all_bounces_unsubscribes",
"schedule": crontab(minute='*/5')
},
"runs-scheduled-campaigns-for-every-one-hour": {
"task": "marketing.tasks.send_scheduled_campaigns",
"schedule": crontab(hour='*/1')
},
}
MAIL_SENDER = 'AMAZON'
INACTIVE_MAIL_SENDER = 'MANDRILL'
@ -236,6 +252,8 @@ MANDRILL_API_KEY = os.getenv('MANDRILL_API_KEY', '')
ADMIN_EMAIL = "admin@micropyramid.com"
URL_FOR_LINKS = "http://djangocrm.com"
try:
from .dev_settings import *
except ImportError:

View file

@ -1,3 +1,5 @@
from django.conf import settings
from django.conf.urls.static import static
from django.contrib.auth import views
from django.urls import include, path
from common.views import handler404, handler500
@ -20,5 +22,9 @@ urlpatterns = [
]
if settings.DEBUG:
urlpatterns = urlpatterns + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
handler404 = handler404
handler500 = handler500

View file

@ -15,6 +15,9 @@ class LeadForm(forms.ModelForm):
self.fields['email'].required = True
self.fields['assigned_to'].queryset = assigned_users
self.fields['assigned_to'].required = False
self.fields['first_name'].required = False
self.fields['last_name'].required = False
self.fields['title'].required = True
for key, value in self.fields.items():
if key == 'phone':
value.widget.attrs['placeholder'] =\

View file

@ -0,0 +1,29 @@
# Generated by Django 2.1.7 on 2019-03-15 09:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('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'),
),
migrations.AlterField(
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'),
preserve_default=False,
),
]

View file

@ -13,9 +13,9 @@ from contacts.models import Contact
class Lead(models.Model):
title = models.CharField(
pgettext_lazy("Treatment Pronouns for the customer",
"Title"), max_length=64, blank=True, null=True)
first_name = models.CharField(("First name"), max_length=255)
last_name = models.CharField(("Last name"), max_length=255)
"Title"), max_length=64)
first_name = models.CharField(("First name"), null=True, max_length=255)
last_name = models.CharField(("Last name"), null=True, max_length=255)
email = models.EmailField(null=True, blank=True)
phone = PhoneNumberField(null=True, blank=True)
status = models.CharField(

View file

@ -113,7 +113,7 @@
{% if open_leads|length > 0 %}
<tr>
<th width="5%">ID</th>
<th width="10%">Name</th>
<th width="10%">Title</th>
<th width="8%">Created By</th>
<th width="10%">Source</th>
<th width="10%">Status</th>
@ -133,7 +133,8 @@
{% for lead in open_leads %}
<tr class="text-center">
<td scope="row">{{ forloop.counter }}</td>
<td><a href="{% url 'leads:view_lead' lead.id %}">{{ lead.first_name }} {{ lead.last_name }}</a></td>
<td><a href="{% url 'leads:view_lead' lead.id %}">
{{ lead.title }} </a></td>
<td>
{% if lead.created_by.profile_pic %}
{% thumbnail lead.created_by.profile_pic "40x40" crop="center" as im %}
@ -207,7 +208,7 @@
{% if close_leads|length > 0 %}
<tr>
<th width="5%">ID</th>
<th width="10%">Name</th>
<th width="10%">Title</th>
<th width="10%">Source</th>
<th width="10%">Status</th>
<th width="20%">Assigned User</th>
@ -226,7 +227,8 @@
{% for lead in close_leads %}
<tr class="text-center">
<td scope="row">{{ forloop.counter }}</td>
<td><a href="{% url 'leads:view_lead' lead.id %}">{{ lead.first_name }} {{ lead.last_name }}</a></td>
<td><a href="{% url 'leads:view_lead' lead.id %}">
{{ lead.title }}</a></td>
<td>{{ lead.source }}</td>
<td>{{ lead.get_status_display }}</td>
<td>

View file

@ -37,11 +37,12 @@
<div class="filter_col col-md-12" id="iname">
<div class="form-group" >
<label class="lead_field_label" for="id_name" data-name="name">Name</label>
<div class="lead_field" id="lead_name" data-name="name">{{ lead_record.first_name }} {{ lead_record.last_name }}</div>
<div class="lead_field" id="lead_name" data-name="name"> {% if lead_record.first_name %}
{{ lead_record.first_name }}{% endif %} {% if lead_record.last_name %}
{{ lead_record.last_name }}{% endif %}</div>
</div>
</div>
<div class="filter_col col-md-12">
<div class="form-group">
<label class="lead_field_label" for="id_contact_account" data-name="name">Account Name</label>
{% if lead_record.account_name %}

View file

@ -1,13 +1,16 @@
from django.test import TestCase, Client
from cases.models import Case
from leads.models import Lead
from common.models import User, Comment, Attachments
from common.models import User, Comment, Attachments, APISettings
from accounts.models import Account
from leads.tasks import *
from leads.forms import *
from django.urls import reverse
from django.core.files.uploadedfile import SimpleUploadedFile
class TestLeadModel(object):
def setUp(self):
self.client = Client()
@ -16,6 +19,14 @@ class TestLeadModel(object):
self.user.set_password('jorge2293')
self.user.save()
self.user1 = User.objects.create(
first_name="mp",
username='mp',
email='mp@micropyramid.com',
role="USER")
self.user1.set_password('mp')
self.user1.save()
self.client.login(username='j@mp.com', password='jorge2293')
self.account = Account.objects.create(name="account",
@ -36,7 +47,7 @@ class TestLeadModel(object):
first_name="Alisa",
last_name="k",
email="Alisak1993@gmail.com",
address_line="",
address_line="hyd",
street="Arcade enclave colony",
city="NewTown",
state="California",
@ -47,7 +58,10 @@ class TestLeadModel(object):
source="Call",
opportunity_amount="700",
description="Iam an Lead",
created_by=self.user)
created_by=self.user,
account_name="account",
phone="+91-123-456-7890")
self.lead.assigned_to.add(self.user)
self.case = Case.objects.create(
name="Rose", case_type="Problem",
status="New",
@ -60,9 +74,12 @@ class TestLeadModel(object):
attachment='image.png',
case=self.case, created_by=self.user,
account=self.account)
self.api_seetings = APISettings.objects.create(
title="api", apikey="api", created_by=self.user)
class LeadsPostrequestTestCase(TestLeadModel, TestCase):
def test_valid_postrequesttestcase_date(self):
data = {'title': 'LeadCreation', 'first_name': "micky",
'last_name': "Alisa", 'email': "Alisamicky1993@gmail.com",
@ -95,6 +112,7 @@ class LeadsPostrequestTestCase(TestLeadModel, TestCase):
class LeadsCreateUrlTestCase(TestLeadModel, TestCase):
def test_leads_create_url(self):
response = self.client.post('/leads/create/', {
'title': 'LeadCreation',
@ -128,8 +146,21 @@ class LeadsCreateUrlTestCase(TestLeadModel, TestCase):
# self.assertTemplateUsed(response, 'create_lead.html')
self.assertEqual(response.status_code, 200)
def test_leads_create_with_status_converted(self):
response = self.client.post('/leads/create/', {
'title': 'LeadCreation', 'name': "micky",
'email': "Alisamicky1993@gmail.com", 'account': self.account,
'address_line': "", 'account_name': 'account1',
'street': "Arcade enclave colony", 'city': "NewTown",
'state': "California", 'postcode': "579", 'country': "AD",
'website': "www.gmail.com", 'status': "converted",
"source": "Call", 'opportunity_amount': "700",
'description': "Iam an Lead", 'created_by': self.user})
self.assertEqual(response.status_code, 200)
class LeadsEditUrlTestCase(TestLeadModel, TestCase):
def test_leads_editurl(self):
response = self.client.get('/leads/' + str(self.lead.id) + '/edit/')
self.assertEqual(response.status_code, 200)
@ -177,6 +208,7 @@ class LeadListTestCase(TestLeadModel, TestCase):
class GetLeadsViewTestCase(TestLeadModel, TestCase):
def test_get_lead(self):
url = '/leads/get/list/'
response = self.client.get(url)
@ -215,6 +247,7 @@ class UpdateLeadTestCase(TestLeadModel, TestCase):
class LeadDetailTestCase(TestLeadModel, TestCase):
def test_lead_detail(self):
url = '/leads/' + str(self.lead.id) + '/view/'
response = self.client.get(url, {'status': ''})
@ -222,12 +255,25 @@ class LeadDetailTestCase(TestLeadModel, TestCase):
class CommentTestCase(TestLeadModel, TestCase):
def test_comment_add(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/leads/comment/add/', {'leadid': self.lead.id})
self.assertEqual(response.status_code, 200)
def test_comment_create(self):
response = self.client.post(
'/leads/comment/add/', {'leadid': self.lead.id, 'comment': "comment"})
self.assertEqual(response.status_code, 200)
def test_comment_edit(self):
response = self.client.post(
'/leads/comment/edit/', {'commentid': self.comment.id, 'comment': "comment"})
self.assertEqual(response.status_code, 200)
def test_comment_update(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/leads/comment/edit/', {'commentid': self.comment.id})
self.assertEqual(response.status_code, 200)
@ -237,9 +283,17 @@ class CommentTestCase(TestLeadModel, TestCase):
'/leads/comment/remove/', {'comment_id': self.comment.id})
self.assertEqual(response.status_code, 200)
def test_comment_deletion(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/leads/comment/remove/', {'comment_id': self.comment.id})
self.assertEqual(response.status_code, 200)
class AttachmentTestCase(TestLeadModel, TestCase):
def test_attachment_add(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/leads/attachment/add/', {'leadid': self.lead.id})
self.assertEqual(response.status_code, 200)
@ -249,8 +303,23 @@ class AttachmentTestCase(TestLeadModel, TestCase):
'/leads/attachment/remove/', {'attachment_id': self.attachment.id})
self.assertEqual(response.status_code, 200)
def test_attachment_deletion(self):
self.client.login(email='mp@micropyramid.com', password='mp')
response = self.client.post(
'/leads/attachment/remove/', {'attachment_id': self.attachment.id})
self.assertEqual(response.status_code, 200)
def test_attachment_valid(self):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post(
'/leads/attachment/add/', {'leadid': self.lead.id,
'attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())})
self.assertEqual(response.status_code, 200)
class TestTemplates(TestLeadModel, TestCase):
def test_lead_list_view(self):
resp = self.client.post('/leads/list/', {'name': 'trevor',
'tag': "123",
@ -266,6 +335,83 @@ class TestTemplates(TestLeadModel, TestCase):
class TestConvertLeadView(TestLeadModel, TestCase):
def test_get_fun(self):
resp = self.client.get('/leads/' + str(self.lead.id) + '/convert/')
self.assertEqual(resp.status_code, 302)
class TestCreateLeadPostView(TestLeadModel, TestCase):
def test_create_lead_post_status(self):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post(reverse(
'leads:add_lead'), {'first_name': 'm',
'last_name': 's',
"created_by": self.user,
'status': 'converted',
'description': 'wgrgre',
'website': 'www.mike.com',
'phone': '+91-123-456-7890',
'email': 'a@gmail.com',
'account_name': 'account',
'address_line': self.lead.address_line,
'street': self.lead.street,
'city': self.lead.city,
'state': self.lead.state,
'postcode': self.lead.postcode,
'country': self.lead.country,
'lead_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read()),
'assigned_to': str(self.user.id),
'tags': 'tag'
})
self.assertEqual(response.status_code, 200)
def test_create_lead_get_request(self):
response = self.client.get(reverse('leads:add_lead'))
self.assertEqual(response.status_code, 200)
def test_update_lead_post_status(self):
upload_file = open('static/images/user.png', 'rb')
response = self.client.post('/leads/' + str(self.lead.id) + '/edit/',
{'first_name': 'm',
'last_name': 's',
"created_by": self.user,
'status': 'converted',
'description': 'wgrgre',
'website': 'www.mike.com',
'phone': '+91-123-456-7890',
'email': 'a@gmail.com',
'account_name': 'account',
'address_line': self.lead.address_line,
'street': self.lead.street,
'city': self.lead.city,
'state': self.lead.state,
'postcode': self.lead.postcode,
'country': self.lead.country,
'lead_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read()),
'assigned_to': str(self.user.id),
'tags': 'tag'
})
self.assertEqual(response.status_code, 200)
def test_lead_convert(self):
response = self.client.get('/leads/' + str(self.lead.id) + '/convert/')
self.assertEqual(response.status_code, 302)
class TestLeadDetailView(TestLeadModel, TestCase):
def test_lead_detail_view(self):
response = self.client.get(
reverse('leads:view_lead', kwargs={'pk': self.lead.id}))
self.assertEqual(response.status_code, 200)
class TestLeadFromSite(TestLeadModel, TestCase):
def create_lead_from_site(self):
response = self.client.post('/leads/create/from-site/', {'apikey': self.api_seetings.apikey})
self.assertEqual(response.status_code, 200)

View file

@ -10,9 +10,12 @@ from marketing.models import (
EmailTemplate, Campaign, Tag
)
email_regex = '^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$'
def csv_doc_validate(document):
temp_row = []
invalid_row = []
reader = csv.reader((document.read().decode("utf-8")).splitlines())
# csv_headers = ["first name", "last name", "email"]
csv_headers = ["first name", "email"]
@ -20,6 +23,7 @@ def csv_doc_validate(document):
required_headers = ["first name", "email"]
for y_index, row in enumerate(reader):
each = {}
invalid_each = {}
if y_index == 0:
csv_headers = [header_name.lower()
for header_name in row if header_name]
@ -40,15 +44,23 @@ def csv_doc_validate(document):
continue
if csv_headers[x_index] in required_headers:
if not cell_value:
message = 'Missing required value %s for row %s' % (
csv_headers[x_index], y_index + 1)
return {"error": True, "message": message}
# message = 'Missing required value %s for row %s' % (
# csv_headers[x_index], y_index + 1)
# return {"error": True, "message": message}
invalid_each[csv_headers[x_index]] = cell_value
else:
if csv_headers[x_index] == "email":
if re.match(email_regex, cell_value) is None:
invalid_each[csv_headers[x_index]] = cell_value
each[csv_headers[x_index]] = cell_value
temp_row.append(each)
return {"error": False, "validated_rows": temp_row}
if invalid_each:
invalid_row.append(each)
else:
temp_row.append(each)
return {"error": False, "validated_rows": temp_row, "invalid_rows": invalid_row}
def get_validated_rows(wb, sheet_name, validated_rows):
def get_validated_rows(wb, sheet_name, validated_rows, invalid_rows):
# headers = ["first name", "last name", "email"]
# required_headers = ["first name", "last name", "email"]
headers = ["first name", "email"]
@ -68,6 +80,7 @@ def get_validated_rows(wb, sheet_name, validated_rows):
continue
else:
temp_row = []
invalid_row = []
for x_index, cell in enumerate(row):
try:
headers[x_index]
@ -75,29 +88,28 @@ def get_validated_rows(wb, sheet_name, validated_rows):
continue
if headers[x_index] in required_headers:
if not cell.value:
message = 'Missing required \
value %s for row %s in sheet %s'\
% (headers[x_index], y_index + 1, sheet_name)
return {"error": True, "message": message}
# elif x_index == 0:
# cell_value = is_valid_date_format(cell.value)
# if not cell_value:
# message = 'Invalid date format(format:yyyy-mm-dd) \
# %s for row %s in sheet %s' % (headers[x_index], y_index + 1, sheet_name)
# return {"error": True, "message": message}
# message = 'Missing required \
# value %s for row %s in sheet %s'\
# % (headers[x_index], y_index + 1, sheet_name)
# return {"error": True, "message": message}
invalid_row.append(headers[x_index])
temp_row.append(cell.value)
if len(temp_row) >= len(required_headers):
validated_rows.append(temp_row)
return validated_rows
if len(invalid_row) > 0:
invalid_rows.append(temp_row)
else:
if len(temp_row) >= len(required_headers):
validated_rows.append(temp_row)
return validated_rows, invalid_rows
def xls_doc_validate(document):
wb = openpyxl.load_workbook(document)
sheets = wb.get_sheet_names()
validated_rows = []
invalid_rows = []
for sheet_name in sheets:
get_validated_rows(wb, sheet_name, validated_rows)
return {"error": False, "validated_rows": validated_rows}
validated_rows, invalid_rows = get_validated_rows(wb, sheet_name, validated_rows, invalid_rows)
return {"error": False, "validated_rows": validated_rows, "invalid_rows": invalid_rows}
def import_document_validator(document):
@ -141,7 +153,8 @@ class ContactListForm(forms.ModelForm):
if data.get("error"):
raise forms.ValidationError(data.get("message"))
else:
self.validated_rows = data.get("validated_rows")
self.validated_rows = data.get("validated_rows", [])
self.invalid_rows = data.get("invalid_rows", [])
return document
def clean_tags(self):
@ -265,7 +278,7 @@ class SendCampaignForm(forms.ModelForm):
class Meta:
model = Campaign
fields = ['title', 'subject', 'html', 'email_template']
fields = ['title', 'subject', 'html', 'email_template', 'attachment']
def __init__(self, *args, **kwargs):
super(SendCampaignForm, self).__init__(*args, **kwargs)
@ -278,11 +291,11 @@ class SendCampaignForm(forms.ModelForm):
def clean_contact_list(self):
contact_list = self.cleaned_data.get("contact_list")
if not contact_list or contact_list == '[]' or \
json.loads(contact_list) == []:
contact_list == []:
raise forms.ValidationError(
"Please choose any of the Contact List")
else:
for each in json.loads(contact_list):
for each in contact_list:
if not ContactList.objects.filter(id=each).first():
raise forms.ValidationError(
"Please choose a valid Contact List")

View file

@ -0,0 +1,24 @@
# Generated by Django 2.1.7 on 2019-03-15 09:13
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('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}$')]),
),
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}$')]),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-03-18 07:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('marketing', '0004_auto_20190315_1443'),
]
operations = [
migrations.AddField(
model_name='campaign',
name='timezone',
field=models.CharField(default='UTC', max_length=100),
),
]

View file

@ -0,0 +1,19 @@
# Generated by Django 2.1.7 on 2019-03-19 10:29
from django.db import migrations, models
import marketing.models
class Migration(migrations.Migration):
dependencies = [
('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),
),
]

View file

@ -1,11 +1,14 @@
import os
from datetime import datetime, timedelta
from django.db import models
from django.utils.timesince import timesince
from django.utils.translation import ugettext_lazy as _
from django.core.validators import RegexValidator
from django.dispatch import receiver
from django.db.models import Sum
from django.template.defaultfilters import slugify
from common.models import User
from common.utils import convert_to_custom_timezone
class Tag(models.Model):
@ -150,6 +153,13 @@ class FailedContact(models.Model):
return self.email
def get_campaign_attachment_path(self, filename):
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
class Campaign(models.Model):
STATUS_CHOICES = (
('Scheduled', 'Scheduled'),
@ -170,6 +180,7 @@ class Campaign(models.Model):
email_template = models.ForeignKey(
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')
reply_to_email = models.EmailField(blank=True, null=True)
subject = models.CharField(max_length=5000)
html = models.TextField()
@ -182,6 +193,8 @@ class Campaign(models.Model):
bounced = models.IntegerField(default='0')
status = models.CharField(
default="Preparing", choices=STATUS_CHOICES, max_length=20)
attachment = models.FileField(
max_length=1000, upload_to=get_campaign_attachment_path, blank=True, null=True)
@property
def no_of_unsubscribers(self):
@ -209,6 +222,27 @@ class Campaign(models.Model):
def created_on_format(self):
return self.created_on.strftime('%b %d, %Y %I:%M %p')
@property
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')
else:
c_created_on = convert_to_custom_timezone(self.created_on, self.timezone)
return c_created_on.strftime('%b %d, %Y %I:%M %p')
@receiver(models.signals.pre_delete, sender=Campaign)
def comment_attachments_delete(sender, instance, **kwargs):
attachment = instance.attachment
if attachment:
try:
if os.path.isfile(attachment.path):
os.remove(attachment.path)
except Exception:
return False
return True
class Link(models.Model):
campaign = models.ForeignKey(

View file

@ -2,12 +2,13 @@ import datetime
import hashlib
import pytz
import requests
from mimetypes import MimeTypes
from celery.task import task
from django.conf import settings
from django.core.mail import EmailMessage
from django.template import Template, Context
from common.utils import convert_to_custom_timezone
from marketing.models import Contact, ContactList, Campaign, CampaignLog
from marketing.models import Contact, FailedContact, ContactList, Campaign, CampaignLog
@task
@ -26,7 +27,7 @@ def campaign_click(request):
@task
def upload_csv_file(data, user, contact_lists):
def upload_csv_file(data, invalid_data, user, contact_lists):
for each in data:
contact = Contact.objects.filter(email=each['email']).first()
if not contact:
@ -45,6 +46,24 @@ def upload_csv_file(data, user, contact_lists):
for contact_list in contact_lists:
contact.contact_list.add(ContactList.objects.get(id=int(contact_list)))
for each in invalid_data:
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']
if each.get("state", None):
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(
@ -76,13 +95,18 @@ def get_campaign_message_id(campaign):
def run_campaign(campaign):
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()
mime = MimeTypes()
mime_type = mime.guess_type(file_path)
attachments.append((file_name, content, mime_type[0]))
subject = campaign.subject
# project_settings = Settings.objects.filter().first()
contacts = []
for each_list in campaign.contact_lists.all():
contacts = Contact.objects.filter(contact_list__in=[each_list])
# contacts = campaign.contacts.filter(Q(is_bounced=False) | Q(is_unsubscribed=False)).distinct()
contacts = Contact.objects.filter(
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
@ -94,18 +118,15 @@ def run_campaign(campaign):
message_id = get_campaign_message_id(campaign_log)
campaign_log.message_id = message_id
campaign_log.save()
domain_name = settings.SUBDOMAIN_NAME + '.' + project_settings.domain if project_settings else ''
# domain_name = 'micro.peeljobs.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 + '') + '>'
if not (each_contact.is_bounced or each_contact.is_unsubscribed):
# link = '<img src="https://micropyramid.localtunnel.me/track-email/' + \
# str(campaign_log.id) + '/" height="1" width="1"</img>'
domain_url = '%s%s' % (settings.SCHEME, settings.HOST_URL)
link = '<img src="' + domain_url + '/track-email/' + \
domain_url = settings.URL_FOR_LINKS
link = '<img src="' + domain_url + '/m/cm/track-email/' + \
str(campaign_log.id) + '/contact/' + str(each_contact.id) + '/" height="1" width="1" />'
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 '',
@ -117,13 +138,9 @@ def run_campaign(campaign):
html = Template(html).render(Context(names_dict))
mail_html = html + link
from_email = str(campaign.from_name) + "<" + str(campaign.from_email) + '>'
# text_content = re.sub(r'<(.*?)>', '', html)
# ReplyMail.objects.create(campaign_log=campaign_log,
# reply_from=campaign.created_by.email,
# text=text_content,
# sent_on=datetime.datetime.now())
to_email = [each_contact.email]
send_campaign_mail(
subject, mail_html, from_email, [each_contact.email], [], [reply_to_email], [])
subject, mail_html, from_email, to_email, [], [reply_to_email], attachments)
except Exception as e:
print (e)
pass
@ -161,14 +178,16 @@ def send_scheduled_campaigns():
from datetime import datetime
campaigns = Campaign.objects.filter(schedule_date_time__isnull=False)
for each in campaigns:
sent_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
sent_time = datetime.strptime(sent_time, '%Y-%m-%d %H:%M:%S')
local_tz = pytz.timezone('UTC')
schedule_date_time_hour = str(each.schedule_date_time.time()).replace(':00:00', '')
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')
local_tz = pytz.timezone(settings.TIME_ZONE)
sent_time = local_tz.localize(sent_time)
sent_time = convert_to_custom_timezone(sent_time, each.timezone)
sent_time = convert_to_custom_timezone(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)
str(schedule_date_time.hour) == str(sent_time.hour)
):
run_campaign(each)

View file

@ -27,7 +27,7 @@ class TestTemplates(TestMarketingModel, TestCase):
url4 = '/m/cl/lists/'
url5 = '/m/cl/list/new/'
url6 = '/m/cl/list/cnew/'
url7 = '/m/cl/list/detail/'
# url7 = '/m/cl/list/detail/'
url8 = '/m/et/list/'
url9 = '/m/et/new/'
# url10 = '/m/et/edit/'
@ -42,7 +42,7 @@ class TestTemplates(TestMarketingModel, TestCase):
resp4 = self.client.get(url4)
resp5 = self.client.get(url5)
resp6 = self.client.get(url6)
resp7 = self.client.get(url7)
# resp7 = self.client.get(url7)
resp8 = self.client.get(url8)
resp9 = self.client.get(url9)
# resp10 = self.client.get(url10)
@ -57,7 +57,7 @@ class TestTemplates(TestMarketingModel, TestCase):
self.assertEqual(resp4.status_code, 200)
self.assertEqual(resp5.status_code, 200)
self.assertEqual(resp6.status_code, 200)
self.assertEqual(resp7.status_code, 200)
# self.assertEqual(resp7.status_code, 200)
self.assertEqual(resp8.status_code, 200)
self.assertEqual(resp9.status_code, 200)
# self.assertEqual(resp10.status_code, 200)

View file

@ -3,7 +3,8 @@ from .views import (
dashboard, contact_lists, contacts_list, contact_list_new, contacts_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_edit, campaign_details, campaign_delete,
edit_contact_list, delete_contact_list
edit_contact_list, delete_contact_list, failed_contact_list_detail, failed_contact_list_download_delete,
campaign_link_click, campaign_open
)
app_name = 'marketing'
@ -15,10 +16,13 @@ urlpatterns = [
path('cl/edit_contact/', edit_contact, name='edit_contact'),
path('cl/lists/', contacts_list, name='contacts_list'),
path('cl/list/new/', contact_list_new, name='contact_list_new'),
path('cl/list/edit/<int:pk>/', edit_contact_list, name='edit_contact_list'),
path('cl/list/delete/<int:pk>/', delete_contact_list, name='delete_contact_list'),
path('cl/list/cnew/', contacts_list_new, name='contacts_list_new'),
path('cl/list/detail/', contact_list_detail, name='contact_list_detail'),
path('cl/list/<int:pk>/detail/', contact_list_detail, name='contact_list_detail'),
path('cl/list/<int:pk>/failed/', failed_contact_list_detail, name='failed_contact_list_detail'),
path('cl/list/<int:pk>/failed/download/',
failed_contact_list_download_delete, name='failed_contact_list_download_delete'),
path('cl/list/<int:pk>/edit/', edit_contact_list, name='edit_contact_list'),
path('cl/list/<int:pk>/delete/', delete_contact_list, name='delete_contact_list'),
path('et/list/', email_template_list, name='email_template_list'),
path('et/new/', email_template_new, name='email_template_new'),
@ -30,5 +34,7 @@ urlpatterns = [
path('cm/new/', campaign_new, name='campaign_new'),
path('cm/<int:pk>/edit/', campaign_edit, name='campaign_edit'),
path('cm/<int:pk>/details/', campaign_details, name='campaign_details'),
path('cm/<int:pk>/delete/', campaign_delete, name='campaign_delete')
path('cm/<int:pk>/delete/', campaign_delete, name='campaign_delete'),
path('cm/link/<int:link_id>/e/<int:email_id>/', campaign_link_click, name='campaign_link_click'),
path('cm/track-email/<int:campaign_log_id>/contact/<int:email_id>/', campaign_open, name='campaign_open'),
]

View file

@ -1,19 +1,23 @@
import csv
import json
import pytz
import lxml
import lxml.html
from lxml.cssselect import CSSSelector
from django.conf import Settings
from datetime import datetime as ddatetime
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import Q
from django.http.response import (JsonResponse,
HttpResponse, HttpResponseRedirect)
from django.shortcuts import render, get_object_or_404
from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse_lazy, reverse
from common import status
from common.utils import convert_to_custom_timezone
from marketing.models import (
Tag, ContactList, Contact, EmailTemplate, Campaign, CampaignLog, Link
Tag, ContactList, Contact, EmailTemplate, Campaign, CampaignLog, Link, CampaignLinkClick,
CampaignOpen
)
from marketing.forms import ContactListForm, ContactForm, EmailTemplateForm, SendCampaignForm
from marketing.tasks import upload_csv_file, run_campaign
@ -97,7 +101,7 @@ def contact_list_new(request):
instance.tags.add(tag)
if request.FILES.get('contacts_file'):
upload_csv_file.delay(
form.validated_rows, request.user.id, [instance.id])
form.validated_rows, form.invalid_rows, request.user.id, [instance.id])
return JsonResponse({'error': False,
'data': form.data},
@ -144,7 +148,7 @@ def edit_contact_list(request, pk):
instance.tags.add(tag)
if request.FILES.get('contacts_file'):
upload_csv_file.delay(
form.validated_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)
@ -215,8 +219,38 @@ def edit_contact(request):
@login_required(login_url='/login')
def contact_list_detail(request):
return render(request, 'marketing/lists/detail.html')
def contact_list_detail(request, pk):
contact_list = get_object_or_404(ContactList, pk=pk)
contacts_list = contact_list.contacts.all()
data = {'contact_list': contact_list, "contacts_list": contacts_list}
return render(request, 'marketing/lists/detail.html', data)
@login_required(login_url='/login')
def failed_contact_list_detail(request, pk):
contact_list = get_object_or_404(ContactList, pk=pk)
failed_contacts_list = contact_list.failed_contacts.all()
data = {'contact_list': contact_list, "failed_contacts_list": failed_contacts_list}
return render(request, 'marketing/lists/failed_detail.html', data)
@login_required(login_url='/login')
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"'
writer = csv.writer(response)
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])
failed_contacts_list.delete()
return response
else:
return HttpResponseRedirect(reverse('marketing:contact_lists', kwargs={"pk": pk}))
@login_required(login_url='/login')
@ -336,23 +370,25 @@ def campaign_new(request):
# contact_list__id__in=json.loads(request.GET.get('contact_list'))).distinct()
data = {
'contact_lists': contact_lists, 'email_templates': email_templates, "timezones": TIMEZONE_CHOICES}
'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)
else:
print ("request.POST", request.POST)
form = SendCampaignForm(request.POST)
form = SendCampaignForm(request.POST, request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.created_by = request.user
if request.POST['from_email']:
if request.POST.get('from_email'):
instance.from_email = request.POST['from_email']
if request.POST['from_name']:
if request.POST.get('from_name'):
instance.from_name = request.POST['from_name']
if 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 json.loads(request.POST['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:
@ -373,33 +409,31 @@ def campaign_new(request):
links = set(llist)
# Replace Links with new One
domain_url = '%s://%s' % (request.scheme, request.META['HTTP_HOST'])
# domain_url = '%s://%s' % (request.scheme, request.META['HTTP_HOST'])
domain_url = settings.URL_FOR_LINKS
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 + '/a/' + str(link.id) + '/e/{{email_id}}"')
'href="' + l + '"', 'href="' + domain_url + '/m/cm/link/' + str(link.id) + '/e/{{email_id}}"')
camp.html_processed = html
camp.sent_status = "scheduled"
camp.save()
if request.POST['schedule_later'] == 'false':
run_campaign.delay(instance.id)
else:
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:
# print ("schedule_date_time", schedule_date_time)
# print ("timezone", user_timezone)
# start_time_object = ddatetime.strptime(schedule_date_time, '%Y-%m-%d %H:%M:%S')
# schedule_date_time = convert_to_custom_timezone(start_time_object, user_timezone, to_utc=True)
# print ("After", 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 = convert_to_custom_timezone(start_time_object, user_timezone, to_utc=True)
instance.schedule_date_time = schedule_date_time
# instance.timezone = request.POST['timezone']
instance.save()
instance.timezone = user_timezone
instance.save()
else:
run_campaign.delay(instance.id)
return JsonResponse({'error': True, 'data': form.data}, status=status.HTTP_201_CREATED)
return JsonResponse({'error': False, 'data': form.data}, status=status.HTTP_201_CREATED)
return JsonResponse({'error': True, 'errors': form.errors}, status=status.HTTP_200_OK)
@ -409,10 +443,186 @@ def campaign_edit(request):
@login_required(login_url='/login')
def campaign_details(request):
return render(request, 'marketing/campaign/details.html')
def campaign_details(request, pk):
try:
campaign = Campaign.objects.get(pk=pk)
except Campaign.DoesNotExist:
return redirect(reverse('marketing:campaign_list'))
contact_lists = campaign.contact_lists.all()
if (request.user.is_admin or request.user.is_superuser):
contact_lists = contact_lists
else:
contact_lists = contact_lists.filter(
is_public=True, 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)
contacts = Contact.objects.filter(contact_list__id__in=contact_list_ids)
all_contacts = contacts
bounced_contacts = contacts.filter(is_bounced=True)
unsubscribe_contacts = contacts.filter(
is_unsubscribed=True)
read_contacts = campaign.marketing_links.filter(Q(clicks__gt=0))
# read_contacts =
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')
per_page = request.GET.get("per_page", 10)
page = request.GET.get('page', 1)
contacts_paginator = Paginator(contacts.order_by('id'), per_page)
if (tab == "contacts"):
page = page
else:
page = 1
try:
contacts = contacts_paginator.page(page)
except PageNotAnInteger:
contacts = contacts_paginator.page(1)
except EmptyPage:
contacts = contacts_paginator.page(contacts_paginator.num_pages)
unsubscribe_contacts_paginator = Paginator(unsubscribe_contacts.order_by('id'), per_page)
if (tab == "unsubscribe_contacts"):
page = page
else:
page = 1
try:
unsubscribe_contacts = unsubscribe_contacts_paginator.page(page)
except PageNotAnInteger:
unsubscribe_contacts = unsubscribe_contacts_paginator.page(1)
except EmptyPage:
unsubscribe_contacts = unsubscribe_contacts_paginator.page(unsubscribe_contacts_paginator.num_pages)
bounced_contacts_paginator = Paginator(bounced_contacts.order_by('id'), per_page)
if (tab == "bounced_contacts"):
page = page
else:
page = 1
try:
bounced_contacts = bounced_contacts_paginator.page(page)
except PageNotAnInteger:
bounced_contacts = bounced_contacts_paginator.page(1)
except EmptyPage:
bounced_contacts = bounced_contacts_paginator.page(bounced_contacts_paginator.num_pages)
sent_contacts_paginator = Paginator(sent_contacts.order_by('id'), per_page)
if (tab == "sent_contacts"):
page = page
else:
page = 1
try:
sent_contacts = sent_contacts_paginator.page(page)
except PageNotAnInteger:
sent_contacts = sent_contacts_paginator.page(1)
except EmptyPage:
sent_contacts = sent_contacts_paginator.page(sent_contacts_paginator.num_pages)
all_contacts_paginator = Paginator(all_contacts.order_by('id'), per_page)
if (tab == "all_contacts"):
page = page
else:
page = 1
try:
all_contacts = all_contacts_paginator.page(page)
except PageNotAnInteger:
all_contacts = all_contacts_paginator.page(1)
except EmptyPage:
all_contacts = all_contacts_paginator.page(all_contacts_paginator.num_pages)
read_contacts_paginator = Paginator(read_contacts.order_by('id'), per_page)
if (tab == "read_contacts"):
page = page
else:
page = 1
try:
read_contacts = read_contacts_paginator.page(page)
except PageNotAnInteger:
read_contacts = read_contacts_paginator.page(1)
except EmptyPage:
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}
return render(request, 'marketing/campaign/details.html', data)
@login_required(login_url='/login')
def campaign_delete(request):
return render(request, 'marketing/campaign/details.html')
def campaign_delete(request, pk):
try:
campaign = Campaign.objects.get(id=pk)
campaign.delete()
redirect_url = reverse('marketing:campaign_list')
except Campaign.DoesNotExist:
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')
if x_forwarded_for:
ipaddress = x_forwarded_for.split(',')[-1].strip()
else:
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()
if not campaign_link_click:
campaign_link_click = CampaignLinkClick.objects.create(
link=link, campaign=link.campaign, contact=contact, ip_address=ipaddress)
link.unique += 1
else:
campaign_link_click.ip_address = ipaddress
link.clicks += 1
link.save()
campaign_link_click.save()
url = link.original
else:
url = settings.URL_FOR_LINKS
return redirect(url)
def campaign_open(request, campaign_log_id, email_id):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ipaddress = x_forwarded_for.split(',')[-1].strip()
else:
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 = settings.STATIC_URL + 'images/'
image_data = open(settings.STATIC_ROOT + '/images/company.png', 'rb').read()
if campaign_log and contact:
campaign_open = CampaignOpen.objects.filter(
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_log.campaign.opens_unique += 1
else:
campaign_open.ip_address = ipaddress
campaign_log.campaign.opens += 1
campaign_log.campaign.status = "Sent"
campaign_log.campaign.save()
campaign_open.save()
return HttpResponse(image_data, content_type="image/png")

View file

@ -4,12 +4,14 @@ from accounts.models import Account
from contacts.models import Contact
from common.models import User, Comment, Attachments, Address
from cases.models import Case
from django.core.files.uploadedfile import SimpleUploadedFile
# Create your tests here.
class OpportunityModel(object):
def setUp(self):
self.user = User.objects.create(
@ -47,6 +49,7 @@ class OpportunityModel(object):
closed_on="2016-05-04",
description="hgfdxc",
created_by=self.user)
self.opportunity.assigned_to.add(self.user)
self.case = Case.objects.create(
name="raghu", case_type="Problem", status="New", account=self.account,
priority="Low", description="something",
@ -58,6 +61,7 @@ class OpportunityModel(object):
class OpportunityCreateTestCase(OpportunityModel, TestCase):
def test_opportunity_create(self):
response = self.client.get('/opportunities/create/', {
'name': "meghan", 'amount': "478",
@ -67,8 +71,14 @@ class OpportunityCreateTestCase(OpportunityModel, TestCase):
self.assertEqual(response.status_code, 200)
def test_opportunity_create_post(self):
upload_file = open('static/images/user.png', 'rb')
url = '/opportunities/create/'
data = {'name': "micky", 'amount': "500", 'stage': "CLOSED WON"}
data = {'name': "micky", 'amount': "500", 'stage': "CLOSED WON",
'assigned_to': str(self.user.id),
'contacts': str(self.contacts.id),
'tags': 'tag',
'oppurtunity_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())}
response = self.client.post(url, data)
# self.assertEqual(response.status_code, 302)
self.assertEqual(response.status_code, 200)
@ -113,16 +123,24 @@ class EditOpportunityTestCase(OpportunityModel, TestCase):
'stage': "negotiation/review",
'lead_source': "Call", 'probability': "58",
'closed_on': "2016-05-04", 'description': "hgfdxc"})
resp = self.client.post( '/opportunities/' + str(self.opportunity.id) + '/edit/', **{'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'})
resp = self.client.post('/opportunities/' + str(self.opportunity.id) +
'/edit/', **{'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'})
self.assertEqual(resp.status_code, 200)
resp1 = self.client.post( '/opportunities/create/', **{'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'})
resp1 = self.client.post(
'/opportunities/create/', **{'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'})
self.assertEqual(resp1.status_code, 200)
def test_update_opportunity(self):
upload_file = open('static/images/user.png', 'rb')
url = '/opportunities/' + str(self.opportunity.id) + '/edit/'
data = {
'name': "meghan", 'amount': "478", 'stage': "QUALIFICATION",
'probability': "58", 'closed_on': "2016-05-04", 'description': "hgfdxc"}
'probability': "58", 'closed_on': "2016-05-04",
'description': "hgfdxc",
'tags': 'tag', 'assigned_to': str(self.user.id),
'contacts': str(self.contacts.id),
'oppurtunity_attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())}
response = self.client.post(url, data)
# self.assertEqual(response.status_code, 302)
self.assertEqual(response.status_code, 200)
@ -137,6 +155,7 @@ class EditOpportunityTestCase(OpportunityModel, TestCase):
class OpportunityListView(OpportunityModel, TestCase):
def test_opportunity_list(self):
self.opportunity = Opportunity.objects.all()
response = self.client.get('/opportunities/list/')
@ -164,6 +183,7 @@ class OpportunityListView(OpportunityModel, TestCase):
class ContactGetViewTestCase(OpportunityModel, TestCase):
def test_get_contact(self):
url = '/opportunities/contacts/'
response = self.client.get(url)
@ -173,6 +193,7 @@ class ContactGetViewTestCase(OpportunityModel, TestCase):
class CommentTestCase(OpportunityModel, TestCase):
def test_comment_add(self):
response = self.client.post(
'/opportunities/comment/add/', {'opportunityid': self.opportunity.id})
@ -188,11 +209,21 @@ class CommentTestCase(OpportunityModel, TestCase):
'/opportunities/comment/remove/', {'comment_id': self.comment.id})
self.assertEqual(response.status_code, 200)
def test_comment_form_valid(self):
response = self.client.post(
'/opportunities/comment/add/', {'opportunityid': self.opportunity.id,
'comment': 'hello'})
self.assertEqual(response.status_code, 200)
class AttachmentTestCase(OpportunityModel, TestCase):
def test_attachment_add(self):
upload_file = open('static/images/user.png', 'rb')
url = "/opportunities/attachment/add/"
data = {'opportunityid': self.opportunity.id}
data = {'opportunityid': self.opportunity.id,
'attachment': SimpleUploadedFile(
upload_file.name, upload_file.read())}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 200)
@ -204,9 +235,9 @@ class AttachmentTestCase(OpportunityModel, TestCase):
class TestGetOpportunitiesView(OpportunityModel, TestCase):
def test_get_page(self):
url = "/opportunities/get/list/"
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertIsNotNone(resp.context['opportunities'])

BIN
static/images/company.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

File diff suppressed because it is too large Load diff

View file

@ -31,7 +31,7 @@
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="{% url 'marketing:campaign_details' campaign.id %}">{{ campaign.title }} </a></td>
<td>5478</td>
<td>
{{ campaign.created_on }}
{{ campaign.sent_on_format }}
</td>
<td>
<span class="badge badge-secondary">20.6%</span> 125 Opened
@ -40,7 +40,10 @@
<span class="badge badge-secondary">15.5%</span> 78 Clicked
</td>
<td class="actions">
<a href="{% url 'marketing:campaign_edit' campaign.id %}" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
<a href="{% url 'marketing:campaign_details' campaign.id %}" class="btn btn-success view"><i class="fas fa-eye"></i></a>
<!--
<a href="{% url 'marketing:campaign_edit' campaign.id %}" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
-->
<a href="{% url 'marketing:campaign_delete' campaign.id %}" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
</td>
</tr>
@ -49,116 +52,6 @@
<td colspan="6"> No records found!</td>
</tr>
{% endfor %}
<!--
<tr style="text-align:center;">
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
<td>5478</td>
<td>
Tue, Nov 22, 2016, 12:00AM
</td>
<td>
<span class="badge badge-secondary">20.6%</span> 125 Opened
</td>
<td>
<span class="badge badge-secondary">15.5%</span> 78 Clicked
</td>
<td class="actions">
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
</td>
</tr>
<tr style="text-align:center;">
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
<td>5478</td>
<td>
Tue, Nov 22, 2016, 12:00AM
</td>
<td>
<span class="badge badge-secondary">20.6%</span> 125 Opened
</td>
<td>
<span class="badge badge-secondary">15.5%</span> 78 Clicked
</td>
<td class="actions">
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
</td>
</tr>
<tr style="text-align:center;">
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
<td>5478</td>
<td>
Tue, Nov 22, 2016, 12:00AM
</td>
<td>
<span class="badge badge-secondary">20.6%</span> 125 Opened
</td>
<td>
<span class="badge badge-secondary">15.5%</span> 78 Clicked
</td>
<td class="actions">
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
</td>
</tr>
<tr style="text-align:center;">
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
<td>5478</td>
<td>
Tue, Nov 22, 2016, 12:00AM
</td>
<td>
<span class="badge badge-secondary">20.6%</span> 125 Opened
</td>
<td>
<span class="badge badge-secondary">15.5%</span> 78 Clicked
</td>
<td class="actions">
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
</td>
</tr>
<tr style="text-align:center;">
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
<td>5478</td>
<td>
Tue, Nov 22, 2016, 12:00AM
</td>
<td>
<span class="badge badge-secondary">20.6%</span> 125 Opened
</td>
<td>
<span class="badge badge-secondary">15.5%</span> 78 Clicked
</td>
<td class="actions">
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
</td>
</tr>
<tr style="text-align:center;">
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="#">Sample scheduled newsletter </a></td>
<td>5478</td>
<td>
Tue, Nov 22, 2016, 12:00AM
</td>
<td>
<span class="badge badge-secondary">20.6%</span> 125 Opened
</td>
<td>
<span class="badge badge-secondary">15.5%</span> 78 Clicked
</td>
<td class="actions">
<a href="/contacts/1/edit/" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
<a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a>
<a href="/contacts/1/delete/" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
</td>
</tr>
-->
</tbody>
</table>
</div>
@ -168,4 +61,4 @@
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View file

@ -6,280 +6,59 @@
<div class="marketing_pages">
<div class="row marl">
<div class="col-md-12">
<form id="add_form" method="POST" action='' novalidate>
<div class="overview_form_block row marl justify-content-center">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<div class="card-title text-center">
Create Campaign
</div>
<form>
<div class="row marl">
<div class="col-md-12">
<div class="form-group">
<label>Title</label>
<input type="text" name="title" class="form-control" placeholder="Title of this campaign">
<small class="form-text text-muted">Set a title for this campaign?</small>
<span id="id_title"></span>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label>Select Template</label>
<select class="form-control" name="email_template">
<option value="">Select a template</option>
{% for email_template in email_templates %}
<option value="{{email_template.id}}">{{ email_template.title }}</option>
{% endfor %}
<!-- <option value="one">First template</option>
<option value="two">Second template</option>
<option value="three">Third template</option>
<option value="four">Fourth template</option> -->
</select>
<span id="id_email_template"></span>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label>Subject</label>
<input type="text" name="subject" class="form-control" placeholder="Subject of this Campaign">
<!-- <small class="form-text text-muted">Set a title for this campaign?</small> -->
<span id="id_subject"></span>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label>From Name</label>
<input type="text" class="form-control" placeholder="John Doe" name="from_name">
<span id="id_from_name"></span>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label>From Email</label>
<input type="email" class="form-control" placeholder="support@mp.com" name="from_email">
<span id="id_from_email"></span>
</div>
</div>
<div class="col-md-12">
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label><input type="checkbox" name="reply_to_crm" value="true"> Reply To CRM</label>
<span id="id_reply_to_crm"></span>
</div>
<div class="form-group">
<label>Reply to email</label>
<input type="email" class="form-control" placeholder="replyto@mp.com" name="reply_to_email">
<span id="id_reply_to_email"></span>
</div>
<div class="form-group">
<label>Query String</label>
<div class="well">
Optionally append a query string to all links in your email newsletter. A good use case is Google Analytics tracking. Don't include '?' in your query string.
</div>
<input type="email" class="form-control" placeholder="url">
</div>
<div class="form-group">
<label>Attachments</label>
<input type="file" class="form-control" name="attachment">
<small class="form-text text-muted">Allowed file types: jpeg, jpg, gif, png, pdf, zip</small>
<span id="id_attachment"></span>
</div>
<div class="form-group">
<label>Track Opens</label>
<button type="button" class="btn btn-toggle" data-toggle="button" aria-pressed="false" autocomplete="off">
<div class="handle"></div>
</button>
</div>
<div class="form-group">
<label>Track Clicks</label>
<button type="button" class="btn btn-toggle" data-toggle="button" aria-pressed="false" autocomplete="off">
<div class="handle"></div>
</button>
</div><br>
<div class="form-group">
<button type="submit" class="primary_btn mar_btm save_btn">Save Campaign</button>
<a href="{% url 'marketing:campaign_list' %}" class="seconadry_btn">Cancel</a>
</div>
</div>
<div class="col-md-9">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label><input type="checkbox" name="schedule_later" value="true"> Schedule Later</label>
<span id="id_schedule_later"></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Timezone</label>
<select name="timezone" id="timezone" class="form-control">
<option value="">Select a Timezone</option>
{% for timezone in timezones %}
<option value="{{timezone.0}}">{{timezone.1}}</option>
{% endfor %}
</select>
<span id="id_timezone"></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="">Schedule Date Time</label>
<input type="datetime" class="form-control" name="schedule_date_time" value="" placeholder="Schedule Date Time">
<span id="id_schedule_date_time"></span>
</div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label>Plain Text</label>
<textarea class="form-control" rows="3" name="plain_html"></textarea>
<span id="id_plain_html"></span>
</div>
<div class="form-group">
<label>Html Code</label>
<div id="editor-container">
</div>
<span class="hint">Use the following tags in your subject, plain text or HTML code and they'll automatically be formatted when your campaign is sent. For web version and unsubscribe tags, you can style them with inline CSS.</span>
<input type="hidden" name="html" id="html_content">
<span id="id_html"></span>
</div>
<div class="col-md-12 no-padding">
<div class="row help_container">
<div class="col-md-6">
<div class="heading">Essential tags (HTML only)</div>
<p class="desc">The following tags can only be used on the HTML version</p>
<ul>
<li>
<strong>Webversion link: </strong>
<code>
<webversion>View web version<webversion>
</code>
</li>
<li>
<strong>Unsubscribe link: </strong>
<code>
<unsubscribe>Unsubscribe here</unsubscribe>
</code>
</li>
</ul>
<div class="heading">Essential tags</div>
<p class="desc">The following tags can only be used on the HTML version</p>
<ul>
<li>
<strong>Webversion link: </strong>
<code>
<webversion>View web version<webversion>
</code>
</li>
<li>
<strong>Unsubscribe link: </strong>
<code>
<unsubscribe>Unsubscribe here</unsubscribe>
</code>
</li>
</ul>
</div>
<div class="col-md-6">
<div class="heading">Personalization tags</div>
<p class="desc">The following tags can be used on both Plain text or HTML version</p>
<ul>
<li>
<strong>Name: </strong>
<code>[Name,fallback=]</code>
</li>
<li>
<strong>Email: </strong>
<code>[Email]</code>
</li>
<li>
<strong>Two digit day of the month (eg. 01 to 31):</strong>
<code>[currentdaynumber]</code>
</li>
</ul>
<div class="heading">Essential tags</div>
<p class="desc">The following tags can only be used on the HTML version</p>
<ul>
<li>
<strong>Name: </strong>
<code>[Name,fallback=]</code>
</li>
<li>
<strong>Email: </strong>
<code>[Email]</code>
</li>
<li>
<strong>Two digit day of the month (eg. 01 to 31):</strong>
<code>[currentdaynumber]</code>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="table_container_row no_mar_top">
<div class="card">
<div class="card-body">
<div class="card-title text-right">
<span class="float-left">Campaigns - {{ campaigns_list|length }}</span>
<a href="{% url 'marketing:campaign_new' %}" class="primary_btn">Add New Campaign</a>
</div>
<div class="table-responsive table-bordered ">
<table class="table">
<thead>
<tr>
<th width="25%">Campaign</th>
<th width="10%">Receipents</th>
<th width="20%">Sent</th>
<th width="15%">Unique Opens</th>
<th width="15%">Unique Clicks</th>
<th width="10%">Actions</th>
</tr>
</thead>
<tbody>
{% for campaign in campaigns_list %}
<tr style="text-align:center;">
<td scope="row"><span class="badge badge-primary">Primary</span> <a href="{% url 'marketing:campaign_details' campaign.id %}">{{ campaign.title }} </a></td>
<td>5478</td>
<td>
{{ campaign.sent_on_format }}
</td>
<td>
<span class="badge badge-secondary">20.6%</span> 125 Opened
</td>
<td>
<span class="badge badge-secondary">15.5%</span> 78 Clicked
</td>
<td class="actions">
<a href="{% url 'marketing:campaign_details' campaign.id %}" class="btn btn-success view"><i class="fas fa-eye"></i></a>
<!--
<a href="{% url 'marketing:campaign_edit' campaign.id %}" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
-->
<a href="{% url 'marketing:campaign_delete' campaign.id %}" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="6"> No records found!</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="https://malsup.github.com/jquery.form.js"></script>
<script>
var quill = new Quill('#editor-container', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['image', 'code-block']
]
},
placeholder: 'Compose an epic...',
theme: 'snow' // or 'bubble'
});
quill.on('text-change', function() {
var justHtml = quill.root.innerHTML;
$('#html_content').val(justHtml);
});
$('.save_btn').click(function(e){
e.preventDefault();
$("form#add_form").submit()
})
$('form#add_form').ajaxForm({
type: 'POST',
dataType: 'json',
url: $(this).attr('action'),
data: $(this).serialize(),
success: function(data) {
console.log("data", data)
if (data.error == false) {
window.location = "{% url 'marketing:campaign_list' %}";
} else {
/*$(document).ajaxStop($.unblockUI);*/
// $.unblockUI();
$('p.failure').remove();
for (var key in data.errors) {
$('#id_' + key).html('<p class="error failure"> *' + data.errors[key] + '</p>');
}
}
},
error: function(xhr, errmsg, err) {
console.log('error data', errmsg, err)
}
});
</script>
{% endblock js_block %}

View file

@ -0,0 +1,55 @@
{% extends 'marketing/base.html' %}
{% load staticfiles %}
{% load paginate %}
{% block content %}
{{ block.super }}
<div class="marketing_pages">
<div class="row marl">
<div class="col-md-12">
<div class="table_container_row no_mar_top">
<div class="card">
<div class="card-body">
<div class="card-title text-right">
<span class="float-left">{{ contact_list.name }} - {{ contacts_list|length }}</span>
{% if contact_list.failed_contacts.all|length > 0 %}
<a href="{% url 'marketing:failed_contact_list_detail' contact_list.id %}" class="primary_btn">Failed Contacts</a>
{% endif %}
<a href="#" class="primary_btn">Add New Contact</a>
</div>
<div class="table-responsive table-bordered ">
<table class="table">
<thead>
<tr>
<th width="5%">ID</th>
<th width="20%">Name</th>
<th width="20%">Email</th>
<th width="10%">Actions</th>
</tr>
</thead>
<tbody>
{% for contact in contacts_list %}
<tr style="text-align:center;">
<td scope="row">{{ forloop.counter }}</td>
<td>{{ contact.name }}</td>
<td>{{ contact.email }}</td>
<td class="actions">
<a href="#" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
<!-- <a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a> -->
<a href="#" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
</td>
</tr>
{% empty %}
<tr style="text-align:center;">
<td colspan="6">No records found!</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,55 @@
{% extends 'marketing/base.html' %}
{% load staticfiles %}
{% load paginate %}
{% block content %}
{{ block.super }}
<div class="marketing_pages">
<div class="row marl">
<div class="col-md-12">
<div class="table_container_row no_mar_top">
<div class="card">
<div class="card-body">
<div class="card-title text-right">
<span class="float-left">{{ contact_list.name }} - Failed: {{ failed_contacts_list|length }}</span>
{% if contact_list.contacts.all|length > 0 %}
<a href="{% url 'marketing:contact_list_detail' contact_list.id %}" class="primary_btn">Contacts</a>
{% endif %}
<a href="{% url 'marketing:failed_contact_list_download_delete' contact_list.id %}" class="primary_btn">Download & Delete</a>
</div>
<div class="table-responsive table-bordered ">
<table class="table">
<thead>
<tr>
<th width="5%">ID</th>
<th width="20%">Name</th>
<th width="20%">Email</th>
<th width="10%">Actions</th>
</tr>
</thead>
<tbody>
{% for contact in failed_contacts_list %}
<tr style="text-align:center;">
<td scope="row">{{ forloop.counter }}</td>
<td>{{ contact.name }}</td>
<td>{{ contact.email }}</td>
<td class="actions">
<a href="#" class="btn btn-success edit"><i class="fas fa-edit"></i></a>
<!-- <a href="/contacts/1/edit/" class="btn btn-info duplicate"><i class="fas fa-copy"></i></a> -->
<a href="#" class="btn btn-danger delete remove_account"><i class="fas fa-trash"></i></a>
</td>
</tr>
{% empty %}
<tr style="text-align:center;">
<td colspan="6">No records found!</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -29,7 +29,7 @@
{% for contact_list in contact_lists %}
<tr style="text-align:center;">
<td scope="row">{{ forloop.counter }}</td>
<td>{{ contact_list.name }}</td>
<td><a href="{% url 'marketing:contact_list_detail' contact_list.id %}">{{ contact_list.name }}</td>
<td>{{ contact_list.created_by }}</td>
<td>
{{ contact_list.updated_on }}
@ -157,4 +157,4 @@
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View file

@ -2,95 +2,94 @@
{% load staticfiles %}
{% load paginate %}
{% block extralinks %}
<link href="https://cdnjs.cloudflare.com/ajax/libs/jquery-tagsinput/1.3.6/jquery.tagsinput.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/jquery-tagsinput/1.3.6/jquery.tagsinput.min.css" rel="stylesheet">
{% endblock extralinks %}
{% block content %}
{{ block.super }}
<div class="marketing_pages">
<div class="row marl">
<div class="col-md-12">
<form id="add_form" method="post" action="." enctype="multipart/form-data" novalidate>
<div class="overview_form_block row marl justify-content-center">
<div class="col-md-5">
<div class="card">
<div class="card-body">
<div class="card-title text-center">
{% if contact_list %}Edit{% else %}Create{% endif %} Contact List
</div>
<div class="row marl justify-content-center">
<div class="filter_col col-md-12">
<div class="form-group">
<label class="required">Name <span class="error">*</span></label>
<input type="text" class="form-control" name="name" value="{% if contact_list %}{{contact_list.name}}{% endif %}" placeholder="Enter Contacts Name">
<span id="id_name"></span>
</div>
</div>
<div class="filter_col col-md-12">
<div class="form-group">
<label>Contacts File </label>
<input type="file" class="form-control" name="contacts_file">
<span id="id_contacts_file"></span>
</div>
</div>
<div class="filter_col col-md-12">
<div class="form-group">
<label>Tags</label>
<div class="txt-box-div" id="tag-div">
<input type="text" name="tags" id="tags_1" class="tags" value="{% if contact_list %}{% for tag in contact_list.tags.all %}{{tag.name}}, {% endfor %}{% endif %}" />
</div>
<span id="id_tags"></span>
</div>
</div>
<div class="filter_col col-md-12">
<div class="demo_clist">
<a href="#">Demo file <i class="fas fa-download"></i> </a>
</div>
</div>
<div class="col-md-12">
<div class="row marl buttons_row text-center form_btn_row">
<button type="submit" class="btn primary_btn save_btn">Save</button>
<a href="{% url 'marketing:contact_lists' %}" class="btn seconadry_btn">Cancel</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
{{ block.super }}
<div class="marketing_pages">
<div class="row marl">
<div class="col-md-12">
<form id="add_form" method="post" action="." enctype="multipart/form-data" novalidate>
<div class="overview_form_block row marl justify-content-center">
<div class="col-md-5">
<div class="card">
<div class="card-body">
<div class="card-title text-center">
{% if contact_list %}Edit{% else %}Create{% endif %} Contact List
</div>
<div class="row marl justify-content-center">
<div class="filter_col col-md-12">
<div class="form-group">
<label class="required">Name <span class="error">*</span></label>
<input type="text" class="form-control" name="name" value="{% if contact_list %}{{contact_list.name}}{% endif %}" placeholder="Enter Contact List Name">
<span id="id_name"></span>
</div>
</div>
<div class="filter_col col-md-12">
<div class="form-group">
<label>Contacts File </label>
<input type="file" class="form-control" name="contacts_file">
<span id="id_contacts_file"></span>
</div>
</div>
<div class="filter_col col-md-12">
<div class="form-group">
<label>Tags</label>
<div class="txt-box-div" id="tag-div">
<input type="text" name="tags" id="tags_1" class="tags" value="{% if contact_list %}{% for tag in contact_list.tags.all %}{{tag.name}}, {% endfor %}{% endif %}" />
</div>
<span id="id_tags"></span>
</div>
</div>
<div class="filter_col col-md-12">
<div class="demo_clist">
<a href="#">Demo file <i class="fas fa-download"></i> </a>
</div>
</div>
<div class="col-md-12">
<div class="row marl buttons_row text-center form_btn_row">
<button type="submit" class="btn primary_btn save_btn">Save</button>
<a href="{% url 'marketing:contact_lists' %}" class="btn seconadry_btn">Cancel</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-tagsinput/1.3.6/jquery.tagsinput.min.js"></script>
<script src="https://malsup.github.com/jquery.form.js"></script>
<script>
// $(document).ajaxStart($.blockUI).ajaxStop($.unblockUI);
$('.save_btn').click(function(e){
e.preventDefault();
$("form#add_form").submit()
})
/*$.blockUI({
message: '<h1>Please wait ...</h1>',
css: {
border: 'none',
padding: '15px',
backgroundColor: '#000',
'-webkit-border-radius': '10px',
'-moz-border-radius': '10px',
opacity: .5,
color: '#fff'
}
});*/
// setTimeout($.unblockUI, 2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-tagsinput/1.3.6/jquery.tagsinput.min.js"></script>
<script src="https://malsup.github.com/jquery.form.js"></script>
<script>
// $(document).ajaxStart($.blockUI).ajaxStop($.unblockUI);
$('.save_btn').click(function(e) {
e.preventDefault();
$("form#add_form").submit()
})
/*$.blockUI({
message: '<h1>Please wait ...</h1>',
css: {
border: 'none',
padding: '15px',
backgroundColor: '#000',
'-webkit-border-radius': '10px',
'-moz-border-radius': '10px',
opacity: .5,
color: '#fff'
}
});*/
// setTimeout($.unblockUI, 2000);
$('form#add_form').ajaxForm({
type: 'POST',
type: 'POST',
dataType: 'json',
url: $(this).attr('action'),
data: $(this).serialize(),
success: function(data) {
console.log("data", data)
if (data.error == false) {
window.location = "{% url 'marketing:contact_lists' %}";
} else {
@ -103,13 +102,12 @@
}
},
error: function(xhr, errmsg, err) {
console.log('error data', errmsg, err)
console.log('error data', errmsg, err)
}
});
$(function() {
$('#tags_1').tagsInput({width:'auto'});
});
</script>
{% endblock js_block %}
$(function() {
$('#tags_1').tagsInput({ width: 'auto' });
});
</script>
{% endblock js_block %}