trex-model 1.4.4__tar.gz → 1.4.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of trex-model might be problematic. Click here for more details.

Files changed (61) hide show
  1. {trex-model-1.4.4 → trex-model-1.4.6}/PKG-INFO +1 -1
  2. {trex-model-1.4.4 → trex-model-1.4.6}/setup.py +1 -1
  3. {trex-model-1.4.4 → trex-model-1.4.6}/trex_model.egg-info/PKG-INFO +1 -1
  4. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/conf.py +8 -7
  5. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/marketing_models.py +244 -2
  6. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/merchant_models.py +49 -74
  7. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/message_model_helper.py +32 -19
  8. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/message_models.py +38 -4
  9. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/product_models.py +7 -6
  10. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/user_models.py +21 -8
  11. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/voucher_models.py +4 -1
  12. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/merchant_helpers.py +7 -1
  13. {trex-model-1.4.4 → trex-model-1.4.6}/LICENSE +0 -0
  14. {trex-model-1.4.4 → trex-model-1.4.6}/MANIFEST.in +0 -0
  15. {trex-model-1.4.4 → trex-model-1.4.6}/README.md +0 -0
  16. {trex-model-1.4.4 → trex-model-1.4.6}/setup.cfg +0 -0
  17. {trex-model-1.4.4 → trex-model-1.4.6}/trex_model.egg-info/SOURCES.txt +0 -0
  18. {trex-model-1.4.4 → trex-model-1.4.6}/trex_model.egg-info/dependency_links.txt +0 -0
  19. {trex-model-1.4.4 → trex-model-1.4.6}/trex_model.egg-info/requires.txt +0 -0
  20. {trex-model-1.4.4 → trex-model-1.4.6}/trex_model.egg-info/top_level.txt +0 -0
  21. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/__init__.py +0 -0
  22. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/__init__.py +0 -0
  23. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/__init__.py +0 -0
  24. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/admin_models.py +0 -0
  25. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/analytic_models.py +0 -0
  26. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/app_models.py +0 -0
  27. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/coporate_models.py +0 -0
  28. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/customer_model_helpers.py +0 -0
  29. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/customer_models.py +0 -0
  30. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/fb_subsriber_models.py +0 -0
  31. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/import_models.py +0 -0
  32. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/inventory_model.py +0 -0
  33. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/loyalty_models.py +0 -0
  34. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/lucky_draw_models.py +0 -0
  35. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/membership_models.py +0 -0
  36. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/model_decorators.py +0 -0
  37. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/ndb_models.py +0 -0
  38. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/pos_models.py +0 -0
  39. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/prepaid_models.py +0 -0
  40. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/program_models.py +0 -0
  41. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/rating_models.py +0 -0
  42. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/recruit_models.py +0 -0
  43. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/redeem_models.py +0 -0
  44. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/redemption_catalogue_models.py +0 -0
  45. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/referral_program_model.py +0 -0
  46. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/reward_model_helpers.py +0 -0
  47. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/reward_models.py +0 -0
  48. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/spending_base_program_model.py +0 -0
  49. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/system_models.py +0 -0
  50. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/task_models.py +0 -0
  51. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/test_models.py +0 -0
  52. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/datastore/transaction_models.py +0 -0
  53. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/model_decorator.py +0 -0
  54. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/models/prepaid_helpers.py +0 -0
  55. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/pos_conf.py +0 -0
  56. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/program_conf.py +0 -0
  57. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/utils/__init__.py +0 -0
  58. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/utils/gcloud/__init__.py +0 -0
  59. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/utils/gcloud/datastore_util.py +0 -0
  60. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/utils/model/__init__.py +0 -0
  61. {trex-model-1.4.4 → trex-model-1.4.6}/trexmodel/utils/model/model_util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trex-model
3
- Version: 1.4.4
3
+ Version: 1.4.6
4
4
  Summary: TRex database module package
5
5
  Home-page: https://bitbucket.org/lokjac/trex-model
6
6
  Author: Jack Lok
@@ -3,7 +3,7 @@ with open("README.md", "r") as fh:
3
3
  long_description = fh.read()
4
4
  setuptools.setup(
5
5
  name='trex-model',
6
- version='1.4.4',
6
+ version='1.4.6',
7
7
  author="Jack Lok",
8
8
  author_email="sglok77@gmail.com",
9
9
  description="TRex database module package",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trex-model
3
- Version: 1.4.4
3
+ Version: 1.4.6
4
4
  Summary: TRex database module package
5
5
  Home-page: https://bitbucket.org/lokjac/trex-model
6
6
  Author: Jack Lok
@@ -28,13 +28,14 @@ DATASTORE_CREDENTIAL_PATH = os.path.abspath(os.path.dirnam
28
28
 
29
29
  MERCHANT_STAT_FIGURE_UPDATE_INTERVAL_IN_MINUTES = os.environ.get('MERCHANT_STAT_FIGURE_UPDATE_INTERVAL_IN_MINUTES') or 60
30
30
 
31
- MESSAGE_CATEGORY_ANNOUNCEMENT = 'announcement'
32
- MESSAGE_CATEGORY_ALERT = 'alert'
33
- MESSAGE_CATEGORY_PROMOTION = 'promotion'
34
- MESSAGE_CATEGORY_SURVEY = 'survey'
35
- MESSAGE_CATEGORY_SYSTEM = 'system'
36
- MESSAGE_CATEGORY_REWARD = 'reward'
37
- MESSAGE_CATEGORY_REDEEM = 'redeem'
31
+ MESSAGE_CATEGORY_ANNOUNCEMENT = 'announcement'
32
+ MESSAGE_CATEGORY_ALERT = 'alert'
33
+ MESSAGE_CATEGORY_PROMOTION = 'promotion'
34
+ MESSAGE_CATEGORY_SURVEY = 'survey'
35
+ MESSAGE_CATEGORY_SYSTEM = 'system'
36
+ MESSAGE_CATEGORY_REWARD = 'reward'
37
+ MESSAGE_CATEGORY_REDEEM = 'redeem'
38
+ MESSAGE_CATEGORY_REDEMPTION_CATALOGUE = 'redemption_catalogue'
38
39
 
39
40
  MESSAGE_CATEGORIES = (MESSAGE_CATEGORY_ANNOUNCEMENT, MESSAGE_CATEGORY_ALERT, MESSAGE_CATEGORY_PROMOTION, MESSAGE_CATEGORY_SURVEY, MESSAGE_CATEGORY_SYSTEM, MESSAGE_CATEGORY_REWARD, MESSAGE_CATEGORY_REDEEM)
40
41
 
@@ -9,9 +9,11 @@ from trexmodel.models.datastore.merchant_models import MerchantUser,\
9
9
  MerchantAcct
10
10
  from trexlib.utils.string_util import is_not_empty, random_string
11
11
  from datetime import datetime
12
- from trexconf import conf
12
+ from trexconf import conf, program_conf
13
13
  import trexmodel.conf as model_conf
14
14
  import logging
15
+ from _datetime import timedelta
16
+ from trexconf.program_conf import MERCHANT_NEWS_STATUS_PUBLISH
15
17
 
16
18
  logger = logging.getLogger('model')
17
19
 
@@ -271,4 +273,244 @@ class ScheduledPushNotificationHistory(BaseNModel, DictModel):
271
273
  ndb.AND(
272
274
  ScheduledPushNotificationHistory.scheduled_datetime<=scheduled_datetime)).count(limit=conf.MAX_FETCH_RECORD)
273
275
 
274
-
276
+ class MerchantNewsFile(BaseNModel, DictModel):
277
+ '''
278
+ Merchant Account as ancestor
279
+ '''
280
+ label = ndb.StringProperty(required=True)
281
+ desc = ndb.StringProperty(required=False)
282
+ news_text = ndb.StringProperty(required=False)
283
+
284
+ news_file_type = ndb.StringProperty(required=False)
285
+ news_file_public_url = ndb.StringProperty(required=False)
286
+ news_file_storage_filename = ndb.StringProperty(required=False)
287
+
288
+ completed_status = ndb.StringProperty(required=True, choices=set(program_conf.MERCHANT_NEWS_STATUS))
289
+
290
+ start_date = ndb.DateProperty(required=True)
291
+ end_date = ndb.DateProperty(required=True)
292
+
293
+ enabled = ndb.BooleanProperty(default=True)
294
+
295
+ is_archived = ndb.BooleanProperty(default=False)
296
+
297
+ created_datetime = ndb.DateTimeProperty(required=True, auto_now_add=True)
298
+ modified_datetime = ndb.DateTimeProperty(required=False)
299
+ published_datetime = ndb.DateTimeProperty(required=False)
300
+ archived_datetime = ndb.DateTimeProperty(required=False)
301
+
302
+ created_by = ndb.KeyProperty(name="created_by", kind=MerchantUser)
303
+ created_by_username = ndb.StringProperty(required=False)
304
+
305
+ modified_by = ndb.KeyProperty(name="modified_by", kind=MerchantUser)
306
+ modified_by_username = ndb.StringProperty(required=False)
307
+
308
+ published_by = ndb.KeyProperty(name="published_by", kind=MerchantUser)
309
+ published_by_username = ndb.StringProperty(required=False)
310
+
311
+ archived_by = ndb.KeyProperty(name="archived_by", kind=MerchantUser)
312
+ archived_by_username = ndb.StringProperty(required=False)
313
+
314
+
315
+ dict_properties = ['image_url', 'completed_progress_in_percentage','is_published', 'is_enabled',
316
+ 'label', 'desc', 'news_text', 'start_date', 'end_date', 'enabled', 'is_archived',
317
+ 'completed_status']
318
+
319
+ @property
320
+ def merchant_acct(self):
321
+ return MerchantAcct.fetch(self.key.parent().urlsafe())
322
+
323
+ @property
324
+ def image_url(self):
325
+ if self.news_file_public_url:
326
+ return self.news_file_public_url
327
+ else:
328
+ return conf.MERCHANT_NEWS_DEFAULT_IMAGE
329
+
330
+ @property
331
+ def completed_progress_in_percentage(self):
332
+
333
+ return program_conf.merchant_news_completed_progress_percentage(self.completed_status)
334
+
335
+ @property
336
+ def is_published(self):
337
+ return self.completed_status == MERCHANT_NEWS_STATUS_PUBLISH
338
+
339
+ @property
340
+ def is_enabled(self):
341
+ return self.enabled
342
+
343
+ @staticmethod
344
+ def list_by_merchant_acct(merchant_acct):
345
+ result = MerchantNewsFile.query(ndb.AND(MerchantNewsFile.is_archived==False), ancestor=merchant_acct.create_ndb_key()).fetch(limit=conf.MAX_FETCH_RECORD)
346
+ return result
347
+
348
+ @staticmethod
349
+ def list_archived_by_merchant_acct(merchant_acct):
350
+ return MerchantNewsFile.query(ndb.AND(MerchantNewsFile.is_archived==True), ancestor=merchant_acct.create_ndb_key()).fetch(limit=model_conf.MAX_FETCH_RECORD)
351
+
352
+ @staticmethod
353
+ def create(merchant_acct, label=None, desc=None, news_text=None,
354
+ start_date=None, end_date=None, created_by=None):
355
+
356
+ created_by_username = None
357
+ if is_not_empty(created_by):
358
+ if isinstance(created_by, MerchantUser):
359
+ created_by_username = created_by.username
360
+
361
+ news_file = MerchantNewsFile(
362
+ parent = merchant_acct.create_ndb_key(),
363
+ label = label,
364
+ desc = desc,
365
+ news_text = news_text,
366
+ start_date = start_date,
367
+ end_date = end_date,
368
+ created_by = created_by.create_ndb_key(),
369
+ created_by_username = created_by_username,
370
+ created_datetime = datetime.utcnow(),
371
+ completed_status = program_conf.MERCHANT_NEWS_STATUS_BASE,
372
+ )
373
+
374
+ news_file.put()
375
+
376
+ return news_file
377
+
378
+ @staticmethod
379
+ def update(merchant_news, label=None, desc=None, news_text=None,
380
+ start_date=None, end_date=None, modified_by=None):
381
+
382
+ modified_by_username = None
383
+ if is_not_empty(modified_by):
384
+ if isinstance(modified_by, MerchantUser):
385
+ modified_by_username = modified_by.username
386
+
387
+ merchant_news.label = label
388
+ merchant_news.desc = desc
389
+ merchant_news.news_text = news_text
390
+ merchant_news.start_date = start_date
391
+ merchant_news.end_date = end_date
392
+ merchant_news.completed_status = program_conf.MERCHANT_NEWS_STATUS_BASE
393
+ merchant_news.modified_by = modified_by
394
+ merchant_news.modified_by_username = modified_by_username
395
+ merchant_news.modified_datetime = datetime.utcnow()
396
+
397
+
398
+ merchant_news.put()
399
+
400
+ @staticmethod
401
+ def upload_file(merchant_news, uploading_file, merchant_acct, bucket, news_file_type=None):
402
+ file_prefix = random_string(8)
403
+ news_file_storage_filename = 'merchant/'+merchant_acct.key_in_str+'/news/'+file_prefix+'-'+uploading_file.filename
404
+ blob = bucket.blob(news_file_storage_filename)
405
+
406
+ logger.debug('news_file_storage_filename=%s', news_file_storage_filename)
407
+
408
+ blob.upload_from_string(
409
+ uploading_file.read(),
410
+ content_type=uploading_file.content_type
411
+ )
412
+
413
+ uploaded_url = blob.public_url
414
+
415
+ logger.debug('uploaded_url=%s', uploaded_url)
416
+ logger.debug('news_file_type=%s', news_file_type)
417
+
418
+ if is_not_empty(merchant_news.news_file_storage_filename):
419
+ old_logo_blob = bucket.get_blob(merchant_news.news_file_storage_filename)
420
+ if old_logo_blob:
421
+ old_logo_blob.delete()
422
+
423
+
424
+ merchant_news.news_file_public_url = uploaded_url
425
+ merchant_news.news_file_storage_filename = news_file_storage_filename
426
+ merchant_news.news_file_type = news_file_type
427
+
428
+
429
+ merchant_news.put()
430
+
431
+ return merchant_news
432
+
433
+ @staticmethod
434
+ def update_news_material_uploaded(merchant_news, modified_by=None):
435
+
436
+ modified_by_username = None
437
+
438
+ if is_not_empty(modified_by):
439
+ if isinstance(modified_by, MerchantUser):
440
+ modified_by_username = modified_by.username
441
+
442
+ merchant_news.completed_status = program_conf.MERCHANT_NEWS_STATUS_UPLOAD_MATERIAL
443
+ merchant_news.modified_by = modified_by.create_ndb_key()
444
+ merchant_news.modified_by_username = modified_by_username
445
+ merchant_news.modified_datetime = datetime.utcnow()
446
+
447
+ merchant_news.put()
448
+
449
+ return merchant_news
450
+
451
+ @staticmethod
452
+ def remove_file(news_file, bucket):
453
+
454
+ old_logo_blob = bucket.get_blob(news_file.news_file_storage_filename)
455
+ if old_logo_blob:
456
+ old_logo_blob.delete()
457
+ news_file.delete()
458
+
459
+ def archived(self, archived_by=None):
460
+
461
+ archived_by_username = None
462
+ if is_not_empty(archived_by):
463
+ if isinstance(archived_by, MerchantUser):
464
+ archived_by_username = archived_by.username
465
+
466
+ self.is_archived = True
467
+ self.archived_datetime = datetime.utcnow()
468
+ self.archived_by = archived_by.create_ndb_key()
469
+ self.archived_by_username = archived_by_username
470
+ self.put()
471
+
472
+ merchant_acct = self.merchant_acct
473
+ merchant_acct.remove_merchant_news(self.key_in_str)
474
+
475
+ def to_configuration(self):
476
+ return {
477
+ 'merchant_news_key' : self.key_in_str,
478
+ 'image_url' : self.image_url,
479
+ 'label' : self.label,
480
+ 'content' : self.news_text,
481
+ 'start_datetime' : self.start_date.strftime('%d-%m-%Y %H:%M:%S'),
482
+ 'end_datetime' : self.end_date.strftime('%d-%m-%Y %H:%M:%S'),
483
+ }
484
+
485
+ def publish(self, published_by=None):
486
+
487
+ published_by_username = None
488
+ if is_not_empty(published_by):
489
+ if isinstance(published_by, MerchantUser):
490
+ published_by_username = published_by.username
491
+
492
+ self.completed_status = program_conf.MERCHANT_NEWS_STATUS_PUBLISH
493
+ self.published_by = published_by.create_ndb_key()
494
+ self.published_by_username = published_by_username
495
+ self.published_datetime = datetime.utcnow()
496
+ self.put()
497
+
498
+ merchant_acct = self.merchant_acct
499
+ merchant_acct.add_merchant_news(self.to_configuration())
500
+
501
+ @staticmethod
502
+ def disable_news(merchant_news):
503
+ merchant_news.enabled = False
504
+ merchant_news.put()
505
+
506
+ merchant_acct = merchant_news.merchant_acct
507
+ merchant_acct.remove_merchant_news(merchant_news.key_in_str)
508
+
509
+
510
+ @staticmethod
511
+ def enable_news(merchant_news):
512
+ merchant_news.enabled = True
513
+ merchant_news.put()
514
+
515
+ merchant_acct = merchant_news.merchant_acct
516
+ merchant_acct.add_merchant_news(merchant_news.to_configuration())
@@ -69,6 +69,7 @@ class MerchantAcct(MerchantMin):
69
69
  published_referral_program_configuration = ndb.JsonProperty()
70
70
  published_voucher_configuration = ndb.JsonProperty()
71
71
  published_redemption_catalogue_configuration = ndb.JsonProperty()
72
+ published_news_configuration = ndb.JsonProperty()
72
73
 
73
74
  membership_configuration = ndb.JsonProperty()
74
75
  tier_membership_configuration = ndb.JsonProperty()
@@ -91,11 +92,11 @@ class MerchantAcct(MerchantMin):
91
92
  'registered_datetime', 'modified_datetime', 'plan_start_date', 'plan_end_date', 'currency_code',
92
93
  'timezone', 'effective_referral_program_count',
93
94
  'published_program_configuration', 'published_referral_program_configuration',
94
- 'published_voucher_configuration', 'membership_configuration',
95
+ 'published_voucher_configuration', 'published_news_configuration', 'membership_configuration',
95
96
  'tier_membership_configuration', 'prepaid_configuration', 'lucky_draw_configuration', 'product_modifier_configuration',
96
97
  'dashboard_stat_figure', 'program_settings', 'is_tier_membership_configured', 'website',
97
98
  'product_package', 'loyalty_package','account_plan','outlet_count', 'outlet_limit',
98
- 'published_redemption_catalogue_configuration',
99
+ 'published_redemption_catalogue_configuration','logo_public_url'
99
100
  ]
100
101
 
101
102
 
@@ -636,7 +637,51 @@ class MerchantAcct(MerchantMin):
636
637
  self.published_voucher_configuration['vouchers'] = new_vouchers_list
637
638
  self.published_voucher_configuration['count'] = len(new_vouchers_list)
638
639
 
639
- self.put()
640
+ self.put()
641
+
642
+ def add_merchant_news(self, merchant_news_configuration):
643
+ if is_empty(self.published_news_configuration):
644
+ self.published_news_configuration = {
645
+ 'news' :[merchant_news_configuration],
646
+ 'count' : 1,
647
+ }
648
+
649
+ else:
650
+ existing_merchant_news_list = self.published_news_configuration.get('news')
651
+
652
+ merchant_news_key = merchant_news_configuration.get('merchant_news_key')
653
+
654
+ if len(existing_merchant_news_list)>0:
655
+ index = 0
656
+ for v in existing_merchant_news_list:
657
+ if v.get('merchant_news_key') == merchant_news_key:
658
+ existing_merchant_news_list.pop(index)
659
+
660
+ index = index+1
661
+
662
+ existing_merchant_news_list.append(merchant_news_configuration)
663
+
664
+ self.published_news_configuration['news'] = existing_merchant_news_list
665
+ self.published_news_configuration['count'] = len(existing_merchant_news_list)
666
+
667
+ self.put()
668
+
669
+ def remove_merchant_news(self, merchant_news_key):
670
+ existing_merchant_news_list = self.published_news_configuration['news']
671
+
672
+ latest_news_list = []
673
+ #now = datetime.now()
674
+ for merchant_news in existing_merchant_news_list:
675
+ if merchant_news.get('merchant_news_key') != merchant_news_key:
676
+ #end_datetime = datetime.strptime('%d-%m-%Y %H:%m:%S', merchant_news.ge('end_datetime'))
677
+ #if end_datetime >
678
+ latest_news_list.append(merchant_news)
679
+
680
+
681
+ self.published_news_configuration['news'] = latest_news_list
682
+ self.published_news_configuration['count'] = len(latest_news_list)
683
+
684
+ self.put()
640
685
 
641
686
  def update_prepaid_program(self, prepaid_configuration):
642
687
  if self.prepaid_configuration is None or len(self.prepaid_configuration)==0:
@@ -1895,74 +1940,4 @@ class BannerFile(BaseNModel, DictModel):
1895
1940
  old_logo_blob.delete()
1896
1941
  banner_file.delete()
1897
1942
 
1898
- class MerchantNewsFile(BaseNModel, DictModel):
1899
- '''
1900
- Merchant Account as ancestor
1901
- '''
1902
- label = ndb.StringProperty(required=False)
1903
- desc = ndb.StringProperty(required=False)
1904
- news_text = ndb.StringProperty(required=False)
1905
-
1906
- news_file_type = ndb.StringProperty(required=True)
1907
- news_file_public_url = ndb.StringProperty(required=True)
1908
- news_file_storage_filename = ndb.StringProperty(required=True)
1909
- start_date = ndb.DateProperty(required=True)
1910
- end_date = ndb.DateProperty(required=True)
1911
-
1912
-
1913
- dict_properties = ['news_file_public_url', 'news_file_storage_filename', 'news_file_type', 'label', 'desc', 'news_text', 'start_date', 'end_date']
1914
-
1915
- @staticmethod
1916
- def list_by_merchant_acct(merchant_acct):
1917
- result = MerchantNewsFile.query(ancestor=merchant_acct.create_ndb_key()).fetch(limit=conf.MAX_FETCH_RECORD)
1918
- return result
1919
-
1920
- @staticmethod
1921
- def create(merchant_acct, label=None, desc=None, news_text=None):
1922
- news_file = MerchantNewsFile(
1923
- parent = merchant_acct.create_ndb_key(),
1924
- label = label,
1925
- desc = desc,
1926
- news_text = news_text,
1927
- )
1928
-
1929
- news_file.put()
1930
-
1931
- @staticmethod
1932
- def upload_file(merchant_news, uploading_file, merchant_acct, bucket, news_file_type=None):
1933
- file_prefix = random_string(8)
1934
- news_file_storage_filename = 'merchant/'+merchant_acct.key_in_str+'/news/'+file_prefix+'-'+uploading_file.filename
1935
- blob = bucket.blob(news_file_storage_filename)
1936
-
1937
- logger.debug('news_file_storage_filename=%s', news_file_storage_filename)
1938
-
1939
- blob.upload_from_string(
1940
- uploading_file.read(),
1941
- content_type=uploading_file.content_type
1942
- )
1943
-
1944
- uploaded_url = blob.public_url
1945
-
1946
- logger.debug('uploaded_url=%s', uploaded_url)
1947
- logger.debug('news_file_type=%s', news_file_type)
1948
-
1949
- if is_not_empty(merchant_news.news_file_storage_filename):
1950
- old_logo_blob = bucket.get_blob(merchant_news.news_file_storage_filename)
1951
- if old_logo_blob:
1952
- old_logo_blob.delete()
1953
-
1954
-
1955
- merchant_news.news_file_public_url = uploaded_url
1956
- merchant_news.news_file_storage_filename = news_file_storage_filename
1957
- merchant_news.news_file_type = news_file_type
1958
-
1959
-
1960
- merchant_news.put()
1961
-
1962
- @staticmethod
1963
- def remove_file(news_file, bucket):
1964
-
1965
- old_logo_blob = bucket.get_blob(news_file.news_file_storage_filename)
1966
- if old_logo_blob:
1967
- old_logo_blob.delete()
1968
- news_file.delete()
1943
+
@@ -6,7 +6,7 @@ Created on 9 Nov 2023
6
6
  from trexmodel.program_conf import REWARD_FORMAT_MAP, REWARD_FORMAT_PREPAID
7
7
  from trexmodel.models.datastore.message_models import Message
8
8
  from trexmodel.conf import MESSAGE_CATEGORY_REWARD, MESSAGE_STATUS_NEW,\
9
- MESSAGE_CATEGORY_REDEEM
9
+ MESSAGE_CATEGORY_REDEEM, MESSAGE_CATEGORY_REDEMPTION_CATALOGUE
10
10
  from trexconf import program_conf
11
11
  from babel.numbers import format_currency
12
12
  import logging
@@ -140,23 +140,26 @@ message_logo_image_template = '''
140
140
 
141
141
 
142
142
  def create_transaction_message(customer_transaction):
143
- user_acct = customer_transaction.transact_user_acct
144
- source_key = customer_transaction.key_in_str
143
+ user_acct = customer_transaction.transact_user_acct
144
+ source_key = customer_transaction.key_in_str
145
+ customer = customer_transaction.transact_customer_acct
146
+ merchant_acct = customer.registered_merchant_acct
145
147
  message = Message.get_by_source_key(source_key)
146
148
 
147
149
  if message is None:
148
150
  message = Message(
151
+ parent = user_acct.create_ndb_key(),
149
152
  message_to = user_acct.create_ndb_key(),
150
153
  title = 'Transaction Reward',
151
154
  message_category = MESSAGE_CATEGORY_REWARD,
152
155
  message_content = __create_entiled_message_from_customer_transaction(customer_transaction),
153
156
  message_data = __create_message_data_from_transaction(customer_transaction),
154
157
  status = MESSAGE_STATUS_NEW,
155
-
158
+ message_from = merchant_acct.brand_name,
156
159
  )
157
160
  message.put()
158
- user_acct.new_message_count+=1
159
- user_acct.put()
161
+ #user_acct.new_message_count+=1
162
+ #user_acct.put()
160
163
 
161
164
  return message
162
165
 
@@ -168,46 +171,50 @@ def create_redemption_message(customer_redemption):
168
171
 
169
172
  if message is None:
170
173
  message = Message(
174
+ parent = user_acct.create_ndb_key(),
171
175
  source_key = source_key,
172
176
  message_to = user_acct.create_ndb_key(),
173
177
  title = 'Redemption',
174
178
  message_category = MESSAGE_CATEGORY_REDEEM,
175
179
  message_content = __create_message_from_redeem_transaction(merchant_acct, customer_redemption),
176
- message_data = __create_message_data_from_transaction(customer_redemption),
180
+ message_data = __create_message_data_from_redemption(customer_redemption),
177
181
  status = MESSAGE_STATUS_NEW,
178
-
182
+ message_from = merchant_acct.brand_name,
179
183
  )
180
184
  message.put()
181
- user_acct.new_message_count+=1
182
- user_acct.put()
185
+ #user_acct.new_message_count+=1
186
+ #user_acct.put()
183
187
 
184
188
  return message
189
+
185
190
 
186
191
  def create_redeem_catalogue_item_message(customer, entitled_vouchers_summary, redemption_catalogue_transaction):
187
- user_acct = customer.registered_user_acct
188
- source_key = redemption_catalogue_transaction.key_in_str
189
- message = Message.get_by_source_key(source_key)
192
+ user_acct = customer.registered_user_acct
193
+ merchant_acct = customer.registered_merchant_acct
194
+ source_key = redemption_catalogue_transaction.key_in_str
195
+ message = Message.get_by_source_key(source_key)
190
196
 
191
197
  if message is None:
192
198
  message = Message(
193
-
199
+ parent = user_acct.create_ndb_key(),
194
200
  message_to = user_acct.create_ndb_key(),
195
201
  title = 'Redemption Catalogue Reward',
196
- message_category = MESSAGE_CATEGORY_REWARD,
202
+ message_category = MESSAGE_CATEGORY_REDEMPTION_CATALOGUE,
197
203
  message_content = __create_message_from_redeem_catalogue_transaction(customer, entitled_vouchers_summary),
198
204
  message_data = __create_message_data_from_redemption_catalogue(redemption_catalogue_transaction),
199
205
  status = MESSAGE_STATUS_NEW,
206
+ message_from = merchant_acct.brand_name,
200
207
 
201
208
  )
202
209
  message.put()
203
- user_acct.new_message_count+=1
204
- user_acct.put()
210
+ #user_acct.new_message_count+=1
211
+ #user_acct.put()
205
212
 
206
213
  return message
207
214
 
208
215
  def __create_message_data_from_transaction(customer_transaction):
209
216
  return {
210
- 'transaction_key' : customer_transaction.key_in_str,
217
+ 'reward_transaction_key' : customer_transaction.key_in_str,
211
218
 
212
219
  }
213
220
 
@@ -215,7 +222,13 @@ def __create_message_data_from_redemption_catalogue(redemption_catalogue_transac
215
222
  return {
216
223
  'redemption_catalogue_transaction_key' : redemption_catalogue_transaction.key_in_str,
217
224
 
218
- }
225
+ }
226
+
227
+ def __create_message_data_from_redemption(redemption_transaction):
228
+ return {
229
+ 'redemption_transaction_key' : redemption_transaction.key_in_str,
230
+
231
+ }
219
232
 
220
233
  def __create_entiled_message_from_customer_transaction(customer_transaction):
221
234
 
@@ -13,6 +13,9 @@ from trexmodel import conf
13
13
  from trexmodel.conf import MESSAGE_STATUS_SET, MESSAGE_STATUS_NEW, MESSAGE_CATEGORY_SYSTEM,\
14
14
  MESSAGE_STATUS_READ, MESSAGE_CATEGORIES, MESSAGE_CATEGORY_ANNOUNCEMENT
15
15
  from trexmodel.models.datastore.transaction_models import CustomerTransaction
16
+ from trexmodel.models.datastore.redeem_models import RedemptionCatalogueTransaction,\
17
+ CustomerRedemption
18
+ from trexmodel.models.datastore.customer_models import Customer
16
19
 
17
20
 
18
21
  logger = logging.getLogger('model')
@@ -29,12 +32,16 @@ content_body=[text in html]
29
32
  '''
30
33
 
31
34
  class Message(BaseNModel, DictModel):
35
+ '''
36
+ User as ancestor
37
+ '''
32
38
  message_to = ndb.KeyProperty(name="message_to", kind=User)
33
39
  source_key = ndb.StringProperty(required=False)
34
40
  title = ndb.StringProperty(required=True)
35
41
  message_category = ndb.StringProperty(required=True, default=MESSAGE_CATEGORY_SYSTEM, choices=MESSAGE_CATEGORIES)
36
42
  message_content = ndb.JsonProperty(required=True, indexed=False)
37
43
  message_data = ndb.JsonProperty(required=True, indexed=False)
44
+ message_from = ndb.StringProperty(required=False)
38
45
  status = ndb.StringProperty(required=True, default=MESSAGE_STATUS_NEW, choices=MESSAGE_STATUS_SET)
39
46
  created_datetime = ndb.DateTimeProperty(required=True, auto_now_add=True)
40
47
  read_datetime = ndb.DateTimeProperty(required=False)
@@ -51,13 +58,27 @@ class Message(BaseNModel, DictModel):
51
58
 
52
59
  @property
53
60
  def customer_transaction_entity(self):
54
- customer_transaction_key = self.message_data.get('transaction_key')
61
+ customer_transaction_key = self.message_data.get('reward_transaction_key')
55
62
  if customer_transaction_key:
56
- return CustomerTransaction.fetch(customer_transaction_key)
63
+ return CustomerTransaction.fetch(customer_transaction_key)
64
+
65
+ @property
66
+ def customer_redemption_catalogue_entity(self):
67
+ customer_redemption_key = self.message_data.get('redemption_catalogue_transaction_key')
68
+ if customer_redemption_key:
69
+ return RedemptionCatalogueTransaction.fetch(customer_redemption_key)
70
+
71
+ @property
72
+ def customer_redemption_entity(self):
73
+ customer_redemption_key = self.message_data.get('redemption_transaction_key')
74
+ if customer_redemption_key:
75
+ return CustomerRedemption.fetch(customer_redemption_key)
76
+
57
77
 
58
78
  @staticmethod
59
- def create(source_key=None, message_to=None, title=None, message_type=MESSAGE_CATEGORY_ANNOUNCEMENT,message_content={}):
79
+ def create(user_acct, source_key=None, message_to=None, title=None, message_type=MESSAGE_CATEGORY_ANNOUNCEMENT,message_content={}):
60
80
  Message(
81
+ parent = user_acct.create_ndb_key(),
61
82
  source_key = source_key,
62
83
  message_to = message_to.create_ndb_key(),
63
84
  title = title,
@@ -75,12 +96,13 @@ class Message(BaseNModel, DictModel):
75
96
  message.status = MESSAGE_STATUS_READ
76
97
  message.read_datetime = datetime.utcnow()
77
98
  message.put()
78
-
99
+ '''
79
100
  user_acct.new_message_count-=1
80
101
  if user_acct.new_message_count<0:
81
102
  user_acct.new_message_count=0
82
103
 
83
104
  user_acct.put()
105
+ '''
84
106
 
85
107
  @staticmethod
86
108
  def update_delete(message_key):
@@ -92,10 +114,22 @@ class Message(BaseNModel, DictModel):
92
114
 
93
115
  @staticmethod
94
116
  def list_paginated_by_user_account(user_acct, limit=conf.MAX_FETCH_RECORD, start_cursor=None):
117
+ #query = Message.query(ancestor=user_acct.create_ndb_key()).order(-Message.created_datetime)
95
118
  query = Message.query(ndb.AND(Message.message_to==user_acct.create_ndb_key())).order(-Message.created_datetime)
96
119
 
97
120
  return Message.list_all_with_condition_query(query, limit=limit, start_cursor=start_cursor, return_with_cursor=True, keys_only=False)
98
121
 
122
+
123
+ @staticmethod
124
+ def count_new_message(user_acct):
125
+ return Message.count_by_status(user_acct, status=conf.MESSAGE_STATUS_NEW)
126
+
127
+ @staticmethod
128
+ def count_by_status(user_acct, status=conf.MESSAGE_STATUS_NEW):
129
+ query = Message.query(ndb.AND(Message.status==status), ancestor=user_acct.create_ndb_key()).order(-Message.created_datetime)
130
+
131
+ return Message.count_with_condition_query(query, limit=conf.MAX_FETCH_RECORD)
132
+
99
133
 
100
134
  @staticmethod
101
135
  def get_by_source_key(source_key):
@@ -382,7 +382,7 @@ class Product(ProductBase, FullTextSearchable):
382
382
  product_sku = ndb.StringProperty(required=True)
383
383
  product_name = ndb.StringProperty(required=True)
384
384
  product_desc = ndb.StringProperty(required=False)
385
- category_key = ndb.StringProperty(required=False)
385
+ category_key = ndb.StringProperty(required=False)
386
386
 
387
387
  barcode = ndb.StringProperty(required=False)
388
388
 
@@ -408,7 +408,8 @@ class Product(ProductBase, FullTextSearchable):
408
408
 
409
409
  fulltextsearch_field_name = 'product_name'
410
410
 
411
- dict_properties = ['product_sku', 'product_name', 'category_key', 'price', 'cost', 'product_desc', 'barcode', 'pos_settings', 'tax_details', 'product_modifier']
411
+ dict_properties = ['product_sku', 'product_name', 'category_key', 'price', 'cost',
412
+ 'product_desc', 'barcode', 'pos_settings', 'tax_details', 'product_modifier']
412
413
 
413
414
  @property
414
415
  def product_default_image(self):
@@ -434,6 +435,7 @@ class Product(ProductBase, FullTextSearchable):
434
435
  'label' : self.product_name,
435
436
  'desc' : self.product_desc,
436
437
  'price' : self.price,
438
+ 'category_key' : self.category_key,
437
439
  'barcode' : self.barcode,
438
440
  'modifiers' : modifier_setting_list_for_pos,
439
441
  'representation_on_pos' : self.pos_settings,
@@ -780,10 +782,9 @@ class ProductCatalogue(ProductBase):
780
782
  return total_count
781
783
 
782
784
  def publish(self):
783
- if self.is_publish == False:
784
- self.published_menu_settings = self.setting_details_for_pos
785
- self.is_publish = True
786
- self.put()
785
+ self.published_menu_settings = self.setting_details_for_pos
786
+ self.is_publish = True
787
+ self.put()
787
788
 
788
789
  def unpublish(self):
789
790
  self.is_publish = False
@@ -6,7 +6,7 @@ Created on 10 Apr 2020
6
6
  from google.cloud import ndb
7
7
  from trexmodel.models.datastore.ndb_models import BaseNModel, DictModel
8
8
  from trexlib.utils.string_util import random_number, random_string,\
9
- is_empty
9
+ is_empty, is_not_empty
10
10
  from flask_login import UserMixin
11
11
  import logging
12
12
  from trexlib.utils.security_util import generate_user_id, hash_password
@@ -37,14 +37,14 @@ class UserMin(BaseNModel, DictModel, UserMixin):
37
37
 
38
38
  locked = ndb.BooleanProperty(required=False, default=False)
39
39
  lock_expiry_datetime = ndb.DateTimeProperty(required=False)
40
- active = ndb.BooleanProperty(required=True, default=True)
40
+ active = ndb.BooleanProperty(required=True, default=True)
41
41
  try_count = ndb.IntegerProperty(required=False)
42
42
  demo_account = ndb.BooleanProperty(required=False, default=False)
43
43
  deleted = ndb.BooleanProperty(required=False, default=False)
44
+ signin_device_session = ndb.JsonProperty()
44
45
 
45
46
 
46
-
47
- dict_properties = ['user_id', 'name', 'email', 'gravatar_url', 'active', 'locked', 'lock_expiry_datetime']
47
+ dict_properties = ['user_id', 'name', 'email', 'signin_device_session', 'gravatar_url', 'active', 'locked', 'lock_expiry_datetime']
48
48
 
49
49
  @classmethod
50
50
  #@cache.memoize(timeout=60)
@@ -63,6 +63,18 @@ class UserMin(BaseNModel, DictModel, UserMixin):
63
63
  def is_admin_user(self):
64
64
  return False
65
65
 
66
+ @property
67
+ def signin_device_id(self):
68
+ if is_not_empty(self.signin_device_session):
69
+ return self.signin_device_session.get('device_id')
70
+
71
+ @property
72
+ def signin_expiry_datetime(self):
73
+ if is_not_empty(self.signin_device_session):
74
+ expiry_datetime_str = self.signin_device_session.get('expiry_datetime')
75
+ if is_not_empty(expiry_datetime_str):
76
+ return datetime.strptime(expiry_datetime_str, '%d-%m-%Y %H:%M:%S')
77
+
66
78
  def is_valid_password(self, checking_password):
67
79
  hashed_signin_password = hash_password(self.user_id, checking_password)
68
80
 
@@ -266,11 +278,11 @@ class UserBase(UserMin):
266
278
 
267
279
  if is_email_verified==False:
268
280
  created_user.email_vc = random_number(6)
269
- created_user.email_vc_expiry_datetime = datetime.now() + timedelta(minutes=10)
281
+ created_user.email_vc_expiry_datetime = datetime.utcnow()() + timedelta(minutes=10)
270
282
 
271
283
  if is_mobile_phone_verified==False:
272
284
  created_user.mobile_phone_vc = random_number(6)
273
- created_user.mobile_phone_vc_expiry_datetime = datetime.now() + timedelta(minutes=10)
285
+ created_user.mobile_phone_vc_expiry_datetime = datetime.utcnow() + timedelta(minutes=10)
274
286
 
275
287
  created_user.put()
276
288
 
@@ -379,7 +391,8 @@ class User(UserBase):
379
391
  'mobile_phone', 'email', 'password', 'name', 'birth_date', 'gender',
380
392
  'reference_code', 'country', 'state', 'city', 'postcode',
381
393
  'address', 'redeem_pin', 'new_message_count', 'status',
382
- 'device_details',
394
+ 'device_details', 'signin_device_session', 'referral_code',
395
+ 'last_login_datetime',
383
396
  ]
384
397
 
385
398
  #unique_attributes = 'email,username'
@@ -387,7 +400,7 @@ class User(UserBase):
387
400
  audit_properties = [
388
401
  'mobile_phone', 'email', 'password', 'name', 'birth_date', 'gender',
389
402
  'reference_code', 'country', 'state', 'city', 'postcode',
390
- 'address', 'redeem_pin', 'status', 'device_details',
403
+ 'address', 'redeem_pin', 'status', 'device_details',
391
404
 
392
405
  ]
393
406
 
@@ -258,10 +258,13 @@ class MerchantVoucher(VoucherBase):
258
258
  merchant_voucher.terms_and_conditions = terms_and_conditions
259
259
  merchant_voucher.redeem_limit_type = redeem_limit_type
260
260
  merchant_voucher.redeem_limit_count = redeem_limit_count
261
- merchant_voucher.completed_status = program_conf.VOUCHER_STATUS_BASE
261
+
262
262
  merchant_voucher.modified_by = modified_by.create_ndb_key()
263
263
  merchant_voucher.modified_by_username = modified_by_username
264
264
 
265
+ if merchant_voucher.completed_status!=program_conf.VOUCHER_STATUS_BASE:
266
+ merchant_voucher.completed_status = program_conf.VOUCHER_STATUS_PUBLISH
267
+
265
268
  merchant_voucher.put()
266
269
 
267
270
  return merchant_voucher
@@ -36,6 +36,8 @@ def construct_merchant_acct_info(merchant_acct):
36
36
  referral_program_settings = merchant_acct.program_settings.get('referral_program', {})
37
37
 
38
38
 
39
+
40
+ '''
39
41
  merchant_news_list = [
40
42
  {
41
43
  'image_url': 'https://scontent.cdninstagram.com/v/t39.30808-6/395358935_817823987014226_84933362912055577_n.jpg?stp=dst-jpg_e35&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi42NTF4NjUwLnNkci5mMzA4MDgifQ&_nc_ht=scontent.cdninstagram.com&_nc_cat=102&_nc_ohc=v_kSSi1t6JgQ7kNvgERj6Vi&edm=APs17CUAAAAA&ccb=7-5&ig_cache_key=MzIyMjUxMzQ0MzI0ODIyMzYzMg%3D%3D.2-ccb7-5&oh=00_AfAUoP1jFeskadyDc4xrwkZb1PuH9t5JSajL6Q5tKAwCew&oe=663DAE72&_nc_sid=10d13b',
@@ -51,8 +53,12 @@ def construct_merchant_acct_info(merchant_acct):
51
53
  }
52
54
 
53
55
  ]
54
-
56
+ '''
55
57
  merchant_news_list=[]
58
+
59
+ if merchant_acct.published_news_configuration:
60
+ merchant_news_list = merchant_acct.published_news_configuration['news']
61
+
56
62
  info = {
57
63
  'key' : merchant_acct.key_in_str,
58
64
  'company_name' : merchant_acct.company_name,
File without changes
File without changes
File without changes
File without changes