Commits

Victor Gavro committed e868040

Added test application, fixed small errors

Comments (0)

Files changed (11)

 
 from smsgate import settings
 from django.core.exceptions import ImproperlyConfigured
+from django.utils.importlib import import_module
 
-backend = __import__(settings.BACKEND)
+backend = import_module(settings.BACKEND)
 
 def _get_callback_url(callback):
   if callback == True:
 
 def send_mass_sms(datatuple,sender=settings.SENDER,callback=False,fail_silently=False):
   '''
-  Given a datatuple of (text,phones,sms_id) (sms_id is optional) sends each sms to each phones. Returns the tuple of results.
+  Given a datatuple of (text,phones,remote_id) (remote_id is optional) sends each sms to each phones. Returns the tuple of results.
   For results and arguments description see send_sms function docstring.
   '''
   callback_url = _get_callback_url(callback)
     if not fail_silently:
       raise
 
-def send_sms(message,recipient_list,sms_id=None,sender=settings.SENDER,callback=False,fail_silently=False):
+def send_sms(message,recipient_list,remote_id=None,sender=settings.SENDER,callback=False,fail_silently=False):
   '''
   Function for sending sms message.
-  Returns tuple of (sms_id,status,status_text), for each recipient in recipient_list.
+  Returns tuple of (remote_id,status,status_text), for each recipient in recipient_list.
 
   message - unicode string of sms body.
   recipient_list - tuple of phones in international phormat, without +.
-  You may specify sms_id. You need this ONLY if your gate doesn\'t support sms_id generation by itself, AND you need to get status of this sms later. In that case backend would generate unique id based on this value and phone number.
+  You may specify remote_id. You need this ONLY if your gate doesn\'t support id generation by itself, AND you need to get status of this sms later. In that case backend would generate unique id based on this value and phone number.
   If sender is not specified, SMSGATE_SENDER setting is used.
   You may specify callback url, or set callback=True to use SMSGATE_CALLBACK_URL. By default callback is false (note that on some servers callback_url setting must be set on serverside).
   If fail_silently is True, all exceptions are suppressed.
   '''
   callback_url = _get_callback_url(callback)
   try:
-    return backend.send_sms(message,recipient_list,sms_ids=sms_ids,sender=sender,callback_url=callback_url)
+    return backend.send_sms(message,recipient_list,remote_id=remote_id,sender=sender,callback_url=callback_url)
   except:
     if not fail_silently:
       raise
 
-def get_sms_statuses(sms_ids,fail_silently=False):
+def get_sms_statuses(remote_ids,fail_silently=False):
   '''
   Function for retrieving sms statuses from server (Note that if you use callback_url, you don\'t need to retrieve status by yourself).
-  Returns tuple of statuses, corresponding to tuple of sms_ids.
+  Returns tuple of statuses, corresponding to tuple of remote_ids.
   If fail_silently is True, all exceptions are suppressed.
   '''
+  if not remote_ids:
+    raise ValueError('remote_ids are empty.')
   try:
-    return backend.get_sms_statuses(sms_ids)
+    return backend.get_sms_statuses(remote_ids)
   except:
     if not fail_silently:
       raise
   for sms in queryset:
     sms.send()
   self.message_user(request, _('%s messages sent') % len(queryset))
-send_sms.short_description = _('Send sms to queued recipients')
+sms_send_queued.short_description = _('Send sms to queued recipients')
 
 def sms_recipient_get_status(self, request, queryset):
   count = SmsRecipient.get_statuses(recipients=queryset)
 def sms_recipients_count(obj):
   return obj.recipients.all().count()
 def sms_success_count(obj):
-  return obj.recipients.filter('status__in': SUCCESS_STATUSES).count()
+  return obj.recipients.filter(status__in = settings.SUCCESS_STATUSES).count()
 
 class SmsRecipientInline(admin.TabularInline):
   model = SmsRecipient
   actions = [sms_recipient_get_status,sms_recipient_resend]
   list_display = ('phone','sms','status','status_text','status_time','sent_time')
 
-admin.site.register(Sms)
-admin.site.register(SmsRecipient)
+admin.site.register(Sms,SmsAdmin)
+admin.site.register(SmsRecipient,SmsRecipientAdmin)

backends/mssgbox_com/http_v1.py

   result = re.findall(regexp,response[1])
   if not result:
     raise SmsGateUnknownResponse(response)
-  return result.groups()
+  return result
 
 def auth():
   '''Returns session_id for mass sms sending.'''
       result.append((match[1],STATUS_SENT,None))
   return tuple(result)
 
-def queryMsg(sms_id,**options):
-   response = send_command('queryMsg',uid=sms_id,**options)
+def queryMsg(remote_id,**options):
+   response = send_command('queryMsg',uid=remote_id,**options)
    match = parse(response,'(Status|ERR): (\d+)')[0]
    if match[0] == 'ERR':
      return STATUS_UNKNOWN,ERROR_CODES.get(match[1],u'Unknown error code: %s' % match[1])
    return STATUS_CODES.get(match[1],(STATUS_UNKNOWN,u'Unknown status: %s' % match[1]))
 
-def get_sms_statuses(sms_ids):
+def get_sms_statuses(remote_ids):
   kwargs = {}
-  if len(sms_ids) > 1:
+  if len(remote_ids) > 1:
     kwargs['session_id'] = auth()
   result = []
-  for sms_id in sms_ids:
-    result.append(queryMsg(sms_id,**kwargs))
+  for remote_id in remote_ids:
+    result.append(queryMsg(remote_id,**kwargs))
   return result
 
-def send_sms(message,recipient_list,sms_id,sender,callback_url):
+def send_sms(message,recipient_list,remote_id,sender,callback_url):
   return sendMsg(message,recipient_list,sender,msg_callback=bool(callback_url))
 
 def send_mass_sms(datatuple,sender,callback_url):
   session_id = auth()
-  for message,recipient_list,sms_id in datatuple:
+  for message,recipient_list,remote_id in datatuple:
     yield sendMsg(message,recipient_list,sender,msg_callback=bool(callback_url),session_id=session_id)
 from smsgate import settings
-from smsgate import send_sms, send_mass_sms
+from smsgate import send_sms, send_mass_sms,get_sms_statuses
 from smsgate.constants import *
 
 from django.db import models
   sender = models.CharField(_('Sender'),max_length=32,default=settings.SENDER)
 
   def __unicode__(self):
-    u'%s...' % sms.text[:16]
+    if len(self.text) > 16:
+      return u'%s...' % self.text[:13]
+    else:
+      return self.text
+
+  class Meta:
+    verbose_name = _('SMS message')
+    verbose_name_plural = _('SMS messages')
 
   def send(self,statuses=(STATUS_QUEUED,),recipients=None):
     if recipients == None:
       sender = self.sender,
       callback = settings.CALLBACK_URL,
     )
-    for n,(sms_id,status,status_text) in enumerate(sent_data):
-      recipients[n].set_processed(sms_id,status,status_text,save=True)
+    for n,(remote_id,status,status_text) in enumerate(sent_data):
+      recipients[n].set_processed(remote_id,status,status_text,save=True)
 
   @classmethod
   def create(cls,text,recipients_list,sender=settings.SENDER,send=settings.SEND_ON_SAVE):
       #setting statuses and messages of sms
       for x,sent_data in enumerate(mass_sent_data):
         recipients = recipients_lists[x]
-        for n,(sms_id,status,status_text) in enumerate(sent_data):
-          recipients[n].set_processed(sms_id,status,status_text,save=True)
+        for n,(remote_id,status,status_text) in enumerate(sent_data):
+          recipients[n].set_processed(remote_id,status,status_text,save=True)
 
     return len(mass_sent_data)
 
     (STATUS_EXPIRED, _('Expired')),
   )
   phone = models.CharField(_('Phone number'),max_length=12,validators=[phone_validator,])
-  sms_id = models.CharField(_('Remote id'),max_length=32,editable=False)
+  remote_id = models.CharField(_('Remote id'),max_length=32,editable=False)
   status = models.SmallIntegerField(_('Status'),choices=STATUS_CHOICES,default=STATUS_QUEUED)
   queued_time = models.DateTimeField(_('Queued time'),default=datetime.now,editable=False)
   status_time = models.DateTimeField(_('Status renew time'),null=True,blank=True,editable=False)
   sent_time = models.DateTimeField(_('Sent time'),null=True,blank=True,editable=False)
   status_text = models.CharField(_('Status message'),max_length=64,null=True,blank=True,editable=False)
   credits = models.IntegerField(_('Credits'),default=1) #You may use this field to store data for your billing system for big messages or different operators
-  sms = models.ForeignKey(Sms,editable=False,related_name='recipients')
+  sms = models.ForeignKey(Sms,related_name='recipients')
 
   class Meta:
+    verbose_name = _('SMS recipient')
+    verbose_name_plural = _('SMS recipients')
     unique_together = (('phone','sms'),)
     ordering = (('-queued_time'),)
 
   def __unicode__(self):
-    return u'%s: %s' % (self.id,self.phone)
+    return u'%s(%s)' % (self.phone, self.get_status_display())
 
   def save(self,*args,**kwargs):
     #We want user to pass telephones with and without +, but saves them only without +. Validators can\'t set new values, but save method of course doesn\'t invoke on form validation, so we have redundant validation as workaround.
   def send(self):
     self.sms.send(recipients=(self,))
 
-  def set_status(status,status_text=None,time=None,save=True):
+  def set_status(self,status,status_text=None,time=None,save=True):
     self.status,self.status_text = status,status_text
     self.status_time = time if time else datetime.now()
     if save:
       self.save()
 
-  def set_processed(self,sms_id,status,status_text,time=None,save=True):
+  def set_processed(self,remote_id,status,status_text,time=None,save=True):
     self.sent_time = time if time else datetime.now()
-    if sms_id:
-      self.sms_id = sms_id
+    if remote_id:
+      self.remote_id = remote_id
     self.set_status(status,status_text,time=self.sent_time,save=False)
     if save:
       self.save()
 
   def get_status(self,save=True):
-    if self.sms_id:
-      status,status_text = get_sms_statuses((self.sms_id,))[0]
+    if self.remote_id:
+      status,status_text = get_sms_statuses((self.remote_id,))[0]
       self.set_status(status,status_text,save=save)
 
   @classmethod
   def get_statuses(cls,statuses=(STATUS_SENT,STATUS_UNKNOWN),set_status=True,set_expired=True,recipients=None):
     time = datetime.now()
     if not recipients:
-      recipients = cls.objects.filter(status__in=statuses,sms_id__isnull=False)
+      recipients = cls.objects.filter(status__in=statuses,remote_id__isnull=False)
 
-    #we can fetch recipients only with sms_id
+    #we can fetch recipients only with remote_id
     fetch_recipients = []
     for obj in recipients:
-      if set_expired and (time.today() - obj.time_sent.date()).days > settings.STATUS_MAX_DAYS:
+      if set_expired and obj.sent_time and (time.date() - obj.sent_time.date()).days > settings.STATUS_MAX_DAYS:
         obj.set_status(STATUS_EXPIRED,time=time)
-      elif obj.sms_id:
-        fetched_recipients.append(obj)
+      elif obj.remote_id:
+        fetch_recipients.append(obj)
 
-    fetched_statuses = get_sms_statuses([r.sms_id for r in fetch_recipients])
+    if not fetch_recipients:
+      return 0
+
+    fetched_statuses = get_sms_statuses([r.remote_id for r in fetch_recipients])
     if set_status:
       for n,(status,status_text) in enumerate(fetched_statuses):
         fetch_recipients[n].set_status(status,status_text,time=time,save=True)

smsgate_test/__init__.py

Empty file added.

smsgate_test/manage.py

+#!/usr/bin/env python
+from django.core.management import execute_manager
+try:
+    import settings # Assumed to be in the same directory.
+except ImportError:
+    import sys
+    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+    sys.exit(1)
+
+if __name__ == "__main__":
+    execute_manager(settings)

smsgate_test/settings.py

+# Django settings for smsgate_test project.
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    # ('Your Name', 'your_email@domain.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+        'NAME': 'test.db',                      # Or path to database file if using sqlite3.
+        'USER': '',                      # Not used with sqlite3.
+        'PASSWORD': '',                  # Not used with sqlite3.
+        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
+        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
+    }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'America/Chicago'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale
+USE_L10N = True
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+MEDIA_URL = ''
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/", "/media/".
+ADMIN_MEDIA_PREFIX = '/media/'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = '@s#8soz&2rtbk7o(vq5y3m^37l34bs-cqi2p57!@a5zc5u&!xr'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+#     'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+)
+
+ROOT_URLCONF = 'smsgate_test.urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.messages',
+    'smsgate',
+    # Uncomment the next line to enable the admin:
+    'django.contrib.admin',
+)
+
+SMSGATE_API_ID = 99
+SMSGATE_LOGIN = 'vasya'
+SMSGATE_PASSWORD = 'pupkin'
+

smsgate_test/smsgate

+../../smsgate

smsgate_test/urls.py

+from django.conf.urls.defaults import *
+
+from django.contrib import admin
+admin.autodiscover()
+
+urlpatterns = patterns('',
+    # Example:
+    # (r'^smsgate_test/', include('smsgate_test.foo.urls')),
+
+    # Uncomment the admin/doc line below and add 'django.contrib.admindocs'
+    # to INSTALLED_APPS to enable admin documentation:
+    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
+    (r'^', include(admin.site.urls)),
+)
 def _urlencode(data):
   '''Small workaround for urlencode to recieve dict and nested tuples'''
   post_encode = []
-  items = post.items() if hasattr(post,'items') else post
+  items = data.items() if hasattr(data,'items') else data
   for key,value in items:
     if hasattr(value,'__iter__'):
       for subvalue in value:
   'This is callback view function, which should process http request, recieved from gate, and save sms statuses to database.'
   if request.META['REMOTE_ADDR'] not in settings.CALLBACK_IPS:
     return HttpResponse(status=403)
-  for sms_id,status,status_text in backend.sms_callback(request):
-    SmsRecipient.objects.get(sms_id=sms_id).set_status(status,status_text,save=True)
+  for remote_id,status,status_text in backend.sms_callback(request):
+    SmsRecipient.objects.get(remote_id=remote_id).set_status(status,status_text,save=True)
   return HttpResponse(status=200)