Beispiel #1
0
    def notify(cls, resource_id):
        """
            Asynchronous task to notify a subscriber about updates,
            runs a POST?format=msg request against the subscribed
            controller which extracts the data and renders and sends
            the notification message (see send()).

            @param resource_id: the pr_subscription_resource record ID
        """

        _debug = current.log.debug
        _debug("S3Notifications.notify(resource_id=%s)" % resource_id)

        db = current.db
        s3db = current.s3db

        stable = s3db.pr_subscription
        rtable = db.pr_subscription_resource
        ftable = s3db.pr_filter

        # Extract the subscription data
        join = stable.on(rtable.subscription_id == stable.id)
        left = ftable.on(ftable.id == stable.filter_id)

        # @todo: should not need rtable.resource here
        row = db(rtable.id == resource_id).select(stable.id,
                                                  stable.pe_id,
                                                  stable.frequency,
                                                  stable.notify_on,
                                                  stable.method,
                                                  stable.email_format,
                                                  stable.attachment,
                                                  rtable.id,
                                                  rtable.resource,
                                                  rtable.url,
                                                  rtable.last_check_time,
                                                  ftable.query,
                                                  join=join,
                                                  left=left).first()
        if not row:
            return True

        s = getattr(row, "pr_subscription")
        r = getattr(row, "pr_subscription_resource")
        f = getattr(row, "pr_filter")

        # Create a temporary token to authorize the lookup request
        auth_token = str(uuid4())

        # Store the auth_token in the subscription record
        r.update_record(auth_token=auth_token)
        db.commit()

        # Construct the send-URL
        public_url = current.deployment_settings.get_base_public_url()
        lookup_url = "%s/%s/%s" % (public_url, current.request.application,
                                   r.url.lstrip("/"))

        # Break up the URL into its components
        purl = list(urlparse.urlparse(lookup_url))

        # Subscription parameters
        last_check_time = s3_encode_iso_datetime(r.last_check_time)
        query = {"subscription": auth_token, "format": "msg"}
        if "upd" in s.notify_on:
            query["~.modified_on__ge"] = last_check_time
        else:
            query["~.created_on__ge"] = last_check_time

        # Filters
        if f.query:
            from s3filter import S3FilterString
            resource = s3db.resource(r.resource)
            fstring = S3FilterString(resource, f.query)
            for k, v in fstring.get_vars.iteritems():
                if v is not None:
                    if k in query:
                        value = query[k]
                        if type(value) is list:
                            value.append(v)
                        else:
                            query[k] = [value, v]
                    else:
                        query[k] = v
            query_nice = s3_unicode(fstring.represent())
        else:
            query_nice = None

        # Add subscription parameters and filters to the URL query, and
        # put the URL back together
        query = urlencode(query)
        if purl[4]:
            query = "&".join((purl[4], query))
        page_url = urlparse.urlunparse([
            purl[0],  # scheme
            purl[1],  # netloc
            purl[2],  # path
            purl[3],  # params
            query,  # query
            purl[5],  # fragment
        ])

        # Serialize data for send (avoid second lookup in send)
        data = json.dumps({
            "pe_id": s.pe_id,
            "notify_on": s.notify_on,
            "method": s.method,
            "email_format": s.email_format,
            "attachment": s.attachment,
            "resource": r.resource,
            "last_check_time": last_check_time,
            "filter_query": query_nice,
            "page_url": lookup_url,
            "item_url": None,
        })

        # Send the request
        _debug("Requesting %s" % page_url)
        req = urllib2.Request(page_url, data=data)
        req.add_header("Content-Type", "application/json")
        success = False
        try:
            response = json.loads(urllib2.urlopen(req).read())
            message = response["message"]
            if response["status"] == "success":
                success = True
        except urllib2.HTTPError, e:
            message = ("HTTP %s: %s" % (e.code, e.read()))
Beispiel #2
0
    def notify(cls, resource_id):
        """
            Asynchronous task to notify a subscriber about updates,
            runs a POST?format=msg request against the subscribed
            controller which extracts the data and renders and sends
            the notification message (see send()).

            @param resource_id: the pr_subscription_resource record ID
        """

        _debug("S3Notifications.notify(resource_id=%s)", resource_id)

        db = current.db
        s3db = current.s3db

        stable = s3db.pr_subscription
        rtable = db.pr_subscription_resource
        ftable = s3db.pr_filter

        # Extract the subscription data
        join = stable.on(rtable.subscription_id == stable.id)
        left = ftable.on(ftable.id == stable.filter_id)

        # @todo: should not need rtable.resource here
        row = db(rtable.id == resource_id).select(stable.id,
                                                  stable.pe_id,
                                                  stable.frequency,
                                                  stable.notify_on,
                                                  stable.method,
                                                  stable.email_format,
                                                  rtable.id,
                                                  rtable.resource,
                                                  rtable.url,
                                                  rtable.last_check_time,
                                                  ftable.query,
                                                  join=join,
                                                  left=left).first()
        if not row:
            return True

        s = getattr(row, "pr_subscription")
        r = getattr(row, "pr_subscription_resource")
        f = getattr(row, "pr_filter")

        # Create a temporary token to authorize the lookup request
        auth_token = str(uuid4())

        # Store the auth_token in the subscription record
        r.update_record(auth_token=auth_token)
        db.commit()

        # Construct the send-URL
        settings = current.deployment_settings
        public_url = settings.get_base_public_url()
        lookup_url = "%s/%s/%s" % (public_url,
                                   current.request.application,
                                   r.url.lstrip("/"))

        # Break up the URL into its components
        purl = list(urlparse.urlparse(lookup_url))

        # Subscription parameters
        last_check_time = s3_encode_iso_datetime(r.last_check_time)
        query = {"subscription": auth_token, "format": "msg"}
        if "upd" in s.notify_on:
            query["~.modified_on__ge"] = last_check_time
        else:
            query["~.created_on__ge"] = last_check_time

        # Filters
        if f.query:
            from s3filter import S3FilterString
            resource = s3db.resource(r.resource)
            fstring = S3FilterString(resource, f.query)
            for k, v in fstring.get_vars.iteritems():
                if v is not None:
                    if k in query:
                        value = query[k]
                        if type(value) is list:
                            value.append(v)
                        else:
                            query[k] = [value, v]
                    else:
                        query[k] = v
            query_nice = s3_unicode(fstring.represent())
        else:
            query_nice = None

        # Add subscription parameters and filters to the URL query, and
        # put the URL back together
        query = urlencode(query)
        if purl[4]:
            query = "&".join((purl[4], query))
        page_url = urlparse.urlunparse([purl[0], # scheme
                                        purl[1], # netloc
                                        purl[2], # path
                                        purl[3], # params
                                        query,   # query
                                        purl[5], # fragment
                                        ])

        # Serialize data for send (avoid second lookup in send)
        data = json.dumps({"pe_id": s.pe_id,
                           "notify_on": s.notify_on,
                           "method": s.method,
                           "email_format": s.email_format,
                           "resource": r.resource,
                           "last_check_time": last_check_time,
                           "filter_query": query_nice,
                           "page_url": lookup_url,
                           "item_url": None,
                           })

        # Send the request
        _debug("Requesting %s", page_url)
        req = urllib2.Request(page_url, data=data)
        req.add_header("Content-Type", "application/json")
        success = False
        try:
            response = json.loads(urllib2.urlopen(req).read())
            message = response["message"]
            if response["status"] == "success":
                success = True
        except urllib2.HTTPError, e:
            message = ("HTTP %s: %s" % (e.code, e.read()))
Beispiel #3
0
    def mdata_export(r, **attr):
        """
            Provide data for mobile app

            @param r: the S3Request instance
            @param attr: controller attributes

            @returns: a JSON string
        """

        ID = "id"
        PID = "pe_id"
        UID = current.xml.UID
        s3db = current.s3db
        resource = r.resource
        resource_tablename = resource.tablename

        # Output in mdata format
        output = {resource_tablename: []}
        # Lookup to ensure we extract ID, UID fields & joining FKs for all tables
        tablenames = [resource_tablename]
        # Keep track of which FKs we have so that we can replace them with UID
        fks = {resource_tablename: {}}

        # Default to the 'mobile_list_fields' setting
        list_fields = resource.get_config("mobile_list_fields")
        if not list_fields:
            # Fallback to the 'list_fields' setting
            list_fields = resource.get_config("list_fields")
        if not list_fields:
            # Fallback to all readable fields
            list_fields = [field.name for field in resource.readable_fields()]
        else:
            # Ensure that we have all UID fields included
            # Ensure we have FKs between all linked records
            components = resource.components

            def find_fks(join):
                """
                    Helper function to get the FKs from a Join
                """
                for q in (join.first, join.second):
                    if isinstance(q, Field):
                        if q.referent:
                            # FK
                            if q.tablename == resource_tablename:
                                fieldname = q.name
                                if fieldname not in list_fields:
                                    list_fields.append(fieldname)
                                    fks[resource_tablename][fieldname] = str(
                                        q.referent)
                            else:
                                tn = q.tablename
                                if tn not in fks:
                                    fks[tn] = {}
                                fks[tn][q.name] = str(q.referent)
                    elif isinstance(q, Query):
                        find_fks(q)

            for selector in list_fields:
                rfield = S3ResourceField(resource, selector)
                tablename = rfield.tname
                if tablename not in tablenames:
                    output[tablename] = []
                    tablenames.append(tablename)
                    # Find the FKs
                    if tablename in fks:
                        this_fks = fks[tablename]
                    else:
                        fks[tablename] = this_fks = {}

                    for tn in rfield.join:
                        join = rfield.join[tn]
                        find_fks(join)

                    # Add the ID, UUID & any FKs to list_fields
                    if "." in selector:
                        component, fieldname = selector.split(".", 1)
                        id_field = "%s.%s" % (component, ID)
                        if id_field not in list_fields:
                            list_fields.append(id_field)
                        uuid_field = "%s.%s" % (component, UID)
                        if uuid_field not in list_fields:
                            list_fields.append(uuid_field)
                        if "$" in fieldname:
                            fk, fieldname = fieldname.split("$", 1)
                            fk_field = "%s.%s" % (component, fk)
                            if fk_field not in list_fields:
                                list_fields.append(fk_field)
                            ctablename = components[
                                component].table._tablename  # Format to handle Aliases
                            if ctablename not in fks:
                                fks[ctablename] = {}
                            #referent = s3db[ctablename][fk].referent
                            if fk not in fks[ctablename]:
                                #fks[ctablename][fk] = str(referent)
                                fks[ctablename][fk] = str(
                                    s3db[ctablename][fk].referent)
                            id_field = "%s.%s$%s" % (component, fk, ID)
                            if id_field not in list_fields:
                                list_fields.append(id_field)
                            uuid_field = "%s.%s$%s" % (component, fk, UID)
                            if uuid_field not in list_fields:
                                list_fields.append(uuid_field)
                            # Restore once list_fields working
                            #if "$" in fieldname:
                            #    # e.g. address.location_id$parent$uuid
                            #    fk2, fieldname = fieldname.split("$", 1)
                            #    tablename = referent.tablename
                            #    if fk2 not in fks[tablename]:
                            #        fks[tablename][fk2] = str(s3db[tablename][fk2].referent)
                            #    id_field = "%s.%s$%s$%s" % (component, fk, fk2, ID)
                            #    if id_field not in list_fields:
                            #        list_fields.append(id_field)
                            #    uuid_field = "%s.%s$%s$%s" % (component, fk, fk2, UID)
                            #    if uuid_field not in list_fields:
                            #        list_fields.append(uuid_field)
                        else:
                            for fk in this_fks:
                                fk_field = "%s.%s" % (component, fk)
                                if fk_field not in list_fields:
                                    list_fields.append(fk_field)

                    elif "$" in selector:
                        fk, fieldname = selector.split("$", 1)
                        if fk not in list_fields:
                            list_fields.append(fk)
                        #referent = s3db[resource_tablename][fk].referent
                        if fk not in fks[resource_tablename]:
                            #fks[resource_tablename][fk] = str(referent)
                            fks[resource_tablename][fk] = str(
                                s3db[resource_tablename][fk].referent)
                        id_field = "%s$%s" % (fk, ID)
                        if id_field not in list_fields:
                            list_fields.append(id_field)
                        uuid_field = "%s$%s" % (fk, UID)
                        if uuid_field not in list_fields:
                            list_fields.append(uuid_field)
                        # Restore once list_fields working
                        #if "$" in fieldname:
                        #    # e.g. location_id$parent$uuid
                        #    fk2, fieldname = fieldname.split("$", 1)
                        #    tablename = referent.tablename
                        #    if fk2 not in fks[tablename]:
                        #        fks[tablename][fk2] = str(s3db[tablename][fk2].referent)
                        #    id_field = "%s$%s$%s" % (fk, fk2, ID)
                        #    if id_field not in list_fields:
                        #        list_fields.append(id_field)
                        #    uuid_field = "%s$%s$%s" % (fk, fk2, UID)
                        #    if uuid_field not in list_fields:
                        #        list_fields.append(uuid_field)

        if ID not in list_fields:
            list_fields.append(ID)
        if UID not in list_fields:
            list_fields.append(UID)

        data = resource.select(list_fields, raw_data=True)
        rows = data.rows

        # Build lookups of IDs to UIDs & PIDs to UIDs
        id_lookup = {}
        pid_lookup = {}
        for record in rows:
            tablenames = {}
            for field in record:
                value = record[field]
                tablename, field = field.split(".", 1)
                if tablename not in tablenames:
                    tablenames[tablename] = {
                        ID: None,
                        PID: None,
                        UID: None,
                    }
                if field == ID:
                    tablenames[tablename][ID] = value
                elif field == PID:
                    tablenames[tablename][PID] = value
                elif field == UID:
                    tablenames[tablename][UID] = value
            for tablename in tablenames:
                if tablename not in id_lookup:
                    id_lookup[tablename] = {}
                id_lookup[tablename][tablenames[tablename]
                                     [ID]] = tablenames[tablename][UID]
                pid = tablenames[tablename][PID]
                if pid:
                    pid_lookup[pid] = tablenames[tablename][UID]

        # Convert to S3Mobile format
        # & replace FKs with UUID
        for record in rows:
            # Keep track of which tables have no data
            empty = []
            row = {tablename: [] for tablename in tablenames}
            for field in record:
                value = record[field]
                tablename, field = field.split(".", 1)
                if field == ID:
                    # Don't include these in output
                    continue
                this_fks = fks[tablename]
                if field in this_fks:
                    if value:
                        # Replace ID with UUID:
                        referent = this_fks[field]
                        tn, fn = referent.split(".", 1)
                        if fn == PID:
                            value = pid_lookup[value]
                        else:
                            value = id_lookup[tn][value]

                elif field == "uuid":
                    if value is None:
                        empty.append(tablename)
                        continue
                elif isinstance(value, datetime.date) or \
                     isinstance(value, datetime.datetime):
                    value = s3_encode_iso_datetime(value).decode("utf-8")
                row[tablename].append((field, value))
            for tablename in tablenames:
                if tablename not in empty:
                    output[tablename].append(row[tablename])

        output = json.dumps(output, separators=SEPARATORS)
        current.response.headers = {"Content-Type": "application/json"}
        return output
Beispiel #4
0
    def mdata_export(r, **attr):
        """
            Provide data for mobile app

            @param r: the S3Request instance
            @param attr: controller attributes

            @returns: a JSON string
        """

        ID = "id"
        PID = "pe_id"
        UID = current.xml.UID
        s3db = current.s3db
        resource = r.resource
        resource_tablename = resource.tablename
        
        # Output in mdata format
        output = {resource_tablename: []}
        # Lookup to ensure we extract ID, UID fields & joining FKs for all tables
        tablenames = [resource_tablename]
        # Keep track of which FKs we have so that we can replace them with UID
        fks = {resource_tablename: {}}

        # Default to the 'mobile_list_fields' setting
        list_fields = resource.get_config("mobile_list_fields")
        if not list_fields:
            # Fallback to the 'list_fields' setting
            list_fields = resource.get_config("list_fields")
        if not list_fields:
            # Fallback to all readable fields
            list_fields = [field.name for field in resource.readable_fields()]
        else:
            # Ensure that we have all UID fields included
            # Ensure we have FKs between all linked records
            components = resource.components
            def find_fks(join):
                """
                    Helper function to get the FKs from a Join
                """
                for q in (join.first, join.second):
                    if isinstance(q, Field):
                        if q.referent:
                            # FK
                            if q.tablename == resource_tablename:
                                fieldname = q.name
                                if fieldname not in list_fields:
                                    list_fields.append(fieldname)
                                    fks[resource_tablename][fieldname] = str(q.referent)
                            else:
                                tn = q.tablename
                                if tn not in fks:
                                    fks[tn] = {} 
                                fks[tn][q.name] = str(q.referent)
                    elif isinstance(q, Query):
                        find_fks(q)

            for selector in list_fields:
                rfield = S3ResourceField(resource, selector)
                tablename = rfield.tname
                if tablename not in tablenames:
                    output[tablename] = []
                    tablenames.append(tablename)
                    # Find the FKs
                    if tablename in fks:
                        this_fks = fks[tablename]
                    else:
                        fks[tablename] = this_fks = {}

                    for tn in rfield.join:
                        join = rfield.join[tn]
                        find_fks(join)

                    # Add the ID, UUID & any FKs to list_fields
                    if "." in selector:
                        component, fieldname = selector.split(".", 1)
                        id_field = "%s.%s" % (component, ID) 
                        if id_field not in list_fields:
                            list_fields.append(id_field)
                        uuid_field = "%s.%s" % (component, UID) 
                        if uuid_field not in list_fields:
                            list_fields.append(uuid_field)
                        if "$" in fieldname:
                            fk, fieldname = fieldname.split("$", 1)
                            fk_field = "%s.%s" % (component, fk)
                            if fk_field not in list_fields:
                                list_fields.append(fk_field)
                            ctablename = components[component].table._tablename # Format to handle Aliases 
                            if ctablename not in fks:
                                fks[ctablename] = {}
                            #referent = s3db[ctablename][fk].referent
                            if fk not in fks[ctablename]:
                                #fks[ctablename][fk] = str(referent) 
                                fks[ctablename][fk] = str(s3db[ctablename][fk].referent) 
                            id_field = "%s.%s$%s" % (component, fk, ID) 
                            if id_field not in list_fields:
                                list_fields.append(id_field)
                            uuid_field = "%s.%s$%s" % (component, fk, UID) 
                            if uuid_field not in list_fields:
                                list_fields.append(uuid_field)
                            # Restore once list_fields working
                            #if "$" in fieldname:
                            #    # e.g. address.location_id$parent$uuid
                            #    fk2, fieldname = fieldname.split("$", 1)
                            #    tablename = referent.tablename
                            #    if fk2 not in fks[tablename]:
                            #        fks[tablename][fk2] = str(s3db[tablename][fk2].referent)
                            #    id_field = "%s.%s$%s$%s" % (component, fk, fk2, ID) 
                            #    if id_field not in list_fields:
                            #        list_fields.append(id_field)
                            #    uuid_field = "%s.%s$%s$%s" % (component, fk, fk2, UID) 
                            #    if uuid_field not in list_fields:
                            #        list_fields.append(uuid_field)
                        else:
                            for fk in this_fks:
                                fk_field = "%s.%s" % (component, fk)
                                if fk_field not in list_fields:
                                    list_fields.append(fk_field)

                    elif "$" in selector:
                        fk, fieldname = selector.split("$", 1)
                        if fk not in list_fields:
                            list_fields.append(fk)
                        #referent = s3db[resource_tablename][fk].referent
                        if fk not in fks[resource_tablename]:
                            #fks[resource_tablename][fk] = str(referent)
                            fks[resource_tablename][fk] = str(s3db[resource_tablename][fk].referent)
                        id_field = "%s$%s" % (fk, ID) 
                        if id_field not in list_fields:
                            list_fields.append(id_field)
                        uuid_field = "%s$%s" % (fk, UID) 
                        if uuid_field not in list_fields:
                            list_fields.append(uuid_field)
                        # Restore once list_fields working
                        #if "$" in fieldname:
                        #    # e.g. location_id$parent$uuid
                        #    fk2, fieldname = fieldname.split("$", 1)
                        #    tablename = referent.tablename
                        #    if fk2 not in fks[tablename]:
                        #        fks[tablename][fk2] = str(s3db[tablename][fk2].referent)
                        #    id_field = "%s$%s$%s" % (fk, fk2, ID) 
                        #    if id_field not in list_fields:
                        #        list_fields.append(id_field)
                        #    uuid_field = "%s$%s$%s" % (fk, fk2, UID) 
                        #    if uuid_field not in list_fields:
                        #        list_fields.append(uuid_field)
                            

        if ID not in list_fields:
            list_fields.append(ID)
        if UID not in list_fields:
            list_fields.append(UID)

        data = resource.select(list_fields, raw_data=True)
        rows = data.rows

        # Build lookups of IDs to UIDs & PIDs to UIDs
        id_lookup = {}
        pid_lookup = {}
        for record in rows:
            tablenames = {}
            for field in record:
                value = record[field]
                tablename, field = field.split(".", 1)
                if tablename not in tablenames:
                    tablenames[tablename] = {ID: None,
                                             PID: None,
                                             UID: None,
                                             }
                if field == ID:
                    tablenames[tablename][ID] = value
                elif field == PID:
                    tablenames[tablename][PID] = value
                elif field == UID:
                    tablenames[tablename][UID] = value
            for tablename in tablenames:
                if tablename not in id_lookup:
                    id_lookup[tablename] = {}
                id_lookup[tablename][tablenames[tablename][ID]] = tablenames[tablename][UID]
                pid = tablenames[tablename][PID]
                if pid:
                    pid_lookup[pid] = tablenames[tablename][UID]

        # Convert to S3Mobile format
        # & replace FKs with UUID
        for record in rows:
            # Keep track of which tables have no data
            empty = []
            row = {tablename: [] for tablename in tablenames}
            for field in record:
                value = record[field]
                tablename, field = field.split(".", 1)
                if field == ID:
                    # Don't include these in output
                    continue
                this_fks = fks[tablename]
                if field in this_fks:
                    if value:
                        # Replace ID with UUID:
                        referent = this_fks[field]
                        tn, fn = referent.split(".", 1)
                        if fn == PID:
                            value = pid_lookup[value]
                        else:
                            value = id_lookup[tn][value]

                elif field == "uuid":
                    if value is None:
                        empty.append(tablename)
                        continue
                elif isinstance(value, datetime.date) or \
                     isinstance(value, datetime.datetime):
                    value = s3_encode_iso_datetime(value).decode("utf-8")
                row[tablename].append((field, value))
            for tablename in tablenames:
                if tablename not in empty:
                    output[tablename].append(row[tablename])

        output = json.dumps(output, separators=SEPARATORS)
        current.response.headers = {"Content-Type": "application/json"}
        return output