spacepaste

  1.  
  2. import sqlalchemy
  3. from sqlalchemy.ext.declarative import declarative_base
  4. from sqlalchemy.orm import scoped_session, sessionmaker
  5. Engine = sqlalchemy.create_engine('sqlite:///:memory:', echo=True)
  6. Session = scoped_session(sessionmaker(bind=Engine))
  7. Base = declarative_base()
  8. #sqlalchemy vanilla model
  9. from sqlalchemy import Column, Integer, String, Text, Date, TIMESTAMP
  10. from sqlalchemy.orm import relationship, synonym
  11. from sqlalchemy import Column, ForeignKey, Integer, String, Text, Date, DateTime, TIMESTAMP, Boolean
  12. #These models represent a Google account object
  13. class GoogleAccount_websites(Base):
  14. #SQLAlchemy Table Definition
  15. __tablename__ = 'googleAccountsWebsites'
  16. #website_id = Column('website_id', Integer, primary_key=True)
  17. googleId = Column('googleId', String, ForeignKey('googleAccounts.id'), primary_key=True)
  18. type = Column('type', String, primary_key=True)
  19. value = Column('value', String)
  20. class GoogleAccount_emails(Base):
  21. #SQLAlchemy Table Definition
  22. __tablename__ = 'googleAccountsEmails'
  23. #email_id = Column('email_id', Integer, primary_key=True)
  24. googleId = Column('googleId', String, ForeignKey('googleAccounts.id'), primary_key=True)
  25. address = Column('address', String, primary_key=True)
  26. primary = Column('primary', Boolean)
  27. class GoogleAccount_externalIds(Base):
  28. #SQLAlchemy Table Mapping
  29. __tablename__ = 'googleAccountsExternalIds'
  30. #external_id = Column('external_id', Integer, primary_key=True)
  31. googleId = Column('googleId', String, ForeignKey('googleAccounts.id'), primary_key=True)
  32. customType = Column('customType', String, primary_key=True)
  33. type = Column('type', String)
  34. value = Column('value', String)
  35. class GoogleAccount_name(Base):
  36. #SQLAlchemy Table Mapping
  37. __tablename__ = 'googleAccountsNames'
  38. #name_id = Column('name_id', Integer, primary_key=True)
  39. googleId = Column('googleId', String, ForeignKey('googleAccounts.id'), primary_key=True)
  40. familyName = Column('familyName', String)
  41. fullName = Column('fullName', String)
  42. givenName = Column('givenName', String)
  43. class GoogleAccount_phones(Base):
  44. #SQLAlchemy Table Mapping
  45. __tablename__ = 'googleAccountsPhones'
  46. #phone_id = Column('phone_id', Integer, primary_key=True)
  47. googleId = Column('googleId', String, ForeignKey('googleAccounts.id'), primary_key=True)
  48. type = Column('type', String, primary_key=True)
  49. customType = Column('customType', String)
  50. value = Column('value', String)
  51. class GoogleAccount_aliases(Base):
  52. #SQLAlchemy Table Definition
  53. __tablename__ = 'googleAccountsAliases'
  54. #alias_id = Column('alias_id', Integer, primary_key=True)
  55. googleId = Column('googleId', String, ForeignKey('googleAccounts.id'), primary_key=True)
  56. emailAddress = Column('emailAddress', String, primary_key=True)
  57. class GoogleAccount_nonEditableAliases(Base):
  58. #SQLAlchemy Table Definition
  59. __tablename__ = 'googleAccountsNoneditableAliases'
  60. #noneditable_alias_id = Column('noneditableAliasId', Integer, primary_key=True)
  61. googleId = Column('googleId', String, ForeignKey('googleAccounts.id'), primary_key=True)
  62. emailAddress = Column('emailAddress', String, primary_key=True)
  63. class GoogleAccountSAModel(Base):
  64. #SQLAlchemy Table Definition
  65. __tablename__ = 'googleAccounts'
  66. id = Column('id', String, primary_key=True, nullable=False)
  67. agreedToTerms = Column('agreedToTerms', Boolean)
  68. changePasswordAtNextLogin = Column('changePasswordAtNextLogin', Boolean)
  69. creationTime = Column('creationTime', String)
  70. customerId = Column('customerId', String)
  71. etag = Column('etag', String)
  72. includeInGlobalAddressList = Column('includeInGlobalAddressList', Boolean)
  73. ipWhitelisted = Column('ipWhitelisted', Boolean)
  74. isAdmin = Column('isAdmin', Boolean)
  75. isDelegatedAdmin = Column('isDelegatedAdmin', Boolean)
  76. isMailboxSetup = Column('isMailboxSetup', Boolean)
  77. kind = Column('kind', String)
  78. lastLoginTime = Column('lastLoginTime', String)
  79. orgUnitPath = Column('orgUnitPath', String)
  80. primaryEmail = Column('primaryEmail', String)
  81. suspended = Column('suspended', Boolean)
  82. thumbnailPhotoEtag = Column('thumbnailPhotoEtag', String)
  83. thumbnailPhotoUrl = Column('thumbnailPhotoUrl', String)
  84. websites = relationship('GoogleAccount_websites', collection_class=list,
  85. cascade='all, delete-orphan', passive_deletes=True, backref='GoogleAccountRecord_SA')
  86. emails = relationship('GoogleAccount_emails', collection_class=list,
  87. cascade='all, delete-orphan', passive_deletes=True, backref='GoogleAccountRecord_SA')
  88. externalIds = relationship('GoogleAccount_externalIds', collection_class=list,
  89. cascade='all, delete-orphan', passive_deletes=True, backref='GoogleAccountRecord_SA')
  90. #Name is an Object, not a List type, it must have uselist=False
  91. name = relationship('GoogleAccount_name', uselist=False,
  92. cascade='all, delete-orphan', passive_deletes=True, backref='GoogleAccountRecord_SA')
  93. phones = relationship('GoogleAccount_phones', collection_class=list,
  94. cascade='all, delete-orphan', passive_deletes=True, backref='GoogleAccountRecord_SA')
  95. aliases = relationship('GoogleAccount_aliases', collection_class=list,
  96. cascade='all, delete-orphan', passive_deletes=True, backref='GoogleAccountRecord_SA')
  97. nonEditableAliases = relationship('GoogleAccount_nonEditableAliases', collection_class=list,
  98. cascade='all, delete-orphan', passive_deletes=True, backref='GoogleAccountRecord_SA')
  99. Base.metadata.create_all(Engine)
  100. #Marshmallow-SQLAlchemy Schema - Builds Schema based on ORM Model
  101. from marshmallow_sqlalchemy import ModelSchema
  102. from marshmallow import fields
  103. class GoogleAccountSASchema(ModelSchema):
  104. aliases = fields.Method(deserialize='processAliases')
  105. nonEditableAliases = fields.Method(deserialize='processNonEditableAliases')
  106. class Meta:
  107. model = GoogleAccountSAModel
  108. def deserializeListType(self, value, saType):
  109. saListOut = []
  110. for item in value:
  111. newAlias = saType()
  112. newAlias.emailAddress = item
  113. saListOut.append(newAlias)
  114. return saListOut
  115. def processAliases(self, value):
  116. return self.deserializeListType(value, GoogleAccount_aliases)
  117. def processNonEditableAliases(self, value):
  118. return self.deserializeListType(value, GoogleAccount_nonEditableAliases)
  119. Base.metadata.create_all(Engine)
  120. #start of our test
  121. googleAccountJson = '''{
  122. "agreedToTerms": true,
  123. "aliases": ["jd@testd.com",
  124. "j.e.doe@testd.com",
  125. "zz.dd@testd.com",
  126. "ff.qwerty@testd.com"
  127. ],
  128. "changePasswordAtNextLogin": false,
  129. "creationTime": "",
  130. "customerId": "C02fmcj1i",
  131. "emails": [{
  132. "address": "john.doe@testd.com",
  133. "primary": true
  134. }],
  135. "etag": "XRsypGOPUmlmxokHB51cC07Vb3s/nnW4dSQR5PacZne6JsXkOrTfeBM",
  136. "externalIds": [{
  137. "customType": "GID",
  138. "type": "custom",
  139. "value": "336168"
  140. }],
  141. "id": "108488927407595610966",
  142. "includeInGlobalAddressList": true,
  143. "ipWhitelisted": false,
  144. "isAdmin": false,
  145. "isDelegatedAdmin": true,
  146. "isMailboxSetup": true,
  147. "kind": "admin#directory#user",
  148. "lastLoginTime": "",
  149. "name": {
  150. "familyName": "Doe",
  151. "fullName": "John E Doe",
  152. "givenName": "John E"
  153. },
  154. "nonEditableAliases": ["john.doe@webmail.testd.com",
  155. "doe@webmail.testd.com",
  156. "john.doe2@webmail.testd.com",
  157. "john_doe3@webmail.testd.com",
  158. "john.doe4@webmail.testd.com"
  159. ],
  160. "orgUnitPath": "/Ops",
  161. "phones": [{
  162. "type": "work",
  163. "value": "+1 839-439-7484"
  164. }, {
  165. "type": "mobile",
  166. "value": "+1 407-393-5048"
  167. }, {
  168. "customType": "Ztel",
  169. "type": "custom",
  170. "value": "4-439-7484"
  171. }],
  172. "primaryEmail": "john.e.doe@testd.com",
  173. "suspended": false,
  174. "thumbnailPhotoEtag": "XRGOPUm7Vb3s/mVS-OxFB0Dyxo8",
  175. "thumbnailPhotoUrl": "https://plus.google.com/_/focus/photos/public/AI0YzcyZjRhNWZjOWExNDkwARP7Z8ODD5GJ",
  176. "websites": [{
  177. "type": "profile",
  178. "value": "https://employees.testd.com/seto/336168"
  179. }]
  180. }'''
  181. from sqlalchemy.orm.attributes import get_history, instance_state
  182. def GetUncommittedChangesDict(saRow):
  183. #{'columnName' : {'newValue' : '2', 'oldValue' : '1'}
  184. #for key in z:
  185. changed = False
  186. changedDict = {}
  187. unchangedDict = {}
  188. outDict = {}
  189. for column in saRow._sa_instance_state.attrs:
  190. if (column.history):
  191. if (column.history.added) and (not column.history.deleted):
  192. changed = True
  193. changedDict[column.key] = {'newValue' : column.history.added, 'oldValue' : None}
  194. outDict[column.key] = {'newValue' : column.history.added, 'oldValue' : None}
  195. if (column.history.unchanged):
  196. unchangedDict[column.key] = {'newValue' : None, 'oldValue' : column.history.unchanged}
  197. outDict[column.key] = {'newValue' : None, 'oldValue' : column.history.unchanged}
  198. if (column.history.deleted):
  199. changed = True
  200. changedDict[column.key] = {'newValue' : column.history.added, 'oldValue' : None}
  201. outDict[column.key] = {'newValue' : column.history.added, 'oldValue' : column.history.deleted}
  202. return changed, changedDict, unchangedDict, outDict
  203. def hasChanged(saRow):
  204. changed, changedDict, unchangedDict, outDict = GetUncommittedChangesDict(saRow)
  205. print('Change detected: {}'.format(changedDict))
  206. return changed
  207. #First pass
  208. x = GoogleAccountSASchema(strict=True, session=Session).loads(googleAccountJson)
  209. firstRow = x.data
  210. print('1.1- Session.dirty: {}\n\n'.format(Session.dirty))
  211. print('1.1- hasChanged(firstRow): {}\n\n'.format(hasChanged(firstRow)))
  212. Session.add(firstRow)
  213. #Expected hasChanged == True
  214. print('1.2- Session.dirty: {}\n'.format(Session.dirty))
  215. print('1.2- hasChanged(firstRow): {}\n\n'.format(hasChanged(firstRow)))
  216. Session.commit()
  217. #Expected hasChanged == False
  218. print('1.3- Session.dirty: {}\n'.format(Session.dirty))
  219. print('1.3- hasChanged(firstRow): {}\n\n'.format(hasChanged(firstRow)))
  220. print()
  221. y = GoogleAccountSASchema(strict=True, session=Session).loads(googleAccountJson_CHANGED)
  222. secondRow = y.data
  223. print('2.1- Session.dirty: {}\n'.format(Session.dirty))
  224. print('2.1- hasChanged(secondRow): {}\n\n'.format(hasChanged(secondRow)))
  225. print(get_history(secondRow, 'websites'))
  226. #Session.merge(secondRow)
  227. #print('2.2- Session.dirty: {}\n'.format(Session.dirty))
  228. #print('2.2- hasChanged(firstRow): {}\n\n'.format(hasChanged(secondRow)))
  229. #z = GoogleAccountSASchema(strict=True, session=Session).loads(googleAccountJson)
  230. #thirdRow = z.data
  231. #print('3.1- Session.dirty: {}\n'.format(Session.dirty))
  232. #print('3.1- hasChanged(firstRow): {}\n\n'.format(hasChanged(secondRow)))
  233. print()
  234. """
  235. Test Output-
  236. 1.1- Session.dirty: IdentitySet([])
  237. Change detected: {'phones': {'newValue': [<__main__.GoogleAccount_phones object at 0x10a44bf50>, <__main__.GoogleAccount_phones object at 0x10a463b50>, <__main__.GoogleAccount_phones object at 0x10a44b890>], 'oldValue': None}, u'isDelegatedAdmin': {'newValue': [True], 'oldValue': None}, u'thumbnailPhotoEtag': {'newValue': [u'XRGOPUm7Vb3s/mVS-OxFB0Dyxo8'], 'oldValue': None}, u'suspended': {'newValue': [False], 'oldValue': None}, u'id': {'newValue': [u'108488927407595610966'], 'oldValue': None}, 'aliases': {'newValue': [<__main__.GoogleAccount_aliases object at 0x10a44b990>, <__main__.GoogleAccount_aliases object at 0x10a44b810>, <__main__.GoogleAccount_aliases object at 0x10a44be50>, <__main__.GoogleAccount_aliases object at 0x10a44bed0>], 'oldValue': None}, 'nonEditableAliases': {'newValue': [<__main__.GoogleAccount_nonEditableAliases object at 0x10a44bd90>, <__main__.GoogleAccount_nonEditableAliases object at 0x10a44bcd0>, <__main__.GoogleAccount_nonEditableAliases object at 0x10a463bd0>, <__main__.GoogleAccount_nonEditableAliases object at 0x10a463dd0>, <__main__.GoogleAccount_nonEditableAliases object at 0x10a463c50>], 'oldValue': None}, u'thumbnailPhotoUrl': {'newValue': [u'https://plus.google.com/_/focus/photos/public/AI0YzcyZjRhNWZjOWExNDkwARP7Z8ODD5GJ'], 'oldValue': None}, u'includeInGlobalAddressList': {'newValue': [True], 'oldValue': None}, u'isAdmin': {'newValue': [False], 'oldValue': None}, u'etag': {'newValue': [u'XRsypGOPUmlmxokHB51cC07Vb3s/nnW4dSQR5PacZne6JsXkOrTfeBM'], 'oldValue': None}, u'lastLoginTime': {'newValue': [u''], 'oldValue': None}, u'orgUnitPath': {'newValue': [u'/Ops'], 'oldValue': None}, u'agreedToTerms': {'newValue': [True], 'oldValue': None}, 'externalIds': {'newValue': [<__main__.GoogleAccount_externalIds object at 0x10a463f90>], 'oldValue': None}, u'ipWhitelisted': {'newValue': [False], 'oldValue': None}, u'primaryEmail': {'newValue': [u'john.e.doe@testd.com'], 'oldValue': None}, u'isMailboxSetup': {'newValue': [True], 'oldValue': None}, 'emails': {'newValue': [<__main__.GoogleAccount_emails object at 0x10a471fd0>], 'oldValue': None}, u'kind': {'newValue': [u'admin#directory#user'], 'oldValue': None}, 'name': {'newValue': [<__main__.GoogleAccount_name object at 0x10a477bd0>], 'oldValue': None}, u'creationTime': {'newValue': [u''], 'oldValue': None}, 'websites': {'newValue': [<__main__.GoogleAccount_websites object at 0x10a47d810>], 'oldValue': None}, u'changePasswordAtNextLogin': {'newValue': [False], 'oldValue': None}, u'customerId': {'newValue': [u'C02fmcj1i'], 'oldValue': None}}
  238. 1.1- hasChanged(firstRow): True
  239. 1.2- Session.dirty: IdentitySet([])
  240. Change detected: {'phones': {'newValue': [<__main__.GoogleAccount_phones object at 0x10a44bf50>, <__main__.GoogleAccount_phones object at 0x10a463b50>, <__main__.GoogleAccount_phones object at 0x10a44b890>], 'oldValue': None}, u'isDelegatedAdmin': {'newValue': [True], 'oldValue': None}, u'thumbnailPhotoEtag': {'newValue': [u'XRGOPUm7Vb3s/mVS-OxFB0Dyxo8'], 'oldValue': None}, u'suspended': {'newValue': [False], 'oldValue': None}, u'id': {'newValue': [u'108488927407595610966'], 'oldValue': None}, 'aliases': {'newValue': [<__main__.GoogleAccount_aliases object at 0x10a44b990>, <__main__.GoogleAccount_aliases object at 0x10a44b810>, <__main__.GoogleAccount_aliases object at 0x10a44be50>, <__main__.GoogleAccount_aliases object at 0x10a44bed0>], 'oldValue': None}, 'nonEditableAliases': {'newValue': [<__main__.GoogleAccount_nonEditableAliases object at 0x10a44bd90>, <__main__.GoogleAccount_nonEditableAliases object at 0x10a44bcd0>, <__main__.GoogleAccount_nonEditableAliases object at 0x10a463bd0>, <__main__.GoogleAccount_nonEditableAliases object at 0x10a463dd0>, <__main__.GoogleAccount_nonEditableAliases object at 0x10a463c50>], 'oldValue': None}, u'thumbnailPhotoUrl': {'newValue': [u'https://plus.google.com/_/focus/photos/public/AI0YzcyZjRhNWZjOWExNDkwARP7Z8ODD5GJ'], 'oldValue': None}, u'includeInGlobalAddressList': {'newValue': [True], 'oldValue': None}, u'isAdmin': {'newValue': [False], 'oldValue': None}, u'etag': {'newValue': [u'XRsypGOPUmlmxokHB51cC07Vb3s/nnW4dSQR5PacZne6JsXkOrTfeBM'], 'oldValue': None}, u'lastLoginTime': {'newValue': [u''], 'oldValue': None}, u'orgUnitPath': {'newValue': [u'/Ops'], 'oldValue': None}, u'agreedToTerms': {'newValue': [True], 'oldValue': None}, 'externalIds': {'newValue': [<__main__.GoogleAccount_externalIds object at 0x10a463f90>], 'oldValue': None}, u'ipWhitelisted': {'newValue': [False], 'oldValue': None}, u'primaryEmail': {'newValue': [u'john.e.doe@testd.com'], 'oldValue': None}, u'isMailboxSetup': {'newValue': [True], 'oldValue': None}, 'emails': {'newValue': [<__main__.GoogleAccount_emails object at 0x10a471fd0>], 'oldValue': None}, u'kind': {'newValue': [u'admin#directory#user'], 'oldValue': None}, 'name': {'newValue': [<__main__.GoogleAccount_name object at 0x10a477bd0>], 'oldValue': None}, u'creationTime': {'newValue': [u''], 'oldValue': None}, 'websites': {'newValue': [<__main__.GoogleAccount_websites object at 0x10a47d810>], 'oldValue': None}, u'changePasswordAtNextLogin': {'newValue': [False], 'oldValue': None}, u'customerId': {'newValue': [u'C02fmcj1i'], 'oldValue': None}}
  241. 1.2- hasChanged(firstRow): True
  242. 1.3- Session.dirty: IdentitySet([])
  243. Change detected: {}
  244. 1.3- hasChanged(firstRow): False
  245. 2.1- Session.dirty: IdentitySet([<__main__.GoogleAccountSAModel object at 0x10a47d890>, <__main__.GoogleAccount_websites object at 0x10a4d2c10>])
  246. Change detected: {'websites': {'newValue': [<__main__.GoogleAccount_websites object at 0x10a4c3d10>], 'oldValue': None}}
  247. 2.1- hasChanged(firstRow): True
  248. History(added=[<__main__.GoogleAccount_websites object at 0x106f38c10>], unchanged=[], deleted=[<__main__.GoogleAccount_websites object at 0x106f38210>])
  249. """
  250.