'use strict';
const _ = require('lodash');
const createError = require('create-error');
const Sync = require('./sync');
const Helpers = require('./helpers');
const EagerRelation = require('./eager');
const Errors = require('./errors');
const ModelBase = require('./base/model');
const Promise = require('bluebird');
/**
* @class Model
* @extends ModelBase
* @inheritdoc
* @classdesc
*
* Models are simple objects representing individual database rows, specifying
* the tableName and any relations to other models. They can be extended with
* any domain-specific methods, which can handle components such as validations,
* computed properties, and access control.
*
* @constructor
* @description
*
* When creating an instance of a model, you can pass in the initial values of
* the attributes, which will be {@link Model#set set} on the
* model. If you define an {@link initialize} function, it will be invoked
* when the model is created.
*
* new Book({
* title: "One Thousand and One Nights",
* author: "Scheherazade"
* });
*
* In rare cases, if you're looking to get fancy, you may want to override
* {@link Model#constructor constructor}, which allows you to replace the
* actual constructor function for your model.
*
* let Book = bookshelf.Model.extend({
* tableName: 'documents',
* constructor: function() {
* bookshelf.Model.apply(this, arguments);
* this.on('saving', function(model, attrs, options) {
* options.query.where('type', '=', 'book');
* });
* }
* });
*
* @param {Object} attributes Initial values for this model's attributes.
* @param {Object=} options Hash of options.
* @param {string=} options.tableName Initial value for {@link Model#tableName tableName}.
* @param {Boolean=} [options.hasTimestamps=false]
*
* Initial value for {@link Model#hasTimestamps hasTimestamps}.
*
* @param {Boolean} [options.parse=false]
*
* Convert attributes by {@link Model#parse parse} before being {@link
* Model#set set} on the model.
*
*/
const BookshelfModel = ModelBase.extend(
{
/**
* This relation specifies that this table has exactly one of another type of object, specified by a foreign key in
* the other table.
*
* @example
* const Record = bookshelf.Model.extend({
* tableName: 'health_records'
* })
*
* const Patient = bookshelf.Model.extend({
* tableName: 'patients',
* record() {
* return this.hasOne(Record)
* }
* })
*
* // select * from `health_records` where `patient_id` = 1
* new Patient({id: 1}).related('record').fetch().then(function(model) {
* // ...
* })
*
* // Alternatively, if you don't need the relation loaded on the patient's relations hash:
* new Patient({id: 1}).record().fetch().then(function(model) {
* // ...
* })
*
* @method Model#hasOne
* @param {Model} Target Constructor of {@link Model} targeted by join.
* @param {string} [foreignKey]
* Foreign key in the `Target` model. By default the foreign key is assumed to be the singular form of this
* model's {@link Model#tableName tableName} followed by `_id` / `_{{{@link Model#idAttribute idAttribute}}}`.
* @param {string} [foreignKeyTarget]
* Column in this model's table which `foreignKey` references, if other than this model's `id` /
* `{@link Model#idAttribute idAttribute}`.
* @returns {Model}
* The return value will always be a model, even if the relation doesn't exist, but in that case the relation will
* be `null` when {@link Model#serialize serializing} the model.
*/
hasOne(Target, foreignKey, foreignKeyTarget) {
return this._relation('hasOne', Target, {
foreignKey,
foreignKeyTarget
}).init(this);
},
/**
* This relation specifies that this model has one or more rows in another table which match on this model's primary
* key.
*
* @example
* const Author = bookshelf.Model.extend({
* tableName: 'authors',
* books() {
* return this.hasMany(Book)
* }
* })
*
* // select * from `authors` where id = 1
* // select * from `books` where author_id = 1
* Author.where({id: 1}).fetch({withRelated: ['books']}).then(function(author) {
* console.log(JSON.stringify(author.related('books')))
* })
*
* @method Model#hasMany
* @param {Model} Target Constructor of {@link Model} targeted by join.
* @param {string} [foreignKey]
* ForeignKey in the `Target` model. By default, the foreign key is assumed to be the singular form of this
* model's tableName, followed by `_id` / `_{{{@link Model#idAttribute idAttribute}}}`.
* @param {string} [foreignKeyTarget]
* Column in this model's table which `foreignKey` references, if other than this model's `id` /
* `{@link Model#idAttribute idAttribute}`.
* @returns {Collection} A new empty Collection.
*/
hasMany(Target, foreignKey, foreignKeyTarget) {
return this._relation('hasMany', Target, {
foreignKey,
foreignKeyTarget
}).init(this);
},
/**
* This relationship is used when a model is a member of another `Target` model.
*
* It can be used in {@tutorial one-to-one} associations as the inverse of a
* {@link Model#hasOne hasOne}. It can also used in {@tutorial one-to-many} associations as the
* inverse of {@link Model#hasMany hasMany}, and is the "one" side of that association. In both
* cases, the belongsTo relationship is used for a model that is a member of another Target
* model, referenced by the `foreignKey` attribute in the current model.
*
* @example
* const Book = bookshelf.Model.extend({
* tableName: 'books',
* author() {
* return this.belongsTo(Author)
* }
* })
*
* // select * from `books` where id = 1
* // select * from `authors` where id = book.author_id
* Book.where({id: 1}).fetch({withRelated: ['author']}).then((book) => {
* console.log(JSON.stringify(book.related('author')))
* })
*
* @method Model#belongsTo
* @param {Model} Target Constructor of {@link Model} targeted by the join.
* @param {string} [foreignKey]
* Foreign key in this model. By default, the `foreignKey` is assumed to be the singular form
* of the `Target` model's tableName, followed by `_id`, or
* `_{{{@link Model#idAttribute idAttribute}}}` if the `idAttribute` property is set.
* @param {string} [foreignKeyTarget]
* Column in the `Target` model's table which `foreignKey` references. This is only needed in
* case it's other than `Target` model's `id` / `{@link Model#idAttribute idAttribute}`.
* @returns {Model}
* The return value will always be a model, even if the relation doesn't exist, but in that
* case the relation will be `null` when {@link Model#serialize serializing} the model.
*/
belongsTo(Target, foreignKey, foreignKeyTarget) {
return this._relation('belongsTo', Target, {
foreignKey,
foreignKeyTarget
}).init(this);
},
/**
* Defines a many-to-many relation, where the current model is joined to one or more of a
* `Target` model through another table. The default name for the joining table is the two
* models' table names joined by an underscore, and ordered alphabetically. For example, a
* `users` table and an `accounts` table would have a joining table named `accounts_users`.
*
* The default key names in the joining table are the singular versions of the model table
* names, followed by `_id` / `_{{{@link Model#idAttribute idAttribute}}}`. So in the above
* example the columns in the joining table would be `user_id`, `account_id`, and `access`,
* which is used as an example of how dynamic relations can be formed using different contexts.
*
* To customize the keys or the {@link Model#tableName tableName} used for the join table, you
* may specify them in the arguments to the function call:
*
* this.belongsToMany(Account, 'users_accounts', 'userId', 'accountId')
*
* If you wish to create a belongsToMany association where the joining table has a primary key
* and extra attributes in the model, you may create a `belongsToMany`
* {@link Relation#through through} relation:
*
* const Doctor = bookshelf.Model.extend({
* patients() {
* return this.belongsToMany(Patient).through(Appointment)
* }
* })
*
* const Appointment = bookshelf.Model.extend({
* patient() {
* return this.belongsTo(Patient)
* },
* doctor() {
* return this.belongsTo(Doctor)
* }
* })
*
* const Patient = bookshelf.Model.extend({
* doctors() {
* return this.belongsToMany(Doctor).through(Appointment)
* }
* })
*
* Collections returned by a `belongsToMany` relation are decorated with several pivot helper
* methods. If you need more information about these methods see
* {@link Collection#attach attach}, {@link Collection#detach detach},
* {@link Collection#updatePivot updatePivot} and {@link Collection#withPivot withPivot}.
*
* @example
* const Account = bookshelf.Model.extend({
* tableName: 'accounts'
* })
*
* const User = bookshelf.Model.extend({
* tableName: 'users',
* allAccounts() {
* return this.belongsToMany(Account)
* },
* adminAccounts() {
* return this.belongsToMany(Account).query({where: {access: 'admin'}})
* },
* viewAccounts() {
* return this.belongsToMany(Account).query({where: {access: 'readonly'}})
* }
* })
*
* @method Model#belongsToMany
* @param {Model} Target Constructor of {@link Model} targeted by join.
* @param {string} [joinTableName]
* Name of the joining table. Defaults to the two table names ordered alphabetically and
* joined by an underscore.
* @param {string} [foreignKey]
* Foreign key in this model. By default, the `foreignKey` is assumed to be the singular form
* of this model's tableName, followed by `_id` / `_{{{@link Model#idAttribute idAttribute}}}`.
* @param {string} [otherKey]
* Foreign key in the `Target` model. By default, this is assumed to be the singular form of
* the `Target` model's tableName, followed by `_id` /
* `_{{{@link Model#idAttribute idAttribute}}}`.
* @param {string} [foreignKeyTarget]
* Column in this model's table which `foreignKey` references. This is only needed if it's not
* the default `id` / `{@link Model#idAttribute idAttribute}`.
* @param {string} [otherKeyTarget]
* Column in the `Target` model's table which `otherKey` references. This is only needed, if
* it's not the expected default of the `Target` model's `id` /
* `{@link Model#idAttribute idAttribute}`.
* @returns {Collection}
* A new empty collection that is decorated with extra pivot helper methods. See the
* description below for more info.
*/
belongsToMany(Target, joinTableName, foreignKey, otherKey, foreignKeyTarget, otherKeyTarget) {
return this._relation('belongsToMany', Target, {
joinTableName,
foreignKey,
otherKey,
foreignKeyTarget,
otherKeyTarget
}).init(this);
},
/**
* The {@link Model#morphOne morphOne} is used to signify a {@link oneToOne
* one-to-one} {@link polymorphicRelation polymorphic relation} with
* another `Target` model, where the `name` of the model is used to determine
* which database table keys are used. The naming convention requires the
* `name` prefix an `_id` and `_type` field in the database. So for the case
* below the table names would be `imageable_type` and `imageable_id`. The
* `morphValue` may be optionally set to store/retrieve a different value in
* the `_type` column than the {@link Model#tableName}.
*
* let Site = bookshelf.Model.extend({
* tableName: 'sites',
* photo: function() {
* return this.morphOne(Photo, 'imageable');
* }
* });
*
* And with custom `columnNames`:
*
* let Site = bookshelf.Model.extend({
* tableName: 'sites',
* photo: function() {
* return this.morphOne(Photo, 'imageable', ["ImageableType", "ImageableId"]);
* }
* });
*
* Note that both `columnNames` and `morphValue` are optional arguments. How
* your argument is treated when only one is specified, depends on the type.
* If your argument is an array, it will be assumed to contain custom
* `columnNames`. If it's not, it will be assumed to indicate a `morphValue`.
*
* @method Model#morphOne
*
* @param {Model} Target Constructor of {@link Model} targeted by join.
* @param {string=} name Prefix for `_id` and `_type` columns.
* @param {(string[])=} columnNames
*
* Array containing two column names, the first is the `_type`, the second
* is the `_id`.
*
* @param {string=} [morphValue=Target#{@link Model#tableName tableName}]
*
* The string value associated with this relationship. Stored in the `_type`
* column of the polymorphic table. Defaults to `Target#{@link
* Model#tableName tableName}`.
*
* @returns {Model} The related model.
*/
morphOne(Target, name, columnNames, morphValue) {
return this._morphOneOrMany(Target, name, columnNames, morphValue, 'morphOne');
},
/**
* {@link Model#morphMany morphMany} is essentially the same as a {@link
* Model#morphOne morphOne}, but creating a {@link Collection collection}
* rather than a {@link Model model} (similar to a {@link Model#hasOne
* hasOne} vs. {@link Model#hasMany hasMany} relation).
*
* {@link Model#morphMany morphMany} is used to signify a {@link oneToMany
* one-to-many} or {@link manyToMany many-to-many} {@link polymorphicRelation
* polymorphic relation} with another `Target` model, where the `name` of the
* model is used to determine which database table keys are used. The naming
* convention requires the `name` prefix an `_id` and `_type` field in the
* database. So for the case below the table names would be `imageable_type`
* and `imageable_id`. The `morphValue` may be optionally set to
* store/retrieve a different value in the `_type` column than the `Target`'s
* {@link Model#tableName tableName}.
*
* let Post = bookshelf.Model.extend({
* tableName: 'posts',
* photos: function() {
* return this.morphMany(Photo, 'imageable');
* }
* });
*
* And with custom columnNames:
*
* let Post = bookshelf.Model.extend({
* tableName: 'posts',
* photos: function() {
* return this.morphMany(Photo, 'imageable', ["ImageableType", "ImageableId"]);
* }
* });
*
* @method Model#morphMany
*
* @param {Model} Target Constructor of {@link Model} targeted by join.
* @param {string=} name Prefix for `_id` and `_type` columns.
* @param {(string[])=} columnNames
*
* Array containing two column names, the first is the `_type`, the second is the `_id`.
*
* @param {string=} [morphValue=Target#{@link Model#tableName tablename}]
*
* The string value associated with this relationship. Stored in the `_type`
* column of the polymorphic table. Defaults to `Target`#{@link Model#tableName
* tablename}.
*
* @returns {Collection} A collection of related models.
*/
morphMany(Target, name, columnNames, morphValue) {
return this._morphOneOrMany(Target, name, columnNames, morphValue, 'morphMany');
},
/**
* This relation is used to specify the inverse of the {@link Model#morphOne morphOne} or
* {@link Model#morphMany morphMany} relations, where the `targets` must be passed to signify which
* {@link Model models} are the potential opposite end of the {@link polymorphicRelation polymorphic relation}:
*
* const Photo = bookshelf.Model.extend({
* tableName: 'photos',
* imageable() {
* return this.morphTo('imageable', Site, Post)
* }
* })
*
* And with custom column names:
*
* const Photo = bookshelf.Model.extend({
* tableName: 'photos',
* imageable() {
* return this.morphTo('imageable', ['ImageableType', 'ImageableId'], Site, Post)
* }
* })
*
* And with custom morphValues, the inverse of the `morphValue` of {@link Model#morphOne morphOne} and
* {@link Model#morphMany morphMany}, where the `morphValues` may be optionally set to check against a different
* value in the `_type` column other than the {@link Model#tableName}, for example, a more descriptive name, or a
* name that betters adheres to whatever standard you are using for models:
*
* const Photo = bookshelf.Model.extend({
* tableName: 'photos',
* imageable() {
* return this.morphTo('imageable', [Site, 'favicon'], [Post, 'cover_photo'])
* }
* })
*
* @method Model#morphTo
* @param {string} name Prefix for `_id` and `_type` columns.
* @param {string[]} [columnNames]
* Array containing two column names, where the first is the `_type` and the second is the `_id`.
* @param {...Model} Target Constructor of {@link Model} targeted by join.
* @returns {Model} The related but empty model.
*/
morphTo(morphName) {
if (!_.isString(morphName)) throw new Error('The `morphTo` name must be specified.');
let columnNames, candidates;
if (arguments[1] == null || (Array.isArray(arguments[1]) && _.isString(arguments[1][0]))) {
columnNames = arguments[1] || null; // may be `null` or `undefined`
candidates = _.drop(arguments, 2);
} else {
columnNames = null;
candidates = _.drop(arguments, 1);
}
candidates = _.map(candidates, (target) => {
if (Array.isArray(target)) return target;
// Set up the morphValue by default as the tableName
return [target, _.result(target.prototype, 'tableName')];
});
return this._relation('morphTo', null, {
morphName,
columnNames,
candidates
}).init(this);
},
/**
* Helps to create dynamic relations between {@link Model models} where a {@link Model#hasOne hasOne} or
* {@link Model#belongsTo belongsTo} relation may run through another `Interim` model. This is exactly like the
* equivalent {@link Collection#through collection method} except that it applies to the models that the above
* mentioned relation methods return instead of collections.
*
* This method creates a pivot model, which it assigns to {@link Model#pivot model.pivot} after it is created. When
* serializing the model with {@link Model#toJSON toJSON}, the pivot model is flattened to values prefixed with
* `_pivot_`.
*
* A good example of where this would be useful is if a paragraph {@link Model#hasMany belongTo} a book *through* a
* chapter. See the example above on how this can be expressed.
*
* @method Model#through
* @example
* const Chapter = bookshelf.Model.extend({
* tableName: 'chapters',
* paragraphs() {
* return this.hasMany(Paragraph)
* }
* })
* const Book = bookshelf.Model.extend({
* tableName: 'books',
* chapters() {
* return this.hasMany(Chapter)
* }
* })
*
const Paragraph = bookshelf.Model.extend({
* tableName: 'paragraphs',
* chapter() {
* return this.belongsTo(Chapter)
* },
*
* // Find the book where this paragraph is included, by passing through
* // the "Chapter" model.
* book() {
* return this.belongsTo(Book).through(Chapter)
* }
* })
*
* @param {Model} Interim Pivot model.
* @param {string} [throughForeignKey]
* Foreign key in this model. By default, the foreign key is assumed to be the singular form of the `Target`
* model's tableName, followed by `_id` or `_{{{@link Model#idAttribute idAttribute}}}`.
* @param {string} [otherKey]
* Foreign key in the `Interim` model. By default, the other key is assumed to be the singular form of this
* model's tableName, followed by `_id` / `_{{{@link Model#idAttribute idAttribute}}}`.
* @param {string} [throughForeignKeyTarget]
* Column in the `Target` model which `throughForeignKey` references, if other than `Target` model's `id` /
* `{@link Model#idAttribute idAttribute}`.
* @param {string} [otherKeyTarget]
* Column in this model which `otherKey` references, if other than `id` / `{@link Model#idAttribute idAttribute}`.
* @returns {Model} The related but empty Model.
*/
through(Interim, throughForeignKey, otherKey, throughForeignKeyTarget, otherKeyTarget) {
return this.relatedData.through(this, Interim, {
throughForeignKey,
otherKey,
throughForeignKeyTarget,
otherKeyTarget
});
},
/**
* @method Model#refresh
* @since 0.8.2
* @description
*
* Update the attributes of a model, fetching it by its primary key. If no
* attribute matches its {@link Model#idAttribute idAttribute}, then fetch by
* all available fields.
*
* @param {Object} options
* A hash of options. See {@link Model#fetch} for details.
* @returns {Promise<Model>}
* A promise resolving to this model.
*/
refresh(options) {
// If this is new, we use all its attributes. Otherwise we just grab the primary key.
const attributes = this.isNew() ? this.attributes : _.pick(this.attributes, this.idAttribute);
return this._doFetch(attributes, options);
},
/**
* Fetches a {@link Model model} from the database, using any {@link
* Model#attributes attributes} currently set on the model to form a `select`
* query.
*
* A {@link Model#event:fetching "fetching"} event will be fired just before the
* record is fetched; a good place to hook into for validation. {@link
* Model#event:fetched "fetched"} event will be fired when a record is
* successfully retrieved.
*
* If you need to constrain the query
* performed by fetch, you can call {@link Model#query query} before calling
* {@link Model#fetch fetch}.
*
* // select * from `books` where `ISBN-13` = '9780440180296'
* new Book({'ISBN-13': '9780440180296'})
* .fetch()
* .then(function(model) {
* // outputs 'Slaughterhouse Five'
* console.log(model.get('title'));
* });
*
* _If you'd like to only fetch specific columns, you may specify a `columns`
* property in the `options` for the {@link Model#fetch fetch} call, or use
* {@link Model#query query}, tapping into the {@link Knex} {@link
* Knex#column column} method to specify which columns will be fetched._
*
* A single property, or an array of properties can be specified as a value for
* the `withRelated` property. You can also execute callbacks on relations
* queries (eg. for sorting a relation). The results of these relation queries
* will be loaded into a {@link Model#relations relations} property on the
* model, may be retrieved with the {@link Model#related related} method, and
* will be serialized as properties on a {@link Model#toJSON toJSON} call
* unless `{shallow: true}` is passed.
*
* let Book = bookshelf.Model.extend({
* tableName: 'books',
* editions: function() {
* return this.hasMany(Edition);
* },
* chapters: function() {
* return this.hasMany(Chapter);
* },
* genre: function() {
* return this.belongsTo(Genre);
* }
* })
*
* new Book({'ISBN-13': '9780440180296'}).fetch({
* withRelated: [
* 'genre', 'editions',
* { chapters: function(query) { query.orderBy('chapter_number'); }}
* ]
* }).then(function(book) {
* console.log(book.related('genre').toJSON());
* console.log(book.related('editions').toJSON());
* console.log(book.toJSON());
* });
*
* @method Model#fetch
*
* @param {Object=} options - Hash of options.
* @param {Boolean=} [options.require=false]
* Reject the returned response with a {@link Model.NotFoundError
* NotFoundError} if results are empty.
* @param {string|string[]} [options.columns='*']
* Specify columns to be retrieved.
* @param {Transaction} [options.transacting]
* Optionally run the query in a transaction.
* @param {string} [options.lock]
* Type of row-level lock to use. Valid options are `forShare` and
* `forUpdate`. This only works in conjunction with the `transacting`
* option, and requires a database that supports it.
* @param {string|Object|mixed[]} [options.withRelated]
* Relations to be retrieved with `Model` instance. Either one or more
* relation names or objects mapping relation names to query callbacks.
*
* @fires Model#fetching
* @fires Model#fetched
*
* @throws {Model.NotFoundError}
*
* @returns {Promise<Model|null>}
* A promise resolving to the fetched {@link Model model} or `null` if
* none exists.
*
*/
fetch(options) {
return this._doFetch(this.attributes, options);
},
_doFetch: Promise.method(function(attributes, options) {
options = options ? _.clone(options) : {};
// Run the `first` call on the `sync` object to fetch a single model.
return (
this.sync(options)
.first(attributes)
.bind(this)
// Jump the rest of the chain if the response doesn't exist...
.tap(function(response) {
if (!response || response.length === 0) {
throw new this.constructor.NotFoundError('EmptyResponse');
}
})
// Now, load all of the data into the model as necessary.
.tap(this._handleResponse)
// If the "withRelated" is specified, we also need to eager load all of the
// data on the model, as a side-effect, before we ultimately jump into the
// next step of the model. Since the `columns` are only relevant to the
// current level, ensure those are omitted from the options.
.tap(function(response) {
if (options.withRelated) {
return this._handleEager(response, _.omit(options, 'columns'));
}
})
.tap(function(response) {
/**
* Fired after a `fetch` operation. A promise may be returned from the
* event handler for async behaviour.
*
* @event Model#fetched
* @tutorial events
* @param {Model} model
* The model firing the event.
* @param {Object} response
* Knex query response.
* @param {Object} options
* Options object passed to {@link Model#fetch fetch}.
* @returns {Promise}
* If the handler returns a promise, `fetch` will wait for it to
* be resolved.
*/
return this.triggerThen('fetched', this, response, options);
})
.return(this)
.catch(this.constructor.NotFoundError, function(err) {
if (options.require) {
throw err;
}
return null;
})
);
}),
// Private for now.
all() {
const collection = this.constructor.collection();
collection._knex = this.query().clone();
this.resetQuery();
if (this.relatedData) collection.relatedData = this.relatedData;
return collection;
},
/**
* @method Model#count
* @since 0.8.2
* @description
*
* Gets the number of matching records in the database, respecting any
* previous calls to {@link Model#query}.
*
* @example
*
* Duck.where('color', 'blue').count('name')
* .then(function(count) { //...
*
* @param {string} [column='*']
* Specify a column to count - rows with null values in this column will be excluded.
* @param {Object=} options
* Hash of options.
* @returns {Promise<Number>}
* A promise resolving to the number of matching rows.
*/
count(column, options) {
return this.all().count(column, options);
},
/**
* Fetches a collection of {@link Model models} from the database, using any
* query parameters currently set on the model to form a select query. Returns
* a Promise, which will resolve with the fetched collection. If you wish to
* trigger an error if no models are found, pass `{require: true}` as one of
* the options.
*
* If you need to constrain the query performed by fetch, you can call the
* {@link Model#query query} method before calling fetch.
*
* @method Model#fetchAll
* @param {Object} [options] Set of options to modify the request.
* @param {Boolean} [options.require=false]
* Rejects the returned Promise with a {@link Collection.EmptyError} if no records can be
* fetched from the databse.
* @param {Transaction} [options.transacting] Optionally run the query in a transaction.
* @fires Model#fetching:collection
* @fires Model#fetched:collection
* @throws {Collection.EmptyError}
* This error is used to reject the Promise in the event of an empty response from the
* database if the `require: true` option is used.
* @returns {Promise} A Promise resolving to the fetched {@link Collection collection}.
*/
fetchAll(options) {
const collection = this.all();
return collection
.once('fetching', (__, columns, opts) => {
/**
* Fired before a {@link Model#fetchAll fetchAll} operation. A promise
* may be returned from the event handler for async behaviour.
*
* @event Model#fetching:collection
* @tutorial events
* @param {Collection} collection
* The collection that is going to be fetched. At this point it's still empty since the
* fetch hasn't happened yet.
* @param {string[]} columns
* The columns to be retrieved by the query as provided by the underlying query builder.
* If the `columns` option is not specified the value of this will usually be an array
* with a single string `'tableName.*'`.
* @param {Object} options Options object passed to {@link Model#fetchAll fetchAll}.
* @returns {Promise}
*/
return this.triggerThen('fetching:collection', collection, columns, opts);
})
.once('fetched', (__, response, opts) => {
/**
* Fired after a {@link Model#fetchAll fetchAll} operation. A promise
* may be returned from the event handler for async behaviour.
*
* @event Model#fetched:collection
* @tutorial events
* @param {Collection} collection The collection that has been fetched.
* @param {Object} response
* The raw response from the underlying query builder. This will be an array with objects
* representing each row, similar to the output of a
* {@link Model#serialize serialized Model}.
* @param {Object} options Options object passed to {@link Model#fetchAll fetchAll}.
* @returns {Promise}
*/
return this.triggerThen('fetched:collection', collection, response, opts);
})
.fetch(options);
},
/**
* The load method takes an array of relations to eager load attributes onto a {@link Model}, in a similar way that
* the `withRelated` option works on {@link Model#fetch fetch}. Dot separated attributes may be used to specify deep
* eager loading.
*
* It is possible to pass an object with query callbacks to filter the relations to eager load. An example is
* presented above.
*
* @example
* // Using an array of strings with relation names
* new Posts().fetch().then(function(collection) {
* return collection.at(0).load(['author', 'content', 'comments.tags'])
* }).then(function(model) {
* JSON.stringify(model)
*
* // {
* // title: 'post title',
* // author: {...},
* // content: {...},
* // comments: [
* // {tags: [...]}, {tags: [...]}
* // ]
* // }
* })
*
* // Using an object with query callbacks to filter the relations
* new Posts().fetch().then(function(collection) {
* return collection.at(0).load({comments: function(qb) {
* qb.where('comments.is_approved', '=', true)
* }})
* }).then(function(model) {
* JSON.stringify(model)
* // the model now includes all approved comments
* })
*
* @method Model#load
* @param {string|Object|mixed[]} relations The relation, or relations, to be loaded.
* @param {Object} [options] Hash of options.
* @param {Transaction} [options.transacting] Optionally run the query in a transaction.
* @param {string} [options.lock]
* Type of row-level lock to use. Valid options are `forShare` and `forUpdate`. This only works in conjunction
* with the `transacting` option, and requires a database that supports it.
* @returns {Promise<Model>} A promise resolving to this {@link Model model}.
*/
load: Promise.method(function(relations, options) {
const columns = this.format(_.assignIn({}, this.attributes));
const withRelated = Array.isArray(relations) ? relations : [relations];
return this._handleEager([columns], _.assignIn({}, options, {shallow: true, withRelated})).return(this);
}),
/**
* @method Model#save
* @description
*
* This method is used to perform either an insert or update query using the
* model's set {@link Model#attributes attributes}.
*
* If the model {@link Model#isNew isNew}, any {@link Model#defaults defaults}
* will be set and an `insert` query will be performed. Otherwise it will
* `update` the record with a corresponding ID. It is also possible to
* set default attributes on an `update` by passing the `{defaults: true}`
* option in the second argument to the `save` call. This will also use the
* same {@link Model#defaults defaults} as the `insert` operation.
*
* The type of operation to perform (either `insert` or `update`) can be
* overriden with the `method` option:
*
* // This forces an insert with the specified id instead of the expected
* // update
* new Post({name: 'New Article', id: 34})
* .save(null, {method: 'insert'})
* .then(function(model) {
* // ...
* });
*
* If you only wish to update with the params passed to the save, you may pass
* a `{patch: true}` option in the second argument to `save`:
*
* // UPDATE authors SET "bio" = 'Short user bio' WHERE "id" = 1
* new Author({id: 1, first_name: 'User'})
* .save({bio: 'Short user bio'}, {patch: true})
* .then(function(model) {
* // ...
* });
*
* Several events fire on the model when saving: a {@link Model#event:creating
* "creating"}, or {@link Model#event:updating "updating"} event if the model is
* being inserted or updated, and a "saving" event in either case.
*
* To prevent saving the model (for example, with validation), throwing an error
* inside one of these event listeners will stop saving the model and reject the
* promise.
*
* A {@link Model#event:created "created"}, or {@link Model#event:updated "updated"}
* event is fired after the model is saved, as well as a {@link Model#event:saved "saved"}
* event either way. If you wish to modify the query when the {@link Model#event:saving
* "saving"} event is fired, the knex query object is available in `options.query`.
*
* See the {@tutorial events} guide for further details.
*
* @example
* // Save with no arguments
* Model.forge({id: 5, firstName: 'John', lastName: 'Smith'}).save().then(function() {
* //...
* });
*
* // Or add attributes during save
* Model.forge({id: 5}).save({firstName: 'John', lastName: 'Smith'}).then(function() {
* //...
* });
*
* // Or, if you prefer, for a single attribute
* Model.forge({id: 5}).save('name', 'John Smith').then(function() {
* //...
* });
*
* @param {string=} key Attribute name.
* @param {string=} val Attribute value.
* @param {Object=} attrs A hash of attributes.
* @param {Object=} options
* @param {Transaction=} options.transacting
* Optionally run the query in a transaction.
* @param {string=} options.method
* Explicitly select a save method, either `"update"` or `"insert"`.
* @param {Boolean} [options.defaults=false]
* Whether to assign or not {@link Model#defaults default} attribute values
* on a model when performing an update or create operation.
* @param {Boolean} [options.patch=false]
* Only save attributes supplied in arguments to `save`.
* @param {Boolean} [options.require=true]
* Throw a {@link Model.NoRowsUpdatedError} if no records are affected by save.
*
* @fires Model#saving
* @fires Model#creating
* @fires Model#updating
* @fires Model#created
* @fires Model#updated
* @fires Model#saved
*
* @throws {Model.NoRowsUpdatedError}
*
* @returns {Promise<Model>} A promise resolving to the saved and updated model.
*/
save: Promise.method(function(key, val, options) {
let attrs;
// Handle both `"key", value` and `{key: value}` -style arguments.
if (key == null || typeof key === 'object') {
attrs = key || {};
options = _.clone(val) || {};
} else {
attrs = {
[key]: val
};
options = options ? _.clone(options) : {};
}
return Promise.bind(this)
.then(function() {
return this.saveMethod(options);
})
.then(function(method) {
// Determine which kind of save we will do: update or insert.
options.method = method;
// If the object is being created, we merge any defaults here rather than
// during object creation.
if (method === 'insert' || options.defaults) {
const defaults = _.result(this, 'defaults');
if (defaults) {
attrs = _.defaultsDeep({}, attrs, this.attributes, defaults);
}
}
// Set the attributes on the model. Note that we do this before adding
// timestamps, as `timestamp` calls `set` internally.
this.set(attrs, {silent: true});
// Now set timestamps if appropriate. Extend `attrs` so that the
// timestamps will be provided for a patch operation.
if (this.hasTimestamps) {
Object.assign(attrs, this.timestamp(options));
}
// If there are any save constraints, set them on the model.
if (this.relatedData && this.relatedData.type !== 'morphTo') {
Helpers.saveConstraints(this, this.relatedData);
}
const attributesToSave = method === 'update' && options.patch ? attrs : this.attributes;
// Gives access to the `query` object in the `options`, in case we need it
// in any event handlers.
const sync = this.sync(options);
options.query = sync.query;
/**
* Saving event.
*
* Fired before an `insert` or `update` query. A Promise may be returned from the event
* handler for async behaviour. Throwing an exception from the handler will cancel the
* save process.
*
* @event Model#saving
* @tutorial events
* @param {Model} model
* The model firing the event. Its attributes are already changed but not commited to
* the database yet.
* @param {Object} attrs Attributes that will be inserted or updated.
* @param {Object} options Options object passed to {@link Model#save save}.
* @param {QueryBuilder} options.query
* Query builder to be used for saving. This can be used to modify or add to the query
* before it is executed.
* @returns {Promise}
*/
/**
* Creating event.
*
* Fired before an `insert` query. A Promise may be returned from the event handler for
* async behaviour. Throwing an exception from the handler will cancel the save process.
*
* @event Model#creating
* @tutorial events
* @param {Model} model The model firing the event.
* @param {Object} attrs Attributes that will be inserted.
* @param {Object} options Options object passed to {@link Model#save save}.
* @param {QueryBuilder} options.query
* Query builder to be used for saving. This can be used to modify or add to the query
* before it is executed.
* @returns {Promise}
*/
/**
* Updating event.
*
* Fired before an `update` query. A Promise may be returned from the event handler for
* async behaviour. Throwing an exception from the handler will cancel the save process.
*
* @event Model#updating
* @tutorial events
* @param {Model} model
* The model firing the event. Its attributes are already changed but not commited to
* the database yet.
* @param {Object} attrs Attributes that will be updated.
* @param {Object} options Options object passed to {@link Model#save save}.
* @param {QueryBuilder} options.query
* Query builder to be used for saving. This can be used to modify or add to the query
* before it is executed.
* @returns {Promise}
*/
return this.triggerThen(
method === 'insert' ? 'saving creating' : 'saving updating',
this,
attributesToSave,
options
)
.bind(this)
.then(function() {
return sync[options.method](attributesToSave);
})
.then(function(resp) {
// After a successful database save, the id is updated if the model was created
if (method === 'insert' && this.id == null) {
const updatedCols = {};
updatedCols[this.idAttribute] = this.id = resp[0];
const updatedAttrs = this.parse(updatedCols);
Object.assign(this.attributes, updatedAttrs);
} else if (method === 'update' && resp === 0) {
if (options.require !== false) {
throw new this.constructor.NoRowsUpdatedError('No Rows Updated');
}
}
this._reset();
/**
* Saved event.
*
* Fired after an `insert` or `update` query.
*
* @event Model#saved
* @tutorial events
* @param {Model} model The model firing the event.
* @param {(Array|Number)} response
* A list containing the id of the newly created model in case of an
* `insert` or a number representing the affected rows in the case of
* an `update` query.
* @param {Object} options Options object passed to {@link Model#save save}.
* @returns {Promise}
*/
/**
* Created event.
*
* Fired after an `insert` query.
*
* @event Model#created
* @tutorial events
* @param {Model} model The model firing the event.
* @param {Array} newId A list containing the id of the newly created model.
* @param {Object} options Options object passed to {@link Model#save save}.
* @returns {Promise}
*/
/**
* Updated event.
*
* Fired after an `update` query.
*
* @event Model#updated
* @tutorial events
* @param {Model} model The model firing the event.
* @param {Number} affectedRows Number of rows affected by the update.
* @param {Object} options Options object passed to {@link Model#save save}.
* @returns {Promise}
*/
return this.triggerThen(method === 'insert' ? 'created saved' : 'updated saved', this, resp, options);
});
})
.return(this);
}),
/**
* `destroy` performs a `delete` on the model, using the model's {@link
* Model#idAttribute idAttribute} to constrain the query.
*
* A {@link Model#event:destroying "destroying"} event is triggered on the model
* before being destroyed. To prevent destroying the model, throwing an error
* inside one of the event listeners will stop destroying the model and reject the
* promise.
*
* A {@link Model#event:destroyed "destroyed"} event is fired after the model's
* removal is completed.
*
* @method Model#destroy
*
* @param {Object=} options Hash of options.
* @param {Transaction=} options.transacting Optionally run the query in a transaction.
* @param {Boolean} [options.require=true]
* Throw a {@link Model.NoRowsDeletedError} if no records are affected by destroy. This is
* the default behavior as of version 0.13.0.
*
* @example
*
* new User({id: 1})
* .destroy()
* .then(function(model) {
* // ...
* });
*
* @fires Model#destroying
* @fires Model#destroyed
*
* @throws {Model.NoRowsDeletedError}
*
* @returns {Promise<Model>} A promise resolving to the destroyed and thus
* empty model, i.e. all attributes are `undefined`.
*/
destroy: Promise.method(function(options) {
options = options ? _.clone(options) : {};
const sync = this.sync(options);
options.query = sync.query;
return Promise.bind(this)
.then(function() {
/**
* Destroying event.
*
* Fired before a `delete` query. A promise may be returned from the event
* handler for async behaviour. Throwing an exception from the handler
* will reject the promise and cancel the deletion.
*
* @event Model#destroying
* @tutorial events
* @param {Model} model The model firing the event.
* @param {Object} options Options object passed to {@link Model#destroy destroy}.
* @returns {Promise}
*/
return this.triggerThen('destroying', this, options);
})
.then(function() {
return sync.del();
})
.then(function(affectedRows) {
if (options.require !== false && affectedRows === 0) {
throw new this.constructor.NoRowsDeletedError('No Rows Deleted');
}
this._previousAttributes = _.clone(this.attributes);
this.clear();
/**
* Destroyed event.
*
* Fired after a `delete` query. A promise may be returned from the event
* handler for async behaviour.
*
* @event Model#destroyed
* @tutorial events
* @param {Model} model The model firing the event.
* @param {Object} options Options object passed to {@link Model#destroy destroy}.
* @returns {Promise}
*/
return this.triggerThen('destroyed', this, options);
})
.then(this._reset);
}),
/**
* Used to reset the internal state of the current query builder instance.
* This method is called internally each time a database action is completed
* by {@link Sync}
*
* @method Model#resetQuery
* @returns {Model} Self, this method is chainable.
*/
resetQuery() {
this._knex = null;
return this;
},
/**
* The `query` method is used to tap into the underlying Knex query builder
* instance for the current model. If called with no arguments, it will
* return the query builder directly. Otherwise, it will call the specified
* method on the query builder, applying any additional arguments from the
* `model.query` call. If the method argument is a function, it will be
* called with the Knex query builder as the context and the first argument,
* returning the current model.
*
* @example
*
* model
* .query('where', 'other_id', '=', '5')
* .fetch()
* .then(function(model) {
* // ...
* });
*
* model
* .query({where: {other_id: '5'}, orWhere: {key: 'value'}})
* .fetch()
* .then(function(model) {
* // ...
* });
*
* model.query(function(qb) {
* qb.where('other_person', 'LIKE', '%Demo').orWhere('other_id', '>', 10);
* }).fetch()
* .then(function(model) {
* // ...
* });
*
* let qb = model.query();
* qb.where({id: 1}).select().then(function(resp) {
* // ...
* });
*
* @method Model#query
* @param {function|Object|...string=} arguments The query method.
* @returns {Model|QueryBuilder}
* Will return this model or, if called with no arguments, the underlying query builder.
*
* @see {@link http://knexjs.org/#Builder Knex `QueryBuilder`}
*/
query() {
return Helpers.query(this, Array.from(arguments));
},
/**
* The where method is used as convenience for the most common {@link
* Model#query query} method, adding a where clause to the builder. Any
* additional knex methods may be accessed using {@link Model#query query}.
*
* Accepts either key, value syntax, or a hash of attributes.
*
* @example
*
* model.where('favorite_color', '<>', 'green').fetch().then(function() { //...
* // or
* model.where('favorite_color', 'red').fetch().then(function() { //...
* // or
* model.where({favorite_color: 'red', shoe_size: 12}).fetch().then(function() { //...
*
* @method Model#where
* @param {Object|...string} method
*
* Either `key, [operator], value` syntax, or a hash of attributes to
* match. Note that these must be formatted as they are in the database,
* not how they are stored after {@link Model#parse}.
*
* @returns {Model} Self, this method is chainable.
*
* @see Model#query
*/
where() {
return this.query.apply(this, ['where'].concat(Array.from(arguments)));
},
/**
* @method Model#orderBy
* @since 0.9.3
* @description
*
* Specifies the column to sort on and sort order.
*
* The order parameter is optional, and defaults to 'ASC'. You may
* also specify 'DESC' order by prepending a hyphen to the sort column
* name. `orderBy("date", 'DESC')` is the same as `orderBy("-date")`.
*
* Unless specified using dot notation (i.e., "table.column"), the default
* table will be the table name of the model `orderBy` was called on.
*
* @example
*
* Car.forge().orderBy('color', 'ASC').fetchAll()
* .then(function (rows) { // ...
*
* @param sort {string}
* Column to sort on
* @param order {string}
* Ascending ('ASC') or descending ('DESC') order
*/
orderBy() {
return Helpers.orderBy.apply(null, [this].concat(Array.from(arguments)));
},
/* Ensure that QueryBuilder is copied on clone. */
clone() {
// This needs to use the direct apply method because the spread operator
// incorrectly converts to `clone.apply(ModelBase.prototype, arguments)`
// instead of `apply(this, arguments)`
const cloned = BookshelfModel.__super__.clone.apply(this, arguments);
if (this._knex != null) {
cloned._knex = cloned._builder(this._knex.clone());
}
return cloned;
},
/**
* Creates and returns a new Bookshelf.Sync instance.
*
* @method Model#sync
* @private
* @returns Sync
*/
sync(options) {
return new Sync(this, options);
},
/**
* Helper for setting up the `morphOne` or `morphMany` relations.
*
* @method Model#_morphOneOrMany
* @private
*/
_morphOneOrMany(Target, morphName, columnNames, morphValue, type) {
if (!Array.isArray(columnNames)) {
// Shift by one place
morphValue = columnNames;
columnNames = null;
}
if (!morphName || !Target) throw new Error('The polymorphic `name` and `Target` are required.');
return this._relation(type, Target, {
morphName: morphName,
morphValue: morphValue,
columnNames: columnNames
}).init(this);
},
/**
* @name Model#_handleResponse
* @private
* @description
*
* Handles the response data for the model, returning from the model's fetch call.
*
* @param {Object} Response from Knex query.
*
* @todo: need to check on Backbone's status there, ticket #2636
* @todo: {silent: true, parse: true}, for parity with collection#set
*/
_handleResponse(response) {
const relatedData = this.relatedData;
this.set(this.parse(response[0]), {silent: true})
.formatTimestamps()
._reset();
this._previousAttributes = _.cloneDeep(this.attributes);
if (relatedData && relatedData.isJoined()) {
relatedData.parsePivot([this]);
}
},
/**
* @name Model#_handleEager
* @private
* @description
*
* Handles the related data loading on the model.
*
* @param {Object} Response from Knex query.
*/
_handleEager(response, options) {
return new EagerRelation([this], response, this).fetch(options);
}
},
{
extended(child) {
/**
* @class Model.NotFoundError
* @description
*
* Thrown when no records are found by {@link Model#fetch fetch} or
* {@link Model#refresh} when called with the
* `{require: true}` option.
*/
child.NotFoundError = createError(this.NotFoundError);
/**
* @class Model.NoRowsUpdatedError
* @description
*
* Thrown when no records are saved by {@link Model#save save}
* unless called with the `{require: false}` option.
*/
child.NoRowsUpdatedError = createError(this.NoRowsUpdatedError);
/**
* @class Model.NoRowsDeletedError
* @description
*
* Thrown when no record is deleted by {@link Model#destroy destroy}
* if called with the `{require: true}` option.
*/
child.NoRowsDeletedError = createError(this.NoRowsDeletedError);
}
}
);
BookshelfModel.NotFoundError = Errors.NotFoundError;
BookshelfModel.NoRowsUpdatedError = Errors.NoRowsUpdatedError;
BookshelfModel.NoRowsDeletedError = Errors.NoRowsDeletedError;
module.exports = BookshelfModel;


Communicator & Group Creation / Deletion. More...
Functions | |
| ErrorHandler | MEL::CommCreateErrorHandler (ErrorHandlerFunc func) |
| Create a Comm error handler by directly passing the function to use. More... | |
| void | MEL::CommSetErrorHandler (const Comm &comm, const ErrorHandler &errHndl) |
| Set a Comm error handler by passing the a error handler reference. More... | |
| void | MEL::CommSetErrorHandler (const Comm &comm, ErrorHandlerFunc func) |
| Set a Comm error handler by directly passing the function to use. More... | |
| ErrorHandler | MEL::CommGetErrorHandler (const Comm &comm) |
| Get the Comm error handler attached to a comm world. More... | |
| int | MEL::CommRank (const Comm &comm) |
| Get the Comm rank of the process. More... | |
| int | MEL::CommSize (const Comm &comm) |
| Get the Comm world size. More... | |
| int | MEL::CommRemoteSize (const Comm &comm) |
| Get the Comm world remote size. More... | |
| Comm | MEL::CommSplit (const Comm &comm, int colour) |
| Split a comm world into seperate comms. More... | |
| Comm | MEL::CommDuplicate (const Comm &comm) |
| Duplicate a comm world so that it can be handled independently. More... | |
| Group | MEL::CommGetGroup (const Comm &comm) |
| Get the group of a comm world. More... | |
| Comm | MEL::CommCreateFromGroup (const Comm &comm, const Group &group) |
| Create a comm object from an existing comm object and a group object. More... | |
| void | MEL::CommFree (Comm &comm) |
| Free a comm world. More... | |
| void | MEL::CommFree (std::vector< Comm > &comms) |
| Free a vector comm world. More... | |
| template<typename T0 , typename T1 , typename... Args> | |
| void | MEL::CommFree (T0 &d0, T1 &d1, Args &&...args) |
| Free the varadic set of comm worlds provided. More... | |
| bool | MEL::CommIsNULL (const Comm &comm) |
| Test if a comm world is the null comm world. More... | |
| Group | MEL::GroupUnion (const Group &lhs, const Group &rhs) |
| Perform a set union of two comm groups. More... | |
| Group | MEL::GroupDifference (const Group &lhs, const Group &rhs) |
| Perform a set difference of two comm groups. More... | |
| Group | MEL::GroupIntersection (const Group &lhs, const Group &rhs) |
| Perform a set intersection of two comm groups. More... | |
| Group | MEL::GroupInclude (const Group &group, const int *ranks, const int num) |
| Create a comm group including just the ranks from an exisitng group given in an array. More... | |
| Group | MEL::GroupInclude (const Group &group, const std::vector< int > &ranks) |
| Create a comm group including just the ranks from an exisitng group given in an array. More... | |
| Group | MEL::GroupExclude (const Group &group, const int *ranks, const int num) |
| Create a comm group excluding just the ranks from an exisitng group given in an array. More... | |
| Group | MEL::GroupExclude (const Group &group, const std::vector< int > &ranks) |
| Create a comm group excluding just the ranks from an exisitng group given in an array. More... | |
| int | MEL::GroupCompare (const Group &lhs, const Group &rhs) |
| Compare two comm groups. More... | |
| bool | MEL::GroupIsSimilar (const Group &lhs, const Group &rhs) |
| Compare if two comm groups are similar. More... | |
| bool | MEL::GroupIsIdentical (const Group &lhs, const Group &rhs) |
| Compare if two comm groups are identical. More... | |
| bool | MEL::GroupIsCongruent (const Group &lhs, const Group &rhs) |
| Compare if two comm groups are congruent. More... | |
| bool | MEL::GroupIsUnequal (const Group &lhs, const Group &rhs) |
| Compare if two comm groups are unequal. More... | |
| bool | MEL::GroupIsNULL (const Group &group) |
| Compare if a comm group is the null comm group. More... | |
| int | MEL::GroupRank (const Group &group) |
| Gets the rank of the process within the given comm group. More... | |
| int | MEL::GroupSize (const Group &group) |
| Gets the size of the given comm group. More... | |
| void | MEL::GroupFree (Group &group) |
| Frees a comm group. More... | |
| void | MEL::GroupFree (std::vector< Group > &groups) |
| Free a vector comm group. More... | |
| template<typename T0 , typename T1 , typename... Args> | |
| void | MEL::GroupFree (T0 &d0, T1 &d1, Args &&...args) |
| Free the varadic set of comm groups provided. More... | |
Detailed Description
Communicator & Group Creation / Deletion.
Function Documentation
|
inline |
Create a Comm error handler by directly passing the function to use.
- Parameters
-
[in] func The function to use as an error handler
- Returns
- Returns an object that MPI can use to reference the error handler
|
inline |
Set a Comm error handler by passing the a error handler reference.
- Parameters
-
[in] comm The comm world to attach the error handler to [in] errHndl The reference to a bound error handler
|
inline |
Set a Comm error handler by directly passing the function to use.
- Parameters
-
[in] comm The comm world to attach the error handler to [in] func The function to use as an error handler
|
inline |
Get the Comm error handler attached to a comm world.
- Parameters
-
[in] comm The comm world to get the error handler of
- Returns
- Returns a reference to a bound error handler
|
inline |
Get the Comm rank of the process.
- Parameters
-
[in] comm The comm world to get the rank in
- Returns
- Returns the rank or the process within comm
|
inline |
Get the Comm world size.
- Parameters
-
[in] comm The comm world to get the size of
- Returns
- Returns the size of the comm world
|
inline |
Get the Comm world remote size.
- Parameters
-
[in] comm The comm world to get the remote size of
- Returns
- Returns the remote size of the comm world
|
inline |
Split a comm world into seperate comms.
Processes with the same colour will end up in the same comm world
- Parameters
-
[in] comm The comm world to split [in] colour The group that this process will end up in in the new comm world
- Returns
- Returns a new comm world
|
inline |
Duplicate a comm world so that it can be handled independently.
- Parameters
-
[in] comm The comm world to duplicate
- Returns
- Returns a new comm world
|
inline |
Get the group of a comm world.
- Parameters
-
[in] comm The comm world to get the group of
- Returns
- Returns a Group object representing the processes in comm
Create a comm object from an existing comm object and a group object.
- Parameters
-
[in] comm The comm world to build off of [in] group The group to use to build the new comm object
- Returns
- Returns a new comm object
|
inline |
Free a comm world.
- Parameters
-
[in] comm The comm world to free
|
inline |
Free a vector comm world.
- Parameters
-
[in] comms A std::vector of comm world
|
inline |
Free the varadic set of comm worlds provided.
- Parameters
-
[in] d0 The first comm world to free [in] d1 The second comm world to free [in] args The varadic set of remaining comm worlds to free
|
inline |
Test if a comm world is the null comm world.
- Parameters
-
[in] comm The comm world to test
- Returns
- Returns true if comm is the null comm world
Perform a set union of two comm groups.
- Parameters
-
[in] lhs The first operand of the union [in] rhs The second operand of the union
- Returns
- Returns the union of the two groups
Perform a set difference of two comm groups.
- Parameters
-
[in] lhs The first operand of the difference [in] rhs The second operand of the difference
- Returns
- Returns the difference of the two groups
Perform a set intersection of two comm groups.
- Parameters
-
[in] lhs The first operand of the intersection [in] rhs The second operand of the intersection
- Returns
- Returns the intersection of the two groups
|
inline |
Create a comm group including just the ranks from an exisitng group given in an array.
- Parameters
-
[in] group The original group to build off of [in] ranks Pointer to the array of ranks [in] num The length of the array
- Returns
- Returns the new groups
|
inline |
Create a comm group including just the ranks from an exisitng group given in an array.
- Parameters
-
[in] group The original group to build off of [in] ranks A std::vector of ranks
- Returns
- Returns the new groups
|
inline |
Create a comm group excluding just the ranks from an exisitng group given in an array.
- Parameters
-
[in] group The original group to build off of [in] ranks Pointer to the array of ranks [in] num The length of the array
- Returns
- Returns the new groups
|
inline |
Create a comm group excluding just the ranks from an exisitng group given in an array.
- Parameters
-
[in] group The original group to build off of [in] ranks A std::vector of ranks
- Returns
- Returns the new groups
Compare two comm groups.
- Parameters
-
[in] lhs The first operand of the compare [in] rhs The second operand of the compare
- Returns
- Returns the comparison of two groups
Compare if two comm groups are similar.
- Parameters
-
[in] lhs The first operand of the compare [in] rhs The second operand of the compare
- Returns
- Returns true if the groups are similar
Compare if two comm groups are identical.
- Parameters
-
[in] lhs The first operand of the compare [in] rhs The second operand of the compare
- Returns
- Returns true if the groups are identical
Compare if two comm groups are congruent.
- Parameters
-
[in] lhs The first operand of the compare [in] rhs The second operand of the compare
- Returns
- Returns true if the groups are congruent
Compare if two comm groups are unequal.
- Parameters
-
[in] lhs The first operand of the compare [in] rhs The second operand of the compare
- Returns
- Returns true if the groups are unequal
|
inline |
Compare if a comm group is the null comm group.
- Parameters
-
[in] group The group to test
- Returns
- Returns true if the group is the null group
|
inline |
Gets the rank of the process within the given comm group.
- Parameters
-
[in] group The group to use
- Returns
- Returns the rank within the given group
|
inline |
Gets the size of the given comm group.
- Parameters
-
[in] group The group to use
- Returns
- Returns the size of the given group
|
inline |
Frees a comm group.
- Parameters
-
[in] group The group to free
|
inline |
Free a vector comm group.
- Parameters
-
[in] groups A std::vector of comm group
|
inline |
Free the varadic set of comm groups provided.
- Parameters
-
[in] d0 The first comm groups to free [in] d1 The second comm groups to free [in] args The varadic set of remaining comm groups to free


Welcome {{user.first_name}}
Here are the cars that you have inquired about
{% extends 'base.html' %} {% block title %} | Dashboard{% endblock title %} {% load static %} {% block content %}
Here are the cars that you have inquired about
| Car Name | Location | Action |
|---|---|---|
| {{inquiry.car_title}} | {{inquiry.city}} | View Car |


Games this week Sunday May 16, Golden State vs Memphis Giants 3 day series vs Reds Oakland A's vs Astros San Jose Earthquakes vs Portland Playoff News San Jose Sharks fall short of Playoff Run


PartTime
JobSeeker
Beranda
Browse Jobs
Student Profile
Manage Company
Workers
FaQ
ToS
Sign In
As Student
As Employeer
Sign Up
As Student
As Employeer
Logout


Signature page senior facilities agreement
– Project Galaxy
Agent
LUCID AGENCY SERVICES LIMITED
By:
Address:
6th Floor, No 1 Building 1-5 London Wall Buildings, London Wall, London, United
Kingdom, EC2M 5PG
Email:
<EMAIL_ADDRESS>Attention:
Transaction Management
Security Agent
LUCID TRUSTEE SERVICES LIMITED
By:
Address:
6th Floor, No 1 Building 1-5 London Wall Buildings, London Wall, London, United
Kingdom, EC2M 5PG
Email:
<EMAIL_ADDRESS>Attention:
Transaction Management
Original Lenders
KOMMUNALKREDIT AUSTRIA AG
By:


The Binospec spectrograph has been commissioned at the MMT telescope in fall 2017. Binospec is a multislit imaging spectrograph with two 8' x 15' fields of view, wide wavelength coverage, and high efficiency. There are two identical all-refractive spectrographs, one for each field. Each channel has several gratings and filters.
This page provides preliminary information about Binospec for potential users and proposers. Binospec was successfully commissioned in fall 2017 and is scheduled for shared-risk science observing in spring 2018.
Each side has a 4K x 4K deep depleted E2V CCD detector with 15 micron pixels and a scale of 0.24 arcsec/pixel. The resolution per pixel is tabulated below. The wavelength coverage of a slitlet depends on the distance from slitlet to optical axis along the X direction (along the short side of the masks). From near (central) side of the mask to the far side, the change in coverage is approximately 900 A to the blue for the 600 gpm grating.
Slitmasks are machined by a laser cutter at SAO (eventually to be relocated to the MMT). Slitmasks are designed by users with the BinoMask software, written at SAO, and running on a server hosted at the MMT. Users log into a web interface to interactively design masks and submit them. Masks must be configured ahead of a Binospec run to allow time for machining. Premade masks with long slits are also provided; the widths available are 0.75", 1.0", 1.25", 1.5", and 5". One long-slit mask will always be loaded, typically the 1.0". If different long-slit widths are needed, please request these widths in your target catalog submission so they can be loaded ahead of time. The longslit masks have one slit for each side of the instrument, so typically your target will be in one side while the other slit is pointed somewhere about 10 arcminutes away. 10 slitmasks can be loaded simultaneously, but two slots will always be occupied by imaging and longslit. Masks can be changed during each afternoon.
Using a typical/standard wavelength setting will help in obtaining standards and calibrations. For wide wavelength coverage, the 270 line grating with a central wavelength of 6500 A is a good choice.
| Grating lines/mm | Order | Blaze angle | Angle of incidence | Anamorph Demag. | Coverage (A) | Dispersion (A/pixel) | Pixels per 1'' slit | Resolution in 1'' slit |
|---|---|---|---|---|---|---|---|---|
| 270 | 1 | 5.5 | 28.0 | 1.08 | 3900-9240 | 1.30 | 3.75 | 1340 |
| 600 | 1 | 16.0 | 33.2 | 1.17 | 4500-6960 | 0.60 | 3.47 | 2740 |
| 600 | 1 | 16.0 | 36.1 | 1.22 | 6000-8480 | 0.61 | 3.32 | 3590 |
| 600 | 1 | 16.0 | 38.5 | 1.27 | 7255-9750 | 0.61 | 3.20 | 4360 |
| 1000 | 1 | 13.75 | 37.1 | 1.24 | 3900-5400 | 0.36 | 3.27 | 3900 |
| Grating lines/mm | Allowable central wavelengths |
|---|---|
| 270 | 5501 - 7838 A (approx 6560 A for full wavelength coverage) |
| 600 | 5146 - 8783 A |
| 1000 | 4108-4683, 5181-7273, 7363-7967, 8153-8772, 8897-9279 |
| Ghosts may be troublesome with 1000 gpm grating at
central wavelengths 5600-8500 A, worst near 7100 A. Throughput of 1000 gpm grating will be low in the red. |
Binospec is expected to be several times more efficient than Hectospec due to throughput and improved sky subtraction. Here are on-sky throughput measurements made with the three gratings during commissioning. These include the telescope and corrector (a factor of about 0.81). There was light cirrus so the true throughput may be slightly higher. Three central wavelength settings are included for the 600gpm grating.
SAO has built a preliminary exposure time / sensitivity calculator for Binospec and Hectospec, written by Michael Kurtz. Relevant information is on several webpages:
For imaging see this calculator (select MMTcam): http://www.cfa.harvard.edu/mmti/megacam/obs_manual/exptime.html
For standard stars and similar calibrations, be careful not to saturate the detector. A preliminary suggestion is an exposure of 15 seconds for a typical spectrophotometric standard at V=11. Please be aware that accurate coordinates in the Gaia frame and proper motions are necessary for standards; many standards have high proper motions. Please see the MMT spectrophotometric standards page for the list of "ESO spectrophotometric standard stars" with up to date Gaia proper motions.
For imaging, PLEASE limit exposures to 120-150 sec or less.  This degrades your data and the data of other observers who come after you.
A model of the optical train of Binospec is shown below, illustrating the single slitmask at the top, and the paired spectrographs, filters, gratings, and detectors.
Optical train of Binospec.
The paired fields of view on the sky are shown below in green - the separation between them is 3.2 arcmin. The yellow boxes are the guider patrol regions, and the blue box is the wavefront sensor patrol region. The slitmasks cover the guider regions and holes are machined into into the masks at the location of guide stars, to allow for slitmask alignment and guiding.
Binospec on the sky. Green rectangles are the spectroscopic fields, yellow are the allowed positions for guide stars. The blue rectangle shows the wavefront-sensor patrol area. Red dots are targets; blue are guide stars; short cyan lines show the slits assigned to targets. Side B is the side that is farther from the wavefront sensor (blue box).
Binospec observations are done in a queue, similar to Hectospec and MMIRS. The queue is administered by MMT staff and the instrument is usually operated by MMT queue observers. PIs/guest observers will typically not need to come to the telescope. PIs submit observing catalogs and mask designs through the MMT queue scheduling interface. Prior to the run, PIs will get an email asking them to log in to the queue scheduler to upload catalogs and observation details. During the run, PIs can log in to see progress on their programs and download data. For more information about the queue catalogs, see the help page for the MMT queue catalog interface.
Binospec can hold 10 slitmasks, and these can be changed out freely during the day (unlike MMIRS). However, there is a lead time for machining masks, and we typically have a mask deadline ahead of the first Binospec run of a trimester.
The grating tilt controls wavelength coverage. A standard grating tilt for full coverage is the 270 lpmm grating at central wavelength 6500 A. The 600 lpmm grating is optimized for the red, and the 1000 lpmm grating is optimized for the blue. Two long-pass filters are provided for spectroscopy, LP3500 and LP3800. These block light shortward of 3500 A and 3800 A respectively, and can prevent 2nd-order blue light from contaminating 1st-order red spectra (eg 2nd-order light at 3500 A on top of 1st-order light at 7000 A). They have essentially clear transmission longward of these wavelengths. Most observations can use the LP3800 filter; observers interested in spectra shortward of 3900 A can use LP3500.
The instrument can be changed from spectroscopic to imaging mode quickly, and has g, r, i, z filters for each side. We do not anticipate initially offering mixed observing between the sides (ie imaging + spectra, or two different gratings). Because the sides are pointed at separate fields on the sky, there is not a simultaneous-observation advantage for mixed modes.
The Binospec catalog page asks users to enter various parameters such as RA, Dec, grating or filter choice, exposure time, and so on. More description is available at the catalog help page. We recommend that:
Binospec uses guiders offset from the main field of view, patrolling the yellow box regions in the figure above. Guide stars can be selected automatically from GAIA (preferred) or GSC2 catalogs. Thus, it is absolutely necessary to supply coordinates that are on the GAIA or GSC2 system. The correct coordinate system is critically important for both longslit and slitmask spectroscopy.
Targets for longslit spectroscopy will be offset onto the slit by acquiring stars onto the guiders, whose positions have been calibrated relative to the slit. Thus, your target position needs to be on the same system as the guide stars. If you provide a target position that is on the Gaia system, the guiders can bring the target directly onto the slit without any extra effort. This is strongly preferred as the way of acquiring a target. The preferred method of acquisition is very accurate, but your coordinates must be good and on the Gaia system. If the target has significant proper motion, these must be provided! Many standard stars have high proper motions and epoch-2000 coordinates are well out of date in 2018. Please see the MMT spectrophotometric standards page for the list of "ESO spectrophotometric standard stars" with up to date Gaia proper motions.
Currently, it is not possible to acquire targets by a blind offset from a star. Rather, you can provide accurate coordinates in the Gaia system and Binospec will set accurately directly to your target. It may be possible in future to tune the pointing up by tilting the grating to zeroth order and taking a direct image of the target through the slit. This would work for objects brighter than some magnitude (R~21). For faint targets, PIs could provide a nearby offset star on the same coordinate system as the target. However, these modes are not in common use now and providing Gaia-frame coordinates is preferred. Finding charts and other useful information about the target can be entered in the "notes" section of your observing catalog.
Acquiring using a slit-viewing camera requires a "mirrored" slit mask whose use has not yet been commissioned, so good coordinates that allow the above procedure to work are a requirement.
For multi-slit masks, the PI provides a list of targets on GAIA (preferred) or GSC2 coordinate systems. The BinoMask slitmask design program adds suitable guide stars and cuts alignment holes in the slitmask, through which the guiders image the stars to tune up the alignment of the mask.
Note that Binospec is behind the f/5 ADC (atmospheric dispersion compensator). Thus, you do not need to observe at the parallactic angle for longslits or for slitmasks. For longslits, you are free to specify a desired position angle on the sky in your observing catalog. For slitmasks, the PA will be determined during mask design, and when you create an observing target from the slitmask, the RA, Dec, and PA in your catalog will be read from the mask design file.
Slitmask design is done by PIs using the BinoMask web based interface. Masks will be machined with a laser cutter at SAO (in future, on the mountain) by SAO staff. Masks need to be submitted well in advance of the start of queue observations for a semester.
Please see the BinoMask slitmask design interface tutorial webpage for a more detailed walkthrough of using the software.
You will be running a web-based mask making program called BinoMask which will take a text file as input, containing your targets to be observed. If your objects are on the GAIA coordinate system, binomask can add guide and wavefront-sensor stars for you. If not, you must create a second catalog with such stars on the same coordinate system as your targets, extending to at least 0.4 degree in radius from the center of your target field. For guide stars, please select a magnitude range of r=14-18; for WFS stars, magnitude r=11-14.
The format for the catalogs is an ASCII file with columns. You may separate entries either with a comma or a tab. The first line of the file is a header:
name, ra, dec, magnitude, priority, pm-ra, pm-dec, epoch, typeThe following lines contain entries for those columns.
Only ra and dec are required, but name, magnitude, & priority are also recommended. (1=highest priority, then 2, 3, etc). Coordinates may be in decimal degrees or sexagesimal. Type should be 1 (or “target”), 2 (“sky”) or 3 (“standard”).
A queue scheduling interface similar to the MMIRS user interface allows PIs to submit mask designs, track their program progress, and download raw data. The materials/machining cost for slitmasks will be determined at a later date.
Mask making is integrated with telescope scheduling software. Thus if you have been granted time, you will receive an email from<EMAIL_ADDRESS>with instructions to log in, make masks, and submit catalogs. For more information on queue catalogs, visit the help page for the MMT queue catalog interface. You may also use the mask making software outside of that framework. To run the BinoMask software visit scheduler.mmto.arizona.edu/BinoMask/. Slit widths are also set by the user. As an aid, the MMT web site states that the median seeing is in the range of 0.77" - 0.85" based on data from 2003 to the present. 1 arcsecond slits are a good starting point.
Note that the differential atmospheric refraction across the 15 arcminute field is significant, so that the location of slitlets depends on the hour angle at which the mask is observed. In practice this means that the mask machining would change slightly depending on whether the mask is observed when HA is East or West. Thus the date and time of observation specified during mask design are significant. Observable hour angles will be taken into account during queue scheduling.
You may get the error “slits can not be made” which often occurs because the rotator setting is not correct. Try adding or subtracting 180 from the present setting.
It is good practice to avoid mask position angles that run into the rotator limits (which are near +180/-180). Binomask will try to avoid PAs that hit the limits in the middle of your observing window. In practice, the limits mean that to avoid hitting the limit at transit, for masks that are north of the MMT (Dec > +33), you should avoid position angles near 0, and for masks south of the MMT (Dec < +33), you should avoid PAs near +180 or -180.
In order to obtain the maximum scientific impact from Binospec, we will to continue the queue scheduling that has proven successful with Hectospec, Hectochelle and MMIRS. Queue scheduling offers the considerable advantage that we can flexibly adapt the observing program to the current conditions. We will reserve conditions of perfect transparency and superb seeing for those projects that can take best advantage of excellent conditions. Queue scheduling also allows observations to be obtained at low air-mass and allows continuous observation of high priority objects. The instrumeht will be operated by MMT staff queue observers, and proposers are not generally expected to travel to the telescope.
Binospec raw data (fitsraw) and minimally processed data (fitsproc, overscan subtracted) are available shortly after an observing block is completed and can be downloaded from the PI's queue catalog interface. An observed target will show a small blue folder icon; clicking this icon brings up a window to select files for download. Imaging data can be reduced using standard astronomical techniques (IRAF, astropy/ccdproc, etc).
The Binospec spectroscopic pipeline is written in IDL by SAO staff, primarily Igor Chilingarian and Sean Moran, and is now publicly available. The pipeline code repository is at Binospec pipeline on Bitbucket. A brief description of running the pipeline and of the output data formats is at the Binospec pipeline repository wiki.
The pipeline uses some similar algorithms to the MMIRS pipeline. The SAO TDC is anticipated to return reduced data to the PIs. The timescale for data reduction is TBD. SAO and MMT staff are currently working on having the data quickly reduced and reduced data made available through the MMT queue observing catalog interface, similar to the way that raw data are downloadable. PIs will get an email when their reduced data is ready.
Availability of quick look reductions or quickly returning raw and reduced data are recognized as desirable for some programs. The pipeline includes a quick reduction routine, binospec_quicklook. A quicklook for queue observers to assess data quality is in progress.
Binospec raw data are stored as multiextension fits files; each CCD has 4 amplifiers, each of which is stored as an extension. Data taken are archived at the CfA. The raw and lightly processed (overscan and trimmed) data can be downloaded from your observing catalog. The processed data typically will have 2 fits extensions, one for each side of the instrument. For longslit data, your object will typically be in side B (extension 2).
The spectroscopic pipeline (now in a preliminary release), is used to process the data and provide wavelength calibrated, relative-flux corrected spectra. Currently, the queue observing system will send the PI a notification the day after any of your data is taken, at which time the PI may login to their queue observing page and download the raw data.
When the pipeline has been run and reduced data are available, the PI will receive another email. The data files are accessible through the MMT queue catalog management pages: each target that has been observed has a blue folder icon. Clicking the folder icon allows the PI to view available data files and download them. Imaging data are in a fairly standard format and will not be reduced by the pipeline, but can be reduced by standard techniques (eg IRAF, mscred, etc). The format of the pipeline-reduced spectrosopic data is described at the Binospec pipeline repository wiki. Example code to read the data is in progress.
setenv SPECPRO_DIR /Users/bjw/idl/specpro setenv IDL_PATH :+${SPECPRO_DIR}/code:$IDL_PATH setenv IDL_PATH :+${SPECPRO_DIR}/external:$IDL_PATH setenv SPVIEW ${SPECPRO_DIR}/templatesbut replace "/Users/bjw/idl/specpro" with the directory where you installed specpro. If you use bash, use export SPECPRO_DIR=... instead of setenv.
convert_binospec_to_specpro, 'obj_counts_slits_extr.fits', 'obj_counts_slits_lin.fits', outdir='specfiles'substituting the appropriate filenames as needed. This will make a 1-d file, 2-file, and info file for each slitlet (it's a lot of files) in the subdirectory. If you put the convert_binospec_to_specpro.pro file in the current directory, or if you get an undefined procedure error when running it, compile it first with the IDL command:
.com convert_binospec_to_specprothen run the convert_binospec_to_specpro command. As of Dec 2018 there is a new version on github that compiles all the subroutines when you run it, to avoid an undefined procedure error.
cd,'specfiles' device, decomposed=1 specpro_bino, 1, /basicYou only need to do the file conversion once per dataset, and can then run specpro for as many sessions as you like.
For more detailed information on a suggested workflow using Specpro, visit the Using Specpro to inspect Binospec data page. Running Specpro will let you look at 1-d and 2-d data, smooth it, enter trial redshifts, enter redshift quality estimates, save output, and step through the slits. Read the tutorial and manual on the Specpro website to understand the GUI features. Fitting for the best redshift now works with the revised zfindspec.pro. If you don't get a good redshift result or if the spectrum has bad data at the ends, redshift fitting can be improved if you click-and-drag in the 1-d spectrum plot window to zoom in on a 1-d spectral region containing emission or absorption features and excluding data problems.
These images were taken during Binospec commissioning in fall 2017.
A multislit single exposure of 600 sec. Blue wavelengths are toward the center; red is to the left in the left frame and to the right in the right frame. Note the cosmic ray hits.
Combination of 4 exposures. Ca H and K absorption lines are visible as a doublet near the blue end of several spectra.
A 600 sec single exposure on a mask with many short, 2 arcsec long slits, using the 600 gpm grating
A direct imaging exposure, 50 sec in g band.
This page is updated with information from Binospec commissioning and the first few semesters of operations starting 2018A. Please contact Benjamin Weiner<EMAIL_ADDRESS>for further information.


This page is a demonstration, showing what a user with an outdated browser will experience.
All browsers in this demo are marked as not supported, so you'll see it even with new browsers!
This page is a demonstration, showing what a user with an outdated browser will experience.
All browsers in this demo are marked as not supported, so you'll see it even with new browsers!


Shrikhand supports the Gujarati and Latin writing systems. The name (શ્રીખંડ) is a sweet and creamy Gujarati dessert that is a particular favourite of the designer, Jonny Pinhorn.
In the three or four years prior to the initial development of this font in June 2015, Jonny lived in Ahmedabad, Gujarat, working for Indian Type Foundry. During this time India had a profound effect on his design philosophy and general worldview. Shrikhand is the culmination of three years absorbing the colourful and vibrant hand-painted lettering that can be seen on the streets in Gujarat. Big, bold, and unapologetic, Shrikhand is a display type that evolved from discussions with colleagues about how good the Gujarati script looks when it is free to be as curvaceous and expressive as possible.
To contribute, see github.com/jonpinhorn/shrikhand


Before you progress with your application you must:
mark the
Subsidy basis
page as complete
There is a problem
I agree to the award terms and conditions set out by Innovate UK.
Agree and continue
Return to join project


The Dual Air Quality Systems (DAQS) is an ambitious project where we aim to create a network of affordable sensors to provide a dense and comprehensive coverage of Abu Dhabi’s air quality. We envisioned the sensors to be placed on top of buses and bus stations, forming a sensor network that will cross calibrate between themselves to improve accuracy. We believe that a dense and comprehensive coverage of air quality, provided by our sensor network, would benefit Abu Dhabi extensively for better policy enactment, for environmental awareness, and for the health of the citizens.
DAQS uses the affordable Alphasense environmental sensors to measure the air quality. The device currently uses the electrochemical sensors to measure carbon monoxide, sulfur dioxide, nitrogen dioxide, and ozone level in the air, and it also uses the optical particle counters to measure the level of particulate matters in the air. The data is acquired through custom PCB and processed in a Raspberry Pi, and through 4G connectivity, the device automatically uploads the collected data to firebase, a real-time data base.
I took over the project one year after its inception with the aim to make this system functional in the real world. To do so, I must make the device field ready and improve the quality of the collected data. These leads me to redesign the physical prototype and to conduct extensive testing of the sensors.
To make the device ready for real world testing, I increased the robustness of the electronics by eliminating majority of wire connection with a custom Printed Circuit Board (PCB) and weather proofed the device by securing it in a NEMA 4X Rated (corrosion proof, splashproof, and dustproof) polycarbonate casing with custom 3D printed parts.
To improve the quality of the collected data, I tested the DAQS sensors extensively in real world condition. The testing lead me to conclude that the original sensors, Alphasense Small Class sensors, are too noisy to for reliable data. As such, we opted for the more accurate Compact Class sensors. After testing and calibration, we collected promising air quality data and presented the improved system to the Prime Minister’s office in Dubai. During the meeting, we met with and was advised by experts in air quality monitoring. The data we have gathered, per the experts, have demonstrated indicatively the of air quality of Abu Dhabi.
The system is currently being actively developed to improve the accuracy and the response time of the sensor data. To improve the accuracy of the data, we are currently implementing a neural network based calibration system to cross calibrate between the sensors. We believe that through the cross calibration of the collected data, we could achieve obtained more representative data for Abu Dhabi’s air quality. Furthermore, to improve the response time of the data, we are decreasing the air chamber size of the system, as advised by the air quality experts.


{{ _('Sorry to hear that.') }}
600 {{_('characters remaining.') }}
{{ _('Please provide more details') }}


UVLog Class Reference
| Inherits from | NSObject |
|---|---|
| Declared in | UVLog.h |
Overview
The main class, exposes all logging mechanisms, loggers, …
For most of the users, this class is hidden behind the logging functions like UVLogInfo
sharedInstance
Returns the singleton UVLog
.
The instance is used by UVLog
class methods.
@property (class, nonatomic, strong, readonly) UVLog *sharedInstance
Declared In
UVLog.h
loggingQueue
Provides access to the underlying logging queue. This may be helpful to Logger classes for things like thread synchronization.
@property (class, nonatomic, DISPATCH_QUEUE_REFERENCE_TYPE, readonly) dispatch_queue_t loggingQueue
Declared In
UVLog.h
+ log:level:flag:context:file:function:line:tag:format:
Logging Primitive.
+ (void)log:(BOOL)asynchronous level:(UVLogLevel)level flag:(UVLogFlag)flag context:(NSInteger)context file:(const char *)file function:(const char *)function line:(NSUInteger)line tag:(id __nullable)tag format:(NSString *)format, ...
Parameters
asynchronous |
YES if the logging is done async, NO if you want to force sync |
|---|---|
level |
the log level |
flag |
the log flag |
context |
the context (if any is defined) |
file |
the current file |
function |
the current function |
line |
the current code line |
tag |
potential tag |
format |
the log format |
Discussion
This method is used by the macros or logging functions. It is suggested you stick with the macros as they’re easier to use.
Declared In
UVLog.h
– log:level:flag:context:file:function:line:tag:format:
Logging Primitive.
- (void)log:(BOOL)asynchronous level:(UVLogLevel)level flag:(UVLogFlag)flag context:(NSInteger)context file:(const char *)file function:(const char *)function line:(NSUInteger)line tag:(id __nullable)tag format:(NSString *)format, ...
Parameters
asynchronous |
YES if the logging is done async, NO if you want to force sync |
|---|---|
level |
the log level |
flag |
the log flag |
context |
the context (if any is defined) |
file |
the current file |
function |
the current function |
line |
the current code line |
tag |
potential tag |
format |
the log format |
Discussion
This method is used by the macros or logging functions. It is suggested you stick with the macros as they’re easier to use.
Declared In
UVLog.h
+ log:level:flag:context:file:function:line:tag:format:args:NS_SWIFT_NAME:
Logging Primitive.
+ (void)log:(BOOL)asynchronous level:(UVLogLevel)level flag:(UVLogFlag)flag context:(NSInteger)context file:(const char *)file function:(const char *)function line:(NSUInteger)line tag:(id __nullable)tag format:(NSString *)format args:(va_list)argList NS_SWIFT_NAME
Parameters
asynchronous |
YES if the logging is done async, NO if you want to force sync |
|---|---|
level |
the log level |
flag |
the log flag |
context |
the context (if any is defined) |
file |
the current file |
function |
the current function |
line |
the current code line |
tag |
potential tag |
format |
the log format |
argList |
the arguments list as a va_list |
Discussion
This method can be used if you have a prepared va_list.
Similar to log:level:flag:context:file:function:line:tag:format:
...
Declared In
UVLog.h
– log:level:flag:context:file:function:line:tag:format:args:NS_SWIFT_NAME:
Logging Primitive.
- (void)log:(BOOL)asynchronous level:(UVLogLevel)level flag:(UVLogFlag)flag context:(NSInteger)context file:(const char *)file function:(const char *)function line:(NSUInteger)line tag:(id __nullable)tag format:(NSString *)format args:(va_list)argList NS_SWIFT_NAME
Parameters
asynchronous |
YES if the logging is done async, NO if you want to force sync |
|---|---|
level |
the log level |
flag |
the log flag |
context |
the context (if any is defined) |
file |
the current file |
function |
the current function |
line |
the current code line |
tag |
potential tag |
format |
the log format |
argList |
the arguments list as a va_list |
Discussion
This method can be used if you have a prepared va_list.
Similar to log:level:flag:context:file:function:line:tag:format:
...
Declared In
UVLog.h
+ log:message:NS_SWIFT_NAME:
Logging Primitive.
+ (void)log:(BOOL)asynchronous message:(UVLogMessage *)logMessage NS_SWIFT_NAME
Parameters
asynchronous |
YES if the logging is done async, NO if you want to force sync |
|---|---|
logMessage |
the log message stored in a |
Discussion
This method can be used if you manualy prepared UVLogMessage.
Declared In
UVLog.h
– log:message:NS_SWIFT_NAME:
Logging Primitive.
- (void)log:(BOOL)asynchronous message:(UVLogMessage *)logMessage NS_SWIFT_NAME
Parameters
asynchronous |
YES if the logging is done async, NO if you want to force sync |
|---|---|
logMessage |
the log message stored in a |
Discussion
This method can be used if you manualy prepared UVLogMessage.
Declared In
UVLog.h
+ flushLog
Since logging can be asynchronous, there may be times when you want to flush the logs. The framework invokes this automatically when the application quits.
+ (void)flushLog
Declared In
UVLog.h
– flushLog
Since logging can be asynchronous, there may be times when you want to flush the logs. The framework invokes this automatically when the application quits.
- (void)flushLog
Declared In
UVLog.h
+ addLogger:
adds the logger to the system.
+ (void)addLogger:(id<UVLogger>)logger
Discussion
This is equivalent to invoking [UVLog addLogger:logger withLogLevel:UVLogLevelAll]
.
Declared In
UVLog.h
– addLogger:
adds the logger to the system.
- (void)addLogger:(id<UVLogger>)logger
Discussion
This is equivalent to invoking [UVLog addLogger:logger withLogLevel:UVLogLevelAll]
.
Declared In
UVLog.h
+ addLogger:withLevel:
adds the logger to the system.
+ (void)addLogger:(id<UVLogger>)logger withLevel:(UVLogLevel)level
Discussion
The level that you provide here is a preemptive filter (for performance). That is, the level specified here will be used to filter out logMessages so that the logger is never even invoked for the messages.
More information: When you issue a log statement, the logging framework iterates over each logger, and checks to see if it should forward the logMessage to the logger. This check is done using the level parameter passed to this method.
For example:
[UVLog addLogger:consoleLogger withLogLevel:UVLogLevelVerbose];
[UVLog addLogger:fileLogger withLogLevel:UVLogLevelWarning];
UVLogError(@"oh no");
=> gets forwarded to consoleLogger & fileLogger
UVLogInfo(@"hi");
=> gets forwarded to consoleLogger only
It is important to remember that Lumberjack uses a BITMASK. Many developers & third party frameworks may define extra log levels & flags. For example:
#define SOME_FRAMEWORK_LOG_FLAG_TRACE (1 << 6) // 0...1000000
So if you specify UVLogLevelVerbose
to this method, you won’t see the framework’s trace messages.
(SOME_FRAMEWORK_LOG_FLAG_TRACE & UVLogLevelVerbose) => (01000000 & 00011111) => NO
Consider passing UVLogLevelAll
to this method, which has all bits set.
You can also use the exclusive-or bitwise operator to get a bitmask that has all flags set,
except the ones you explicitly don’t want. For example, if you wanted everything except verbose & debug:
((UVLogLevelAll ^ UVLogLevelVerbose) | UVLogLevelInfo)
Declared In
UVLog.h
– addLogger:withLevel:
adds the logger to the system.
- (void)addLogger:(id<UVLogger>)logger withLevel:(UVLogLevel)level
Discussion
The level that you provide here is a preemptive filter (for performance). That is, the level specified here will be used to filter out logMessages so that the logger is never even invoked for the messages.
More information: When you issue a log statement, the logging framework iterates over each logger, and checks to see if it should forward the logMessage to the logger. This check is done using the level parameter passed to this method.
For example:
[UVLog addLogger:consoleLogger withLogLevel:UVLogLevelVerbose];
[UVLog addLogger:fileLogger withLogLevel:UVLogLevelWarning];
UVLogError(@"oh no");
=> gets forwarded to consoleLogger & fileLogger
UVLogInfo(@"hi");
=> gets forwarded to consoleLogger only
It is important to remember that Lumberjack uses a BITMASK. Many developers & third party frameworks may define extra log levels & flags. For example:
#define SOME_FRAMEWORK_LOG_FLAG_TRACE (1 << 6) // 0...1000000
So if you specify UVLogLevelVerbose
to this method, you won’t see the framework’s trace messages.
(SOME_FRAMEWORK_LOG_FLAG_TRACE & UVLogLevelVerbose) => (01000000 & 00011111) => NO
Consider passing UVLogLevelAll
to this method, which has all bits set.
You can also use the exclusive-or bitwise operator to get a bitmask that has all flags set,
except the ones you explicitly don’t want. For example, if you wanted everything except verbose & debug:
((UVLogLevelAll ^ UVLogLevelVerbose) | UVLogLevelInfo)
Declared In
UVLog.h
+ removeLogger:
Remove the logger from the system
+ (void)removeLogger:(id<UVLogger>)logger
Declared In
UVLog.h
– removeLogger:
Remove the logger from the system
- (void)removeLogger:(id<UVLogger>)logger
Declared In
UVLog.h
allLoggers
Return all the current loggers
@property (class, nonatomic, copy, readonly) NSArray<id<UVLogger> > *allLoggers
Declared In
UVLog.h
allLoggersWithLevel
Return all the current loggers with their level (aka UVLoggerInformation).
@property (class, nonatomic, copy, readonly) NSArray<UVLoggerInformation*> *allLoggersWithLevel
Declared In
UVLog.h
registeredClasses
Returns an array with the classes that are using registered dynamic logging
@property (class, nonatomic, copy, readonly) NSArray<Class> *registeredClasses
Declared In
UVLog.h
registeredClassNames
Returns an array with the classes names that are using registered dynamic logging
@property (class, nonatomic, copy, readonly) NSArray<NSString*> *registeredClassNames
Declared In
UVLog.h
+ levelForClass:
Returns the current log level for a certain class
+ (UVLogLevel)levelForClass:(Class)aClass
Parameters
aClass |
|
|---|
Declared In
UVLog.h
+ levelForClassWithName:
Returns the current log level for a certain class
+ (UVLogLevel)levelForClassWithName:(NSString *)aClassName
Parameters
aClassName |
string param |
|---|
Declared In
UVLog.h
+ setLevel:forClass:
Set the log level for a certain class
+ (void)setLevel:(UVLogLevel)level forClass:(Class)aClass
Parameters
level |
the new level |
|---|---|
aClass |
|
Declared In
UVLog.h
+ setLevel:forClassWithName:
Set the log level for a certain class
+ (void)setLevel:(UVLogLevel)level forClassWithName:(NSString *)aClassName
Parameters
level |
the new level |
|---|---|
aClassName |
string param |
Declared In
UVLog.h


[Launcher configuration files are created and edited using the LaunchBuilder program.]
Launcher configurations, application data, and associated documentation are organized using an Extensible Markup Language (xml) text file named launcher_default.xset. Although this file is packaged inside the Java archives on the OSP CD, it can also be distributed in an external "zip" archive or as a stand-alone file. Launcher searches for external configurations in the directory from which Launcher was executed. Because external configurations can be modified without recompiling code or rebuilding jar files, teachers and authors can adapt Open Source Physics curricular material for their own needs or author their own curricular material.
Although Launcher configuration xml files can opened with any text editor, they are most easily created and edited using the LaunchBuilder program. Select the Edit menu item under the Launcher frame's File menu to explore the capabilities of LaunchBuilder.


News story
Presentation from The Skills Conference, 8 March 2016
Ofqual's Ian Pursglove examined 'The Future of Functional Skills' at the Skills Conference in Leicester on 8 March.
Ian Pursglove, Ofqual’s Associate Director, Standards for Literacy and Numeracy Qualifications gave a presentation on ‘The Future of Functional Skills’ at the Skills Conference 2016 in Leicester on Tuesday 8 March. A copy of his presentation is available below.
The Future of Functional Skills - The Regulatory Picture Now and Beyond
(
MS Powerpoint Presentation
,
947KB
)


Before Change
The wider model
// The last conv layer cannot be widen since wider operator cannot be done over the two sides of flatten.
conv_layers = list(filter(lambda x: is_conv_layer(x), graph.layer_list))[:-1]
// The first layer cannot be widen since widen operator cannot be done over the two sides of flatten.
// The last layer is softmax, which also cannot be widen.
dense_layers = list(filter(lambda x: is_dense_layer(x), graph.layer_list))[1:-1]
if len(dense_layers) == 0:
weighted_layers = conv_layers
elif randint(0, 1) == 0:
weighted_layers = conv_layers
else:
weighted_layers = dense_layers
if len(weighted_layers) <= 1:
After Change
if is_conv_layer(graph.layer_list[target_id]):
n_add = randint(1, 4 * graph.layer_list[target_id].filters)
else:
n_add = randint(1, 4 * graph.layer_list[target_id].units)
graph.to_wider_model(target_id, n_add)
return graph


Homer Yen (c) 1997
Operation Condor: More Kung Pow
Admittedly, it gets increasingly difficult to review films that star the ebullient Jackie Chan. One film is just like another. Fortunately, each film is as cutely entertaining as another. Like all is films, he chops, socks, flips and somersaults with the greatest of ease. His playfulness makes him very likable. You can't help but laugh at the cheesiness of the dubbing; you can't help but stare in awe at the wonderfully choreographed fight scenes; you can't help but gasp in delight and amazement as he performs his own stunts. In this movie, you get 90 fun-filled minutes of cheesiness, awe and delight.
This latest Chan vehicle to hit the American landscape is basically a take-off of the Raiders of the Lost Ark genre.  It's up to Jackie to find it. Now, this is not a really creative story. I mean, even Jackie Chan's character name is…uh…Jackie. But, Chan movies are not meant to have significant depth (or any for that matter). Watch as scenes are seemingly incongruously edited together. Watch mysterious bad guys appear out of nowhere and pop back up at the most awkward of moments. This is no masterpiece to be sure. But it is the situations that Jackie finds himself in, and the incredibly amusing way in which he gets out that is the hallmark of these films. Jackie is also accompanied on his quest by 3 different women. All are attractive and have a minor purpose for their inclusion, but their roles amount to little more than fodder for more comedy and silliness. And, there are the usual odd assortment of bandits, thugs (my favorite rarely-used word), nazis and mercenaries that all try to get Jackie. My feeling is that when there are more people in his films, more fighting can happen. After all, that is what we want to see. That is what we've come to expect. And, smartly, that is exactly what we get.
For those that have never scene the previous Chan releases (Rumble in the Bronx, Supercop, or First Strike), this is a unique movie-going experience, mainly because Chan is so different than the Van Dammes, Bruce Lees or Steven Segals. Chan can fight with the best of them (if not better), but he wants you to laugh at him and with him too. Like all of his other movies, Jackie is a 3-ring circus of entertainment. In one ring, we can enjoy his boyish charm. In another ring, we can see him at the center of some amazing action-sequences and martial arts encounter. And in the 3rd ring, we can enjoy the humor that he injects to brighten anybody's spirit. Chan is a trapeze artist, lion tamer and clown rolled into one. In this case, Chan and his film can be enjoyed by ladies and gentlemen and children of all ages.
Grade: B
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews