Merge pull request #84 from alphagov/integrate-python-client

Integrate python client
This commit is contained in:
Adam Shimali
2016-01-19 11:57:33 +00:00
66 changed files with 1497 additions and 1017 deletions

View File

@@ -7,8 +7,7 @@ from flask.ext.sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_wtf import CsrfProtect
from werkzeug.exceptions import abort
from app.notify_client.api_client import AdminAPIClient
from app.notify_client.api_client import NotificationsAdminAPIClient
from app.its_dangerous_session import ItsdangerousSessionInterface
import app.proxy_fix
from config import configs
@@ -18,7 +17,7 @@ db = SQLAlchemy()
login_manager = LoginManager()
csrf = CsrfProtect()
admin_api_client = AdminAPIClient()
notifications_api_client = NotificationsAdminAPIClient()
def create_app(config_name, config_overrides=None):
@@ -31,6 +30,8 @@ def create_app(config_name, config_overrides=None):
init_csrf(application)
logging.init_app(application)
notifications_api_client.init_app(application)
login_manager.init_app(application)
login_manager.login_view = 'main.sign_in'
@@ -43,7 +44,6 @@ def create_app(config_name, config_overrides=None):
proxy_fix.init_app(application)
application.session_interface = ItsdangerousSessionInterface()
admin_api_client.init_app(application)
application.add_template_filter(placeholders)
application.add_template_filter(replace_placeholders)

View File

@@ -1,47 +1,97 @@
from flask import url_for
from datetime import datetime
from client.errors import HTTPError, InvalidResponse
from sqlalchemy.orm import load_only
from app import db
from app.models import Service
from flask.ext.login import current_user
from app import (db, notifications_api_client)
from app.main.utils import BrowsableItem
def insert_new_service(service_name, user):
service = Service(name=service_name,
created_at=datetime.now(),
limit=1000,
active=False,
restricted=True)
add_service(service)
service.users.append(user)
db.session.commit()
return service.id
def insert_new_service(service_name, user_id):
# Add a service with default attributes
# Should we try and handle exception here
resp = notifications_api_client.create_service(
service_name, False, 1000, True, user_id)
return resp['data']['id']
def get_service_by_id(id):
return Service.query.get(id)
def update_service(service):
return notifications_api_client.update_service(
service['id'],
service['name'],
service['active'],
service['limit'],
service['restricted'],
service['users'])
def get_service_by_id(id_):
return notifications_api_client.get_service(id_)
def get_services():
return notifications_api_client.get_services()
def unrestrict_service(service_id):
service = get_service_by_id(service_id)
service.restricted = False
add_service(service)
resp = notifications_api_client.get_service(service_id)
if resp['data']['restricted']:
resp = notifications_api_client.update_service(
service_id,
resp['data']['name'],
resp['data']['active'],
resp['data']['limit'],
False,
resp['data']['users'])
def activate_service(service_id):
service = get_service_by_id(service_id)
service.active = True
add_service(service)
def add_service(service):
db.session.add(service)
db.session.commit()
resp = notifications_api_client.get_service(service_id)
if not resp['data']['active']:
resp = notifications_api_client.update_service(
service_id,
resp['data']['name'],
True,
resp['data']['limit'],
resp['data']['restricted'],
resp['data']['users'])
# TODO Fix when functionality is added to the api.
def find_service_by_service_name(service_name):
return Service.query.filter_by(name=service_name).first()
resp = notifications_api_client.get_services()
retval = None
for srv_json in resp['data']:
if srv_json['name'] == service_name:
retval = srv_json
break
return retval
def delete_service(id_):
return notifications_api_client.delete_service(id_)
def find_all_service_names():
return [x.name for x in Service.query.options(load_only("name")).all()]
resp = notifications_api_client.get_services()
return [x['name'] for x in resp['data']]
class ServicesBrowsableItem(BrowsableItem):
@property
def title(self):
return self._item['name']
@property
def link(self):
return url_for('main.service_dashboard', service_id=self._item['id'])
@property
def destructive(self):
return False
@property
def hint(self):
return "Some service hint here"

View File

@@ -157,24 +157,29 @@ class TextNotReceivedForm(Form):
class AddServiceForm(Form):
def __init__(self, service_names, *args, **kwargs):
self.service_names = service_names
def __init__(self, names_func, *args, **kwargs):
"""
Keyword arguments:
names_func -- Returns a list of unique service_names already registered
on the system.
"""
self._names_func = names_func
super(AddServiceForm, self).__init__(*args, **kwargs)
service_name = StringField(
name = StringField(
'Service name',
validators=[
DataRequired(message='Service name can not be empty')
]
)
def validate_service_name(self, a):
if self.service_name.data in self.service_names:
def validate_name(self, a):
if a.data in self._names_func():
raise ValidationError('Service name already exists')
class ServiceNameForm(Form):
service_name = StringField(u'New name')
name = StringField(u'New name')
class ConfirmPasswordForm(Form):

26
app/main/utils.py Normal file
View File

@@ -0,0 +1,26 @@
class BrowsableItem(object):
"""
Maps for the template browse-list.
"""
def __init__(self, item, *args, **kwargs):
self._item = item
super(BrowsableItem, self).__init__()
@property
def title(self):
pass
@property
def link(self):
pass
@property
def hint(self):
pass
@property
def destructive(self):
pass

View File

@@ -6,22 +6,18 @@ from app.main.forms import AddServiceForm
@main.route("/add-service", methods=['GET', 'POST'])
@main.route("/add-service/<string:first>", methods=['GET', 'POST'])
@login_required
def add_service(first=False):
if first:
if first == 'first':
heading = 'Set up notifications for your service'
else:
abort(404)
def add_service():
form = AddServiceForm(services_dao.find_all_service_names)
services = services_dao.get_services()
if len(services) > 0:
heading = 'Set up notifications for your service'
else:
heading = 'Add a new service'
form = AddServiceForm(services_dao.find_all_service_names())
if form.validate_on_submit():
user = users_dao.get_user_by_id(session['user_id'])
services_dao.insert_new_service(form.service_name.data, user)
return redirect(url_for('.dashboard', service_id=123))
service_id = services_dao.insert_new_service(form.name.data, user)
return redirect(url_for('main.service_dashboard', service_id=service_id))
else:
return render_template(
'views/add-service.html',

View File

@@ -1,9 +1,18 @@
from flask import render_template
from flask import (render_template, redirect, url_for)
from flask_login import login_required
from app.main.dao import services_dao
from app.main import main
@main.route("/services")
@login_required
def choose_service():
return render_template('views/choose-service.html')
services = services_dao.get_services()
# If there is only one service redirect
# to the service dashboard.
if len(services['data']) == 1:
return redirect(url_for(
'main.service_dashboard', service_id=services['data'][0]['id']))
return render_template(
'views/choose-service.html',
services=[services_dao.ServicesBrowsableItem(x) for x in services['data']])

View File

@@ -1,18 +1,24 @@
from flask import render_template
from flask import (abort, render_template)
from flask_login import login_required
from app.main import main
from app.main.dao.services_dao import get_service_by_id
from client.errors import HTTPError
from ._jobs import jobs
@main.route("/services/<int:service_id>/dashboard")
@login_required
def dashboard(service_id):
def service_dashboard(service_id):
try:
service = get_service_by_id(service_id)
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
return render_template(
'views/dashboard.html',
'views/service_dashboard.html',
jobs=jobs,
free_text_messages_remaining=560,
spent_this_month='0.00',
service_id=service_id
)
service_id=service_id)

View File

@@ -10,29 +10,29 @@ def index():
@main.route("/register-from-invite")
@login_required
def registerfrominvite():
def register_from_invite():
return render_template('views/register-from-invite.html')
@main.route("/verify-mobile")
@login_required
def verifymobile():
def verify_mobile():
return render_template('views/verify-mobile.html')
@main.route("/services/<int:service_id>/send-email")
@login_required
def sendemail(service_id):
def send_email(service_id):
return render_template('views/send-email.html')
@main.route("/services/<int:service_id>/check-email")
@login_required
def checkemail(service_id):
def check_email(service_id):
return render_template('views/check-email.html')
@main.route("/services/<int:service_id>/manage-users")
@login_required
def manageusers(service_id):
def manage_users(service_id):
return render_template('views/manage-users.html', service_id=service_id)

View File

@@ -47,7 +47,7 @@ messages = [
@main.route("/services/<int:service_id>/jobs")
@login_required
def showjobs(service_id):
def view_jobs(service_id):
return render_template(
'views/jobs.html',
jobs=jobs,
@@ -57,7 +57,7 @@ def showjobs(service_id):
@main.route("/services/<int:service_id>/jobs/<job_id>")
@login_required
def showjob(service_id, job_id):
def view_job(service_id, job_id):
# TODO the uploaded file name could be part of job definition
# so won't need to be passed on from last view via session
@@ -86,7 +86,7 @@ def showjob(service_id, job_id):
@main.route("/services/<int:service_id>/jobs/<job_id>/notification/<string:notification_id>")
@login_required
def shownotification(service_id, job_id, notification_id):
def view_notification(service_id, job_id, notification_id):
return render_template(
'views/notification.html',
message=[

View File

@@ -1,19 +1,24 @@
from flask import render_template, redirect, request, url_for, abort
from flask import (
render_template, redirect, request, url_for, abort, session)
from flask_login import login_required
from app.main import main
from app.main.dao.services_dao import (
get_service_by_id, delete_service, update_service)
from app.main.forms import ConfirmPasswordForm, ServiceNameForm
service = {
'name': 'Service name',
'live': False,
'active': True
}
from client.errors import HTTPError
@main.route("/services/<int:service_id>/service-settings")
@login_required
def service_settings(service_id):
try:
service = get_service_by_id(service_id)['data']
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
return render_template(
'views/service-settings.html',
service=service,
@@ -23,42 +28,62 @@ def service_settings(service_id):
@main.route("/services/<int:service_id>/service-settings/name", methods=['GET', 'POST'])
@login_required
def name(service_id):
def service_name_change(service_id):
try:
service = get_service_by_id(service_id)['data']
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
form = ServiceNameForm()
form.service_name.data = 'Service name'
if request.method == 'GET':
return render_template(
'views/service-settings/name.html',
service=service,
form=form,
service_id=service_id
)
elif request.method == 'POST':
return redirect(url_for('.confirm_name_change', service_id=service_id))
if form.validate_on_submit():
session['service_name_change'] = form.name.data
return redirect(url_for('.service_name_change_confirm', service_id=service_id))
return render_template(
'views/service-settings/name.html',
service=service,
form=form,
service_id=service_id)
@main.route("/services/<int:service_id>/service-settings/name/confirm", methods=['GET', 'POST'])
@login_required
def confirm_name_change(service_id):
def service_name_change_confirm(service_id):
try:
service = get_service_by_id(service_id)['data']
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
form = ConfirmPasswordForm()
if request.method == 'GET':
return render_template(
'views/service-settings/confirm.html',
heading='Change your service name',
form=form,
service_id=service_id
)
elif request.method == 'POST':
if form.validate_on_submit():
service['name'] = session['service_name_change']
update_service(service)
return redirect(url_for('.service_settings', service_id=service_id))
return render_template(
'views/service-settings/confirm.html',
heading='Change your service name',
form=form,
service_id=service_id)
@main.route("/services/<int:service_id>/service-settings/request-to-go-live", methods=['GET', 'POST'])
@login_required
def request_to_go_live(service_id):
def service_request_to_go_live(service_id):
try:
service = get_service_by_id(service_id)['data']
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
if request.method == 'GET':
return render_template(
'views/service-settings/request-to-go-live.html',
@@ -66,12 +91,22 @@ def request_to_go_live(service_id):
service_id=service_id
)
elif request.method == 'POST':
service['restricted']
update_service(service)
return redirect(url_for('.service_settings', service_id=service_id))
@main.route("/services/<int:service_id>/service-settings/status", methods=['GET', 'POST'])
@login_required
def status(service_id):
def service_status_change(service_id):
try:
service = get_service_by_id(service_id)['data']
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
if request.method == 'GET':
return render_template(
'views/service-settings/status.html',
@@ -79,30 +114,47 @@ def status(service_id):
service_id=service_id
)
elif request.method == 'POST':
return redirect(url_for('.confirm_status_change', service_id=service_id))
return redirect(url_for('.service_status_change_confirm', service_id=service_id))
@main.route("/services/<int:service_id>/service-settings/status/confirm", methods=['GET', 'POST'])
@login_required
def confirm_status_change(service_id):
def service_status_change_confirm(service_id):
try:
service = get_service_by_id(service_id)['data']
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
# TODO validate password, will leave until
# user management has been moved to the api.
form = ConfirmPasswordForm()
if request.method == 'GET':
return render_template(
'views/service-settings/confirm.html',
heading='Turn off all outgoing notifications',
destructive=True,
form=form,
service_id=service_id
)
elif request.method == 'POST':
if form.validate_on_submit():
service['active'] = True
update_service(service)
return redirect(url_for('.service_settings', service_id=service_id))
return render_template(
'views/service-settings/confirm.html',
heading='Turn off all outgoing notifications',
destructive=True,
form=form,
service_id=service_id)
@main.route("/services/<int:service_id>/service-settings/delete", methods=['GET', 'POST'])
@login_required
def delete(service_id):
def service_delete(service_id):
try:
service = get_service_by_id(service_id)['data']
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
if request.method == 'GET':
return render_template(
'views/service-settings/delete.html',
@@ -110,22 +162,36 @@ def delete(service_id):
service_id=service_id
)
elif request.method == 'POST':
return redirect(url_for('.confirm_delete', service_id=service_id))
return redirect(url_for('.service_delete_confirm', service_id=service_id))
@main.route("/services/<int:service_id>/service-settings/delete/confirm", methods=['GET', 'POST'])
@login_required
def confirm_delete(service_id):
def service_delete_confirm(service_id):
try:
service = get_service_by_id(service_id)['data']
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
# TODO validate password, will leave until
# user management has been moved to the api.
form = ConfirmPasswordForm()
if request.method == 'GET':
return render_template(
'views/service-settings/confirm.html',
heading='Delete this service from Notify',
destructive=True,
form=form,
service_id=service_id
)
elif request.method == 'POST':
return redirect(url_for('.dashboard', service_id=service_id))
if form.validate_on_submit():
try:
service = delete_service(service_id)
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
return redirect(url_for('.choose_service'))
return render_template(
'views/service-settings/confirm.html',
heading='Delete this service from Notify',
destructive=True,
form=form,
service_id=service_id)

View File

@@ -34,14 +34,14 @@ sms_templates = [
@main.route("/services/<int:service_id>/sms/send", methods=['GET', 'POST'])
@login_required
def sendsms(service_id):
def send_sms(service_id):
form = CsvUploadForm()
if form.validate_on_submit():
try:
csv_file = form.file.data
filedata = _get_filedata(csv_file)
upload_id = s3upload(service_id, filedata)
return redirect(url_for('.checksms',
return redirect(url_for('.check_sms',
service_id=service_id,
upload_id=upload_id))
except ValueError as e:
@@ -49,7 +49,7 @@ def sendsms(service_id):
csv_file.filename)
flash(message)
flash(str(e))
return redirect(url_for('.sendsms', service_id=service_id))
return redirect(url_for('.send_sms', service_id=service_id))
return render_template('views/send-sms.html',
message_templates=sms_templates,
@@ -60,7 +60,7 @@ def sendsms(service_id):
@main.route("/services/<int:service_id>/sms/check/<upload_id>",
methods=['GET', 'POST'])
@login_required
def checksms(service_id, upload_id):
def check_sms(service_id, upload_id):
if request.method == 'GET':
contents = s3download(service_id, upload_id)
upload_result = _get_numbers(contents)
@@ -74,7 +74,7 @@ def checksms(service_id, upload_id):
)
elif request.method == 'POST':
# TODO create the job with template, file location etc.
return redirect(url_for('main.showjob',
return redirect(url_for('main.view_job',
service_id=service_id,
job_id=upload_id))

View File

@@ -8,12 +8,12 @@ from app.main.forms import (
@main.route("/user-profile")
def userprofile():
def user_profile():
return render_template('views/user-profile.html')
@main.route("/user-profile/name", methods=['GET', 'POST'])
def userprofile_name():
def user_profile_name():
form = ChangeNameForm()
@@ -26,11 +26,11 @@ def userprofile_name():
form_field=form.new_name
)
elif request.method == 'POST':
return redirect(url_for('.userprofile'))
return redirect(url_for('.user_profile'))
@main.route("/user-profile/email", methods=['GET', 'POST'])
def userprofile_email():
def user_profile_email():
form = ChangeEmailForm()
@@ -43,11 +43,11 @@ def userprofile_email():
form_field=form.email_address
)
elif request.method == 'POST':
return redirect(url_for('.userprofile_email_authenticate'))
return redirect(url_for('.user_profile_email_authenticate'))
@main.route("/user-profile/email/authenticate", methods=['GET', 'POST'])
def userprofile_email_authenticate():
def user_profile_email_authenticate():
form = ConfirmPasswordForm()
@@ -56,14 +56,14 @@ def userprofile_email_authenticate():
'views/user-profile/authenticate.html',
thing='email address',
form=form,
back_link=url_for('.userprofile_email')
back_link=url_for('.user_profile_email')
)
elif request.method == 'POST':
return redirect(url_for('.userprofile_email_confirm'))
return redirect(url_for('.user_profile_email_confirm'))
@main.route("/user-profile/email/confirm", methods=['GET', 'POST'])
def userprofile_email_confirm():
def user_profile_email_confirm():
form = ConfirmEmailForm()
@@ -74,11 +74,11 @@ def userprofile_email_confirm():
thing='email address'
)
elif request.method == 'POST':
return redirect(url_for('.userprofile'))
return redirect(url_for('.user_profile'))
@main.route("/user-profile/mobile-number", methods=['GET', 'POST'])
def userprofile_mobile_number():
def user_profile_mobile_number():
form = ChangeMobileNumberForm()
@@ -91,11 +91,11 @@ def userprofile_mobile_number():
form_field=form.mobile_number
)
elif request.method == 'POST':
return redirect(url_for('.userprofile_mobile_number_authenticate'))
return redirect(url_for('.user_profile_mobile_number_authenticate'))
@main.route("/user-profile/mobile-number/authenticate", methods=['GET', 'POST'])
def userprofile_mobile_number_authenticate():
def user_profile_mobile_number_authenticate():
form = ConfirmPasswordForm()
@@ -104,14 +104,14 @@ def userprofile_mobile_number_authenticate():
'views/user-profile/authenticate.html',
thing='mobile number',
form=form,
back_link=url_for('.userprofile_mobile_number_confirm')
back_link=url_for('.user_profile_mobile_number_confirm')
)
elif request.method == 'POST':
return redirect(url_for('.userprofile_mobile_number_confirm'))
return redirect(url_for('.user_profile_mobile_number_confirm'))
@main.route("/user-profile/mobile-number/confirm", methods=['GET', 'POST'])
def userprofile_mobile_number_confirm():
def user_profile_mobile_number_confirm():
form = ConfirmMobileNumberForm()
@@ -122,11 +122,11 @@ def userprofile_mobile_number_confirm():
thing='mobile number'
)
elif request.method == 'POST':
return redirect(url_for('.userprofile'))
return redirect(url_for('.user_profile'))
@main.route("/user-profile/password", methods=['GET', 'POST'])
def userprofile_password():
def user_profile_password():
form = ChangePasswordForm()
@@ -136,4 +136,4 @@ def userprofile_password():
form=form
)
elif request.method == 'POST':
return redirect(url_for('.userprofile'))
return redirect(url_for('.user_profile'))

View File

@@ -1,3 +1,4 @@
import datetime
from app import db
from flask import current_app
@@ -33,12 +34,21 @@ class User(db.Model):
email_address = db.Column(db.String(255), nullable=False, index=True, unique=True)
password = db.Column(db.String, index=False, unique=False, nullable=False)
mobile_number = db.Column(db.String, index=False, unique=False, nullable=False)
created_at = db.Column(db.DateTime, index=False, unique=False, nullable=False)
updated_at = db.Column(db.DateTime, index=False, unique=False, nullable=True)
created_at = db.Column(db.DateTime,
index=False,
unique=False,
nullable=False,
default=datetime.datetime.now)
updated_at = db.Column(db.DateTime,
index=False,
unique=False,
nullable=True,
onupdate=datetime.datetime.now)
password_changed_at = db.Column(db.DateTime, index=False, unique=False, nullable=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'), index=True, unique=False, nullable=False)
logged_in_at = db.Column(db.DateTime, nullable=True)
failed_login_count = db.Column(db.Integer, nullable=False, default=0)
# TODO should this be an enum?
state = db.Column(db.String, nullable=False, default='pending')
def serialize(self):
@@ -78,37 +88,37 @@ class User(db.Model):
return True
user_to_service = db.Table(
'user_to_service',
db.Model.metadata,
db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
db.Column('service_id', db.Integer, db.ForeignKey('services.id'))
)
# user_to_service = db.Table(
# 'user_to_service',
# db.Model.metadata,
# db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
# db.Column('service_id', db.Integer, db.ForeignKey('services.id'))
# )
class Service(db.Model):
__tablename__ = 'services'
# class Service(db.Model):
# __tablename__ = 'services'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)
created_at = db.Column(db.DateTime, index=False, unique=False, nullable=False)
active = db.Column(db.Boolean, index=False, unique=False, nullable=False)
limit = db.Column(db.BigInteger, index=False, unique=False, nullable=False)
users = db.relationship('User', secondary=user_to_service, backref=db.backref('user_to_service', lazy='dynamic'))
restricted = db.Column(db.Boolean, index=False, unique=False, nullable=False)
# id = db.Column(db.Integer, primary_key=True)
# name = db.Column(db.String(255), nullable=False, unique=True)
# created_at = db.Column(db.DateTime, index=False, unique=False, nullable=False)
# active = db.Column(db.Boolean, index=False, unique=False, nullable=False)
# limit = db.Column(db.BigInteger, index=False, unique=False, nullable=False)
# users = db.relationship('User', secondary=user_to_service, backref=db.backref('user_to_service', lazy='dynamic'))
# restricted = db.Column(db.Boolean, index=False, unique=False, nullable=False)
def serialize(self):
serialized = {
'id': self.id,
'name': self.name,
'createdAt': self.created_at.strftime(DATETIME_FORMAT),
'active': self.active,
'restricted': self.restricted,
'limit': self.limit,
'user': self.users.serialize()
}
# def serialize(self):
# serialized = {
# 'id': self.id,
# 'name': self.name,
# 'createdAt': self.created_at.strftime(DATETIME_FORMAT),
# 'active': self.active,
# 'restricted': self.restricted,
# 'limit': self.limit,
# 'user': self.users.serialize()
# }
return filter_null_value_fields(serialized)
# return filter_null_value_fields(serialized)
def filter_null_value_fields(obj):

View File

@@ -1,8 +1,126 @@
from __future__ import unicode_literals
from notify_client import NotifyAPIClient
from client.notifications import NotificationsAPIClient
class AdminAPIClient(NotifyAPIClient):
def init_app(self, app):
self.base_url = app.config['NOTIFY_DATA_API_URL']
self.auth_token = app.config['NOTIFY_DATA_API_AUTH_TOKEN']
class NotificationsAdminAPIClient(NotificationsAPIClient):
# Fudge assert in the super __init__ so
# we can set those variables later.
def __init__(self):
super(NotificationsAdminAPIClient, self).__init__("api_url",
"client",
"secret")
def init_app(self, application):
self.base_url = application.config['NOTIFY_API_URL']
self.client_id = application.config['NOTIFY_API_CLIENT']
self.secret = application.config['NOTIFY_API_SECRET']
def create_service(self, service_name, active, limit, restricted, user_id):
"""
Create a service and return the json.
"""
data = {
"name": service_name,
"active": active,
"limit": limit,
"users": [user_id],
"restricted": restricted
}
return self.post("/service", data)
def delete_service(self, service_id):
"""
Delete a service.
"""
endpoint = "/service/{0}".format(service_id)
return self.delete(endpoint)
def get_service(self, service_id, *params):
"""
Retrieve a service.
"""
return self.get(
'/service/{0}'.format(service_id))
def get_services(self, *params):
"""
Retrieve a list of services.
"""
return self.get('/service', *params)
def update_service(self,
service_id,
service_name,
active,
limit,
restricted,
users):
"""
Update a service.
"""
data = {
"id": service_id,
"name": service_name,
"active": active,
"limit": limit,
"restricted": restricted,
"users": users
}
endpoint = "/service/{0}".format(service_id)
return self.put(endpoint, update_dict)
def create_service_template(self, name, type_, content, service_id):
"""
Create a service template.
"""
data = {
"name": name,
"template_type": type_,
"content": content,
"service": service_id
}
endpoint = "/service/{0}/template".format(service_id)
return self.post(endpoint, data)
def get_service_template(self, service_id, template_id, *params):
"""
Retrieve a service template.
"""
endpoint = '/service/{service_id}/template/{template_id}'.format(
service_id=service_id,
template_id=template_id)
return self.get(endpoint, *params)
def get_service_templates(self, service_id, *params):
"""
Retrieve all templates for service.
"""
endpoint = '/service/{service_id}/template'.format(
service_id=service_id)
return self.get(endpoint, *params)
def delete_service_template(self, service_id, template_id):
"""
Delete a service template.
"""
endpoint = "/service/{0}/template/{1}".format(service_id, template_id)
return self.delete(endpoint)
# The implementation of these will change after the notifications-api
# functionality updates to include the ability to send notifications.
def send_sms(self,
mobile_number,
message,
job_id=None,
description=None):
pass
def send_email(self,
email_address,
message,
from_address,
subject,
job_id=None,
description=None):
pass

View File

@@ -1,8 +1,8 @@
from random import randint
from flask import url_for, current_app
from itsdangerous import URLSafeTimedSerializer, SignatureExpired
from app import admin_api_client
from app.main.dao import verify_codes_dao
from app import notifications_api_client
def create_verify_code():
@@ -12,32 +12,27 @@ def create_verify_code():
def send_sms_code(user_id, mobile_number):
sms_code = create_verify_code()
verify_codes_dao.add_code(user_id=user_id, code=sms_code, code_type='sms')
admin_api_client.send_sms(
mobile_number=mobile_number.replace(" ", ""),
message=sms_code,
token=admin_api_client.auth_token)
notifications_api_client.send_sms(mobile_number=mobile_number,
message=sms_code)
return sms_code
def send_email_code(user_id, email):
email_code = create_verify_code()
verify_codes_dao.add_code(user_id=user_id, code=email_code, code_type='email')
admin_api_client.send_email(email_address=email,
from_str='notify@digital.cabinet-office.gov.uk',
message=email_code,
subject='Verification code',
token=admin_api_client.auth_token)
notifications_api_client.send_email(email_address=email,
from_address='notify@digital.cabinet-office.gov.uk',
message=email_code,
subject='Verification code')
return email_code
def send_change_password_email(email):
link_to_change_password = url_for('.new_password', token=generate_token(email), _external=True)
admin_api_client.send_email(email_address=email,
from_str='notify@digital.cabinet-office.gov.uk',
message=link_to_change_password,
subject='Reset password for GOV.UK Notify',
token=admin_api_client.auth_token)
notifications_api_client.send_email(email_address=email,
from_address='notify@digital.cabinet-office.gov.uk',
message=link_to_change_password,
subject='Reset password for GOV.UK Notify')
def generate_token(email):

View File

@@ -42,7 +42,7 @@
{% if not current_user.is_authenticated() %}
{% set homepage_url = url_for('main.index') %}
{% else %}
{% set homepage_url = url_for('main.dashboard', service_id=123) %}
{% set homepage_url = url_for('main.choose_service') %}
{% endif %}
{% block content %}
@@ -60,7 +60,7 @@
</div>
</div>
<div class="column-half management-navigation-account">
<a href="{{ url_for('main.userprofile') }}">{{ current_user.name }}</a>
<a href="{{ url_for('main.user_profile') }}">{{ current_user.name }}</a>
<a href="{{ url_for('main.sign_out')}}">Sign out</a>
</div>
</div>

View File

@@ -1,18 +1,18 @@
<nav class="navigation">
<ul>
<li><a href="{{ url_for('.dashboard', service_id=123) }}">Dashboard</a></li>
<li><a href="{{ url_for('.service_dashboard', service_id=service_id) }}">Dashboard</a></li>
</ul>
<ul>
<li><a href="{{ url_for('.sendsms', service_id=123) }}">Send text messages</a></li>
<li><a href="{{ url_for('.sendemail', service_id=123) }}">Send emails</a></li>
<li><a href="{{ url_for('.showjobs', service_id=123) }}">Activity</a></li>
<li><a href="{{ url_for('.manage_templates', service_id=123) }}">Templates</a></li>
<li><a href="{{ url_for('.send_sms', service_id=service_id) }}">Send text messages</a></li>
<li><a href="{{ url_for('.send_email', service_id=service_id) }}">Send emails</a></li>
<li><a href="{{ url_for('.view_jobs', service_id=service_id) }}">Activity</a></li>
<li><a href="{{ url_for('.manage_templates', service_id=service_id) }}">Templates</a></li>
</ul>
<ul>
<li><a href="{{ url_for('.api_keys', service_id=123) }}">API keys and documentation</a></li>
<li><a href="{{ url_for('.api_keys', service_id=service_id) }}">API keys and documentation</a></li>
</ul>
<ul>
<li><a href="{{ url_for('.manageusers', service_id=123) }}">Manage users</a></li>
<li><a href="{{ url_for('.service_settings', service_id=123) }}">Service settings</a></li>
<li><a href="{{ url_for('.manage_users', service_id=service_id) }}">Manage users</a></li>
<li><a href="{{ url_for('.service_settings', service_id=service_id) }}">Service settings</a></li>
</ul>
</nav>

View File

@@ -30,7 +30,7 @@ GOV.UK Notify | Set up service
</ul>
<form autocomplete="off" method="post">
{{ textbox(form.service_name) }}
{{ textbox(form.name) }}
{{ page_footer(
'Continue'
) }}

View File

@@ -49,7 +49,7 @@
</div>
{{ page_footer(
back_link=url_for('.dashboard', service_id=service_id),
back_link=url_for('.service_dashboard', service_id=service_id),
back_link_text='Back to dashboard'
) }}

View File

@@ -17,7 +17,7 @@
{% for rejected in upload_result.rejects %}
<p>Line {{rejected.line_number}}: {{rejected.phone }}</a>
{% endfor %}
<p><a href="{{url_for('.sendsms', service_id=service_id)}}" class="button">Go back and resolve errors</a></p>
<p><a href="{{url_for('.send_sms', service_id=service_id)}}" class="button">Go back and resolve errors</a></p>
{% else %}
@@ -26,7 +26,7 @@
{{ page_footer(
button_text = "Send {} text messages".format(upload_result.valid|count),
back_link = url_for(".sendsms", service_id=service_id)
back_link = url_for(".send_sms", service_id=service_id)
)}}
{% if upload_result.valid | count > 6 %}
@@ -56,7 +56,7 @@
{{ page_footer(
button_text = "Send {} text messages".format(upload_result.valid|count),
back_link = url_for(".sendsms", service_id=service_id)
back_link = url_for(".send_sms", service_id=service_id)
)}}
</form>

View File

@@ -1,4 +1,4 @@
{% extends "withnav_template.html" %}
{% extends "admin_template.html" %}
{% from "components/browse-list.html" import browse_list %}
{% block page_title %}
@@ -11,16 +11,7 @@
Choose service
</h1>
{{ browse_list([
{
'title': 'MOT Reminders',
'link': url_for('.dashboard', service_id=123)
},
{
'title': 'Vehicle Tax',
'link': url_for('.dashboard', service_id=123)
},
]) }}
{{ browse_list(services) }}
{{ browse_list([
{
'title': 'Add a new service…',

View File

@@ -52,10 +52,10 @@ GOV.UK Notify | Notifications activity
]
) %}
{% call field() %}
<a href="{{ url_for('.shownotification', service_id=service_id, job_id=456, notification_id=item.id) }}">{{item.phone}}</a>
<a href="{{ url_for('.view_notification', service_id=service_id, job_id=456, notification_id=item.id) }}">{{item.phone}}</a>
{% endcall %}
{% call field() %}
<a href="{{ url_for('.shownotification', service_id=service_id, job_id=456, notification_id=item.id) }}">{{item.message[:50]}}…</a>
<a href="{{ url_for('.view_notification', service_id=service_id, job_id=456, notification_id=item.id) }}">{{item.message[:50]}}…</a>
{% endcall %}
{% call field(
align='right',

View File

@@ -16,10 +16,10 @@ GOV.UK Notify | Notifications activity
field_headings=['Job', 'File', 'Time', 'Status']
) %}
{% call field() %}
<a href="{{ url_for('.showjob', service_id=service_id, job_id=456) }}">{{ item.file }}</a>
<a href="{{ url_for('.view_job', service_id=service_id, job_id=456) }}">{{ item.file }}</a>
{% endcall %}
{% call field() %}
<a href="{{ url_for('.showjob', service_id=service_id, job_id=456) }}">{{ item.job }}</a>
<a href="{{ url_for('.view_job', service_id=service_id, job_id=456) }}">{{ item.job }}</a>
{% endcall %}
{% call field() %}
{{ item.time }}

View File

@@ -12,7 +12,7 @@ GOV.UK Notify | Manage users
<p>Here's where you can add or remove users of a service.</p>
{{ page_footer(
back_link = url_for('.dashboard', service_id=service_id),
back_link = url_for('.service_dashboard', service_id=service_id),
back_link_text = 'Back to dashboard'
) }}

View File

@@ -26,7 +26,7 @@ GOV.UK Notify | Notifications activity
</div>
{{ page_footer(
back_link = url_for('.showjob', service_id=service_id, job_id=job_id),
back_link = url_for('.view_job', service_id=service_id, job_id=job_id),
back_link_text = 'View other messages in this job'
) }}

View File

@@ -12,26 +12,26 @@
{{ browse_list([
{
'title': 'Change your service name',
'link': url_for('.name', service_id=service_id),
'link': url_for('.service_name_change', service_id=service_id),
'hint': 'Your service name ({}) is included in every sent notification'.format(service.name)
},
{
'title': 'Request to go live and turn off sending restrictions',
'link': url_for('.request_to_go_live', service_id=service_id),
'link': url_for('.service_request_to_go_live', service_id=service_id),
'hint': 'A live service can send notifications to any phone number or email address',
} if not service.live else {
},
{
'title': 'Turn off all outgoing notifications',
'link': url_for('.status', service_id=service_id),
'link': url_for('.service_status_change', service_id=service_id),
'destructive': True
} if service.active else {
'title': 'Restart sending notifications',
'link': url_for('.status', service_id=service_id)
'link': url_for('.service_status_change', service_id=service_id)
},
{
'title': 'Delete this service from Notify',
'link': url_for('.delete', service_id=service_id),
'link': url_for('.service_delete', service_id=service_id),
'destructive': True
},
]) }}

View File

@@ -21,7 +21,7 @@ GOV.UK Notify | Service settings
</ul>
<form method="post">
{{ textbox(form.service_name) }}
{{ textbox(form.name) }}
{{ page_footer(
'Save',
back_link=url_for('.service_settings', service_id=service_id)

View File

@@ -30,10 +30,10 @@
field_headings=['Job', 'File', 'Time', 'Status']
) %}
{% call field() %}
<a href="{{ url_for('.showjob', service_id=service_id, job_id=456) }}">{{ item.file }}</a>
<a href="{{ url_for('.view_job', service_id=service_id, job_id=456) }}">{{ item.file }}</a>
{% endcall %}
{% call field() %}
<a href="{{ url_for('.showjob', service_id=service_id, job_id=456) }}">{{ item.job }}</a>
<a href="{{ url_for('.view_job', service_id=service_id, job_id=456) }}">{{ item.job }}</a>
{% endcall %}
{% call field() %}
{{ item.time }}
@@ -43,7 +43,7 @@
{% endcall %}
{% endcall %}
<p>
<a href={{ url_for('.showjobs', service_id=service_id) }}>See all notifications activity</a>
<a href={{ url_for('.view_jobs', service_id=service_id) }}>See all notifications activity</a>
</p>

View File

@@ -11,10 +11,10 @@
{% call(item) list_table(
[
{'label': 'Name', 'value': current_user.name, 'url': url_for('.userprofile_name')},
{'label': 'Email address', 'value': current_user.email_address, 'url': url_for('.userprofile_email')},
{'label': 'Mobile number', 'value': current_user.mobile_number, 'url': url_for('.userprofile_mobile_number')},
{'label': 'Password', 'value': 'Last changed 1 January 2016, 10:00AM', 'url': url_for('.userprofile_password')},
{'label': 'Name', 'value': current_user.name, 'url': url_for('.user_profile_name')},
{'label': 'Email address', 'value': current_user.email_address, 'url': url_for('.user_profile_email')},
{'label': 'Mobile number', 'value': current_user.mobile_number, 'url': url_for('.user_profile_mobile_number')},
{'label': 'Password', 'value': 'Last changed 1 January 2016, 10:00AM', 'url': url_for('.user_profile_password')},
],
caption='Account settings',
field_headings=['Setting', 'Value', 'Link to change'],

View File

@@ -17,7 +17,7 @@ GOV.UK Notify | Service settings
{{ textbox(form.new_password) }}
{{ page_footer(
'Save',
back_link=url_for('.userprofile'),
back_link=url_for('.user_profile'),
back_link_text="Back to your profile"
) }}
</form>

View File

@@ -21,7 +21,7 @@ GOV.UK Notify | Service settings
{{ textbox(form_field) }}
{{ page_footer(
'Save',
back_link=url_for('.userprofile'),
back_link=url_for('.user_profile'),
back_link_text="Back to your profile"
) }}
</form>

View File

@@ -20,7 +20,7 @@ GOV.UK Notify | Service settings
{{ page_footer(
'Confirm',
destructive=destructive,
back_link=url_for('.userprofile')
back_link=url_for('.user_profile')
) }}
</form>
</div>