示例#1
0
 def propagate_contact_details(self, ar=None):
     ContactDetailTypes = rt.models.phones.ContactDetailTypes
     watcher = ChangeWatcher(self)
     for cdt in ContactDetailTypes.get_list_items():
         self.propagate_contact_detail(cdt)
     if ar is not None:
         watcher.send_update(ar)
示例#2
0
文件: mixins.py 项目: einarfelix/xl
 def propagate_contact_details(self, ar=None):
     ContactDetailTypes = rt.models.phones.ContactDetailTypes
     watcher = ChangeWatcher(self)
     for cdt in ContactDetailTypes.get_list_items():
         self.propagate_contact_detail(cdt)
     if ar is not None:
         watcher.send_update(ar)
示例#3
0
文件: models.py 项目: khchine5/xl
 def after_ui_save(self, ar, cw):
     super(Account, self).after_ui_save(ar, cw)
     if self.primary:
         mi = self.partner
         for o in mi.sepa_accounts.exclude(id=self.id):
             if o.primary:
                 o.primary = False
                 o.save()
                 ar.set_response(refresh_all=True)
         watcher = ChangeWatcher(mi)
         for k in PRIMARY_FIELDS:
             setattr(mi, k, getattr(self, k))
         mi.save()
         watcher.send_update(ar)
示例#4
0
 def after_ui_save(self, ar, cw):
     super(Account, self).after_ui_save(ar, cw)
     if self.primary:
         mi = self.partner
         for o in mi.sepa_accounts.exclude(id=self.id):
             if o.primary:
                 o.primary = False
                 o.save()
                 ar.set_response(refresh_all=True)
         watcher = ChangeWatcher(mi)
         for k in PRIMARY_FIELDS:
             setattr(mi, k, getattr(self, k))
         mi.save()
         watcher.send_update(ar.request)
示例#5
0
    def set_workflow_state(row, ar, state_field, target_state):
        """Called by workflow actions (:class:`ChangeStateAction
        <lino.core.workflows.ChangeStateAction>`) to perform the
        actual state change.

        """
        watcher = ChangeWatcher(row)
        old = getattr(row, state_field.attname)
        target_state.choicelist.before_state_change(row, ar, old, target_state)
        row.before_state_change(ar, old, target_state)
        setattr(row, state_field.attname, target_state)
        row.save()
        target_state.choicelist.after_state_change(row, ar, old, target_state)
        row.after_state_change(ar, old, target_state)
        watcher.send_update(ar.request)
        row.after_ui_save(ar, watcher)
示例#6
0
文件: model.py 项目: khchine5/lino
    def set_workflow_state(row, ar, state_field, target_state):
        """Called by workflow actions (:class:`ChangeStateAction
        <lino.core.workflows.ChangeStateAction>`) to perform the
        actual state change.

        """
        watcher = ChangeWatcher(row)
        old = getattr(row, state_field.attname)
        target_state.choicelist.before_state_change(row, ar, old, target_state)
        row.before_state_change(ar, old, target_state)
        setattr(row, state_field.attname, target_state)
        row.save()
        target_state.choicelist.after_state_change(row, ar, old, target_state)
        row.after_state_change(ar, old, target_state)
        watcher.send_update(ar.request)
        row.after_ui_save(ar, watcher)
示例#7
0
    def run_from_code(self, ar, **known_values):
        obj = ar.selected_rows[0]
        related = []
        for m, fk in obj._lino_ddh.fklist:
            # print(fk.name, m.allow_cascaded_delete, m.allow_cascaded_copy, obj)
            if fk.name in m.allow_cascaded_delete or fk.name in m.allow_cascaded_copy:
                related.append((fk, m.objects.filter(**{fk.name: obj})))

        if AFTER17:
            fields_list = obj._meta.concrete_fields
        else:
            fields_list = obj._meta.fields
        if True:
            for f in fields_list:
                if not f.primary_key:
                    if f.name not in known_values:
                        known_values[f.name] = getattr(obj, f.name)
            new = obj.__class__(**known_values)
            # 20120704 create_instances causes fill_from_person() on a
            # CBSS request.
        else:
            # doesn't seem to want to work
            new = obj
            for f in fields_list:
                if f.primary_key:
                    # causes Django to consider this an unsaved instance
                    setattr(new, f.name, None)

        new.on_duplicate(ar, None)
        new.save(force_insert=True)
        cw = ChangeWatcher(new)

        for fk, qs in related:
            for relobj in qs:
                relobj.pk = None  # causes Django to save a copy
                setattr(relobj, fk.name, new)
                relobj.on_duplicate(ar, new)
                relobj.save(force_insert=True)

        new.after_duplicate(ar, obj)

        if cw.is_dirty():
            new.full_clean()
            new.save()

        return new
示例#8
0
文件: models.py 项目: forexblog/xl
 def after_ui_save(self, ar, cw):
     super(ContactDetail, self).after_ui_save(ar, cw)
     mi = self.partner
     if mi is None:
         return
     if self.primary and self.detail_type:
         for o in mi.phones_by_partner.exclude(id=self.id).filter(
                 detail_type=self.detail_type):
             if o.primary:
                 o.primary = False
                 o.save()
                 ar.set_response(refresh_all=True)
     k = self.detail_type.field_name
     if k:
         watcher = ChangeWatcher(mi)
         setattr(mi, k, self.value)
         watcher.send_update(ar)
         mi.save()
示例#9
0
 def after_ui_save(self, ar, cw):
     super(ContactDetail, self).after_ui_save(ar, cw)
     mi = self.partner
     if mi is None:
         return
     if self.primary and self.detail_type:
         for o in mi.phones_by_partner.exclude(id=self.id).filter(
                 detail_type=self.detail_type):
             if o.primary:
                 o.primary = False
                 o.save()
                 ar.set_response(refresh_all=True)
     k = self.detail_type.field_name
     if k:
         watcher = ChangeWatcher(mi)
         setattr(mi, k, self.value)
         watcher.send_update(ar)
         mi.save()
示例#10
0
    def run_from_code(self, ar, **known_values):
        obj = ar.selected_rows[0]
        related = []
        for m, fk in obj._lino_ddh.fklist:
            # print(fk.name, m.allow_cascaded_delete, m.allow_cascaded_copy, obj)
            if fk.name in m.allow_cascaded_delete or fk.name in m.allow_cascaded_copy:
                related.append((fk, m.objects.filter(**{fk.name: obj})))

        fields_list = obj._meta.concrete_fields
        if True:
            for f in fields_list:
                if not f.primary_key:
                    if f.name not in known_values:
                        known_values[f.name] = getattr(obj, f.name)
            new = obj.__class__(**known_values)
            # 20120704 create_instances causes fill_from_person() on a
            # CBSS request.
        else:
            # doesn't seem to want to work
            new = obj
            for f in fields_list:
                if f.primary_key:
                    # causes Django to consider this an unsaved instance
                    setattr(new, f.name, None)

        new.on_duplicate(ar, None)
        new.save(force_insert=True)
        cw = ChangeWatcher(new)

        for fk, qs in related:
            for relobj in qs:
                relobj.pk = None  # causes Django to save a copy
                setattr(relobj, fk.name, new)
                relobj.on_duplicate(ar, new)
                relobj.save(force_insert=True)

        new.after_duplicate(ar, obj)

        if cw.is_dirty():
            new.full_clean()
            new.save()
            
        return new
示例#11
0
 def PUT(self, **kw):
     # ~ dblogger.info("%s.PUT(%s)",self.__class__.__name__,kw)
     obj = self.get_object(kw)
     if obj is None:
         if self.allow_put2post:
             dblogger.info("%s:%s : PUT becomes POST", kw["alias"], kw["id"])
             kw["method"] = "POST"
             return self.POST(**kw)
         else:
             dblogger.warning("%s:%s : PUT ignored (row does not exist)", kw["alias"], kw["id"])
             return
     watcher = ChangeWatcher(obj)
     if self.PUT_special(watcher, **kw):
         return
     self.set_timestamp(kw["time"])
     self.applydata(obj, kw["data"])
     dblogger.info("%s:%s (%s) : PUT %s", kw["alias"], kw["id"], dd.obj2str(obj), kw["data"])
     self.validate_and_save(obj)
     watcher.send_update(REQUEST)
示例#12
0
    def form2obj_and_save(ar, data, elem, is_new):
        """
        Parses the data from HttpRequest to the model instance and saves
        it.

        This is used by `ApiList.post` and `ApiElement.put`, and by
        `Restful.post` and `Restful.put`.

        20140505 : no longer used by ApiList and ApiElement, but still
        by Restful.*
        """
        if is_new:
            watcher = None
        else:
            watcher = ChangeWatcher(elem)
        ar.ah.store.form2obj(ar, data, elem, is_new)
        elem.full_clean()

        if is_new or watcher.is_dirty():
            pre_ui_save.send(sender=elem.__class__, instance=elem, ar=ar)
            elem.before_ui_save(ar)

            kw2save = {}
            if is_new:
                kw2save.update(force_insert=True)
            else:
                kw2save.update(force_update=True)

            elem.save(**kw2save)

            if is_new:
                on_ui_created.send(elem, request=ar.request)
                ar.success(_("%s has been created.") % obj2unicode(elem))
            else:
                watcher.send_update(ar)
                ar.success(_("%s has been updated.") % obj2unicode(elem))
        else:
            ar.success(_("%s : nothing to save.") % obj2unicode(elem))

        elem.after_ui_save(ar, watcher)
示例#13
0
文件: requests.py 项目: khchine5/lino
    def form2obj_and_save(ar, data, elem, is_new):
        """Parses the data from HttpRequest to the model instance and saves it

        This is used by `ApiList.post` and `ApiElement.put`, and by
        `Restful.post` and `Restful.put`.

        20140505 : no longer used by ApiList and ApiElement, but still
        by Restful.*

        """
        if is_new:
            watcher = None
        else:
            watcher = ChangeWatcher(elem)
        ar.ah.store.form2obj(ar, data, elem, is_new)
        elem.full_clean()

        if is_new or watcher.is_dirty():
            pre_ui_save.send(sender=elem.__class__, instance=elem, ar=ar)
            elem.before_ui_save(ar)

            kw2save = {}
            if is_new:
                kw2save.update(force_insert=True)
            else:
                kw2save.update(force_update=True)

            elem.save(**kw2save)

            if is_new:
                on_ui_created.send(elem, request=ar.request)
                ar.success(_("%s has been created.") % obj2unicode(elem))
            else:
                watcher.send_update(ar.request)
                ar.success(_("%s has been updated.") % obj2unicode(elem))
        else:
            ar.success(_("%s : nothing to save.") % obj2unicode(elem))

        elem.after_ui_save(ar, watcher)
示例#14
0
    def run_from_ui(self, ar, **kw):
        # replaces the default implementation
        obj = ar.selected_rows[0]
        # obj is a User instance
        client = ar.master_instance
        watcher = ChangeWatcher(client)

        coaching = pcsw.Coaching(
            client=client, user=obj,
            start_date=settings.SITE.today(),
            type=obj.coaching_type)
        coaching.full_clean()
        coaching.save()
        dd.on_ui_created.send(coaching, request=ar.request)
        client.client_state = pcsw.ClientStates.coached
        client.full_clean()
        client.save()
        watcher.send_update(ar.request)

        self.emit_message(ar, client)

        ar.success(ar.action_param_values.notify_body,
                   alert=True, refresh_all=True, **kw)
示例#15
0
 def POST(self, **kw):
     # ~ dblogger.info("%s.POST(%s)",self.__class__.__name__,kw)
     # ~ self.prepare_data(kw['data'])
     obj = self.get_object(kw)
     if obj is None:
         obj = self.create_object(kw)
         if obj is None:
             dblogger.warning("%s:%s (%s) : ignored POST %s", kw["alias"], kw["id"], obj, kw["data"])
             return
         # ~ watcher = changes.Watcher(obj,True)
         self.set_timestamp(kw["time"])
         self.applydata(obj, kw["data"])
         dblogger.info("%s:%s (%s) : POST %s", kw["alias"], kw["id"], dd.obj2str(obj), kw["data"])
         self.validate_and_save(obj)
         dd.on_ui_created.send(sender=obj, request=REQUEST)
         # ~ changes.log_create(REQUEST,obj)
     else:
         watcher = ChangeWatcher(obj)
         dblogger.info("%s:%s : POST becomes PUT", kw["alias"], kw["id"])
         self.set_timestamp(kw["time"])
         self.applydata(obj, kw["data"])
         dblogger.info("%s:%s (%s) : POST %s", kw["alias"], kw["id"], dd.obj2str(obj), kw["data"])
         self.validate_and_save(obj)
         watcher.send_update(REQUEST)
示例#16
0
    def process_row(self, ar, obj, attrs):
        """Generate a confirmation which asks to update the given data row
        `obj` using the data read from the eid card (given in `attr`).

        """
        objects, diffs = obj.get_beid_diffs(attrs)

        if len(diffs) == 0:
            return self.goto_client_response(
                ar, obj,
                _("Client %s is up-to-date") % str(obj))

        oldobj = obj
        watcher = ChangeWatcher(obj)

        msg = _("Click OK to apply the following changes for %s") % obj
        msg = simulate_wrap(msg)
        msg += ' :<br/>'
        # UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 10: ordinal not in range(128)
        diffs = [str(i) for i in diffs]
        diffs = sorted(diffs)
        msg += u'\n<br/>'.join(diffs)

        # msg += u'\n<br/>'.join(sorted(diffs))

        def yes(ar2):
            msg = _("%s has been saved.") % dd.obj2unicode(obj)
            if not dd.plugins.beid.read_only_simulate:
                for o in objects:
                    o.full_clean()
                    o.save()
                watcher.send_update(ar2)
            msg = simulate_wrap(msg)
            return self.goto_client_response(ar2, obj, msg)

        def no(ar2):
            return self.goto_client_response(ar2, oldobj)

        #~ print 20131108, msg
        cb = ar.add_callback(msg)
        cb.add_choice('yes', yes, _("Yes"))
        cb.add_choice('no', no, _("No"))
        ar.set_callback(cb)
示例#17
0
def objects():

    Company = rt.models.contacts.Company

    ar = rt.login(request=PseudoRequest("robin"))

    obj = Company(name="My pub")
    obj.full_clean()
    obj.save_new_instance(ar)

    cw = ChangeWatcher(obj)
    obj.name = "Our pub"
    obj.save_watched_instance(ar, cw)

    obj.delete_instance(ar)

    # this is a special fixture : it creates objects as a side effect
    # but does not yield them.
    return []
示例#18
0
    def test_comment(self, logger):
        """Test what happens when a comment is posted on a ticket with
        watchers.

        """
        ContentType = rt.models.contenttypes.ContentType
        Comment = rt.models.comments.Comment
        Ticket = rt.models.tickets.Ticket
        Project = rt.models.tickets.Project
        Vote = rt.models.votes.Vote
        Message = rt.models.notify.Message
        User = settings.SITE.user_model
        create(Project, name="Project")
        robin = create(
            User, username='******',
            first_name="Robin",
            user_type=UserTypes.admin)
        aline = create(
            User, username='******',
            first_name="Aline",
            email="*****@*****.**", language='fr')
        obj = create(
            Ticket, summary="Save the world, après moi le déluge",
            user=robin)
        create(Vote, votable=obj, user=aline)
        
        self.assertEqual(Message.objects.count(), 0)
        
        url = "/api/comments/CommentsByRFC"
        post_data = dict()
        post_data[constants.URL_PARAM_ACTION_NAME] = 'submit_insert'
        post_data.update(short_text="I don't agree.")
        post_data[constants.URL_PARAM_MASTER_PK] = obj.pk
        ct = ContentType.objects.get_for_model(Ticket)
        post_data[constants.URL_PARAM_MASTER_TYPE] = ct.id
        # post_data[constants.URL_PARAM_REQUESTING_PANEL] = '123'
        response = self.client.post(
            url, post_data,
            REMOTE_USER='******',
            HTTP_ACCEPT_LANGUAGE='en')
        result = self.check_json_result(
            response, 'rows success message close_window')
        self.assertEqual(result['success'], True)
        self.assertEqual(
            result['message'],
            """Comment "Comment #1" has been created.""")

        self.assertEqual(Message.objects.count(), 1)
        msg = Message.objects.all()[0]
        # self.assertEqual(msg.message_type)
        self.assertEqual(msg.seen, None)
        self.assertEqual(msg.user, aline)
        expected = """Robin a commenté [ticket 1] (Save the world, """\
                   """après moi le déluge): I don't agree."""
        self.assertEqual(expected, msg.body)
        
        # manually set created timestamp so we can test on it later.
        now = datetime.datetime(2016, 12, 22, 19, 45, 55)
        if settings.USE_TZ:
            now = make_aware(now)
        msg.created = now
        msg.save()
        
        settings.SERVER_EMAIL = '*****@*****.**'
        
        with capture_stdout() as out:
            send_pending_emails_often()
            
        out = out.getvalue().strip()
        print(out)

        expected = """send email
Sender: [email protected]
To: [email protected]
Subject: [Django] Robin a comment? #1 (? Save the world, apr?s moi le d?luge)
<body>
(22/12/2016 19:45)
Robin a comment? <a href="http://127.0.0.1:8000/api/tickets/Ticket/1" title="Save the world, apr&#232;s moi le d&#233;luge">#1</a> (Save the world, apr?s moi le d?luge): I don't agree.
</body>
"""        
        self.assertEquivalent(expected, out)
        
        self.assertEqual(logger.debug.call_count, 1)
        logger.debug.assert_called_with(
            'Send out %s summaries for %d users.',
            MailModes.often, 1)
        # logger.info.assert_called_with(
        #     'Notify %s users about %s', 1, 'Change by robin')

        Message.objects.all().delete()
        self.assertEqual(Message.objects.count(), 0)
        
        ar = rt.login('robin')
        cw = ChangeWatcher(obj)
        obj.priority = 200
        obj.save_watched_instance(ar, cw)


        with capture_stdout() as out:
            send_pending_emails_often()
            
        out = out.getvalue().strip()
        # print(out)
        expected = ""
        # self.assertEquivalent(expected, out)
        
        # we do not test the output because the datetime changes. But
        # we actually just wanted to see if there is no
        # UnicodeException. We capture it in order to hide it from
        # test runner output.
        
        self.assertEqual(logger.debug.call_count, 2)
        logger.debug.assert_called_with(
            'Send out %s summaries for %d users.',
            MailModes.often, 1)
示例#19
0
文件: mixins.py 项目: forexblog/xl
 def sync_primary_address(self, request):
     watcher = ChangeWatcher(self)
     self.sync_from_address(self.get_primary_address())
     self.save()
     watcher.send_update(request)
示例#20
0
文件: mixins.py 项目: khchine5/xl
 def sync_primary_address(self, ar):
     watcher = ChangeWatcher(self)
     self.sync_primary_address_()
     self.save()
     watcher.send_update(ar)
示例#21
0
文件: mixins.py 项目: einarfelix/xl
 def sync_primary_address(self, ar):
     watcher = ChangeWatcher(self)
     self.sync_primary_address_()
     self.save()
     watcher.send_update(ar)
示例#22
0
 def sync_primary_address(self, request):
     watcher = ChangeWatcher(self)
     self.sync_primary_address_()
     watcher.send_update(request)
示例#23
0
    # ~ return
    except Company.DoesNotExist, e:
        raise Exception("%s : Pharmacy or Health Insurance %s doesn't exist" % (dd.obj2str(person), pk))
        # ~ dblogger.warning(u"%s : Company %s doesn't exist (please create manually in Lino).",
        # ~ dd.obj2str(person),pk)
        # ~ return
    qs = pcsw.ClientContact.objects.filter(client=person, type__id=nType)
    if qs.count() == 0:
        cc = pcsw.ClientContact(client=person, company_id=pk, type=pcsw.ClientContactType.objects.get(id=nType))
        cc.save()
        dd.on_ui_created.send(sender=cc, request=REQUEST)
        # ~ changes.log_create(REQUEST,cc)
    elif qs.count() == 1:
        cc = qs[0]
        if cc.company_id != pk:
            watcher = ChangeWatcher(cc)
            cc.company_id = pk
            cc.save()
            watcher.send_update(REQUEST)
            # ~ watcher.log_diff(REQUEST)
    else:
        dblogger.warning(u"%s : more than 1 ClientContact (type=%r)", dd.obj2str(person), nType)


def pxs2client(row, person):

    kw = {}
    store(
        kw,
        card_number=row["CARDNUMBER"],
        card_issuer=row.get("CARDISSUER", ""),  # 20110110
示例#24
0
 def save_existing_instance(self, ar):
     watcher = ChangeWatcher(self)
     ar.ah.store.form2obj(ar, ar.rqdata, self, False)
     self.full_clean()
     self.save_watched_instance(ar, watcher)
示例#25
0
    def applydata(self, obj, data, **mapper):
        mapper.update(id="IDPAR", remarks="MEMO", bank_account1="COMPTE1", bank_account2="COMPTE2")
        ADR_applydata(obj, data)  # ,**mapper)
        # ~ kw.update(street2kw(join_words(data['RUE'],

        store_date(data, obj, "DATCREA", "created")

        if data.has_key("LANGUE"):
            obj.language = isolang(data["LANGUE"])

        # ~ dblogger.info("20111223 %r",data)
        if data.has_key("ATTRIB"):
            # ~ obj.newcomer = ("N" in data['ATTRIB'])
            # ~ obj.is_obsolete = ("A" in data['ATTRIB'] or "W" in data['ATTRIB'])
            obj.is_obsolete = "W" in data["ATTRIB"]

        if issubclass(obj.__class__, Person):
            # ~ mapper.update(title='ALLO')
            title = data.get("ALLO", "")
            if title in ("Herr", "Herrn", "Frau", u"Fräulein", "Madame", "Monsieur"):
                title = ""
            obj.title = title
            if data.has_key("FIRME"):
                for k, v in name2kw(data["FIRME"]).items():
                    setattr(obj, k, v)
            if data.has_key("NAME2"):
                setattr(obj, "addr1", data["NAME2"])
            if obj.__class__ is Client:
                par2client(data, obj)
                mapper.update(gesdos_id="NB1")
                if data.has_key("NB2"):
                    obj.national_id = data["NB2"]
                    # ~ if obj.national_id:
                    # ~ if not is_valid_ssin(obj.national_id):
                    # ~ dblogger.info("%s : invalid SSIN %s",dd.obj2str(obj),obj.national_id)
                    # ~ obj.national_id = None
                # ~ else 20121108:
                # ~ obj.national_id = str(obj.id)
                # ~ if obj.is_deprecated:
                # ~ obj.national_id += ' (A)'
                if data.has_key("ATTRIB") and "N" in data["ATTRIB"]:
                    obj.client_state = pcsw.ClientStates.newcomer
                elif data["IDPRT"] == "I":
                    obj.client_state = pcsw.ClientStates.former
                # ~
                else:
                    obj.client_state = pcsw.ClientStates.coached
                # ~ elif obj.national_id and is_valid_ssin(obj.national_id):
                # ~ obj.client_state = pcsw.ClientStates.coached
                # ~ else:
                # ~ obj.client_state = pcsw.ClientStates.invalid
                # ~ if data.has_key('NB1'):
                # ~ obj.gesdos_id = data['NB1']
                # ~ if not obj.national_id:
                # ~ obj.national_id = str()
                if data.has_key("IDUSR"):
                    username = settings.TIM2LINO_USERNAME(data["IDUSR"])
                    if username:
                        # ~ print 20130222, username
                        u = users.User.get_by_username(username)
                        # ~ u = users.User.objects.get(username=username)
                        """
                        typical cases:
                        - imported client has been assigned a coach in Lino, 
                          then filled IDUSR and removed PARATTR_N in TIM
                        """
                    else:
                        u = None

                    if obj.pk is None:
                        # must pre-save the client here to save related
                        # coachings
                        obj.save()

                    try:
                        coaching = pcsw.Coaching.objects.get(client=obj, primary=True)
                    except pcsw.Coaching.DoesNotExist, e:
                        try:
                            coaching = pcsw.Coaching.objects.get(client=obj, user=u, end_date__isnull=True)
                            watcher = ChangeWatcher(coaching)
                            coaching.primary = True
                            coaching.save()
                            watcher.send_update(REQUEST)
                            # ~ watcher.log_diff(REQUEST)
                        except pcsw.Coaching.DoesNotExist, e:
                            if u is not None:
                                coaching = pcsw.Coaching(
                                    client=obj, primary=True, user=u, type=u.coaching_type, start_date=obj.created
                                )
                                coaching.save()
                                dd.on_ui_created.send(sender=coaching, request=REQUEST)
                                # ~ changes.log_create(REQUEST,coaching)
                        except Exception, e:
                            raise Exception("More than one active coaching for %r by %r" % (obj, u))

                    except Exception, e:
                        raise Exception("More than one primary coaching for %r : %s" % (obj, e))