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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions cloud/endagaweb/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,8 @@ class Subscriber(models.Model):
# When toggled, this will protect a subsriber from getting "vacuumed." You
# can still delete subs with the usual "deactivate" button.
prevent_automatic_deactivation = models.BooleanField(default=False)
# role of subscriber
role = models.TextField(null=True, blank=True, default="Subscriber")

@classmethod
def update_balance(cls, imsi, other_bal):
Expand Down Expand Up @@ -576,8 +578,8 @@ def change_balance(self, amt):
self.crdt_balance = bal.serialize()

def __unicode__(self):
return "Sub %s, %s, network: %s, balance: %d" % (
self.name, self.imsi, self.network, self.balance)
return "Sub %s, %s, network: %s, balance: %d, role: %s" % (
self.name, self.imsi, self.network, self.balance, self.role)

def numbers(self):
n = self.number_set.all()
Expand Down
244 changes: 235 additions & 9 deletions cloud/endagaweb/templates/dashboard/subscribers.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,246 @@ <h3>Subscribers</h3>
<div class="col-xs-12 col-md-8">
{% if number_of_filtered_subscribers %}
{% render_table subscriber_table %}
<div class='modal fade' id='deactivate-subscriber-modal'>
<div class='modal-dialog'>
<div class='modal-content'>
<div class='modal-header'>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class='modal-title'>
Confirmation
</h4>
</div>
<div class='modal-body'>
<p style="word-wrap: break-word">
Are you sure you want to deactivate
<strong id ="subscriber_imsi"></strong>?<br/>
The subscriber will no longer have service,
but can re-register with the same SIM card by texting 101.
</p>
</div>
<div class='modal-footer'>
<button type='button' class='btn btn-default' data-dismiss='modal'>Cancel</button>
<button class='btn btn-primary' type='button' id='deactivate-subscriber-submit'>Deactivate Subscriber</button>
<div id='subscriber-messages-container'></div>
</div>
</div>
</div>
</div>
<div class='modal fade' id='update-subscriber-modal'>
<div class='modal-dialog'>
<div class='modal-content'>
<div class='modal-header'>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class='modal-title'>
Confirmation
</h4>
</div>
<div class='modal-body'>
<p style="word-wrap: break-word">
Are you sure you want to update role of
<strong id ="update_imsi"></strong>?<br/>
</p>
<table class="table table">
<tr>
<label class="radio-inline"><input type="radio"
name="subscriberType"
id="subscriber"
value="Subscriber"><b>Subscriber</b>
</label>

{% elif total_number_of_subscribers > 0 %}
<h4>No subscribers matched your search.</h4>
<label class="radio-inline"><input type="radio"
name="subscriberType"
id="retailer"
value="Retailer"><b>Retailer</b></label>

{% else %}
<p>
There are currently no subscribers registered on this network.
Go find some users!
</p>
<label class="radio-inline"><input type="radio"
name="subscriberType"
id="test_sim"
value="Test Sim"><b>Test
SIM</b></label>
</tr>
</table>

{% endif %}
</div>
<div class='modal-footer'>
<button type='button' class='btn btn-default' data-dismiss='modal'>Cancel</button>
<button class='btn btn-primary' type='button' id='update-subscriber-submit'>Update Subscriber</button>
<div id='update-subscriber-messages-container'></div>
</div>
</div>
</div>
</div>
<div class="dropdown actions">
<button id ="actions" class="btn-btn-primary dropdown-toogle" type="button" data-toggle="dropdown" > Action(s)
<span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a data-toggle='modal' data-target='#deactivate-subscriber-modal'
href='#'>Deactivate Subscriber</a></li>
<li><a data-toggle='modal' data-target='#update-subscriber-modal'
href='#'>Change Subscriber Role</a>
</ul>
</div>
</div>
{% elif total_number_of_subscribers > 0 %}
<h4>No subscribers matched your search.</h4>

{% else %}
<p>
There are currently no subscribers registered on this network.
Go find some users!
</p>

{% endif %}
</div>

</div>
</div> <!-- /.row (subscribers) -->


{% endblock %}
{% block js %}
<script>
var messageClearTimeout;
$('.update-subscriber-link').click(function(event) {
event.preventDefault();
});

$('.deactivate-subscriber-link').click(function(event) {
event.preventDefault();
});
toggle();
function toggle(source) {
var imsi = $('.imsi_id:checked').length;
if(document.getElementById("subscriber-select-all").checked==true){
$(':checkbox').each(function() {
this.checked = true;
});
}
if(document.getElementById("subscriber-select-all").checked==false){
$(':checkbox').each(function() {
this.checked = false;
});
var imsi = $('.imsi_id:checked').length;
}
imsiSelected();
}
function imsiSelected(check){

var imsi = $('.imsi_id:checked').length;
var updateDiv = document.getElementById('actions');
if(imsi){
updateDiv.style.display = "block";
}
else{
updateDiv.style.display = "none";
}
var i=0;
var imsi_val=[];
var imsi_list = $('.imsi_id:checked');
$.each(imsi_list, function(key, val){
imsi_val[i++]=val.value;
});
$('#subscriber_imsi').html(imsi_val.join());
$('#update_imsi').html(imsi_val.join());

}
$('#deactivate-subscriber-submit').click(function(event) {
var i=0;
var imsi_val=[];
var imsi_list = $('.imsi_id:checked');
$.each(imsi_list, function(key, val){
imsi_val[i++]=val.value;
});
imsi_list_value = imsi_val.join()
// Show a 'working' message.
var message = 'Working..';
var html = "<div class='alert alert-success'>" + message + "</div>";
$('#subscriber-messages-container').html(html).show();
// Post to the endagaweb API.
$.ajax({
url: '/api/v2/subscribers/' + imsi_list_value,
type: 'DELETE',
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", '{{ csrf_token }}');
},
success: function(response) {
// Show a success message after a small delay.
// Then, after some more time has passed, redirect back to /subscribers.
setTimeout(function() {
var message = 'Subscriber deactivated successfully.';
var html = "<div class='alert alert-success'>" + message + "</div>";
$('#subscriber-messages-container').html(html).show();
}, 1000);
clearTimeout(messageClearTimeout);
setTimeout(function() {
window.location.href = '/dashboard/subscribers';
}, 2000);
},
error: function(response) {
// Show an error message after a small delay.
setTimeout(function() {
var message = 'Error: ' + response.status;
var html = "<div class='alert alert-danger'>" + message + "</div>";
$('#subscriber-messages-container').html(html).show();
}, 800);
},
});
});
$('#update-subscriber-submit').click(function(){
if($('input[name="subscriberType"]:checked').length<=0){
var html = "<div class='alert alert-danger'> Please select category</div>";
$('#update-subscriber-messages-container').html(html).show().delay(3000).fadeOut();
return
}
category=document.querySelector('input[name="subscriberType"]:checked').value
var i=0;
var imsi_val=[];
var imsi_list = $('.imsi_id:checked');
$.each(imsi_list, function(key, val){
imsi_val[i++]=val.value;
});
var data = {
category: category,
imsi_val : imsi_val,

};
$.ajax({
url: '/dashboard/subscribers',
data: data,
type: 'POST',
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", '{{ csrf_token }}');
},
success: function(response) {
setTimeout(function() {
var message = 'Subscriber updated successfully.';
var html = "<div class='alert alert-success'>" + message+"</div>";
$('#update-subscriber-messages-container').html(html).show();
}, 1000);
clearTimeout(messageClearTimeout);
setTimeout(function() {
window.location.href = '/dashboard/subscribers';
}, 2000);
},
error: function(response) {
// Show an error message after a small delay.
setTimeout(function() {
var message = 'Error: ' + response;
var html = "<div class='alert alert-danger'>" + message + "</div>";
$('#update-subscriber-messages-container').html(html).show();
}, 800);
},
});
});

$('#deactivate-subscriber-modal').on('hidden.bs.modal', function() {
$('#subscriber-messages-container').html('');
});
$('#update-subscriber-modal').on('hidden.bs.modal', function() {
$('#updateddate-subscriber-messages-container').html('');
});
</script>
{% endblock %}
74 changes: 74 additions & 0 deletions cloud/endagaweb/tests/test_api_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,21 @@ def setUpClass(cls):
cls.pcu = models.PendingCreditUpdate(subscriber=cls.sub, amount=100,
uuid='abc123')
cls.pcu.save()
# create more subscriber for same network with different number
cls.imsi3 = "IMSI999990000000455"
cls.sub2_network1 = models.Subscriber(
balance=100000, name='sub-two', imsi=cls.imsi3,
network=cls.bts.network, bts=cls.bts)
cls.sub2_network1.save()
cls.number4 = models.Number(number='6285574719326',
state="inuse",
network=cls.user_profile.network,
subscriber=cls.sub2_network1,
kind="number.nexmo.monthly")
cls.number4.save()
cls.pcu2 = models.PendingCreditUpdate(subscriber=cls.sub2_network1, amount=300,
uuid='abc345')
cls.pcu2.save()

@classmethod
def tearDownClass(cls):
Expand All @@ -511,6 +526,9 @@ def tearDownClass(cls):
cls.number2.delete()
cls.number3.delete()
cls.pcu.delete()
cls.sub2_network1.delete()
cls.number4.delete()
cls.pcu2.delete()

def setUp(self):
self.client = Client()
Expand Down Expand Up @@ -627,3 +645,59 @@ def test_deactivate_subscriber_sans_bts(self):
event_count = models.UsageEvent.objects.filter(
subscriber_imsi=self.sub2.imsi, kind='delete_imsi').count()
self.assertEqual(1, event_count)

def test_bulk_deactivate_subscriber(self):
"""We can deactivate the bulk Subscriber via DELETE """


url = '/api/v2/subscribers/%s,%s' % (self.sub.imsi,self.sub2_network1.imsi)
print(url)
header = {
'HTTP_AUTHORIZATION': 'Token %s' % self.user_profile.network.api_token
}
with mock.patch('endagaweb.celery.app.send_task') as mocked_task:
response = self.client.delete(url, **header)
self.assertEqual(200, response.status_code)
# The both subscriber should no longer be in the DB.
self.assertEqual(0, models.Subscriber.objects.filter(
imsi=self.sub.imsi).count())
self.assertEqual(0, models.Subscriber.objects.filter(
imsi=self.sub2_network1.imsi).count())
# associated numbers of both subscriber of same network should
# have been deactivated -- reload
# them from the DB to check their state.
number = models.Number.objects.get(id=self.number.id)
self.assertEqual('available', number.state)
self.assertEqual(None, number.network)
self.assertEqual(None, number.subscriber)
number2 = models.Number.objects.get(id=self.number2.id)
self.assertEqual('available', number2.state)
number3 = models.Number.objects.get(id=self.number4.id)
self.assertEqual('available', number3.state)
# The associated PendingCreditUpdate should be gone.
self.assertEqual(0, models.PendingCreditUpdate.objects.filter(
pk=self.pcu.pk).count())
self.assertEqual(0, models.PendingCreditUpdate.objects.filter(
pk=self.pcu2.pk).count())
# The mocked task should have been called with specific arguments
self.assertTrue(mocked_task.called)
args, _ = mocked_task.call_args
task_name, task_args = args
task_endpoint, task_data = task_args
self.assertEqual('endagaweb.tasks.async_post', task_name)
expected_url = '%s/config/deactivate_subscriber' % self.bts.inbound_url
self.assertEqual(expected_url, task_endpoint)
# The task_data should be signed with the BTS UUID and should have a
# jwt key which is a dict with a imsi key.

serializer = itsdangerous.JSONWebSignatureSerializer(self.bts.secret)
task_data = serializer.loads(task_data['jwt'])
self.assertEqual(self.sub2_network1.imsi, task_data['imsi'])
# A 'delete_imsi' UsageEvent should have been created for both subscriber
event_count = models.UsageEvent.objects.filter(
subscriber_imsi=self.sub.imsi, kind='delete_imsi').count()
event_count1 = models.UsageEvent.objects.filter(
subscriber_imsi=self.sub2_network1.imsi, kind='delete_imsi').count()
total_event = event_count + event_count1
self.assertEqual(2, total_event)

Loading