示例#1
0
def _messaging_seal(service_user, message_key, message_parent_key,
                    parent_message_key, dirty_behavior):
    users.set_user(service_user)
    try:
        messaging.seal(message_key, message_parent_key, parent_message_key, 1)
    finally:
        users.clear_user()
示例#2
0
def _regenerate_branding_with_logo(service_user):
    users.set_user(service_user)
    logging.info(
        "%s: Replacing logo.png in the sln main branding zip with the uploaded logo"
        % service_user.email())

    logo = get_solution_logo(service_user)
    sln_main_branding = get_solution_main_branding(service_user)

    zip_content = replace_file_in_zip_blob(sln_main_branding.blob, "logo.jpg",
                                           str(logo.picture))

    def trans():
        sln_main_branding = get_solution_main_branding(service_user)
        sln_main_branding.blob = db.Blob(zip_content)
        sln_main_branding.branding_creation_time = 0

        common_settings = get_solution_settings(service_user)
        common_settings.updates_pending = True
        common_settings.events_branding_hash = None

        put_and_invalidate_cache(sln_main_branding, common_settings)
        return common_settings

    common_settings = run_in_transaction(trans, xg=True)
    broadcast_updates_pending(common_settings)
示例#3
0
def _send_order_confirmation(service_user, lang, message_flow_run_id, member,
                             steps, end_id, end_message_flow_id,
                             parent_message_key, tag, result_key, flush_id,
                             flush_message_flow_id, service_identity,
                             user_details, order_details):
    if now() - steps[-1].acknowledged_timestamp < 10:
        alert_flags = Message.ALERT_FLAG_SILENT
    else:
        alert_flags = Message.ALERT_FLAG_VIBRATE

    users.set_user(service_user)
    try:
        messaging.send(
            parent_key=parent_message_key,
            parent_message_key=parent_message_key,
            message=u'%s\n\n%s:\n%s' %
            (translate(lang, SOLUTION_COMMON, u'order_complete_will_notify'),
             translate(lang, SOLUTION_COMMON, u'usage-detail'), order_details),
            answers=list(),
            flags=Message.FLAG_ALLOW_DISMISS | Message.FLAG_AUTO_LOCK,
            members=[
                MemberTO.from_user(user_details[0].toAppUser(), alert_flags)
            ],
            branding=get_solution_main_branding(service_user).branding_key,
            tag=None,
            service_identity=service_identity,
            alert_flags=alert_flags,
            step_id=u'order_complete')
    finally:
        users.clear_user()
示例#4
0
    def post(self):
        settings = get_server_settings()
        secret = self.request.headers.get("X-Nuntiuz-Secret", None)
        if secret != settings.jabberSecret:
            logging.error(u"Received unauthenticated callback response, ignoring ...")
            return
        sik = self.request.headers.get("X-Nuntiuz-Service-Key", None)
        if not sik:
            logging.error(u"Received invalid Callback response without Service Identifier Key")
            return
        sik = get_sik(sik)
        if not sik:
            logging.error("Received invalid Callback response with unknown Service Identifier Key:\nsik: %s\nbody:\n%s" % (
                self.request.headers.get("X-Nuntiuz-Service-Key", None), self.request.body))
            return
        users.set_user(sik.user)

        raw_result = self.request.body
        try:
            from google.appengine.api.memcache import get  # @UnresolvedImport
            if get(sik.user.email() + "_interactive_logs"):
                content_type = self.request.headers.get('content-type', 'unknown')
                status = self.request.headers.get('X-Nuntiuz-Service-Status', 'unknown')
                if status == "600":
                    status = "unknown"
                result_url = self.request.headers.get('X-Nuntiuz-Service-Result-Url', 'unknown')
                channel.send_message(sik.user, "rogerthat.service.interactive_logs", content_type=content_type,
                                     status=status, result_url=result_url, body=raw_result.decode('utf-8', 'replace'))
        except:
            logging.exception("Error during interactive logging.")

        try:
            result = json.loads(raw_result)
        except Exception:
            raw_result_unicode = raw_result.decode('utf-8', 'replace')
            logging.warning(u"Could not parse request body as JSON!\n" + raw_result_unicode)
            error_code = ERROR_CODE_INVALID_JSON
            error_message = u"The JSON_RPC response could not be parsed as a valid JSON."

            log_service_activity(sik.user, str(time.time()), ServiceLog.TYPE_CALLBACK, ServiceLog.STATUS_ERROR,
                                 None, None, raw_result_unicode, error_code, error_message)
            return
        raw_result_unicode = json.dumps(privatize(deepcopy(result)), ensure_ascii=False)
        logging.info(u"Incoming call back response:\n" + raw_result_unicode)

        if not result:
            error_code = ERROR_CODE_INVALID_JSON
            error_message = u"The JSON_RPC response could not be parsed as a valid json."
            log_service_activity(sik.user, None, ServiceLog.TYPE_CALLBACK, ServiceLog.STATUS_ERROR, None, None,
                                 raw_result_unicode, error_code, error_message)
            return

        from rogerthat.dal import parent_key
        service_api_callback = ServiceAPICallback.get_by_key_name(result["id"], parent=parent_key(sik.user))
        if not service_api_callback:
            logging.warning(u"Service api call back response record not found !")
            return
        _process_callback_result(sik, result, raw_result_unicode, service_api_callback, True)
示例#5
0
def _get_service_user_for_app_id(sln_settings, app_id):
    users.set_user(sln_settings.service_user)
    try:
        identity = system.get_identity()
        if app_id == identity.app_ids[0]:
            return sln_settings.service_user
        return None
    finally:
        users.clear_user()
示例#6
0
def _restaurant_invite(service_user, service_identity, invitee, message, tag, sln_settings, app_id):
    from rogerthat.service.api.friends import invite as invite_api_call
    from rogerthat.bizz.friends import CanNotInviteFriendException
    language = sln_settings.main_language or DEFAULT_LANGUAGE

    users.set_user(service_user)
    try:
        invite_api_call(invitee, None, message, language, tag, service_identity, app_id)
    except CanNotInviteFriendException:
        logging.debug('%s is already connected with %s', invitee, service_user)
        pass
    finally:
        users.clear_user()
示例#7
0
 def authenticate_request(self):
     ak = self.request.headers.get('X-Nuntiuz-API-Key', None) or self.request.get('X-Nuntiuz-API-Key', None)
     if not ak:
         self.response.set_status(401, "Missing X-Nuntiuz-API-Key, cannot authenticate!")
         return None
     aki = get_api_key(ak)
     if not aki:
         self.response.set_status(401, "Could not match %s to a service account!" % ak)
         return None
     logging.info("Authenticated %s as %s" % (ak, aki.user))
     if aki.mfr:
         logging.info("Executed by message flow engine.")
     users.set_user(aki.user, mfr=aki.mfr == True)
     return aki
示例#8
0
def _remove_new_event_item(sln_settings):
    users.set_user(sln_settings.service_user)
    try:
        service_menu = system.get_menu()
        current_coords = get_coords_of_service_menu_item(service_menu, POKE_TAG_NEW_EVENT)
        logging.info("remove new event item for %s coords %s", sln_settings.service_user, current_coords)
        if current_coords:
            system.delete_menu_item(current_coords)

        sln_settings.events_branding_hash = None
        put_and_invalidate_cache(sln_settings)
        deferred.defer(common_provision, sln_settings.service_user)
    finally:
        users.clear_user()
示例#9
0
def _process_event_reminder(reminder_key):
    reminder = EventReminder.get(reminder_key)

    service_user = reminder.service_user
    settings = get_solution_settings(service_user)
    event = Event.get_by_id(reminder.event_id,
                            parent_key(service_user, settings.solution))

    if event and reminder.event_start_epoch in event.start_dates:
        now_ = now()
        if (now_ +
                reminder.remind_before) > (reminder.event_start_epoch +
                                           timezone_offset(settings.timezone)):
            if event.place:
                place = "\n@ " + event.place + "\n"
            else:
                place = ""

            dt = datetime.datetime.fromtimestamp(reminder.event_start_epoch)
            language = settings.main_language or DEFAULT_LANGUAGE
            when = "%s, %s" % (format_date(dt, format='full', locale=language),
                               format_time(dt, format='short',
                                           locale=language))
            reminderMessage = "Reminder:\n\nTitle:\n%s\n\nWhen:\n%s\n%s\nDescription:\n%s" % (
                event.title, when, place, event.description)
            main_branding = get_solution_main_branding(service_user)

            human_user = get_human_user_from_app_user(reminder.human_user)
            members = list()
            members.append(human_user.email())
            users.set_user(reminder.service_user)
            try:
                messaging.send(parent_key=None,
                               parent_message_key=None,
                               message=reminderMessage,
                               answers=[],
                               flags=Message.FLAG_ALLOW_DISMISS,
                               members=members,
                               branding=main_branding.branding_key,
                               tag=None,
                               service_identity=reminder.service_identity)
            finally:
                users.clear_user()

            reminder.status = EventReminder.STATUS_REMINDED
            reminder.put()
    else:
        reminder.status = EventReminder.STATUS_REMINDED
        reminder.put()
示例#10
0
def common_new_chat_message(parent_message_key, message_key, sender, message, answers, timestamp, tag, service_identity,
                            attachments):
    if tag and tag.startswith(POKE_TAG_INBOX_FORWARDING_REPLY):
        info = json.loads(tag[len(POKE_TAG_INBOX_FORWARDING_REPLY):])
        message_key = info['message_key']
        sim_parent = SolutionInboxMessage.get(reconstruct_key(db.Key(message_key)))

        if sim_parent.awaiting_first_message:
            sim_parent.awaiting_first_message = False
            sim_parent.put()
        else:
            service_user = sim_parent.service_user
            sln_settings = get_solution_settings(service_user)
            sent_by_service = True
            if sim_parent.sender.email == sender.email and sim_parent.sender.app_id == sender.app_id:
                sent_by_service = False

            picture_attachments = []
            video_attachments = []
            for a in attachments:
                if a.content_type.startswith("image"):
                    picture_attachments.append(a.download_url)
                if a.content_type.startswith("video"):
                    video_attachments.append(a.download_url)

            if sent_by_service:
                sim_parent, _ = add_solution_inbox_message(service_user, message_key, sent_by_service, [sender], timestamp, message, picture_attachments, video_attachments, mark_as_unread=False, mark_as_read=True)
            else:
                sim_parent, _ = add_solution_inbox_message(service_user, message_key, sent_by_service, [sender], timestamp, message, picture_attachments, video_attachments)

            sln_i_settings = get_solution_settings_or_identity_settings(sln_settings, service_identity)
            send_message(service_user, u"solutions.common.messaging.update",
                         service_identity=service_identity,
                         message=serialize_complex_value(SolutionInboxMessageTO.fromModel(sim_parent, sln_settings, sln_i_settings, True), SolutionInboxMessageTO, False))

            member_sender_user = create_app_user_by_email(sim_parent.sender.email, sim_parent.sender.app_id)
            sln_i_settings = get_solution_settings_or_identity_settings(sln_settings, service_identity)
            members = [MemberTO.from_user(users.User(f)) for f in sln_i_settings.inbox_forwarders]
            if member_sender_user.email() not in sln_i_settings.inbox_forwarders:
                members.append(MemberTO.from_user(member_sender_user))

            users.set_user(service_user)
            try:
                messaging.add_chat_members(sim_parent.message_key, members)
            finally:
                users.clear_user()
    else:
        raise NotImplementedError()
示例#11
0
def solution_coupon_redeem(service_user, email, method, params, tag,
                           service_identity, user_details):
    data = json.loads(params)
    coupon_id = data.get('coupon_id')
    redeeming_user = users.User(data.get('redeeming_user'))
    response = SendApiCallCallbackResultTO()
    lang = get_solution_settings(service_user).main_language
    service_identity_user = get_and_validate_service_identity_user(
        service_user, service_identity)

    try:
        coupon = redeem_news_coupon(coupon_id, service_identity_user,
                                    redeeming_user)
        with users.set_user(service_user):
            news_item = news.get(coupon.news_id, service_identity)
            response.result = u'%s' % json.dumps(
                serialize_complex_value(news_item, NewsItemTO, False))
    except NewsCouponNotFoundException:
        response.error = t(lang, 'coupon_not_found')
    except NewsCouponAlreadyUsedException:
        response.error = t(lang, 'you_have_already_used_this_coupon')
        user, app_id = get_app_user_tuple(redeeming_user)
        member = BaseMemberTO(user.email(), app_id)
        disable_news_with_coupon(coupon_id, service_identity_user, member)
    except Exception as exception:
        logging.error(exception)
        response.error = t(lang, 'error-occured-unknown')
    return response
示例#12
0
def send_expired_voucher_message(voucher_key, sln_settings, days):
    voucher = SolutionCityVoucher.get(voucher_key)
    if not voucher or days in voucher.expiration_reminders_sent:
        return

    language = sln_settings.main_language
    service_user = sln_settings.service_user
    branding = get_solution_main_branding(service_user).branding_key

    if voucher.owner:
        activation_date = format_timestamp(voucher.activation_date,
                                           sln_settings,
                                           format='medium')
        message = common_translate(language,
                                   SOLUTION_COMMON,
                                   u'voucher_expiration_message',
                                   days=days,
                                   date=activation_date)
        with users.set_user(sln_settings.service_user):
            member = MemberTO()
            member.alert_flags = Message.ALERT_FLAG_VIBRATE
            member.member = voucher.owner.email()
            member.app_id = voucher.app_id
            messaging.send(parent_key=None,
                           parent_message_key=None,
                           message=message,
                           answers=[],
                           flags=Message.FLAG_ALLOW_DISMISS,
                           members=[member],
                           branding=branding,
                           tag=None)

        voucher.expiration_reminders_sent.append(days)
        db.put(voucher)
示例#13
0
def create_news_item(sln_settings, broadcast_type, message, title, permalink):
    service_user = sln_settings.service_user
    logging.info('Creating news item:\n- %s\n- %s\n- %s\n- %s\n- %s', service_user, message, title, broadcast_type,
                 permalink)

    with users.set_user(service_user):
        sticky = False
        sticky_until = 0
        image = None
        news_type = NewsItem.TYPE_NORMAL
        link_caption = transl(u'More info', sln_settings.main_language)
        action_button = NewsActionButtonTO(u'url', link_caption, permalink)
        qr_code_content = None
        qr_code_caption = None
        si = get_default_service_identity(service_user)
        app_ids = si.appIds

        title = limit_string(title, NewsItem.MAX_TITLE_LENGTH)
        news.publish(sticky=sticky,
                     sticky_until=sticky_until,
                     title=title,
                     message=message,
                     image=image,
                     news_type=news_type,
                     flags=NewsItem.DEFAULT_FLAGS | NewsItem.FLAG_SILENT,
                     broadcast_type=broadcast_type,
                     action_buttons=[action_button],
                     qr_code_content=qr_code_content,
                     qr_code_caption=qr_code_caption,
                     scheduled_at=0,
                     app_ids=app_ids)
def _set_content_branding(sln_settings):
    users.set_user(sln_settings.service_user)
    try:
        sln_main_branding = get_solution_main_branding(sln_settings.service_user)
        populate_identity(sln_settings, sln_main_branding.branding_key)

        identities = [None]
        if sln_settings.identities:
            identities.extend(sln_settings.identities)
        for service_identity in identities:
            if is_default_service_identity(service_identity):
                service_identity_user = create_service_identity_user(sln_settings.service_user)
            else:
                service_identity_user = create_service_identity_user(sln_settings.service_user, service_identity)
            deferred.defer(_update_tablets, service_identity_user, None, _queue=HIGH_LOAD_CONTROLLER_QUEUE)
    finally:
        users.clear_user()
def _worker(branding_settings):
    if not branding_settings.show_identity_name:
        branding_settings.modification_time = now()
        branding_settings.put()

        service_user = branding_settings.service_user
        with users.set_user(service_user):
            get_and_store_main_branding(service_user)
            system.publish_changes()
示例#16
0
def _create_service_identity(service_user,
                             sln_i_settings,
                             broadcast_to_users=None):

    users.set_user(service_user)
    try:
        si_details = system.get_identity()

        si_details.identifier = sln_i_settings.service_identity
        si_details.name = sln_i_settings.name
        si_details.app_ids_use_default = True
        # the other info will be completed during provision
        system.put_identity(si_details)
        deferred.defer(common_provision,
                       service_user,
                       broadcast_to_users=broadcast_to_users,
                       _queue=FAST_QUEUE)
    finally:
        users.clear_user()
示例#17
0
def _generate_vouchers_qr_codes(service_user, app_id, sln_qr_export_key,
                                voucher_ids):
    tags = list()
    for voucher_id in voucher_ids:
        data = dict(app_id=app_id, voucher_id=voucher_id)
        info = json.dumps(data).decode('utf8')
        tag = POKE_TAG_CITY_VOUCHER_QR + info
        tags.append(tag)

    users.set_user(service_user)
    try:
        qr_details = qr.bulk_create("City Voucher QR Code", tags, None)
    finally:
        users.clear_user()

    def trans():
        voucher_ancestor_key = SolutionCityVoucher.create_parent_key(app_id)
        vouchers = SolutionCityVoucher.get_by_id(voucher_ids,
                                                 voucher_ancestor_key)
        to_put = list()
        for voucher, qr_detail in zip(vouchers, qr_details):
            voucher.image_uri = qr_detail.image_uri
            voucher.content_uri = qr_detail.content_uri
            _set_search_fields(voucher)
            to_put.append(voucher)

            history = SolutionCityVoucherTransaction(parent=voucher)
            history.created = voucher.created
            history.action = SolutionCityVoucherTransaction.ACTION_CREATED
            history.value = 0
            history.service_user = None
            history.service_identity = None
            to_put.append(history)

        sln_qr_export = db.get(sln_qr_export_key)
        sln_qr_export.ready = True
        to_put.append(sln_qr_export)
        put_in_chunks(to_put)
        channel.send_message(
            service_user,
            'solutions.common.city.vouchers.qrcode_export.updated')

    run_in_xg_transaction(trans)
示例#18
0
    def get(self, city_voucher_id):
        city_voucher_id = long(city_voucher_id)
        service_user = users.get_current_user()

        users.set_user(service_user)
        try:
            identity = system.get_identity()
        except:
            self.abort(500)
            return
        finally:
            users.clear_user()

        app_id = identity.app_ids[0]
        ancestor_key = SolutionCityVoucherQRCodeExport.create_parent_key(
            app_id)
        sln_qr_export = SolutionCityVoucherQRCodeExport.get_by_id(
            city_voucher_id, parent=ancestor_key)
        if not sln_qr_export:
            self.abort(500)
            return

        Export = namedtuple('Export', 'url uid')

        result = dict()
        voucher_ancestor_key = SolutionCityVoucher.create_parent_key(app_id)
        vouchers = SolutionCityVoucher.get_by_id(sln_qr_export.voucher_ids,
                                                 parent=voucher_ancestor_key)
        for voucher in vouchers:
            result[voucher.uid] = Export(voucher.content_uri, voucher.uid)

        self.response.headers['Content-Type'] = 'application/vnd.ms-excel'
        self.response.headers['Content-Disposition'] = str(
            'attachment; filename=city_vouchers_%s.csv' %
            sln_qr_export.created)
        writer = csv.writer(self.response.out, dialect='excel')
        for export in result.values():
            writer.writerow(
                (export.url.encode("utf-8"), export.uid.encode("utf-8")))
示例#19
0
def post_to_social_media_scheduled(str_key):
    scheduled_broadcast = SolutionScheduledBroadcast.get(str_key)
    if not scheduled_broadcast or scheduled_broadcast.deleted:
        return

    news_id = scheduled_broadcast.news_id
    on_facebook = scheduled_broadcast.broadcast_on_facebook
    on_twitter = scheduled_broadcast.broadcast_on_twitter
    facebook_access_token = scheduled_broadcast.facebook_access_token

    service_user = scheduled_broadcast.service_user
    with users.set_user(service_user):
        post_to_social_media(service_user, on_facebook, on_twitter,
                             facebook_access_token, news_id)
        scheduled_broadcast.delete()
def export_service_data(service_user, service_identity, result_path):
    stream = StringIO()
    zip_file = zipfile.ZipFile(stream, 'w', zipfile.ZIP_DEFLATED)

    with users.set_user(service_user):
        menu = get_menu(service_identity)
        message_flow_designs = get_service_message_flow_designs(service_user)
        brandings, _ = _list_branding_for_export()
        try:
            _export_brandings(zip_file, service_user, brandings)
            _export_message_flows(zip_file, map(ExportMessageFlowDesignTO.fromMessageFlowDesign, message_flow_designs))
            _export_roles(zip_file)
            _export_menu(zip_file, menu)
        finally:
            zip_file.close()
    stream.seek(0)
    with cloudstorage.open(result_path, 'w') as gcs_file:
        gcs_file.write(stream.getvalue())
示例#21
0
def service_auto_connect(service_user):
    from rogerthat.service.api.friends import invite as invite_api_call
    from rogerthat.bizz.friends import CanNotInviteFriendException

    solution_server_settings = get_solution_server_settings()
    with users.set_user(service_user):
        for invitee in solution_server_settings.solution_service_auto_connect_emails:
            try:
                invite_api_call(invitee, None, u"New Flex service created",
                                DEFAULT_LANGUAGE,
                                SERVICE_AUTOCONNECT_INVITE_TAG, None,
                                App.APP_ID_ROGERTHAT)
            except CanNotInviteFriendException:
                logging.debug('%s is already connected with %s', invitee,
                              service_user)
            except InvalidAppIdException:
                logging.debug('%s is not supported for %s',
                              App.APP_ID_ROGERTHAT, service_user)
示例#22
0
def _provision_without_publish(sln_settings_key):
    service_user = users.User(sln_settings_key.parent().name())
    service_profile = get_service_profile(service_user)
    if not service_profile or service_profile.solution != SOLUTION_FLEX:
        return

    with users.set_user(service_user):
        default_lang = get_default_language()
        sln_settings = get_and_complete_solution_settings(
            service_user, SOLUTION_FLEX)
        put_avatar_if_needed(service_user)
        # Force update branding settings
        branding_settings = SolutionBrandingSettings.get(
            SolutionBrandingSettings.create_key(service_user))
        if not branding_settings:
            return

        if branding_settings.color_scheme == u'dark':
            branding_settings.menu_item_color = SolutionBrandingSettings.default_menu_item_color(
                u'light')

        branding_settings.modification_time = now()
        branding_settings.color_scheme = u'light'
        branding_settings.background_color = SolutionBrandingSettings.default_background_color(
            branding_settings.color_scheme)
        branding_settings.text_color = SolutionBrandingSettings.default_text_color(
            branding_settings.color_scheme)
        branding_settings.show_avatar = False
        branding_settings.put()
        main_branding = get_and_store_main_branding(service_user)

        populate_identity(sln_settings, main_branding.branding_key)

        for i, label in enumerate(['About', 'History', 'Call', 'Recommend']):
            system.put_reserved_menu_item_label(
                i, translate(sln_settings.main_language, SOLUTION_COMMON,
                             label))

        xg_on = db.create_transaction_options(xg=True)
        allow_transaction_propagation(db.run_in_transaction_options, xg_on,
                                      provision_all_modules, sln_settings,
                                      DEFAULT_COORDS, main_branding,
                                      default_lang)
示例#23
0
def _find_voucher(url, app_ids):
    poke_information = None
    city_service_user = None
    for app_id in app_ids:
        for city_service_user in get_service_users_for_city(app_id):
            with users.set_user(city_service_user):
                try:
                    poke_information = messaging.poke_information(url)
                    if poke_information:
                        break
                except InvalidURLException:
                    break
        else:
            continue  # we did not break

        break
    else:
        raise Exception("city_service_user not found for app_ids: %s" %
                        app_ids)

    return poke_information, city_service_user
 def wrapped(service_user, **kwargs):
     users.set_user(service_user)
     return f(**kwargs)
示例#25
0
def _7000_migrate_solution(job_key):
    phase = MigrateServiceJob.PHASE_7000_MIGRATE_SOLUTION
    next_phase = MigrateServiceJob.PHASE_8000_FINISH_MODELS

    # Validate that the job still exists
    job = _get_job(job_key, phase)

    # Do the work
    _log_progress(job)

    if job.solution:
        logging.info('0/ Migrate the solution models without ancestor, but with service_user as key name.')

        def trans0():
            new_models = list()
            old_models = list()

            for model_class in (SolutionMainBranding, SolutionLogo, SolutionAvatar):
                old_model = db.get(model_class.create_key(job.from_service_user))
                if old_model:
                    kwargs = copy_model_properties(old_model)
                    new_model = model_class(key=model_class.create_key(job.to_service_user),
                                            **kwargs)
                    new_models.append(new_model)
                    old_models.append(old_model)

            if new_models:
                put_and_invalidate_cache(*new_models)
            if old_models:
                db.delete(old_models)

        xg_on = db.create_transaction_options(xg=True)
        db.run_in_transaction_options(xg_on, trans0)

        old_sln_settings = get_solution_settings(job.from_service_user)
        identities = [None]
        if old_sln_settings.identities:
            identities.extend(old_sln_settings.identities)

        logging.info('1/ Migrate RestaurantReservations')
        for service_identity in identities:
            from_si_user = create_service_identity_user_wo_default(job.from_service_user, service_identity)
            to_si_user = create_service_identity_user_wo_default(job.to_service_user, service_identity)
            while True:
                reservations = RestaurantReservation.all().filter('service_user', from_si_user).fetch(300)
                if not reservations:
                    break
                for r in reservations:
                    r.service_user = to_si_user
                _put_and_invalidate_cache_and_allocate_ids(*reservations)

        logging.info('2/ Migrate EventReminders')
        for service_identity in identities:
            from_si_user = create_service_identity_user(job.from_service_user)
            to_si_user = create_service_identity_user(job.to_service_user)
            while True:
                reminders = EventReminder.all().filter('service_identity_user', from_si_user).fetch(300)
                if not reminders:
                    break
                for r in reminders:
                    r.service_identity_user = to_si_user
                _put_and_invalidate_cache_and_allocate_ids(*reminders)

        logging.info('3/ Migrate RestaurantProfile.reserve_flow_part2')
        restaurant_profile = db.get(RestaurantProfile.create_key(job.from_service_user))
        if restaurant_profile and restaurant_profile.reserve_flow_part2:
            old_part2_key = db.Key(restaurant_profile.reserve_flow_part2)
            new_part2_key = db.Key.from_path(old_part2_key.kind(), old_part2_key.id_or_name(),
                                             parent=parent_key(job.to_service_user))
            logging.debug('New key: %r', new_part2_key)
            restaurant_profile.reserve_flow_part2 = str(new_part2_key)
            _put_and_invalidate_cache_and_allocate_ids(restaurant_profile)

        logging.info('4/ Delete all SolutionQRs. They will be recreated when provisioning.')
        for service_identity in identities:
            service_identity_user = create_service_identity_user_wo_default(job.from_service_user, service_identity)
            db.delete(SolutionQR.all(keys_only=True).ancestor(parent_key_unsafe(service_identity_user, job.solution)))

        logging.info('5/ Delete Loyalty QR. It will be recreated when provisioning.')
        if SolutionModule.LOYALTY in old_sln_settings.modules:
            for service_identity in identities:
                if is_default_service_identity(service_identity):
                    loyalty_i_settings = db.get(SolutionLoyaltySettings.create_key(job.from_service_user))
                else:
                    loyalty_i_settings = db.get(
                        SolutionLoyaltyIdentitySettings.create_key(job.from_service_user, service_identity))
                if loyalty_i_settings:
                    loyalty_i_settings.image_uri = None
                    loyalty_i_settings.content_uri = None
                    _put_and_invalidate_cache_and_allocate_ids(loyalty_i_settings)

        logging.info('6/ Change ancestor keys of solution models.')
        for solution in ['common', job.solution]:
            for service_identity in identities:
                if is_default_service_identity(service_identity):
                    _migrate_ancestor_models(job, parent_key(job.from_service_user, solution))
                else:
                    service_identity_user = create_service_identity_user_wo_default(
                        job.from_service_user, service_identity)
                    _migrate_ancestor_models(job, parent_key_unsafe(service_identity_user, solution))

        sln_settings = get_solution_settings(job.to_service_user)
        sln_main_branding = get_solution_main_branding(job.to_service_user)
        users.set_user(job.to_service_user)
        try:
            populate_identity(sln_settings, sln_main_branding.branding_key)
        finally:
            users.clear_user()

    # Set the next phase
    _set_job_in_next_phase(job_key, phase, next_phase)
示例#26
0
def put_news_item(service_identity_user,
                  title,
                  message,
                  broadcast_type,
                  sponsored,
                  image,
                  action_button,
                  order_items,
                  news_type,
                  qr_code_caption,
                  app_ids,
                  scheduled_at,
                  news_id=None,
                  broadcast_on_facebook=False,
                  broadcast_on_twitter=False,
                  facebook_access_token=None,
                  target_audience=None,
                  role_ids=None,
                  host=None):
    """
    Creates a news item first then processes the payment if necessary (not necessary for non-promoted posts).
    If the payment was unsuccessful it will be retried in a deferred task.

    Args:
        service_identity_user (users.User)
        title (unicode)
        message (unicode)
        broadcast_type (unicode)
        sponsored (bool)
        image (unicode)
        action_button (NewsActionButtonTO)
        order_items (list of OrderItemTO)
        news_type (int)
        qr_code_caption (unicode)
        app_ids (list of unicode)
        scheduled_at (long)
        news_id (long): id of the news item to update. When not provided a new news item will be created.
        broadcast_on_facebook (bool)
        broadcast_on_twitter (bool)
        facebook_access_token (unicode): user or page access token
        target_audience (NewsTargetAudienceTO)
        role_ids (list of long) the list of role ids to filter sending the news to their members
        host (unicode): host of the api request (used for social media apps)

    Returns:
        news_item (NewsBroadcastItemTO)
    """
    if not order_items or order_items is MISSING:
        order_items = []
    if news_type == NewsItem.TYPE_QR_CODE:
        sln_settings = get_solution_settings(
            get_service_user_from_service_identity_user(service_identity_user))
        azzert(SolutionModule.LOYALTY in sln_settings.modules)
    sponsored_until = None
    should_save_coupon = news_type == NewsItem.TYPE_QR_CODE and not news_id
    sponsored_app_ids = set()
    extra_app_ids = []
    si = get_service_identity(service_identity_user)
    for order_item in reversed(order_items):
        if order_item.product == Product.PRODUCT_NEWS_PROMOTION and sponsored:
            azzert(order_item.app_id)
            azzert(order_item.app_id not in sponsored_app_ids)
            sponsored_app_ids.add(order_item.app_id)
            order_item.count = get_sponsored_news_count_in_app(
                service_identity_user, order_item.app_id).count
        elif order_item.product == Product.PRODUCT_EXTRA_CITY:
            azzert(order_item.app_id)
            azzert(order_item.app_id not in extra_app_ids)
            extra_app_ids.append(order_item.app_id)
            if order_item.app_id in si.appIds:
                order_items.remove(order_item)
        else:
            raise BusinessException('Invalid product %s' % order_item.product)

    if not news_id and not app_ids:
        raise BusinessException(
            'Please select at least one app to publish this news in')
    if sponsored:
        sponsored_until_date = datetime.datetime.utcnow() + datetime.timedelta(
            days=SPONSOR_DAYS)
        sponsored_until = long(sponsored_until_date.strftime('%s'))
        # for sponsored news that is free in certain apps no order item is given, so add it here
        sponsored_counts = get_sponsored_news_count(service_identity_user,
                                                    app_ids)
        for sponsored_count in sponsored_counts:
            if sponsored_count.remaining_free != 0 and sponsored_count.app_id in app_ids:
                sponsored_app_ids.add(sponsored_count.app_id)
        app_ids = list(sponsored_app_ids)

    service_user, identity = get_service_identity_tuple(service_identity_user)
    default_app = get_app(si.defaultAppId)
    if App.APP_ID_ROGERTHAT in si.appIds and App.APP_ID_ROGERTHAT not in app_ids:
        app_ids.append(App.APP_ID_ROGERTHAT)
    if default_app.demo and App.APP_ID_ROGERTHAT in app_ids:
        app_ids.remove(App.APP_ID_ROGERTHAT)
    kwargs = {
        'sticky': sponsored,
        'sticky_until': sponsored_until,
        'message': message,
        'broadcast_type': broadcast_type,
        'service_identity': identity,
        'news_id': news_id,
        'app_ids': app_ids,
        'image': image,
        'scheduled_at': scheduled_at,
        'target_audience': target_audience,
        'role_ids': role_ids
    }
    if not news_id:
        kwargs['news_type'] = news_type

    if news_type == NewsItem.TYPE_QR_CODE:
        if should_save_coupon:

            def trans():
                coupon = NewsCoupon(
                    parent=NewsCoupon.create_parent_key(service_identity_user),
                    content=qr_code_caption)
                coupon.put()
                return coupon

            coupon = db.run_in_transaction(trans)
            kwargs['qr_code_content'] = u'%s' % json.dumps({'c': coupon.id})
        kwargs['qr_code_caption'] = qr_code_caption
    elif news_type == NewsItem.TYPE_NORMAL:
        kwargs.update({
            'action_buttons': [action_button] if action_button else [],
            'title':
            title
        })
    else:
        raise BusinessException('Invalid news type')
    for key, value in kwargs.items():
        if value is MISSING:
            del kwargs[key]

    with users.set_user(service_user):
        try:

            def trans():
                news_item = news.publish(accept_missing=True, **kwargs)
                if should_save_coupon:
                    _save_coupon_news_id(news_item.id, coupon)
                elif news_type == NewsItem.TYPE_QR_CODE and qr_code_caption is not MISSING and qr_code_caption and news_id:
                    news_coupon = NewsCoupon.get_by_news_id(
                        service_identity_user, news_id)
                    if news_coupon:
                        news_coupon.content = qr_code_caption
                        news_coupon.put()
                    else:
                        logging.warn(
                            'Not updating qr_code_caption for non-existing coupon for news with id %d',
                            news_id)
                if order_items:
                    create_and_pay_news_order(service_user, news_item.id,
                                              order_items)
                return news_item

            news_item = run_in_xg_transaction(trans)
            if broadcast_on_facebook or broadcast_on_twitter:
                if scheduled_at is not MISSING and scheduled_at > 0:
                    schedule_post_to_social_media(service_user, host,
                                                  broadcast_on_facebook,
                                                  broadcast_on_twitter,
                                                  facebook_access_token,
                                                  news_item.id, scheduled_at)
                else:
                    post_to_social_media(service_user, broadcast_on_facebook,
                                         broadcast_on_twitter,
                                         facebook_access_token, news_item.id)

            return NewsBroadcastItemTO.from_news_item_to(
                news_item, broadcast_on_facebook, broadcast_on_twitter)
        except:
            if should_save_coupon:
                db.delete_async(coupon)
            raise
示例#27
0
def _8000_finish_models(job_key):
    phase = MigrateServiceJob.PHASE_8000_FINISH_MODELS
    next_phase = MigrateServiceJob.PHASE_9000_RECONNECT_FRIENDS

    # Validate that the job still exists
    job = _get_job(job_key, phase)

    # Do the work
    _log_progress(job)

    try:
        logging.info("1/ re_index all service identities asynchronously")
        if not is_trial_service(job.to_service_user):
            run_job(get_service_identities_query, [job.to_service_user, True],
                    _re_index_service_identity, [])

        logging.info("2/ set solution and enabled on the new ServiceProfile")

        def trans2():
            to_service_profile = get_service_profile(job.to_service_user)
            to_service_profile.enabled = job.service_enabled
            to_service_profile.solution = job.solution
            to_service_profile.put()
        db.run_in_transaction(trans2)

        logging.info("3/ couple the service to the customer")
        if job.customer_key:
            def trans3():
                customer = db.get(job.customer_key)
                customer.service_email = job.to_service_user.email()
                customer.migration_job = None
                customer.put()

                deferred.defer(re_index_customer, db.Key(job.customer_key),
                               _queue=MIGRATION_QUEUE, _transactional=True)
            db.run_in_transaction(trans3)
        else:
            logging.debug('There was no customer')

        logging.info("4/ grant service roles")
        service_grants = job.get_service_grants()  # { app_user : { si_user : [roles] } }
        for app_user_email, service_grant_dict in service_grants.iteritems():
            def trans4():
                p = get_user_profile(users.User(app_user_email), cached=False)
                for old_si_email, service_roles in service_grant_dict.iteritems():
                    old_si_user = users.User(old_si_email)
                    new_si_user = create_service_identity_user(job.to_service_user,
                                                               get_identity_from_service_identity_user(old_si_user))
                    for role in service_roles:
                        logging.debug("Granting role %s to %s for %s", role, app_user_email, new_si_user)
                        p.grant_role(new_si_user, role)
                p.put()
            db.run_in_transaction(trans4)

        logging.debug("5/ finish solution models")
        if job.solution:
            sln_settings = get_solution_settings(job.to_service_user)
            main_branding = get_solution_main_branding(job.to_service_user)
            users.set_user(job.to_service_user)
            try:
                if SolutionModule.LOYALTY in sln_settings.modules:
                    logging.debug('- Provisioning %s', SolutionModule.LOYALTY)
                    put_loyalty(sln_settings, None, main_branding, sln_settings.main_language, None)

                if SolutionModule.QR_CODES in sln_settings.modules:
                    logging.debug('- Provisioning %s', SolutionModule.QR_CODES)
                    put_qr_codes(sln_settings, None, main_branding, sln_settings.main_language, None)

                logging.debug('- Creating QR codes for calendar admins')
                calendars_to_put = []
                for sc in SolutionCalendar.all().ancestor(parent_key(sln_settings.service_user, sln_settings.solution)).filter("deleted =", False):
                    qr_code = create_calendar_admin_qr_code(sc, main_branding.branding_key, sln_settings.main_language)
                    sc.connector_qrcode = qr_code.image_uri
                    calendars_to_put.append(sc)
                if calendars_to_put:
                    db.put(calendars_to_put)

                logging.debug('- Creating QR codes for inbox forwarding')
                flow_identifier = create_inbox_forwarding_flow(main_branding.branding_key, sln_settings.main_language)
                qrcode = create_inbox_forwarding_qr_code(None, flow_identifier)
                sln_settings.inbox_connector_qrcode = qrcode.image_uri
                if sln_settings.identities:
                    for service_identity in sln_settings.identities:
                        qrcode = create_inbox_forwarding_qr_code(service_identity, flow_identifier)

                        def trans():
                            sln_i_settings = get_solution_identity_settings(sln_settings.service_user, service_identity)
                            sln_i_settings.inbox_connector_qrcode = qrcode.image_uri
                            sln_i_settings.put()
                        db.run_in_transaction(trans)
                put_and_invalidate_cache(sln_settings)
            finally:
                users.clear_user()
        else:
            logging.debug("The service was not a solution")
    except BusinessException, e:
        logging.exception("Caught BusinessException")
        azzert(False, "Caught BusinessException: %s" % e)
示例#28
0
def _broadcast(sln_settings_key, sandwich_settings_key):
    sln_settings, sandwich_settings = db.get(
        [sln_settings_key, sandwich_settings_key])
    if not sln_settings:
        logging.info("Service has been deleted in the meantime")
        return
    solution_datetime = datetime.now(pytz.timezone(sln_settings.timezone))
    if not sandwich_settings.can_order_sandwiches_on(solution_datetime):
        logging.info("No email_reminders anymore today for %s",
                     sln_settings.service_user.email())
        return
    broadcast_type = get_sandwich_reminder_broadcast_type(
        sln_settings.main_language or DEFAULT_LANGUAGE,
        SandwichSettings.DAYS[solution_datetime.weekday()])
    message = sandwich_settings.reminder_broadcast_message
    order_sandwich_answer = AnswerTO()
    order_sandwich_answer.action = None
    order_sandwich_answer.caption = translate(sln_settings.main_language,
                                              SOLUTION_COMMON, u'order')
    order_sandwich_answer.type = u'button'
    order_sandwich_answer.id = u'order'
    order_sandwich_answer.ui_flags = 1
    no_sandwich_today_answer = AnswerTO()
    no_sandwich_today_answer.action = None
    no_sandwich_today_answer.caption = translate(
        sln_settings.main_language, SOLUTION_COMMON,
        u'order-sandwiches-not-today')
    no_sandwich_today_answer.type = u'button'
    no_sandwich_today_answer.id = u'Not now'
    no_sandwich_today_answer.ui_flags = 0
    answers = list()
    answers.append(order_sandwich_answer)
    answers.append(no_sandwich_today_answer)
    flags = 0
    branding = db.get(
        SolutionMainBranding.create_key(
            sln_settings.service_user)).branding_key
    tag = MESSAGE_TAG_SANDWICH_ORDER_NOW
    alert_flags = 0
    timeout = sandwich_settings.get_reminder_broadcast_timeout(
        solution_datetime)

    users.set_user(sln_settings.service_user)
    try:
        identities = [None]
        if sln_settings.identities:
            identities.extend(sln_settings.identities)
        for service_identity in identities:
            sln_i_settings = get_solution_settings_or_identity_settings(
                sln_settings, service_identity)
            if sln_i_settings.is_in_holiday_for_date(now()):
                logging.info(
                    "Not sending out sandwich broadcast '%s'. %s is in holiday.",
                    broadcast_type, sln_i_settings.service_user)
            else:
                logging.info(
                    "Sending broadcast to users of %s with broadcast type %s",
                    sln_i_settings.service_user, broadcast_type)
                messaging.broadcast(broadcast_type,
                                    message,
                                    answers,
                                    flags,
                                    branding,
                                    tag,
                                    service_identity,
                                    alert_flags,
                                    timeout=timeout)
    finally:
        users.clear_user()
示例#29
0
def _pick_city_wide_lottery_winner(service_user, sln_cwl_lottery_key):
    sln_cwl = db.get(sln_cwl_lottery_key)
    if sln_cwl.winners:
        return
    slls = db.get(SolutionCityWideLotteryStatistics.create_key(sln_cwl.app_id))
    logging.info("city wide lottery loot: %s", sln_cwl.app_id)
    possible_winners = []

    if slls:
        for i, app_user in enumerate(slls.app_users):
            if app_user not in sln_cwl.skip_winners and app_user not in sln_cwl.winners:
                for i in xrange(slls.count[i]):
                    possible_winners.append(app_user)

    logging.debug("possible winners count: %s", len(possible_winners))

    if len(possible_winners) == 0:
        if sln_cwl.winners:
            logging.debug("can not assign winners, keep old")
        else:
            logging.debug("can not assign winners, delete city wide lottery")
            sln_cwl.deleted = True
            sln_cwl.put()
        return
    else:
        winners_needed = sln_cwl.x_winners
        logging.debug("winners_needed: %s", winners_needed)
        if len(possible_winners) < winners_needed:
            winners_needed = len(possible_winners)

        winners = []
        while True:
            if not possible_winners:
                break
            if len(winners) >= winners_needed:
                break
            winner = random.choice(possible_winners)
            possible_winners = filter(lambda a: a != winner, possible_winners)
            winners.append(winner)

    sln_settings = get_solution_settings(service_user)
    winners_info = []
    slvl_parent_key = SolutionCityWideLotteryVisit.create_city_parent_key(
        sln_cwl.app_id)
    winner_text = ""
    for winner in winners:
        slvl = SolutionCityWideLotteryVisit.all() \
            .ancestor(slvl_parent_key) \
            .filter('redeemed =', False) \
            .filter('app_user ='******'t use get_profile_infos
            profile_info = get_profile_infos([slvl.app_user],
                                             allow_none_in_results=True)[0]
            if not profile_info or profile_info.isServiceIdentity:
                continue
            else:
                eud = ExtendedUserDetailsTO.fromUserProfile(profile_info, None)

        with set_user(service_user):
            app_info = get_app_info_cached(eud.app_id)
        eud.app_name = app_info.name
        eud.public_key = None
        eud.public_keys = []
        winners_info.append(eud)

        winner_text = winner_text + "\n - %s (%s)" % (eud.name, eud.email)

    def trans():
        sln_cwl.pending = False
        sln_cwl.winners = winners
        sln_cwl.winners_info = json.dumps(
            serialize_complex_value(winners_info, ExtendedUserDetailsTO, True))
        sln_cwl.put()
        deferred.defer(_redeem_city_wide_lottery_visits,
                       service_user,
                       sln_cwl_lottery_key,
                       now(),
                       _transactional=True)

        to_emails = sln_settings.inbox_mail_forwarders
        if to_emails:
            solution_server_settings = get_solution_server_settings()
            subject = 'Winnaars gemeentelijke tombola'
            body = """Beste,
Volgende mensen hebben gewonnen met de tombola: %s


Met vriendelijke groeten,

Het Onze Stad App Team
""" % winner_text

            send_mail(solution_server_settings.shop_export_email, to_emails,
                      subject, body)

    xg_on = db.create_transaction_options(xg=True)
    db.run_in_transaction_options(xg_on, trans)
示例#30
0
def delete_static_content(service_user, static_content_id):
    users.set_user(service_user)
    try:
        _delete_static_content(service_user, static_content_id)
    finally:
        users.clear_user()
示例#31
0
def create_voucher_statistics_for_city_service(service_user, language,
                                               first_day_of_last_month,
                                               first_day_of_current_month):
    customer = Customer.get_by_service_email(service_user.email())
    translate = partial(common_translate, language, SOLUTION_COMMON)
    if not customer:
        logging.error("failed to create voucher statistics customer not found")
        return
    sln_settings = get_solution_settings(service_user)

    users.set_user(service_user)
    try:
        si = system.get_identity()
    finally:
        users.clear_user()
    app_id = si.app_ids[0]
    ancestor_key = SolutionCityVoucher.create_parent_key(app_id)

    qry = SolutionCityVoucherTransaction.all().ancestor(ancestor_key)
    qry.filter("action =", SolutionCityVoucherTransaction.ACTION_REDEEMED)
    qry.filter("created >=", first_day_of_last_month)
    qry.filter("created <", first_day_of_current_month)

    transactions = []
    merchant_transactions = dict()
    merchants = dict()
    unique_vouchers = dict()
    for t in qry:
        t.dt = format_timestamp(t.created, sln_settings)
        t.voucher = t.parent()
        transactions.append(t)
        if t.service_user not in merchant_transactions:
            merchant_transactions[t.service_user] = {
                "value": 0,
                "transactions": []
            }
        merchant_transactions[t.service_user]["value"] += t.value
        merchant_transactions[t.service_user]["transactions"].append(t.key())
        unique_vouchers[t.voucher.key()] = t.voucher

    for merchant_service_user in merchant_transactions.keys():
        merchants[merchant_service_user] = get_solution_settings(
            merchant_service_user)

    qry = SolutionCityVoucher.all().ancestor(ancestor_key)
    qry.filter("activated =", True)
    qry.filter("redeemed = ", False)
    vouchers = []
    expired_vouchers = []
    for v in qry:
        v.dt = format_timestamp(v.created, sln_settings)
        if v.expired:
            if v.expiration_date >= first_day_of_last_month and \
               v.expiration_date < first_day_of_current_month:
                expired_vouchers.append(v)
        else:
            vouchers.append(v)

    book = xlwt.Workbook(encoding="utf-8")

    # TAB 1
    sheet_transactions = book.add_sheet(translate("Transactions"))
    row = 0
    write_header(sheet_transactions, row, translate, "Date", "Voucher",
                 "Internal account", "Cost center", "merchant",
                 "Withdrawn value")

    for transaction in transactions:
        row += 1
        sheet_transactions.write(row, 0, transaction.dt)
        sheet_transactions.write(row, 1, transaction.voucher.uid)
        sheet_transactions.write(row, 2, transaction.voucher.internal_account)
        sheet_transactions.write(row, 3, transaction.voucher.cost_center)
        sheet_transactions.write(row, 4,
                                 merchants[transaction.service_user].name)
        sheet_transactions.write(row, 5, round(transaction.value / 100.0, 2))
    row += 2
    sheet_transactions.write(row, 0, translate("total"))
    sheet_transactions.write(row, 5, xlwt.Formula('SUM(F2:F%s)' % (row - 1)))

    # TAB 2
    sheet_merchants = book.add_sheet(translate("merchants"))
    row = 0
    sheet_merchants.write(row, 0, translate("merchant"))
    sheet_merchants.write(row, 1, translate("address"))
    sheet_merchants.write(row, 2, "IBAN")
    sheet_merchants.write(row, 3, "BIC")
    sheet_merchants.write(row, 4, translate("Total value to be paid"))
    for merchant_service_user in merchants.keys():
        merchant = merchants[merchant_service_user]
        row += 1
        sheet_merchants.write(row, 0, merchant.name)
        sheet_merchants.write(row, 1, merchant.address)
        sheet_merchants.write(row, 2, merchant.iban or u"")
        sheet_merchants.write(row, 3, merchant.bic or u"")
        sheet_merchants.write(
            row, 4,
            round(
                merchant_transactions[merchant_service_user]["value"] / 100.0,
                2))

    row += 2
    sheet_merchants.write(row, 0, translate("total"))
    sheet_merchants.write(row, 4, xlwt.Formula('SUM(E2:E%s)' % (row - 1)))

    # TAB 3
    sheet_vouchers = book.add_sheet(translate("Vouchers in circulation"))
    row = 0
    write_header(sheet_vouchers, row, translate, "Voucher", "Internal account",
                 "Cost center", "Date", "Remaining value")

    for voucher in vouchers:
        unique_vouchers[voucher.key()] = voucher
        row += 1
        sheet_vouchers.write(row, 0, voucher.uid)
        sheet_vouchers.write(row, 1, voucher.internal_account)
        sheet_vouchers.write(row, 2, voucher.cost_center)
        sheet_vouchers.write(row, 3, voucher.dt)
        value = voucher.value - voucher.redeemed_value
        sheet_vouchers.write(row, 4, round(value / 100.0, 2))

    row += 2
    sheet_vouchers.write(row, 0, translate("total"))
    sheet_vouchers.write(row, 2, xlwt.Formula('SUM(E2:E%s)' % (row - 1)))

    # TAB 4
    expired_vouchers_sheet = book.add_sheet(translate("expired"))
    row = 0
    write_header(expired_vouchers_sheet, row, translate, "Voucher",
                 "Internal account", "Cost center", "Date", "Expiration date",
                 "Remaining value")

    for voucher in expired_vouchers:
        row += 1
        expired_vouchers_sheet.write(row, 0, voucher.uid)
        expired_vouchers_sheet.write(row, 1, voucher.internal_account)
        expired_vouchers_sheet.write(row, 2, voucher.cost_center)
        expired_vouchers_sheet.write(
            row, 3, format_timestamp(voucher.created, sln_settings))
        expired_vouchers_sheet.write(
            row, 4,
            format_timestamp(voucher.expiration_date,
                             sln_settings,
                             format='yyyy-MM-dd'))
        value = voucher.value - voucher.redeemed_value
        expired_vouchers_sheet.write(row, 5, round(value / 100.0, 2))

    row += 2
    expired_vouchers_sheet.write(row, 0, translate("total"))
    expired_vouchers_sheet.write(row, 5,
                                 xlwt.Formula('SUM(F2:F%s)' % (row - 1)))

    # TAB 5
    sheet_voucher_details = book.add_sheet(translate("Voucher details"))
    row = 0
    for voucher in sorted(unique_vouchers.itervalues(),
                          key=lambda v: v.created):
        voucher_transactions = [h for h in voucher.load_transactions()]
        sheet_voucher_details.write(row, 0, translate("Voucher"))
        sheet_voucher_details.write(row, 1, voucher.uid)
        sheet_voucher_details.write(row, 2, translate("Remaining value"))
        sheet_voucher_details.write(
            row, 3,
            xlwt.Formula('SUM(D%s:D%s)' %
                         (row + 2, row + 1 + len(voucher_transactions))))

        row += 1
        sheet_voucher_details.write(row, 0, translate("Internal account"))
        sheet_voucher_details.write(row, 1, voucher.internal_account)
        sheet_voucher_details.write(row, 2, translate("Cost center"))
        sheet_voucher_details.write(row, 3, voucher.cost_center)

        for history in reversed(voucher_transactions):
            merchant_service_user = history.service_user or service_user
            if merchant_service_user not in merchants:
                merchants[merchant_service_user] = get_solution_settings(
                    merchant_service_user)

            row += 1
            dt = format_timestamp(history.created, sln_settings)

            sheet_voucher_details.write(row, 0, dt)
            sheet_voucher_details.write(row, 1,
                                        merchants[merchant_service_user].name)
            sheet_voucher_details.write(row, 2, history.action_str)
            if history.action == SolutionCityVoucherTransaction.ACTION_ACTIVATED or \
                    history.action == SolutionCityVoucherTransaction.ACTION_REDEEMED:
                sheet_voucher_details.write(row, 3,
                                            round(history.value / 100.0, 2))

        row += 2

    excel_file = StringIO()
    book.save(excel_file)
    excel_string = excel_file.getvalue()

    second_day_of_last_month = first_day_of_last_month + 86400
    d = datetime.fromtimestamp(second_day_of_last_month)

    sln_city_voucher_export_key = SolutionCityVoucherExport.create_key(
        app_id, d.year, d.month)
    sln_city_voucher_export = SolutionCityVoucherExport(
        key=sln_city_voucher_export_key)
    sln_city_voucher_export.xls = excel_string
    sln_city_voucher_export.year_month = d.year * 100 + d.month
    sln_city_voucher_export.put()

    for merchant_service_user in merchant_transactions.keys():
        deferred.defer(
            create_voucher_statistics_for_service,
            merchants[merchant_service_user], app_id, customer.language,
            merchant_transactions[merchant_service_user]["transactions"],
            d.year, d.month)

    to_emails = sln_settings.inbox_mail_forwarders
    if to_emails:
        solution_server_settings = get_solution_server_settings()
        attachments = []
        attachments.append(
            ('%s %s-%s.xls' % (translate('Vouchers'), d.year, d.month),
             base64.b64encode(excel_string)))
        subject = translate('Vouchers export')
        message = translate('see_attachment_for_vouchers_export')
        send_mail(solution_server_settings.shop_export_email,
                  to_emails,
                  subject,
                  message,
                  attachments=attachments)