class Workflow(BaseModel): mailbox_id = properties.Integer( 'The mailbox ID that this workflow is associated with.', required=True, ) type = properties.StringChoice( 'The type of workflow.', choices=['automatic', 'manual'], default='manual', required=True, ) status = properties.StringChoice( 'The status of the workflow.', choices=['active', 'inactive', 'invalid'], default='invalid', required=True, ) order = properties.Integer( 'The order of the workflow.', default=1, required=True, ) name = properties.String( 'Workflow name.', required=True, ) created_at = properties.DateTime( 'UTC time when this workflow was created.', ) modified_at = properties.DateTime( 'UTC time when this workflow was modified.', )
class Person(BaseModel): """This is a subset of the data representing a Customer, User or Team. The ``type`` property will specify if this person is represented by a ``user``, ``customer`` or ``team``. """ def __init__(self, **kwargs): value = kwargs.pop('customer_person_type', False) self.type = 'customer' if value else 'user' return super(Person, self).__init__(**kwargs) full_name = properties.String( 'Full name for the customer', ) first_name = properties.String( 'First name', required=True, ) last_name = properties.String( 'Last name', required=True, ) email = properties.String( 'Email', ) emails = properties.List( 'Email addresses for this person.', prop=properties.String( 'Email Address', ), ) phone = properties.String( 'Phone is only populated when the Person is a customer associated ' 'with a Conversation of type ``phone`` and a phone number was ' 'specified at the time the conversation was created.', ) type = properties.StringChoice( 'The type of person.', choices=['user', 'customer', 'team'], default='customer', required=True, ) photo_url = properties.String( 'The user\'s photo, if one exists.', ) created_at = properties.DateTime( 'UTC time when this customer was created.', ) modified_at = properties.DateTime( 'UTC time when this customer was modified.', ) @properties.Bool('customer boolean') def customer_person_type(self): return self.type == 'customer' @customer_person_type.setter def customer_person_type(self, value): self.type = 'customer' if value else 'user'
def test_datetime(self): class DateTimeOpts(properties.HasProperties): mydate = properties.DateTime('my date') dttm = DateTimeOpts() with self.assertRaises(ValueError): dttm.mydate = 2010 with self.assertRaises(ValueError): dttm.mydate = '2010' dttm.mydate = datetime.datetime(2010, 1, 2) dttm.mydate = '2010-01-02' assert dttm.mydate == datetime.datetime(2010, 1, 2) dttm.mydate = '2010/01/02' assert dttm.mydate == datetime.datetime(2010, 1, 2) dttm.mydate = '2010-01-02T00:00:00Z' assert dttm.mydate == datetime.datetime(2010, 1, 2) assert properties.DateTime.to_json(dttm.mydate) == '2010-01-02T00:00:00Z' self.assertEqual(dttm.serialize(include_class=False), {'mydate': '2010-01-02T00:00:00Z'}) assert DateTimeOpts.deserialize( {'mydate': '2010-01-02'} ).mydate == datetime.datetime(2010, 1, 2) assert properties.DateTime('').equal(datetime.datetime(2010, 1, 2), datetime.datetime(2010, 1, 2)) assert not properties.DateTime('').equal(datetime.datetime(2010, 1, 2), datetime.datetime(2010, 1, 3))
class Address(BaseModel): """This represents an address.""" lines = properties.List( 'Address line strings', prop=properties.String('Address line string', ), ) city = properties.String( 'City', required=True, ) state = properties.String( 'State', required=True, ) postal_code = properties.String( 'Postal Code', required=True, ) country = properties.String( 'Country', required=True, ) created_at = properties.DateTime( 'UTC time when this address was created.', ) modified_at = properties.DateTime( 'UTC time when this address was modified.', )
class Rating(BaseModel): customer = properties.Instance( 'Partial customer object.', instance_class=Customer, required=True, ) ticket_id = properties.Integer( 'Ticket ID', required=True, ) thread_id = properties.Integer( 'Thread ID', required=True, ) mailbox = properties.Instance( 'Reference to the mailbox that the conversation belongs to.', instance_class=MailboxRef, required=True, ) rating = properties.StringChoice( 'Satisfaction rating.', choices=['Great', 'Okay', 'Bad'], required=True, ) comments = properties.String('Additional comments', ) created_at = properties.DateTime( 'UTC time when this rating was created.', ) modified_at = properties.DateTime( 'UTC time when this rating was modified.', )
class DomainConditionDateTime(DomainCondition): """This represents a date time query.""" value = properties.DateTime( 'Date From', required=True, ) value_to = properties.DateTime('Date To', ) def __init__(self, field, value_from, value_to=None): """Initialize a new datetime query condition. Args: field (str): Field name to search on. This should be the Pythonified name as in the internal models, not the name as provided in the API e.g. ``first_name`` for the Customer's first name instead of ``firstName``. value_from (date or datetime): The start value of the field. value_to (date or datetime, optional): The ending value for the field. If omitted, will search to now. """ return super(DomainConditionDateTime, self).__init__( field=field, value=value_from, value_to=value_to, ) def __str__(self): """Return a string usable as a query part in an API request.""" value_to = self.value_to.isoformat() if self.value_to else '*' return '%s:[%sZ TO %sZ]' % ( self.field_name, self.value.isoformat(), value_to, )
class DateTimeArray(ScalarArray): """Shared array of DateTimes""" array = properties.List( 'Shared array of DateTimes', prop=properties.DateTime(''), default=list, )
class Folder(BaseModel): name = properties.String( 'Folder name', required=True, ) type = properties.StringChoice( 'The type of folder.', choices=[ 'needsattention', 'drafts', 'assigned', 'open', 'closed', 'spam', 'mine', 'team', ], default='drafts', required=True, ) user_id = properties.Integer( 'If the folder type is ``MyTickets``, this represents the Help Scout ' 'user to which this folder belongs. Otherwise it is empty.', ) total_count = properties.Integer( 'Total number of conversations in this folder.', ) active_count = properties.Integer( 'Total number of conversations in this folder that are in an active ' 'state (vs pending).', ) modified_at = properties.DateTime( 'UTC time when this folder was modified.', )
class BaseConversation(BaseModel): """This represents a basic conversation, meant to be subclassed.""" number = properties.Integer( 'The conversation number displayed in the UI. This number can be used ' 'in combination with the id to construct a URI to the conversation on ' 'the Help Scout website. Example: ' '``https://secure.helpscout.net/conversation/<id>/<number>/``', required=True, ) subject = properties.String( 'The subject of the conversation.', required=True, ) status = properties.StringChoice( 'Status of the conversation.', choices=['active', 'pending', 'closed', 'spam'], default='pending', required=True, ) thread_count = properties.Integer( 'This count represents the number of published threads found on the ' 'conversation (it does not include line items, drafts or threads held ' 'for review by Traffic Cop).', required=True, ) preview = properties.String( 'Conversation preview.', ) modified_at = properties.DateTime( 'UTC time when a user last modified this conversation.', )
class Mailbox(MailboxRef): slug = properties.String( 'Key used to represent this Mailbox.', required=True, ) email = properties.String( 'Email address', required=True, ) folders = properties.List( 'Folders that this mailbox contains.', prop=Folder, ) created_at = properties.DateTime( 'UTC time when this mailbox was created.', ) modified_at = properties.DateTime( 'UTC time when this mailbox was modified.', )
class DateTimeColormap(ScalarColormap): """Length-128 color gradient with min/max values, used with DateTimeData""" limits = properties.List( 'Data range associated with the gradient', prop=properties.DateTime(''), min_length=2, max_length=2, default=properties.undefined, )
class Tag(BaseModel): tag = properties.String( 'The tag value.', required=True, ) slug = properties.String( 'Slugified version of the tag value.', ) count = properties.Integer( 'The number of times the tag is used.', ) color = properties.Color( 'The tag color.', ) created_at = properties.DateTime( 'UTC time when this tag was created.', ) modified_at = properties.DateTime( 'UTC time when this tag was modified.', )
class Project(ContentModel): """OMF Project for serializing to .omf file""" author = properties.String('Author', default='') revision = properties.String('Revision', default='') date = properties.DateTime('Date associated with the project data', required=False) units = properties.String('Spatial units of project', default='') elements = properties.List( 'Project Elements', prop=ProjectElement, default=list, ) origin = properties.Vector3('Origin point for all elements in the project', default=[0., 0., 0.])
class TestModel(BaseModel): a_key = properties.String('A key') sub_instance = properties.Instance( 'Sub Instance', instance_class=BaseModel, ) list = properties.List( 'List', prop=properties.Instance('List Instance', instance_class=BaseModel), ) list_string = properties.List( 'List of Strings', prop=properties.String('String'), ) date = properties.DateTime('DateTime') color = properties.Color('Color') not_a_field = True
class DateTimeOpts(properties.HasProperties): mydate = properties.DateTime('my date')
class Conversation(BaseConversation): """This represents a full conversation result.""" type = properties.StringChoice( 'The type of conversation.', choices=['email', 'chat', 'phone', 'spam'], required=True, ) folder_id = properties.Integer( 'ID of the Mailbox Folder to which this conversation resides.', required=True, ) is_draft = properties.Bool( 'Is this a draft conversation? This property duplicates ``draft``, ' 'but both are received in API responses at the same time so neither ' 'can be considered "deprecated".', ) draft = properties.Bool( 'Is this a draft conversation? This property duplicates ' '``is_draft``, but both are received in API responses at the same ' 'time so neither can be considered "deprecated".', ) owner = properties.Instance( 'The Help Scout user who is currently assigned to this conversation.', instance_class=Person, required=True, ) mailbox = properties.Instance( 'The mailbox to which this conversation belongs.', instance_class=MailboxRef, required=True, ) customer = properties.Instance( 'The customer who this conversation is associated with.', instance_class=Person, required=True, ) created_by = properties.Instance( 'The ``Person`` who created this conversation. The ``type`` property ' 'will specify whether it was created by a ``user`` or a ``customer``.', instance_class=Person, ) created_by_type = properties.String( 'The type of user that created this conversation.', ) created_at = properties.DateTime( 'UTC time when this conversation was created.', ) closed_at = properties.DateTime( 'UTC time when this conversation was closed. Null if not closed.', ) closed_by = properties.Instance( 'The Help Scout user who closed this conversation.', instance_class=Person, ) source = properties.Instance( 'Specifies the method in which this conversation was created.', instance_class=Source, ) threads = properties.List( 'Threads associated with the conversation.', prop=Thread, ) cc = properties.List( 'Emails that are CCd.', prop=properties.String('Email Address', ), ) bcc = properties.List( 'Emails that are BCCd.', prop=properties.String('Email Address', ), ) tags = properties.List( 'Tags for the conversation', prop=properties.String('Tag Name'), ) spam = properties.Bool('If this conversation is marked as SPAM.', ) locked = properties.Bool('If this conversation is locked from editing.') user_modified_at = properties.DateTime( 'Last time that this conversation was edited by a user.', )
class EarthquakeInterferogram(properties.HasProperties): title = properties.String( 'name of the earthquake', required=True ) description = properties.String( 'description of the event', required=False ) location = properties.Vector2( 'interferogram location (bottom N, left E)', required=True ) location_UTM_zone = properties.Integer( 'UTM zone', required=True ) shape = properties.Array( 'number of pixels in the interferogram', shape=(2,), dtype=int, required=True ) pixel_size = properties.Array( 'Size of each pixel (northing, easting)', shape=(2,), dtype=float, required=True ) data = properties.Array( 'Processed interferogram data (unwrapped)', dtype=float, required=True ) ref = properties.Vector2( 'interferogram reference', required=True ) ref_incidence = properties.Float( 'Incidence angle', required=True ) scaling = properties.Float( 'Scaling of the interferogram', default=1.0 ) satellite_name = properties.String('Name of the satelite.') satellite_fringe_interval = properties.Float( 'Fringe interval', default=0.028333 ) satellite_azimuth = properties.Float( 'satellite_azimuth', required=True ) satellite_altitude = properties.Float( 'satellite_altitude', required=True ) local_rigidity = properties.Float( 'Local rigidity', default=3e10 ) local_earth_radius = properties.Float( 'Earth radius', default=6371000. ) date1 = properties.DateTime( 'date1', required=True ) date2 = properties.DateTime( 'date2', required=True ) processed_by = properties.String( 'processed_by', required=True ) processed_date = properties.DateTime( 'processed_date', required=True ) copyright = properties.String( 'copyright', required=True ) data_source = properties.String( 'data_source', required=True ) event_date = properties.DateTime('Date of the earthquake') event_gcmt_id = properties.String('GCMT ID') event_name = properties.String('Earthquake name') event_country = properties.String('Earthquake country') def _get_plot_data(self): vectorNx = ( np.r_[ 0, np.cumsum( (self.pixel_size[0],) * self.shape[0] ) ] + self.location[0] ) vectorNy = ( np.r_[ 0, np.cumsum( (self.pixel_size[1],) * self.shape[1] ) ] + self.location[1] ) - self.pixel_size[1] * self.shape[1] data = self.data.copy() data = np.flipud(data.reshape(self.shape, order='F').T) data[data == 0] = np.nan data *= self.scaling return vectorNx, vectorNy, data def plot_interferogram(self, wrap=True, ax=None): self.assert_valid if ax is None: plt.figure() ax = plt.subplot(111) vectorNx, vectorNy, data = self._get_plot_data() if wrap: cmap = plt.cm.hsv data = data % self.satellite_fringe_interval vmin, vmax = 0.0, self.satellite_fringe_interval else: cmap = plt.cm.jet vmin = np.nanmin(data) vmax = np.nanmax(data) out = ax.pcolormesh( vectorNx, vectorNy, np.ma.masked_where(np.isnan(data), data), vmin=vmin, vmax=vmax, cmap=cmap ) ax.set_title(self.title) ax.axis('equal') ax.set_xlabel('Easting, m (UTM Zone {})'.format( self.location_UTM_zone )) ax.set_ylabel('Northing, m') cb = plt.colorbar(out, ax=ax) cb.set_label('Displacement, m') return out def plot_mask(self, ax=None, opacity=0.2): if ax is None: plt.figure() ax = plt.subplot(111) vectorNx, vectorNy, data = self._get_plot_data() from matplotlib import colors cmap = colors.ListedColormap([(1, 1, 1, opacity)]) out = ax.pcolormesh( vectorNx, vectorNy, np.ma.masked_where(~np.isnan(data), data), cmap=cmap ) ax.set_title(self.title) ax.axis('equal') ax.set_xlabel('Easting, m (UTM Zone {})'.format( self.location_UTM_zone )) ax.set_ylabel('Northing, m') return out def get_LOS_vector(self, locations): """ calculate beta - the angle at earth center between reference point and satellite nadir """ if not isinstance(locations, list): locations = [locations] utmZone = self.location_UTM_zone refPoint = vmath.Vector3(self.ref.x, self.ref.y, 0) satAltitude = self.satellite_altitude satAzimuth = self.satellite_azimuth satIncidence = self.ref_incidence earthRadius = self.local_earth_radius DEG2RAD = np.pi / 180. alpha = satIncidence * DEG2RAD beta = (earthRadius / (satAltitude + earthRadius)) * np.sin(alpha) beta = alpha - np.arcsin(beta) beta = beta / DEG2RAD # calculate angular separation of (x,y) from satellite track passing # through (origx, origy) with azimuth satAzimuth # Long lat **NOT** lat long origy, origx = utm.to_latlon( refPoint.x, refPoint.y, np.abs(utmZone), northern=utmZone > 0 ) xy = np.array([ utm.to_latlon(u[0], u[1], np.abs(utmZone), northern=utmZone > 0) for u in locations ]) y = xy[:, 0] x = xy[:, 1] angdist = self._ang_to_gc(x, y, origx, origy, satAzimuth) # calculate beta2, the angle at earth center between roaming point and # satellite nadir track, assuming right-looking satellite beta2 = beta - angdist beta2 = beta2 * DEG2RAD # calculate alpha2, the new incidence angle alpha2 = np.sin(beta2) / ( np.cos(beta2) - (earthRadius / (earthRadius + satAltitude)) ) alpha2 = np.arctan(alpha2) alpha2 = alpha2 / DEG2RAD # calculate pointing vector satIncidence = 90 - alpha2 satAzimuth = 360 - satAzimuth los_x = -np.cos(satAzimuth * DEG2RAD) * np.cos(satIncidence * DEG2RAD) los_y = -np.sin(satAzimuth * DEG2RAD) * np.cos(satIncidence * DEG2RAD) los_z = np.sin(satIncidence * DEG2RAD) return vmath.Vector3Array([los_x, los_y, los_z]) @staticmethod def _ang_to_gc(x, y, origx, origy, satAzimuth): """ Calculate angular distance to great circle passing through given point """ Ngc = np.zeros(3) cartxy = np.zeros((len(x), 3)) satAzimuth = np.deg2rad(satAzimuth) origx = np.deg2rad(origx) origy = np.deg2rad(origy) x = np.deg2rad(x) y = np.deg2rad(y) # 1. calc geocentric norm vec to great circle, Ngc = Rz*Ry*Rx*[0;1;0] # where Rz = rotation of origx about geocentric z-axis # where Ry = rotation of origy about geocentric y-axis # where Rx = rotation of satAzimuth about geocentric x-axis # and [0;1;0] is geocentric norm vec to N-S Great Circle through 0 0 Ngc[0] = ( (np.sin(satAzimuth) * np.sin(origy) * np.cos(origx)) - (np.cos(satAzimuth) * np.sin(origx)) ) Ngc[1] = ( (np.sin(satAzimuth) * np.sin(origy) * np.sin(origx)) + (np.cos(satAzimuth) * np.cos(origx)) ) Ngc[2] = -np.sin(satAzimuth) * np.cos(origy) # 2. calculate unit vector geocentric coordinates for lon/lat # position (x,y) cartxy[:, 0] = np.cos(x) * np.cos(y) cartxy[:, 1] = np.sin(x) * np.cos(y) cartxy[:, 2] = np.sin(y) # 3. Dot product between Ngc and cartxy gives angle 90 degrees # bigger than what we want angdist = ( Ngc[0]*cartxy[:, 0] + Ngc[1]*cartxy[:, 1] + Ngc[2]*cartxy[:, 2] ) angdist = np.rad2deg(np.arccos(angdist)) - 90 return angdist