trivial: apply black

This commit is contained in:
Frédéric Péters 2021-01-11 20:10:12 +01:00
parent 54aae08cfe
commit ce7f2dd500
44 changed files with 571 additions and 454 deletions

6
debian/settings.py vendored
View File

@ -16,15 +16,15 @@
DEBUG = False
TEMPLATE_DEBUG = False
#ADMINS = (
# ADMINS = (
# # ('User 1', 'watchdog@example.net'),
# # ('User 2', 'janitor@example.net'),
#)
# )
# ALLOWED_HOSTS must be correct in production!
# See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
ALLOWED_HOSTS = [
'*',
'*',
]
# Databases

View File

@ -25,16 +25,18 @@ class eo_sdist(sdist):
def get_version():
'''Use the VERSION, if absent generates a version with git describe, if not
tag exists, take 0.0- and add the length of the commit log.
'''
"""Use the VERSION, if absent generates a version with git describe, if not
tag exists, take 0.0- and add the length of the commit log.
"""
if os.path.exists('VERSION'):
with open('VERSION', 'r') as v:
return v.read()
if os.path.exists('.git'):
p = subprocess.Popen(
['git', 'describe', '--dirty=.dirty', '--match=v*'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
result = p.communicate()[0]
if p.returncode == 0:
result = result.decode('ascii').strip()[1:] # strip spaces/newlines and initial v
@ -45,9 +47,7 @@ def get_version():
version = result
return version
else:
return '0.0.post%s' % len(
subprocess.check_output(
['git', 'rev-list', 'HEAD']).splitlines())
return '0.0.post%s' % len(subprocess.check_output(['git', 'rev-list', 'HEAD']).splitlines())
return '0.0'
@ -65,6 +65,7 @@ class compile_translations(Command):
curdir = os.getcwd()
try:
from django.core.management import call_command
for path, dirs, files in os.walk('welco'):
if 'locale' not in dirs:
continue
@ -108,7 +109,8 @@ setup(
'Programming Language :: Python',
'Programming Language :: Python :: 2',
],
install_requires=['django>=1.11,<2.3',
install_requires=[
'django>=1.11,<2.3',
'gadjo',
'django-ckeditor<4.5.4',
'django-haystack<2.8',
@ -118,7 +120,7 @@ setup(
'whoosh',
'XStatic-Select2',
'python-dateutil',
],
],
zip_safe=False,
cmdclass={
'build': build,

View File

@ -29,14 +29,9 @@ def test_get_contacts_zone_view(app, db):
resp = app.get('/ajax/contacts', status=200)
assert resp.html.find('button')['data-url'] == '/contacts/add/'
mail = Mail.objects.create(
content=ContentFile('foo', name='bar.txt'),
contact_id='42')
mail = Mail.objects.create(content=ContentFile('foo', name='bar.txt'), contact_id='42')
source_type = ContentType.objects.get_for_model(Mail).pk
resp = app.get(
'/ajax/contacts',
params={'source_type': source_type, 'source_pk': mail.pk},
status=200)
resp = app.get('/ajax/contacts', params={'source_type': source_type, 'source_pk': mail.pk}, status=200)
assert resp.html.find('a').text == '...'
assert resp.html.find('a')['data-page-slug'] == '42'
@ -46,9 +41,8 @@ def test_post_contacts_zone_view(app, db):
assert not mail.contact_id
source_type = ContentType.objects.get_for_model(Mail).pk
resp = app.post(
'/ajax/contacts',
params={'source_type': source_type, 'source_pk': mail.pk, 'user_id': 42},
status=200)
'/ajax/contacts', params={'source_type': source_type, 'source_pk': mail.pk, 'user_id': 42}, status=200
)
assert resp.text == 'ok'
assert Mail.objects.get(id=mail.pk).contact_id == '42'
@ -90,20 +84,24 @@ def test_search_json_view(settings, app, user, mail_group):
def response(url, request):
headers = {'content-type': 'application/json'}
content = {
'data': [{
'user_display_name': 'John Doe',
'user_email': 'john@example.net',
'user_var_phone': '0123456789',
'user_var_mobile': '0612345789',
'user_id': '42',
'user_roles': [{
'name': 'Agent',
'text': 'Agent',
'slug': 'agent',
'id': '8d73434814484aa0b8555ac9c68a9300'
}],
}],
'err': 0
'data': [
{
'user_display_name': 'John Doe',
'user_email': 'john@example.net',
'user_var_phone': '0123456789',
'user_var_mobile': '0612345789',
'user_id': '42',
'user_roles': [
{
'name': 'Agent',
'text': 'Agent',
'slug': 'agent',
'id': '8d73434814484aa0b8555ac9c68a9300',
}
],
}
],
'err': 0,
}
return httmock.response(200, content, headers)
@ -123,6 +121,7 @@ def test_contact_detail_fragment_view(settings, app, db):
}
}
}
@httmock.urlmatch(netloc='wcs.example.net', path='/api/users/42/', method='GET')
def response(url, request):
headers = {'content-type': 'application/json'}
@ -132,12 +131,9 @@ def test_contact_detail_fragment_view(settings, app, db):
'user_var_phone': '0123456789',
'user_var_mobile': '0612345789',
'user_id': '42',
'user_roles': [{
'name': 'Agent',
'text': 'Agent',
'slug': 'agent',
'id': '8d73434814484aa0b8555ac9c68a9300'
}],
'user_roles': [
{'name': 'Agent', 'text': 'Agent', 'slug': 'agent', 'id': '8d73434814484aa0b8555ac9c68a9300'}
],
}
return httmock.response(200, content, headers)
@ -148,15 +144,12 @@ def test_contact_detail_fragment_view(settings, app, db):
assert resp.html.find('li').text == 'Phone: 0123456789'
# unused 'is_pinned_user' context
mail = Mail.objects.create(
content=ContentFile('foo', name='bar.txt'),
contact_id='42')
mail = Mail.objects.create(content=ContentFile('foo', name='bar.txt'), contact_id='42')
source_type = ContentType.objects.get_for_model(Mail).pk
with httmock.HTTMock(response):
resp = app.get(
'/ajax/contacts/42/',
params={'source_type': source_type, 'source_pk': mail.pk},
status=200)
'/ajax/contacts/42/', params={'source_type': source_type, 'source_pk': mail.pk}, status=200
)
assert resp.html.find('h3').text == 'John Doe'
@ -183,7 +176,7 @@ def test_post_contact_add_view(mocked_sleep, settings, app, db):
'orig': 'http://welco.example.net/',
'secret': 'xxx',
}
}
},
}
# normal case
@ -210,7 +203,8 @@ def test_post_contact_add_view(mocked_sleep, settings, app, db):
'first_name': 'John',
'last_name': 'Doe',
},
status=200)
status=200,
)
assert resp.content_type == 'application/json'
assert resp.json['data']['user_id'] == '43'

View File

@ -46,10 +46,7 @@ def test_get_feeder_view(app, user):
def test_post_feeder_view(app, user):
app.set_user(user.username)
resp = app.post(
'/mail/feeder/',
params={'mail': Upload('filename.txt', b'contents')},
status=302)
resp = app.post('/mail/feeder/', params={'mail': Upload('filename.txt', b'contents')}, status=302)
assert resp.location == '/mail/feeder/'
resp = resp.follow()
assert resp.html.find('li', {'class': 'info'}).text == '1 files uploaded successfully.'
@ -63,27 +60,28 @@ def test_qualification_save_view(settings, app, db):
}
}
}
mail = Mail.objects.create(
content=ContentFile('foo', name='bar.txt'),
subject='spam')
mail = Mail.objects.create(content=ContentFile('foo', name='bar.txt'), subject='spam')
assert not mail.contact_id
source_type = ContentType.objects.get_for_model(Mail).pk
resp = app.post(
'/ajax/qualification-mail-save',
params={'source_type': source_type, 'source_pk': mail.pk, 'subject': 'eggs'},
status=302)
assert resp.location == '/ajax/qualification?source_type=%s&source_pk=%s' % (
source_type, mail.pk)
status=302,
)
assert resp.location == '/ajax/qualification?source_type=%s&source_pk=%s' % (source_type, mail.pk)
@httmock.urlmatch(netloc='wcs.example.net', path='/api/formdefs/', method='GET')
def response_get(url, request):
headers = {'content-type': 'application/json'}
content = {
"err": 0,
"data": [{
"title": "Foo",
"slug": "foo",
}]}
"data": [
{
"title": "Foo",
"slug": "foo",
}
],
}
return httmock.response(200, content, headers)
with httmock.HTTMock(response_get):
@ -97,9 +95,7 @@ def test_edit_note_view(app, user):
assert resp.location.startswith('/login/?next=')
app.set_user(user.username)
mail = Mail.objects.create(
content=ContentFile('foo', name='bar.txt'),
note='spam')
mail = Mail.objects.create(content=ContentFile('foo', name='bar.txt'), note='spam')
resp = app.get('/ajax/mail/edit-note/', params={'mail': mail.pk}, status=200)
assert resp.html.find('h2').text == 'Note'
assert resp.html.find('textarea', {'name': 'note'}).text == 'spam'
@ -123,7 +119,7 @@ def test_note_view(app, user):
def test_reject_view(settings, app, user):
settings.MAARCH_FEED= {
settings.MAARCH_FEED = {
'URL': 'http://maarch.example.net',
'ENABLE': True,
'USERNAME': 'xxx',
@ -134,9 +130,7 @@ def test_reject_view(settings, app, user):
assert resp.location.startswith('/login/?next=')
app.set_user(user.username)
mail = Mail.objects.create(
content=ContentFile('foo', name='bar.txt'),
external_id='maarch-42')
mail = Mail.objects.create(content=ContentFile('foo', name='bar.txt'), external_id='maarch-42')
@httmock.urlmatch(netloc='maarch.example.net', path='/rest/res/resource/status', method='PUT')
def response_ok(url, request):
@ -150,9 +144,7 @@ def test_reject_view(settings, app, user):
assert Mail.objects.count() == 0
# errors
mail = Mail.objects.create(
content=ContentFile('foo', name='bar.txt'),
external_id='maarch-42')
mail = Mail.objects.create(content=ContentFile('foo', name='bar.txt'), external_id='maarch-42')
@httmock.urlmatch(netloc='maarch.example.net', path='/rest/res/resource/status', method='PUT')
def response_error1(url, request):

View File

@ -112,15 +112,11 @@ def test_wcs_summary_view(app, mail_group, user):
mail = Mail.objects.create(content=ContentFile('foo', name='bar.txt'))
source_type = ContentType.objects.get_for_model(Mail).pk
resp = app.get(
'/ajax/summary/%s/%s/?callback=spam' % (source_type, mail.pk),
status=302)
resp = app.get('/ajax/summary/%s/%s/?callback=spam' % (source_type, mail.pk), status=302)
assert resp.location.startswith('/login/?next=')
app.set_user(user.username)
resp = app.get(
'/ajax/summary/%s/%s/?callback=spam' % (source_type, mail.pk),
status=200)
resp = app.get('/ajax/summary/%s/%s/?callback=spam' % (source_type, mail.pk), status=200)
assert resp.content_type == 'application/javascript'
assert 'bar' in resp.text
assert resp.text.startswith('spam({')
@ -130,8 +126,8 @@ def test_remove_association_view(app, mail_group, user):
mail = Mail.objects.create(content=ContentFile('foo', name='bar.txt'))
source_type = ContentType.objects.get_for_model(Mail).pk
association = Association.objects.create(
source_type=ContentType.objects.get(id=source_type),
source_pk=mail.pk)
source_type=ContentType.objects.get(id=source_type), source_pk=mail.pk
)
assert Association.objects.filter(id=association.pk).count() == 1
resp = app.get('/ajax/remove-association/%s' % association.pk, status=302)
@ -154,8 +150,8 @@ def test_create_formdata_view(settings, app, mail_group, user):
mail = Mail.objects.create(content=ContentFile('foo', name='bar.txt'))
source_type = ContentType.objects.get_for_model(Mail).pk
association = Association.objects.create(
source_type=ContentType.objects.get(id=source_type),
source_pk=mail.pk)
source_type=ContentType.objects.get(id=source_type), source_pk=mail.pk
)
resp = app.get('/ajax/create-formdata/%s' % association.pk, status=302)
assert resp.location.startswith('/login/?next=')
@ -185,7 +181,8 @@ def test_create_formdata_view(settings, app, mail_group, user):
'data': {
'id': 42,
'backoffice_url': 'http://example.net',
}}
},
}
return httmock.response(200, content, headers)
with httmock.HTTMock(response_get, response_post):
@ -204,8 +201,7 @@ def test_menu_json_view(app, user, mail_group, phone_group, counter_group, kb_gr
app.set_user(user.username)
resp = app.get('/menu.json', status=200)
assert resp.content_type == 'application/json'
assert sorted([x['label'] for x in resp.json]) == [
'Call Center', 'Counter', 'Knowledge Base', 'Mails']
assert sorted([x['label'] for x in resp.json]) == ['Call Center', 'Counter', 'Knowledge Base', 'Mails']
resp = app.get('/menu.json?callback=foo', status=200)
assert resp.content_type == 'application/javascript'

View File

@ -23,27 +23,25 @@ from welco.forms import QualificationForm
pytestmark = pytest.mark.django_db
KNOWN_SERVICES = {
'wcs': {
'eservices': {
'url': 'http://localhost/',
'title': 'Eservices',
'orig': 'welco'
}
}
}
KNOWN_SERVICES = {'wcs': {'eservices': {'url': 'http://localhost/', 'title': 'Eservices', 'orig': 'welco'}}}
@mock.patch('welco.utils.requests.get')
def test_get_qualification(mocked_get, client):
with override_settings(KNOWN_SERVICES=KNOWN_SERVICES):
forms = mock.Mock()
forms.json.return_value = {'data': [{'category': 'Test',
'authentication_required': False,
'description': '',
'title': 'Test form',
'slug': 'test-form'}],
'err': 0}
forms.json.return_value = {
'data': [
{
'category': 'Test',
'authentication_required': False,
'description': '',
'title': 'Test form',
'slug': 'test-form',
}
],
'err': 0,
}
mocked_get.return_value = forms
user = mock.Mock()

View File

@ -59,11 +59,13 @@ class MaarchMock(BaseMock):
},
'status_code': 200,
}
list_endpoint.path = '^/rest/res/list$'
def update_external_infos(self, url, request):
self.requests.append(('update_external_infos', url, request, json.loads(force_text(request.body))))
return json.dumps({})
update_external_infos.path = '^/rest/res/externalInfos$'
def update_status(self, url, request):
@ -75,10 +77,12 @@ class MaarchMock(BaseMock):
},
'status_code': 200,
}
update_status.path = '^/rest/res/resource/status$'
def post_courrier(self, url, request):
self.requests.append(('post_courrier', url, request, json.loads(force_text(request.body))))
post_courrier.path = '^/rest/res$'
@ -96,37 +100,50 @@ def maarch(settings, mail_group):
class WcsMock(BaseMock):
def api_formdefs(self, url, request):
return json.dumps({
'data': [{
'slug': 'slug1',
'title': 'title1',
}]
})
return json.dumps(
{
'data': [
{
'slug': 'slug1',
'title': 'title1',
}
]
}
)
api_formdefs.path = '^/api/formdefs/$'
def json(self, url, request):
return json.dumps({
'data': [{
'slug': 'slug1',
'title': 'title1',
'category': 'category1',
}]
})
return json.dumps(
{
'data': [
{
'slug': 'slug1',
'title': 'title1',
'category': 'category1',
}
]
}
)
json.path = '^/json$'
def api_formdefs_slug1_schema(self, url, request):
return json.dumps({
})
return json.dumps({})
api_formdefs_slug1_schema.path = '^/api/formdefs/slug-1/schema$'
def api_formdefs_slug1_submit(self, url, request):
return json.dumps({
'err': 0,
'data': {
'id': 1,
'backoffice_url': 'http://wcs.example.net/slug-1/1',
},
})
return json.dumps(
{
'err': 0,
'data': {
'id': 1,
'backoffice_url': 'http://wcs.example.net/slug-1/1',
},
}
)
api_formdefs_slug1_submit.path = '^/api/formdefs/slug-1/submit$'
@ -172,14 +189,16 @@ def test_feed(settings, app, maarch, wcs, user):
# feed mails from maarch
with maarch.ctx_manager:
# list request
maarch.responses.append({
'resources': [
{
'res_id': 1,
'fileBase64Content': force_text(base64.b64encode(PDF_MOCK)),
}
],
})
maarch.responses.append(
{
'resources': [
{
'res_id': 1,
'fileBase64Content': force_text(base64.b64encode(PDF_MOCK)),
}
],
}
)
# update status request
maarch.responses.append({})
# last list request
@ -214,20 +233,26 @@ def test_feed(settings, app, maarch, wcs, user):
maarch.clear()
pk = Mail.objects.get().pk
with wcs.ctx_manager, maarch.ctx_manager:
source_type = str(ContentType.objects.get_for_model(Mail).pk),
source_type = (str(ContentType.objects.get_for_model(Mail).pk),)
source_pk = str(pk)
response = app.get('/ajax/qualification', params={
'source_type': source_type,
'source_pk': source_pk,
})
response = app.get(
'/ajax/qualification',
params={
'source_type': source_type,
'source_pk': source_pk,
},
)
assert len(response.pyquery('a[data-association-pk]')) == 0
response = app.post('/ajax/qualification', params={
'source_type': source_type,
'source_pk': str(pk),
'formdef_reference': 'demarches:slug-1',
})
response = app.post(
'/ajax/qualification',
params={
'source_type': source_type,
'source_pk': str(pk),
'formdef_reference': 'demarches:slug-1',
},
)
# verify qualification was done
assert len(response.pyquery('a[data-association-pk]')) == 1
@ -243,7 +268,7 @@ def test_feed(settings, app, maarch, wcs, user):
'external_link': 'http://wcs.example.net/slug-1/1',
'res_id': 1,
}
]
],
}
# verify we can answer
@ -260,24 +285,24 @@ def test_feed(settings, app, maarch, wcs, user):
assert response.json['err'] == 1
# verify error when maarch feed is not configured
settings.MAARCH_FEED['ENABLE'] = False
response = app.post_json('/api/mail/response/',
params={'mail_id': 'maarch-1', 'content': 'coucou'},
status=200)
response = app.post_json(
'/api/mail/response/', params={'mail_id': 'maarch-1', 'content': 'coucou'}, status=200
)
assert response.json['err'] == 1
assert response.json['err_desc'] == 'maarch is unconfigured'
settings.MAARCH_FEED['ENABLE'] = True
# verify error when mail_id is unknown
response = app.post_json('/api/mail/response/',
params={'mail_id': 'maarch-231', 'content': 'coucou'},
status=404)
response = app.post_json(
'/api/mail/response/', params={'mail_id': 'maarch-231', 'content': 'coucou'}, status=404
)
assert response.json['err'] == 1
# successfull call
maarch.responses.append({})
with maarch.ctx_manager:
response = app.post_json('/api/mail/response/',
params={'mail_id': 'maarch-1', 'content': 'coucou'},
status=200)
response = app.post_json(
'/api/mail/response/', params={'mail_id': 'maarch-1', 'content': 'coucou'}, status=200
)
assert maarch.requests[0][3] == {
'historyMessage': 'coucou',
'resId': [1],

View File

@ -38,95 +38,104 @@ def test_call_start_stop(client):
'callee': '102',
'data': {
'user': 'boby.lapointe',
}
},
}
response = client.post(reverse('phone-call-event'), json.dumps(payload),
content_type='application/json')
response = client.post(reverse('phone-call-event'), json.dumps(payload), content_type='application/json')
assert response.status_code == 200
assert response['content-type'] == 'application/json'
assert response.json() == {'err': 0}
assert models.PhoneCall.objects.count() == 1
assert models.PhoneCall.objects.filter(
caller='0033699999999',
callee='102',
data=json.dumps(payload['data']), stop__isnull=True).count() == 1
assert (
models.PhoneCall.objects.filter(
caller='0033699999999', callee='102', data=json.dumps(payload['data']), stop__isnull=True
).count()
== 1
)
# new start event
response = client.post(reverse('phone-call-event'), json.dumps(payload),
content_type='application/json')
response = client.post(reverse('phone-call-event'), json.dumps(payload), content_type='application/json')
assert response.status_code == 200
assert response['content-type'] == 'application/json'
assert response.json() == {'err': 0}
assert models.PhoneCall.objects.count() == 2
assert models.PhoneCall.objects.filter(
caller='0033699999999',
callee='102',
data=json.dumps(payload['data']), stop__isnull=True).count() == 1
assert (
models.PhoneCall.objects.filter(
caller='0033699999999', callee='102', data=json.dumps(payload['data']), stop__isnull=True
).count()
== 1
)
# first call has been closed
assert models.PhoneCall.objects.filter(
caller='0033699999999',
callee='102',
data=json.dumps(payload['data']), stop__isnull=False).count() == 1
assert (
models.PhoneCall.objects.filter(
caller='0033699999999', callee='102', data=json.dumps(payload['data']), stop__isnull=False
).count()
== 1
)
payload['event'] = 'stop'
response = client.post(reverse('phone-call-event'), json.dumps(payload),
content_type='application/json')
response = client.post(reverse('phone-call-event'), json.dumps(payload), content_type='application/json')
assert response.status_code == 200
assert response['content-type'] == 'application/json'
assert response.json() == {'err': 0}
assert models.PhoneCall.objects.count() == 2
assert models.PhoneCall.objects.filter(
caller='0033699999999',
callee='102',
data=json.dumps(payload['data']), stop__isnull=False).count() == 2
assert (
models.PhoneCall.objects.filter(
caller='0033699999999', callee='102', data=json.dumps(payload['data']), stop__isnull=False
).count()
== 2
)
# stop is idempotent
response = client.post(reverse('phone-call-event'), json.dumps(payload),
content_type='application/json')
response = client.post(reverse('phone-call-event'), json.dumps(payload), content_type='application/json')
assert response.status_code == 200
assert response['content-type'] == 'application/json'
assert response.json() == {'err': 0}
assert models.PhoneCall.objects.count() == 2
assert models.PhoneCall.objects.filter(
caller='0033699999999',
callee='102',
data=json.dumps(payload['data']), stop__isnull=False).count() == 2
assert (
models.PhoneCall.objects.filter(
caller='0033699999999', callee='102', data=json.dumps(payload['data']), stop__isnull=False
).count()
== 2
)
def test_one_call_per_callee(user, client):
assert models.PhoneCall.objects.count() == 0
payload = {'event': 'start', 'caller': '0033699999999', 'callee': '102'}
response = client.post(reverse('phone-call-event'), json.dumps(payload),
content_type='application/json')
response = client.post(reverse('phone-call-event'), json.dumps(payload), content_type='application/json')
assert response.status_code == 200
assert models.PhoneCall.objects.filter(callee='102', stop__isnull=True).count() == 1 # active
assert models.PhoneCall.objects.filter(callee='102', stop__isnull=False).count() == 0 # inactive
assert models.PhoneCall.objects.filter(callee='102', stop__isnull=False).count() == 0 # inactive
# new caller, same callee: stops the last call, start a new one
payload['caller'] = '00337123456789'
response = client.post(reverse('phone-call-event'), json.dumps(payload),
content_type='application/json')
response = client.post(reverse('phone-call-event'), json.dumps(payload), content_type='application/json')
assert response.status_code == 200
assert models.PhoneCall.objects.count() == 2
assert models.PhoneCall.objects.filter(
caller='00337123456789', callee='102', stop__isnull=True).count() == 1
assert models.PhoneCall.objects.filter(
caller='0033699999999', callee='102', stop__isnull=False).count() == 1
assert (
models.PhoneCall.objects.filter(caller='00337123456789', callee='102', stop__isnull=True).count() == 1
)
assert (
models.PhoneCall.objects.filter(caller='0033699999999', callee='102', stop__isnull=False).count() == 1
)
with override_settings(PHONE_ONE_CALL_PER_CALLEE=False):
# accept multiple call: start a new one, don't stop anything
payload['caller'] = '00221774261500'
response = client.post(reverse('phone-call-event'), json.dumps(payload),
content_type='application/json')
response = client.post(
reverse('phone-call-event'), json.dumps(payload), content_type='application/json'
)
assert response.status_code == 200
assert models.PhoneCall.objects.count() == 3
assert models.PhoneCall.objects.filter(callee='102', stop__isnull=True).count() == 2
assert models.PhoneCall.objects.filter(callee='102', stop__isnull=False).count() == 1
# same caller: stop his last call, add a new one
response = client.post(reverse('phone-call-event'), json.dumps(payload),
content_type='application/json')
response = client.post(
reverse('phone-call-event'), json.dumps(payload), content_type='application/json'
)
assert response.status_code == 200
assert models.PhoneCall.objects.count() == 4
assert models.PhoneCall.objects.filter(callee='102', stop__isnull=True).count() == 2
assert models.PhoneCall.objects.filter(callee='102', stop__isnull=False).count() == 2
def test_current_calls(user, client):
# create some calls
for number in range(0, 10):
@ -136,10 +145,11 @@ def test_current_calls(user, client):
'callee': '1%02d' % number,
'data': {
'user': 'boby.lapointe',
}
},
}
response = client.post(reverse('phone-call-event'), json.dumps(payload),
content_type='application/json')
response = client.post(
reverse('phone-call-event'), json.dumps(payload), content_type='application/json'
)
assert response.status_code == 200
assert response['content-type'] == 'application/json'
assert response.json() == {'err': 0}
@ -199,22 +209,20 @@ def test_take_release_line(user, client):
payload = {
'callee': '102',
}
response = client.post(reverse('phone-take-line'), json.dumps(payload),
content_type='application/json')
response = client.post(reverse('phone-take-line'), json.dumps(payload), content_type='application/json')
assert response.status_code == 200
assert response['content-type'] == 'application/json'
assert response.json() == {'err': 0}
assert models.PhoneLine.objects.count() == 1
assert models.PhoneLine.objects.filter(
users=user, callee='102').count() == 1
response = client.post(reverse('phone-release-line'), json.dumps(payload),
content_type='application/json')
assert models.PhoneLine.objects.filter(users=user, callee='102').count() == 1
response = client.post(
reverse('phone-release-line'), json.dumps(payload), content_type='application/json'
)
assert response.status_code == 200
assert response['content-type'] == 'application/json'
assert response.json() == {'err': 0}
assert models.PhoneLine.objects.count() == 1
assert models.PhoneLine.objects.filter(
users=user, callee='102').count() == 0
assert models.PhoneLine.objects.filter(users=user, callee='102').count() == 0
def test_phone_zone(user, client):
@ -230,15 +238,16 @@ def test_phone_zone(user, client):
assert 'You do not have a phoneline configured' not in force_text(response.content)
assert '<li>102' in force_text(response.content)
assert 'data-callee="102"' in force_text(response.content)
currents = re.search('<div id="source-mainarea" '
'data-current-calls="/api/phone/current-calls/">'
'(.*?)</div>', force_text(response.content), flags=re.DOTALL)
currents = re.search(
'<div id="source-mainarea" ' 'data-current-calls="/api/phone/current-calls/">' '(.*?)</div>',
force_text(response.content),
flags=re.DOTALL,
)
assert currents.group(1).strip() == ''
# create a call
payload = {'event': 'start', 'caller': '003369999999', 'callee': '102'}
response = client.post(reverse('phone-call-event'), json.dumps(payload),
content_type='application/json')
response = client.post(reverse('phone-call-event'), json.dumps(payload), content_type='application/json')
assert response.status_code == 200
response = client.get(reverse('phone-zone'))
assert response.status_code == 200
@ -269,8 +278,7 @@ def test_call_expiration(user, client):
assert models.PhoneCall.objects.count() == 0
# create a call
payload = {'event': 'start', 'caller': '003369999999', 'callee': '102'}
response = client.post(reverse('phone-call-event'), json.dumps(payload),
content_type='application/json')
response = client.post(reverse('phone-call-event'), json.dumps(payload), content_type='application/json')
assert response.status_code == 200
assert models.PhoneCall.objects.filter(stop__isnull=True).count() == 1
@ -284,15 +292,14 @@ def test_call_expiration(user, client):
assert len(payload['data']['calls']) == 1
# start call 10 minutes ago
models.PhoneCall.objects.filter(stop__isnull=True).update(
start=now()-timedelta(minutes=10))
models.PhoneCall.objects.filter(stop__isnull=True).update(start=now() - timedelta(minutes=10))
# get list of calls without expiration
response = client.get(reverse('phone-current-calls'))
assert response.status_code == 200
payload = response.json()
assert payload['err'] == 0
assert len(payload['data']['calls']) == 1 # still here
assert len(payload['data']['calls']) == 1 # still here
# get list of calls with an expiration of 2 minutes (< 10 minutes)
with override_settings(PHONE_MAX_CALL_DURATION=2):
@ -300,7 +307,7 @@ def test_call_expiration(user, client):
assert response.status_code == 200
payload = response.json()
assert payload['err'] == 0
assert len(payload['data']['calls']) == 0 # call is expired
assert len(payload['data']['calls']) == 0 # call is expired
assert models.PhoneCall.objects.filter(stop__isnull=True).count() == 0 # active calls
assert models.PhoneCall.objects.filter(stop__isnull=False).count() == 1 # stopped calls
assert models.PhoneCall.objects.filter(stop__isnull=False).count() == 1 # stopped calls

View File

@ -17,6 +17,7 @@
from django.apps import apps
from django.conf.urls import include, url
def register_urls(urlpatterns):
pre_urls = []
post_urls = []

View File

@ -24,14 +24,15 @@ DEFAULT_TITLE_CHOICES = (
(pgettext_lazy('title', 'Mr'), pgettext_lazy('title', 'Mr')),
)
class ContactAddForm(forms.Form):
title = forms.CharField(label=_('Title'),
required=False,
widget=forms.Select(choices=DEFAULT_TITLE_CHOICES))
title = forms.CharField(
label=_('Title'), required=False, widget=forms.Select(choices=DEFAULT_TITLE_CHOICES)
)
first_name = forms.CharField(label=_('First Name'), required=False)
last_name = forms.CharField(label=_('Last Name'),
required=True,
widget=forms.TextInput(attrs={'required': 'required'}))
last_name = forms.CharField(
label=_('Last Name'), required=True, widget=forms.TextInput(attrs={'required': 'required'})
)
email = forms.CharField(label=_('Email'), required=False)
address = forms.CharField(label=_('Address'), required=False)
zipcode = forms.CharField(label=_('Zip Code'), required=False)

View File

@ -33,6 +33,7 @@ from welco.utils import get_wcs_data, sign_url
from .forms import ContactAddForm
class HomeZone(object):
def __init__(self, request):
self.request = request
@ -49,21 +50,20 @@ class ContactsZone(TemplateView):
context = super(ContactsZone, self).get_context_data(**kwargs)
context['source_pk'] = self.request.GET.get('source_pk')
if 'source_pk' in self.request.GET:
source_class = ContentType.objects.get(
id=self.request.GET['source_type']).model_class()
source_class = ContentType.objects.get(id=self.request.GET['source_type']).model_class()
source_object = source_class.objects.get(id=self.request.GET['source_pk'])
context['contact_user_id'] = source_object.contact_id
return context
def post(self, request, *args, **kwargs):
if 'user_id' in request.POST:
source_class = ContentType.objects.get(
id=self.request.POST['source_type']).model_class()
source_class = ContentType.objects.get(id=self.request.POST['source_type']).model_class()
source_object = source_class.objects.get(id=self.request.POST['source_pk'])
source_object.contact_id = request.POST['user_id']
source_object.save()
return HttpResponse('ok')
zone = csrf_exempt(ContactsZone.as_view())
@ -83,8 +83,12 @@ def search_json(request):
raise Exception('error %r' % result)
for user in result.get('data'):
user['title'] = user['user_display_name']
more = [user.get('user_var_address'), user.get('user_var_phone'),
user.get('user_var_mobile'), user.get('user_var_email')]
more = [
user.get('user_var_address'),
user.get('user_var_phone'),
user.get('user_var_mobile'),
user.get('user_var_email'),
]
user['more'] = ' / '.join([x for x in more if x])
if user.get('user_roles'):
user['roles'] = ' / '.join([r['text'] for r in user['user_roles']])
@ -109,13 +113,13 @@ class ContactDetailFragmentView(TemplateView):
context['user_id'] = user_id
if 'source_pk' in self.request.GET:
source_class = ContentType.objects.get(
id=self.request.GET['source_type']).model_class()
source_class = ContentType.objects.get(id=self.request.GET['source_type']).model_class()
source_object = source_class.objects.get(id=self.request.GET['source_pk'])
context['is_pinned_user'] = bool(source_object.contact_id == user_id)
return context
contact_detail_fragment = ContactDetailFragmentView.as_view()
@ -144,9 +148,8 @@ class ContactAdd(FormView):
logger = logging.getLogger(__name__)
logger.info('POST to authentic (%r)', json.dumps(msg))
authentic_response = requests.post(
signed_url,
data=json.dumps(msg),
headers={'Content-type': 'application/json'})
signed_url, data=json.dumps(msg), headers={'Content-type': 'application/json'}
)
logger.info('Got authentic response (%r)', authentic_response.text)
user_uuid = authentic_response.json().get('uuid')
@ -165,4 +168,5 @@ class ContactAdd(FormView):
json.dump(result, response, indent=2)
return response
contact_add = csrf_exempt(ContactAdd.as_view())

View File

@ -16,6 +16,7 @@
from django.apps import AppConfig
class KbAppConfig(AppConfig):
name = 'welco.kb'

View File

@ -19,6 +19,7 @@ from django.utils.text import slugify
from .models import Page
class PageForm(forms.ModelForm):
class Meta:
model = Page

View File

@ -7,14 +7,16 @@ import ckeditor.fields
class Migration(migrations.Migration):
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='Page',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('title', models.CharField(max_length=200, verbose_name='Title')),
('slug', models.SlugField(verbose_name='Slug')),
('content', ckeditor.fields.RichTextField(verbose_name='Text')),

View File

@ -16,7 +16,13 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='page',
name='tags',
field=taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Keywords'),
field=taggit.managers.TaggableManager(
to='taggit.Tag',
through='taggit.TaggedItem',
blank=True,
help_text='A comma-separated list of tags.',
verbose_name='Keywords',
),
preserve_default=True,
),
]

View File

@ -26,8 +26,7 @@ class Page(models.Model):
title = models.CharField(_('Title'), max_length=200)
slug = models.SlugField(_('Slug'))
content = RichTextField(_('Text'))
tags = TaggableManager(_('Keywords'), blank=True,
help_text=_('A comma-separated list of tags.'))
tags = TaggableManager(_('Keywords'), blank=True, help_text=_('A comma-separated list of tags.'))
class Meta:
ordering = ['title']

View File

@ -21,6 +21,7 @@ from haystack import indexes
from .models import Page
class PageIndex(indexes.SearchIndex, indexes.Indexable):
title = indexes.CharField(model_attr='title', boost=3)
text = indexes.CharField(document=True)

View File

@ -25,8 +25,7 @@ from django.db.models import Count
from django.http import HttpResponse, HttpResponseRedirect
from django.template import RequestContext
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import (DetailView, CreateView, UpdateView,
ListView, DeleteView, TemplateView)
from django.views.generic import DetailView, CreateView, UpdateView, ListView, DeleteView, TemplateView
from haystack.forms import SearchForm
from haystack.generic_views import SearchView
@ -36,15 +35,17 @@ from taggit.models import Tag
from .models import Page
from .forms import PageForm
def check_user_perms(user, access=False):
allowed_roles = settings.KB_MANAGE_ROLES[:]
if access:
allowed_roles.extend(settings.KB_ACCESS_ROLES)
if settings.KB_ROLE:
allowed_roles.append(settings.KB_ROLE) # legacy
allowed_roles.append(settings.KB_ROLE) # legacy
user_groups = set([x.name for x in user.groups.all()])
return user_groups.intersection(allowed_roles)
def check_request_perms(request, access=False):
if not check_user_perms(request.user, access=access):
raise PermissionDenied()
@ -63,6 +64,7 @@ class PageListView(ListView):
context['can_manage'] = check_user_perms(self.request.user)
return context
page_list = login_required(PageListView.as_view())
@ -74,6 +76,7 @@ class PageAddView(CreateView):
check_request_perms(request)
return super(PageAddView, self).dispatch(request, *args, **kwargs)
page_add = login_required(PageAddView.as_view())
@ -85,6 +88,7 @@ class PageEditView(UpdateView):
check_request_perms(request)
return super(PageEditView, self).dispatch(request, *args, **kwargs)
page_edit = login_required(PageEditView.as_view())
@ -108,6 +112,7 @@ class PageDetailFragmentView(DetailView):
model = Page
template_name = 'kb/page_detail_fragment.html'
page_detail_fragment = PageDetailFragmentView.as_view()
@ -119,6 +124,7 @@ class PageDeleteView(DeleteView):
check_request_perms(request)
return super(PageDeleteView, self).dispatch(request, *args, **kwargs)
page_delete = login_required(PageDeleteView.as_view())
@ -130,6 +136,7 @@ class PageSearchView(SearchView):
check_request_perms(request, access=True)
return super(PageSearchView, self).dispatch(request, *args, **kwargs)
page_search = login_required(PageSearchView.as_view())
@ -140,8 +147,9 @@ class KbZone(TemplateView):
context = super(KbZone, self).get_context_data(**kwargs)
context['source_pk'] = self.request.GET.get('source_pk')
context['form'] = SearchForm()
context['tags'] = Tag.objects.all().annotate(
num_times=Count('taggit_taggeditem_items')).filter(num_times__gt=0)
context['tags'] = (
Tag.objects.all().annotate(num_times=Count('taggit_taggeditem_items')).filter(num_times__gt=0)
)
num_times = context['tags'].values_list('num_times', flat=True)
if not num_times:
num_times = [0]
@ -163,6 +171,7 @@ class KbZone(TemplateView):
tag.font_size = 'x-large'
return context
zone = csrf_exempt(KbZone.as_view())

View File

@ -24,6 +24,7 @@ from django.utils.translation import get_language
import ckeditor.widgets
def ckeditor_render(self, name, value, attrs=None):
if value is None:
value = ''
@ -40,14 +41,22 @@ def ckeditor_render(self, name, value, attrs=None):
self.config['language'] = get_language()
# Force to text to evaluate possible lazy objects
external_plugin_resources = [[force_text(a), force_text(b), force_text(c)] for a, b, c in self.external_plugin_resources]
external_plugin_resources = [
[force_text(a), force_text(b), force_text(c)] for a, b, c in self.external_plugin_resources
]
return mark_safe(
render_to_string(
'ckeditor/widget.html',
{
'final_attrs': flatatt(final_attrs),
'value': conditional_escape(force_text(value)),
'id': final_attrs['id'],
'config': ckeditor.widgets.json_encode(self.config),
'external_plugin_resources': ckeditor.widgets.json_encode(external_plugin_resources),
},
)
)
return mark_safe(render_to_string('ckeditor/widget.html', {
'final_attrs': flatatt(final_attrs),
'value': conditional_escape(force_text(value)),
'id': final_attrs['id'],
'config': ckeditor.widgets.json_encode(self.config),
'external_plugin_resources' : ckeditor.widgets.json_encode(external_plugin_resources)
}))
ckeditor.widgets.CKEditorWidget.render = ckeditor_render

View File

@ -14,31 +14,37 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Association',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('source_pk', models.PositiveIntegerField()),
],
options={
},
options={},
bases=(models.Model,),
),
migrations.CreateModel(
name='FormdataReference',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('reference', models.CharField(max_length=250)),
],
options={
},
options={},
bases=(models.Model,),
),
migrations.CreateModel(
name='FormdefReference',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('reference', models.CharField(max_length=250)),
],
options={
},
options={},
bases=(models.Model,),
),
migrations.AddField(

View File

@ -38,10 +38,12 @@ class Association(models.Model):
if self.source.contact_id:
context['user_id'] = self.source.contact_id
context['summary_url'] = request.build_absolute_uri(
reverse('wcs-summary', kwargs={'source_type': self.source_type_id,
'source_pk': self.source_pk}))
reverse('wcs-summary', kwargs={'source_type': self.source_type_id, 'source_pk': self.source_pk})
)
context.update(self.source.get_source_context(request))
self.formdata_id, self.formdata_url_backoffice = push_wcs_formdata(request, self.formdef_reference, context)
self.formdata_id, self.formdata_url_backoffice = push_wcs_formdata(
request, self.formdef_reference, context
)
self.save()
@property

View File

@ -90,7 +90,7 @@ USE_L10N = True
USE_TZ = True
LOCALE_PATHS = (os.path.join(BASE_DIR, 'welco', 'locale'), )
LOCALE_PATHS = (os.path.join(BASE_DIR, 'welco', 'locale'),)
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/
@ -99,9 +99,7 @@ STATIC_URL = '/static/'
STATICFILES_FINDERS = tuple(global_settings.STATICFILES_FINDERS) + ('gadjo.finders.XStaticFinder',)
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'welco', 'static'),
)
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'welco', 'static'),)
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
@ -131,12 +129,18 @@ CKEDITOR_UPLOAD_PATH = 'uploads/'
CKEDITOR_CONFIGS = {
'default': {
'toolbar_Own': [['Source', 'Format', '-', 'Bold', 'Italic'],
['NumberedList', 'BulletedList'],
['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
['Link', 'Unlink'],
['Image',],
['RemoveFormat',]],
'toolbar_Own': [
['Source', 'Format', '-', 'Bold', 'Italic'],
['NumberedList', 'BulletedList'],
['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
['Link', 'Unlink'],
[
'Image',
],
[
'RemoveFormat',
],
],
'toolbar': 'Own',
},
}
@ -190,7 +194,7 @@ CHANNEL_ROLES = {
}
# role allowed to manage knowledge base
KB_ROLE = None # deprecated
KB_ROLE = None # deprecated
KB_MANAGE_ROLES = []
# roles allowed to visit knowledge base
@ -200,9 +204,7 @@ KB_ACCESS_ROLES = []
SCREEN_PANELS = ['contacts', 'qualif']
# useful links for counter
COUNTER_LINKS = [
{'label': 'Wikipedia', 'url': 'https://fr.wikipedia.org'}
]
COUNTER_LINKS = [{'label': 'Wikipedia', 'url': 'https://fr.wikipedia.org'}]
# phone system
PHONE_ONE_CALL_PER_CALLEE = True
@ -214,7 +216,8 @@ PHONE_AUTOTAKE_MELLON_USERNAME = False
REST_FRAMEWORK = {}
REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] = ['rest_framework.authentication.BasicAuthentication']
local_settings_file = os.environ.get('WELCO_SETTINGS_FILE',
os.path.join(os.path.dirname(__file__), 'local_settings.py'))
local_settings_file = os.environ.get(
'WELCO_SETTINGS_FILE', os.path.join(os.path.dirname(__file__), 'local_settings.py')
)
if os.path.exists(local_settings_file):
exec(open(local_settings_file).read())

View File

@ -6,14 +6,16 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='CounterPresence',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('status', models.CharField(max_length=50, verbose_name='Status', blank=True)),
('contact_id', models.CharField(max_length=50, null=True)),
('creation_timestamp', models.DateTimeField(auto_now_add=True)),

View File

@ -20,16 +20,15 @@ from django.utils.translation import ugettext_lazy as _
from welco.qualif.models import Association
class CounterPresence(models.Model):
class CounterPresence(models.Model):
class Meta:
verbose_name = _('Counter Presence')
# common to all source types:
status = models.CharField(_('Status'), blank=True, max_length=50)
contact_id = models.CharField(max_length=50, null=True)
associations = GenericRelation(Association,
content_type_field='source_type', object_id_field='source_pk')
associations = GenericRelation(Association, content_type_field='source_type', object_id_field='source_pk')
creation_timestamp = models.DateTimeField(auto_now_add=True)
last_update_timestamp = models.DateTimeField(auto_now=True)

View File

@ -54,6 +54,7 @@ class CounterZone(TemplateView):
context['useful_links'] = settings.COUNTER_LINKS
return context
zone = csrf_exempt(CounterZone.as_view())

View File

@ -22,14 +22,14 @@ class AppConfig(django.apps.AppConfig):
def get_before_urls(self):
from . import urls
return urls.urlpatterns
def ready(self):
from welco.qualif.models import Association
from django.db.models import signals
signals.post_save.connect(self.association_post_save,
sender=Association)
signals.post_save.connect(self.association_post_save, sender=Association)
def association_post_save(self, sender, instance, **kwargs):
from .utils import get_maarch
@ -47,6 +47,8 @@ class AppConfig(django.apps.AppConfig):
maarch.set_grc_sent_status(
mail_pk=maarch_pk,
formdata_id=instance.formdata_id,
formdata_url_backoffice=instance.formdata_url_backoffice)
formdata_url_backoffice=instance.formdata_url_backoffice,
)
default_app_config = 'welco.sources.mail.AppConfig'

View File

@ -18,6 +18,7 @@ from django import forms
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
class MailQualificationForm(forms.Form):
post_date = forms.DateTimeField(label=_('Post Date (*)'), required=False)
registered_mail_number = forms.CharField(label=_('Registered Mail Number'), required=False)

View File

@ -125,7 +125,7 @@ class MaarchCourrier(object):
read=self.max_retries,
connect=self.max_retries,
backoff_factor=0.5,
status_forcelist=(500, 502, 504)
status_forcelist=(500, 502, 504),
)
adapter = HTTPAdapter(max_retries=retry)
s.mount('http://', adapter)
@ -175,13 +175,16 @@ class MaarchCourrier(object):
fields = ','.join(fields) if fields else '*'
limit = limit or self.default_limit
order_by = order_by or []
response = self.post_json(self.list_url, {
'select': fields,
'clause': clause,
'limit': limit,
'withFile': include_file,
'orderBy': order_by,
})
response = self.post_json(
self.list_url,
{
'select': fields,
'clause': clause,
'limit': limit,
'withFile': include_file,
'orderBy': order_by,
},
)
if not hasattr(response.get('resources'), 'append'):
raise MaarchError('missing resources field or bad type', response)
return [self.Courrier(self, **resource) for resource in response['resources']]

View File

@ -22,10 +22,10 @@ from django.core.management.base import BaseCommand, CommandError
from ...models import Mail
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
'--category', metavar='CATEGORY', default=None)
parser.add_argument('--category', metavar='CATEGORY', default=None)
parser.add_argument('filenames', metavar='FILENAME', nargs='+')
def handle(self, filenames, *args, **kwargs):

View File

@ -25,14 +25,15 @@ from django.db import transaction
from ...models import Mail
from ...utils import get_maarch
class Command(BaseCommand):
"""Inject mail coming from Maarch into welco.
Only mail with a status "GRC" are injected,
After injection, their status is immediately changed to "GRC_TRT".
After injection in w.c.s., their status is changed to "GRCSENT" and an
id and an URL of the request in w.c.s. is attached to the mail in
Maarch.
Only mail with a status "GRC" are injected,
After injection, their status is immediately changed to "GRC_TRT".
After injection in w.c.s., their status is changed to "GRCSENT" and an
id and an URL of the request in w.c.s. is attached to the mail in
Maarch.
"""
def handle(self, *args, **kwargs):

View File

@ -6,14 +6,16 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='Mail',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('content', models.FileField(upload_to=b'', verbose_name='Content')),
('triaged', models.BooleanField(default=False)),
('creation_timestamp', models.DateTimeField(auto_now_add=True)),

View File

@ -18,7 +18,6 @@ class Migration(migrations.Migration):
),
migrations.AlterModelOptions(
name='mail',
options={'ordering': ['post_date', 'creation_timestamp'],
'verbose_name': 'Mail'},
options={'ordering': ['post_date', 'creation_timestamp'], 'verbose_name': 'Mail'},
),
]

View File

@ -32,15 +32,13 @@ from welco.utils import get_wcs_data
class Mail(models.Model):
class Meta:
verbose_name = _('Mail')
ordering = ['post_date', 'creation_timestamp']
content = models.FileField(_('Content'))
post_date = models.DateField(_('Post Date'), null=True)
registered_mail_number = models.CharField(_('Registered Mail Number'),
null=True, max_length=50)
registered_mail_number = models.CharField(_('Registered Mail Number'), null=True, max_length=50)
note = models.TextField(_('Note'), null=True)
external_id = models.CharField(_('External Id'), null=True, max_length=32)
@ -52,8 +50,7 @@ class Mail(models.Model):
# common to all source types:
status = models.CharField(_('Status'), blank=True, max_length=50)
contact_id = models.CharField(max_length=50, null=True)
associations = GenericRelation(Association,
content_type_field='source_type', object_id_field='source_pk')
associations = GenericRelation(Association, content_type_field='source_type', object_id_field='source_pk')
creation_timestamp = models.DateTimeField(auto_now_add=True)
last_update_timestamp = models.DateTimeField(auto_now=True)
@ -61,6 +58,7 @@ class Mail(models.Model):
@classmethod
def get_qualification_form_class(cls):
from .forms import MailQualificationForm
return MailQualificationForm
def get_qualification_form(self):
@ -111,5 +109,13 @@ class Mail(models.Model):
def create_thumbnail(sender, instance, created, **kwargs):
if not created:
return
subprocess.call(['gm', 'convert', '-geometry', '200x',
instance.content.file.name, instance.content.file.name + '.png'])
subprocess.call(
[
'gm',
'convert',
'-geometry',
'200x',
instance.content.file.name,
instance.content.file.name + '.png',
]
)

View File

@ -16,8 +16,7 @@
from django.conf.urls import url
from .views import (viewer, feeder, qualification_save, edit_note, note,
reject, mail_count, mail_response)
from .views import viewer, feeder, qualification_save, edit_note, note, reject, mail_count, mail_response
urlpatterns = [
url('viewer/$', viewer, name='mail-viewer'),

View File

@ -20,9 +20,18 @@ from .maarch import MaarchCourrier, MaarchError
class WelcoMaarchCourrier(MaarchCourrier):
def __init__(self, url, username, password, grc_status,
grc_received_status, grc_send_status, grc_refused_status,
grc_response_status, batch_size=10):
def __init__(
self,
url,
username,
password,
grc_status,
grc_received_status,
grc_send_status,
grc_refused_status,
grc_response_status,
batch_size=10,
):
super(WelcoMaarchCourrier, self).__init__(url, username, password)
self.grc_status = grc_status
self.grc_received_status = grc_received_status
@ -36,7 +45,8 @@ class WelcoMaarchCourrier(MaarchCourrier):
clause="status='%s'" % self.grc_status,
include_file=True,
order_by=['res_id'],
limit=self.batch_size)
limit=self.batch_size,
)
def get_mail(self, mail_id):
return self.get_courriers(clause="res_id=%s" % mail_id)[0]
@ -74,5 +84,5 @@ def get_maarch():
grc_received_status=config.get('STATUS_RECEIVED', 'GRC_TRT'),
grc_send_status=config.get('STATUS_SEND', 'GRCSENT'),
grc_refused_status=config.get('STATUS_REFUSED', 'GRCREFUSED'),
grc_response_status=config.get('STATUS_RESPONSE', 'GRC_RESPONSE'))
grc_response_status=config.get('STATUS_RESPONSE', 'GRC_RESPONSE'),
)

View File

@ -42,11 +42,11 @@ from .utils import get_maarch, MaarchError
logger = logging.getLogger(__name__)
def viewer(request, *args, **kwargs):
if not 'file' in request.GET:
return HttpResponseRedirect('?file=')
body = template.loader.get_template('welco/mail_viewer.html').render(
request=request)
body = template.loader.get_template('welco/mail_viewer.html').render(request=request)
return HttpResponse(body)
@ -57,12 +57,13 @@ class Feeder(TemplateView):
for upload in request.FILES.getlist('mail'):
mail = Mail(content=upload)
mail.save()
messages.info(request, _('%d files uploaded successfully.') %
len(request.FILES.getlist('mail')))
messages.info(request, _('%d files uploaded successfully.') % len(request.FILES.getlist('mail')))
return HttpResponseRedirect(reverse('mail-feeder'))
feeder = login_required(csrf_exempt(Feeder.as_view()))
class Home(object):
source_key = 'mail'
display_filter = True
@ -101,9 +102,10 @@ def qualification_save(request, *args, **kwargs):
mail.reference = form.cleaned_data['reference']
mail.subject = form.cleaned_data['subject']
mail.save()
return HttpResponseRedirect(reverse('qualif-zone') +
'?source_type=%s&source_pk=%s' % (request.POST['source_type'],
request.POST['source_pk']))
return HttpResponseRedirect(
reverse('qualif-zone')
+ '?source_type=%s&source_pk=%s' % (request.POST['source_type'], request.POST['source_pk'])
)
class EditNote(TemplateView):
@ -120,6 +122,7 @@ class EditNote(TemplateView):
mail.save()
return HttpResponse(json.dumps({'result': 'ok'}))
edit_note = login_required(csrf_exempt(EditNote.as_view()))
@ -192,4 +195,5 @@ class MailResponseAPIView(GenericAPIView):
return Response({'err': 1, 'err_desc': str(e)})
return Response({'err': 0})
mail_response = MailResponseAPIView.as_view()

View File

@ -6,14 +6,16 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='PhoneCall',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('number', models.CharField(max_length=20, verbose_name='Number')),
('creation_timestamp', models.DateTimeField(auto_now_add=True)),
('last_update_timestamp', models.DateTimeField(auto_now=True)),

View File

@ -19,7 +19,10 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='PhoneLine',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('callee', models.CharField(unique=True, max_length=20, verbose_name='Callee')),
('users', models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='User')),
],

View File

@ -25,8 +25,8 @@ from django.utils.timezone import now, timedelta
from welco.qualif.models import Association
class PhoneCall(models.Model):
class PhoneCall(models.Model):
class Meta:
verbose_name = _('Phone Call')
@ -39,8 +39,7 @@ class PhoneCall(models.Model):
# common to all source types:
status = models.CharField(_('Status'), blank=True, max_length=50)
contact_id = models.CharField(max_length=50, null=True)
associations = GenericRelation(Association,
content_type_field='source_type', object_id_field='source_pk')
associations = GenericRelation(Association, content_type_field='source_type', object_id_field='source_pk')
creation_timestamp = models.DateTimeField(auto_now_add=True)
last_update_timestamp = models.DateTimeField(auto_now=True)
@ -61,14 +60,13 @@ class PhoneCall(models.Model):
if settings.PHONE_MAX_CALL_DURATION:
logger = logging.getLogger(__name__)
start_after = now() - timedelta(minutes=settings.PHONE_MAX_CALL_DURATION)
for call in cls.objects.filter(callee__in=PhoneLine.get_callees(user),
stop__isnull=True,
start__lt=start_after):
for call in cls.objects.filter(
callee__in=PhoneLine.get_callees(user), stop__isnull=True, start__lt=start_after
):
logger.info('stop expired call from %s to %s', call.caller, call.callee)
call.stop = now()
call.save()
return cls.objects.filter(callee__in=PhoneLine.get_callees(user),
stop__isnull=True).order_by('start')
return cls.objects.filter(callee__in=PhoneLine.get_callees(user), stop__isnull=True).order_by('start')
@classmethod
def get_all_callees(cls):
@ -80,15 +78,15 @@ class PhoneCall(models.Model):
}
def previous_calls(self):
return PhoneCall.objects.filter(caller=self.caller).exclude(
id=self.id).order_by('-start')[:5]
return PhoneCall.objects.filter(caller=self.caller).exclude(id=self.id).order_by('-start')[:5]
@property
def duration(self):
if not self.stop:
return 'n.a.'
seconds = (self.stop - self.start).seconds
return '%02d:%02d' % (seconds//60, seconds%60)
return '%02d:%02d' % (seconds // 60, seconds % 60)
class PhoneLine(models.Model):
callee = models.CharField(_('Callee'), unique=True, max_length=80)

View File

@ -57,7 +57,7 @@ class PhoneZone(TemplateView):
username = self.request.session.get('mellon_session', {}).get('username')
if username:
# user is from SSO, username is a phone line (callee), create a link to it
username = username[0].split('@', 1)[0][:80] # remove realm
username = username[0].split('@', 1)[0][:80] # remove realm
if username:
PhoneLine.take(callee=username, user=self.request.user)
context = super(PhoneZone, self).get_context_data(**kwargs)
@ -66,50 +66,51 @@ class PhoneZone(TemplateView):
context['phonecalls'] = PhoneCall.get_current_calls(self.request.user)
return context
zone = csrf_exempt(PhoneZone.as_view())
zone = csrf_exempt(PhoneZone.as_view())
@csrf_exempt
def call_event(request):
'''Log a new call start or stop, input is JSON:
"""Log a new call start or stop, input is JSON:
{
'event': 'start' or 'stop',
'caller': '003399999999',
'callee': '102',
'data': {
'user': 'zozo',
},
}
'''
{
'event': 'start' or 'stop',
'caller': '003399999999',
'callee': '102',
'data': {
'user': 'zozo',
},
}
"""
logger = logging.getLogger(__name__)
try:
payload = json.loads(force_text(request.body))
assert isinstance(payload, dict), 'payload is not a JSON object'
assert set(payload.keys()) <= set(['event', 'caller', 'callee', 'data']), \
'payload keys must be "event", "caller", "callee" and optionnaly "data"'
assert set(['event', 'caller', 'callee']) <= set(payload.keys()), \
'payload keys must be "event", "caller", "callee" and optionnaly "data"'
assert set(payload.keys()) <= set(
['event', 'caller', 'callee', 'data']
), 'payload keys must be "event", "caller", "callee" and optionnaly "data"'
assert set(['event', 'caller', 'callee']) <= set(
payload.keys()
), 'payload keys must be "event", "caller", "callee" and optionnaly "data"'
assert payload['event'] in ('start', 'stop'), 'event must be "start" or "stop"'
assert isinstance(payload['caller'], six.string_types), 'caller must be a string'
assert isinstance(payload['callee'], six.string_types), 'callee must be a string'
if 'data' in payload:
assert isinstance(payload['data'], dict), 'data must be a JSON object'
except (TypeError, ValueError, AssertionError) as e:
return HttpResponseBadRequest(json.dumps({'err': 1, 'msg':
force_text(e)}),
content_type='application/json')
return HttpResponseBadRequest(
json.dumps({'err': 1, 'msg': force_text(e)}), content_type='application/json'
)
# janitoring: stop active calls to the callee
if settings.PHONE_ONE_CALL_PER_CALLEE:
logger.info('stop all calls to %s', payload['callee'])
PhoneCall.objects.filter(callee=payload['callee'],
stop__isnull=True).update(stop=now())
PhoneCall.objects.filter(callee=payload['callee'], stop__isnull=True).update(stop=now())
else:
logger.info('stop call from %s to %s', payload['caller'], payload['callee'])
PhoneCall.objects.filter(caller=payload['caller'],
callee=payload['callee'],
stop__isnull=True).update(stop=now())
PhoneCall.objects.filter(
caller=payload['caller'], callee=payload['callee'], stop__isnull=True
).update(stop=now())
if payload['event'] == 'start':
# start a new call
kwargs = {
@ -129,40 +130,39 @@ def active_call(request, *args, **kwargs):
result = {
'caller': call.caller,
'callee': call.callee,
'active': not(bool(call.stop)),
'active': not (bool(call.stop)),
'start_timestamp': call.start.strftime('%Y-%m-%dT%H:%M:%S'),
}
return HttpResponse(json.dumps(result, indent=2),
content_type='application/json')
}
return HttpResponse(json.dumps(result, indent=2), content_type='application/json')
@login_required
def current_calls(request):
'''Returns the list of current calls for current user as JSON:
"""Returns the list of current calls for current user as JSON:
{
'err': 0,
'data': {
'calls': [
{
'caller': '00334545445',
'callee': '102',
'data': { ... },
},
...
],
'lines': [
'102',
],
'all-lines': [
'102',
],
}
}
{
'err': 0,
'data': {
'calls': [
{
'caller': '00334545445',
'callee': '102',
'data': { ... },
},
...
],
'lines': [
'102',
],
'all-lines': [
'102',
],
}
}
lines are number the user is currently watching, all-lines is all
registered numbers.
'''
lines are number the user is currently watching, all-lines is all
registered numbers.
"""
all_callees = PhoneCall.get_all_callees()
callees = PhoneLine.get_callees(request.user)
phonecalls = PhoneCall.get_current_calls(request.user)
@ -177,11 +177,13 @@ def current_calls(request):
},
}
for call in phonecalls:
calls.append({
'caller': call.caller,
'callee': call.callee,
'start': call.start.isoformat('T').split('.')[0],
})
calls.append(
{
'caller': call.caller,
'callee': call.callee,
'start': call.start.isoformat('T').split('.')[0],
}
)
if call.data:
calls[-1]['data'] = json.loads(call.data)
response = HttpResponse(content_type='application/json')
@ -192,19 +194,19 @@ def current_calls(request):
@csrf_exempt
@login_required
def take_line(request):
'''Take a line, input is JSON:
"""Take a line, input is JSON:
{ 'callee': '003369999999' }
'''
{ 'callee': '003369999999' }
"""
logger = logging.getLogger(__name__)
try:
payload = json.loads(force_text(request.body))
assert isinstance(payload, dict), 'payload is not a JSON object'
assert list(payload.keys()) == ['callee'], 'payload must have only one key: callee'
except (TypeError, ValueError, AssertionError) as e:
return HttpResponseBadRequest(json.dumps({'err': 1, 'msg':
force_text(e)}),
content_type='application/json')
return HttpResponseBadRequest(
json.dumps({'err': 1, 'msg': force_text(e)}), content_type='application/json'
)
PhoneLine.take(payload['callee'], request.user)
logger.info(u'user %s took line %s', request.user, payload['callee'])
return HttpResponse(json.dumps({'err': 0}), content_type='application/json')
@ -213,19 +215,19 @@ def take_line(request):
@csrf_exempt
@login_required
def release_line(request):
'''Release a line, input is JSON:
"""Release a line, input is JSON:
{ 'callee': '003369999999' }
'''
{ 'callee': '003369999999' }
"""
logger = logging.getLogger(__name__)
try:
payload = json.loads(force_text(request.body))
assert isinstance(payload, dict), 'payload is not a JSON object'
assert list(payload.keys()) == ['callee'], 'payload must have only one key: callee'
except (TypeError, ValueError, AssertionError) as e:
return HttpResponseBadRequest(json.dumps({'err': 1, 'msg':
force_text(e)}),
content_type='application/json')
return HttpResponseBadRequest(
json.dumps({'err': 1, 'msg': force_text(e)}), content_type='application/json'
)
PhoneLine.release(payload['callee'], request.user)
logger.info(u'user %s released line %s', request.user, payload['callee'])
return HttpResponse(json.dumps({'err': 0}), content_type='application/json')

View File

@ -39,11 +39,12 @@ urlpatterns = [
url(r'^', include('welco.sources.phone.urls')),
url(r'^', include('welco.sources.counter.urls')),
url(r'^ajax/qualification$', welco.views.qualification, name='qualif-zone'),
url(r'^ajax/remove-association/(?P<pk>\w+)$',
welco.views.remove_association, name='ajax-remove-association'),
url(r'^ajax/create-formdata/(?P<pk>\w+)$',
welco.views.create_formdata, name='ajax-create-formdata'),
url(
r'^ajax/remove-association/(?P<pk>\w+)$',
welco.views.remove_association,
name='ajax-remove-association',
),
url(r'^ajax/create-formdata/(?P<pk>\w+)$', welco.views.create_formdata, name='ajax-create-formdata'),
url(r'^ajax/kb$', welco.kb.views.zone, name='kb-zone'),
url(r'^kb/$', welco.kb.views.page_list, name='kb-home'),
url(r'^kb/add/$', welco.kb.views.page_add, name='kb-page-add'),
@ -53,23 +54,27 @@ urlpatterns = [
url(r'^ajax/kb/(?P<slug>[\w-]+)/$', welco.kb.views.page_detail_fragment, name='kb-page-fragment'),
url(r'^kb/(?P<slug>[\w-]+)/edit$', welco.kb.views.page_edit, name='kb-page-edit'),
url(r'^kb/(?P<slug>[\w-]+)/delete$', welco.kb.views.page_delete, name='kb-page-delete'),
url(r'^ajax/contacts$', welco.contacts.views.zone, name='contacts-zone'),
url(r'^contacts/search/json/$', welco.contacts.views.search_json, name='contacts-search-json'),
url(r'^ajax/contacts/(?P<slug>[\w-]+)/$',
welco.contacts.views.contact_detail_fragment, name='contact-page-fragment'),
url(
r'^ajax/contacts/(?P<slug>[\w-]+)/$',
welco.contacts.views.contact_detail_fragment,
name='contact-page-fragment',
),
url(r'^contacts/add/$', welco.contacts.views.contact_add, name='contacts-add'),
url(r'^ajax/summary/(?P<source_type>\w+)/(?P<source_pk>\w+)/$',
welco.views.wcs_summary, name='wcs-summary'),
url(
r'^ajax/summary/(?P<source_type>\w+)/(?P<source_pk>\w+)/$',
welco.views.wcs_summary,
name='wcs-summary',
),
url(r'^admin/', admin.site.urls),
url(r'^logout/$', welco.views.logout, name='auth_logout'),
url(r'^login/$', welco.views.login, name='auth_login'),
url(r'^menu.json$', welco.views.menu_json, name='menu_json'),
url(r'^ckeditor/upload/', kb_manager_required(ckeditor_views.upload), name='ckeditor_upload'),
url(r'^ckeditor/browse/', never_cache(kb_manager_required(ckeditor_views.browse)), name='ckeditor_browse'),
url(
r'^ckeditor/browse/', never_cache(kb_manager_required(ckeditor_views.browse)), name='ckeditor_browse'
),
]
if 'mellon' in settings.INSTALLED_APPS:

View File

@ -36,6 +36,7 @@ def sign_url(url, key, algo='sha256', timestamp=None, nonce=None):
new_query = sign_query(parsed.query, key, algo, timestamp, nonce)
return urlparse.urlunparse(parsed[:4] + (new_query,) + parsed[5:])
def sign_query(query, key, algo='sha256', timestamp=None, nonce=None):
if timestamp is None:
timestamp = datetime.datetime.utcnow()
@ -45,22 +46,22 @@ def sign_query(query, key, algo='sha256', timestamp=None, nonce=None):
new_query = query
if new_query:
new_query += '&'
new_query += urlencode((
('algo', algo),
('timestamp', timestamp),
('nonce', nonce)))
new_query += urlencode((('algo', algo), ('timestamp', timestamp), ('nonce', nonce)))
signature = base64.b64encode(sign_string(new_query, key, algo=algo))
new_query += '&signature=' + quote(signature)
return new_query
def sign_string(s, key, algo='sha256', timedelta=30):
digestmod = getattr(hashlib, algo)
hash = hmac.HMAC(smart_bytes(key), digestmod=digestmod, msg=smart_bytes(s))
return hash.digest()
def get_wcs_services():
return settings.KNOWN_SERVICES.get('wcs')
def get_wcs_json(wcs_url, path, wcs_site, params={}):
if not wcs_url.endswith('/'):
wcs_url += '/'
@ -70,13 +71,13 @@ def get_wcs_json(wcs_url, path, wcs_site, params={}):
response_json = cache.get(url)
if response_json is None:
signed_url = sign_url(url, wcs_site.get('secret'))
response_json = requests.get(signed_url, headers={'accept': 'application/json'},
timeout=10).json()
response_json = requests.get(signed_url, headers={'accept': 'application/json'}, timeout=10).json()
if not isinstance(response_json, dict):
response_json = {'data': response_json}
cache.set(url, response_json)
return response_json
def get_wcs_options(url, condition=None, params={}):
categories = {}
for wcs_key, wcs_site in get_wcs_services().items():
@ -103,6 +104,7 @@ def get_wcs_options(url, condition=None, params={}):
options.append((category, sorted(categories[category], key=lambda x: x[1])))
return options
def get_wcs_formdef_details(formdef_reference):
wcs_key, form_slug = formdef_reference.split(':')
wcs_site = get_wcs_services()[wcs_key]
@ -113,6 +115,7 @@ def get_wcs_formdef_details(formdef_reference):
return form
return None
def push_wcs_formdata(request, formdef_reference, context=None):
wcs_key, form_slug = formdef_reference.split(':')
wcs_site = get_wcs_services()[wcs_key]
@ -121,7 +124,7 @@ def push_wcs_formdata(request, formdef_reference, context=None):
wcs_site_url += '/'
url = wcs_site_url + 'api/formdefs/%s/schema' % form_slug
response = requests.get(url)
create_draft = not(bool('welco-direct' in (response.json().get('keywords') or '')))
create_draft = not (bool('welco-direct' in (response.json().get('keywords') or '')))
url = wcs_site_url + 'api/formdefs/%s/submit?' % form_slug
data = {
@ -139,8 +142,7 @@ def push_wcs_formdata(request, formdef_reference, context=None):
url = sign_url(url, wcs_site.get('secret'))
response = requests.post(url, data=json.dumps(data),
headers={'Content-type': 'application/json'})
response = requests.post(url, data=json.dumps(data), headers={'Content-type': 'application/json'})
if response.json().get('err') != 0:
raise Exception('error %r' % response.content)
data = response.json()['data']
@ -169,6 +171,7 @@ def get_wcs_data(endpoint, params=None):
json_response = {'data': json_response}
return json_response
def response_for_json(request, data):
json_str = json.dumps(data)
for variable in ('jsonpCallback', 'callback'):

View File

@ -88,19 +88,22 @@ class Qualification(TemplateView):
context['source_pk'] = self.request.GET['source_pk']
if self.request.GET.get('source_pk'):
context['associations'] = Association.objects.filter(
source_type=ContentType.objects.get(id=self.request.GET['source_type']),
source_pk=self.request.GET['source_pk']).order_by('id')
source_type=ContentType.objects.get(id=self.request.GET['source_type']),
source_pk=self.request.GET['source_pk'],
).order_by('id')
return context
def post(self, request, *args, **kwargs):
association = Association(
source_type=ContentType.objects.get(id=request.POST['source_type']),
source_pk=request.POST['source_pk'])
source_type=ContentType.objects.get(id=request.POST['source_type']),
source_pk=request.POST['source_pk'],
)
association.formdef_reference = request.POST['formdef_reference']
association.save()
request.GET = request.POST
return self.get(request)
qualification = csrf_exempt(Qualification.as_view())
@ -117,8 +120,7 @@ class ChannelHome(TemplateView):
if not self.check_user_ok():
raise PermissionDenied()
context = super(ChannelHome, self).get_context_data(**kwargs)
context['panels'] = [
{'key': x, 'zone_url': x + '-zone'} for x in settings.SCREEN_PANELS]
context['panels'] = [{'key': x, 'zone_url': x + '-zone'} for x in settings.SCREEN_PANELS]
context['source'] = self.source_klass(self.request, **kwargs)
context['kb'] = KbHomeZone(self.request)
context['contacts'] = ContactsHomeZone(self.request)
@ -141,20 +143,25 @@ def home(request):
return HttpResponseRedirect('%s/' % channel)
raise PermissionDenied()
class HomePhone(ChannelHome):
source_klass = PhoneHome
home_phone = login_required(HomePhone.as_view())
class HomeMail(ChannelHome):
source_klass = MailHome
home_mail = login_required(HomeMail.as_view())
class HomeCounter(ChannelHome):
source_klass = CounterHome
home_counter = login_required(HomeCounter.as_view())
@ -174,11 +181,13 @@ def wcs_summary(request, *args, **kwargs):
break
return HttpResponse(json_str, content_type='application/javascript')
@login_required
def remove_association(request, *args, **kwargs):
Association.objects.filter(id=kwargs.get('pk')).delete()
return HttpResponseRedirect(resolve_url('home'))
@login_required
@csrf_exempt
def create_formdata(request, *args, **kwargs):
@ -196,6 +205,7 @@ def create_formdata(request, *args, **kwargs):
json.dump({'result': 'ok', 'url': qualif.formdata_url}, response)
return response
@login_required
def menu_json(request):
response = HttpResponse(content_type='application/json')
@ -209,17 +219,21 @@ def menu_json(request):
for channel in settings.CHANNEL_ROLES:
channel_groups = set(settings.CHANNEL_ROLES[channel])
if user_groups.intersection(channel_groups):
menu.append({
'label': force_text(labels.get(channel)),
'slug': channel,
'url': request.build_absolute_uri(reverse('home-%s' % channel)),
})
menu.append(
{
'label': force_text(labels.get(channel)),
'slug': channel,
'url': request.build_absolute_uri(reverse('home-%s' % channel)),
}
)
if check_kb_user_perms(request.user, access=True):
menu.append({
'label': force_text(_('Knowledge Base')),
'slug': 'book',
'url': request.build_absolute_uri(reverse('kb-home'))
})
menu.append(
{
'label': force_text(_('Knowledge Base')),
'slug': 'book',
'url': request.build_absolute_uri(reverse('kb-home')),
}
)
json_str = json.dumps(menu)
for variable in ('jsonpCallback', 'callback'):
if variable in request.GET:

View File

@ -8,7 +8,9 @@ https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
"""
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "welco.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()