class Processor(Publishable, db.DynamicDocument): identifier = db.StringField(max_length=100, unique=True) module = db.StringField(max_length=255) requires = db.ListField(db.StringField(max_length=255)) description = db.StringField() title = db.StringField() image = db.ReferenceField(Image, reverse_delete_rule=db.NULLIFY) link = db.StringField(max_length=255) config = db.DictField(default=lambda: {}) pipeline = db.ListField(db.StringField(max_length=255), default=[]) def import_processor(self): return import_string(self.module) def get_instance(self, *args, **kwargs): if 'config' not in kwargs: kwargs['config'] = self.config kwargs['_record'] = self return self.import_processor()(*args, **kwargs) def clean(self, *args, **kwargs): for item in (self.requires or []): import_string(item) super(Processor, self).clean(*args, **kwargs) def __unicode__(self): return self.identifier @classmethod def get_instance_by_identifier(cls, identifier, cart=None): processor = cls.objects.get(identifier=identifier) return processor.get_instance(cart=cart) @classmethod def get_default_processor(cls): default = lazy_str_setting('CART_DEFAULT_PROCESSOR', default={ 'module': 'quokka.modules.cart.processors.Dummy', 'identifier': 'dummy', 'published': True, 'title': "Test" }) try: return cls.objects.get(identifier=default['identifier']) except: return cls.objects.create(**default) def save(self, *args, **kwargs): self.import_processor() super(Processor, self).save(*args, **kwargs)
class Course(BaseProduct): pre_requisites = db.StringField() duration = db.StringField(max_length=255) classes = db.ListField(db.EmbeddedDocumentField(ClassRoom)) variants = db.ListField(db.EmbeddedDocumentField(CourseVariant)) def get_description(self, *args, **kwargs): try: classroom = self.classes.get(slug=kwargs.get('classroom')) except: classroom = None if not classroom: return self.description return "".join([self.description, classroom.get_description()]) def get_summary(self, *args, **kwargs): try: classroom = self.classes.get(slug=kwargs.get('classroom')) except: classroom = None summary = getattr(self, 'summary', None) if not summary: try: return self.get_description(*args, **kwargs)[:140] except: summary = self.title if not classroom: return summary return "<br>".join([summary, classroom.get_description()]) def is_unique_slug(self, items): if not items: return True slugs = [item.slug for item in items] return len(slugs) == len(set(slugs)) def clean(self): [cls.clean() for cls in self.classes] if not self.is_unique_slug(self.classes): raise db.ValidationError("Classroom slugs duplicated") if not self.is_unique_slug(self.variants): raise db.ValidationError("Variants slugs duplicated")
class Channeling(object): channel = db.ReferenceField(Channel, required=True, reverse_delete_rule=db.DENY) related_channels = db.ListField( db.ReferenceField('Channel', reverse_delete_rule=db.PULL)) related_mpath = db.ListField(db.StringField()) show_on_channel = db.BooleanField(default=True) channel_roles = db.ListField(db.StringField()) def populate_related_mpath(self): self.related_mpath = [rel.mpath for rel in self.related_channels] def populate_channel_roles(self): self.channel_roles = [role.name for role in self.channel.roles]
class HasCustomValue(object): values = db.ListField(db.EmbeddedDocumentField(CustomValue)) def get_values_tuple(self): return [(item.name, item.value, item.formatter) for item in self.values] def get_value(self, name, default=None): try: return self.values.get(name=name).value except: return default def add_value(self, name, value, formatter='text'): custom_value = CustomValue( name=name, value=value, formatter=formatter ) self.values.append(custom_value) def clean(self): current_names = [value.name for value in self.values] for name in current_names: if current_names.count(name) > 1: raise Exception(_l( '%(name)s already exists', name=name )) super(HasCustomValue, self).clean()
class Channeling(object): channel = db.ReferenceField(Channel, required=True, reverse_delete_rule=db.DENY) related_channels = db.ListField( db.ReferenceField('Channel', reverse_delete_rule=db.PULL) ) show_on_channel = db.BooleanField(default=True)
class ClassRoom(Slugged, Publishable, db.EmbeddedDocument): WEEKDAYS = ( ("sun", _l("Sunday")), ("mon", _l("Monday")), ("tue", _l("Tuesday")), ("wed", _l("Wednesday")), ("thu", _l("Thursday")), ("fri", _l("Friday")), ("sat", _l("Saturday")), ) title = db.StringField(required=True, max_length=100) description = db.StringField() weekdays = db.ListField(db.StringField(choices=WEEKDAYS), default=[]) start_date = db.DateTimeField() end_date = db.DateTimeField() status = db.StringField() def get_description(self): return "<br>".join([ self.title, self.description, ",".join(self.weekdays), self.start_date.strftime("%Y-%m-%d") if self.start_date else '' ]) def get_weekdays_display(self, **kwargs): data = dict(self.WEEKDAYS) data.update(kwargs) return [data.get(k) for k in self.weekdays] def clean(self): self.validate_slug() def __unicode__(self): return self.title
class HasCustomValue(object): values = db.ListField(db.EmbeddedDocumentField(CustomValue)) def get_values_tuple(self): return [(value.name, value.value, value.formatter) for value in self.values] def get_value(self, name, default=None): try: return self.values.get(name=name).value except: return default def add_value(self, name, value, formatter='text'): """ Another implementation data = {"name": name, "rawvalue": value, "formatter": formatter} self.values.update(data, name=name) or self.values.create(**data) """ custom_value = CustomValue( name=name, value=value, formatter=formatter ) self.values.append(custom_value) def clean(self): current_names = [value.name for value in self.values] for name in current_names: if current_names.count(name) > 1: raise Exception(_l("%(name)s already exists", name=name)) super(HasCustomValue, self).clean()
class Channeling(object): channel = db.ReferenceField(Channel, required=True, reverse_delete_rule=db.DENY) # Objects can be in only one main channel it gives an url # but the objects can also be relates to other channels related_channels = db.ListField( db.ReferenceField('Channel', reverse_delete_rule=db.NULLIFY) ) show_on_channel = db.BooleanField(default=True)
class Channeling(object): channel = db.ReferenceField(Channel, required=True, reverse_delete_rule=db.DENY, verbose_name=_l('Channel')) related_channels = db.ListField( db.ReferenceField('Channel', reverse_delete_rule=db.PULL), verbose_name=_l('Related Channels') ) show_on_channel = db.BooleanField(default=True, verbose_name=_l('Show On Channel'))
class HasCustomValue(object): values = db.ListField(db.EmbeddedDocumentField(CustomValue)) def clean(self): current_names = [value.name for value in self.values] for name in current_names: if current_names.count(name) > 1: raise Exception(lazy_gettext("%(name)s already exists", name=name)) super(HasCustomValue, self).clean()
class Content(HasCustomValue, Publishable, LongSlugged, Commentable, Channeling, Tagged, db.DynamicDocument): title = db.StringField(max_length=255, required=True) summary = db.StringField(required=False) template_type = db.ReferenceField(ContentTemplateType, required=False, reverse_delete_rule=db.NULLIFY) contents = db.ListField(db.EmbeddedDocumentField(SubContent)) model = db.StringField() meta = { 'allow_inheritance': True, 'indexes': ['-created_at', 'slug'], 'ordering': ['-created_at'] } def get_themes(self): themes = self.channel.get_themes() theme = self.template_type and self.template_type.theme_name if theme: themes.insert(0, theme) return list(set(themes)) def get_absolute_url(self, endpoint='detail'): if self.channel.is_homepage: long_slug = self.slug else: long_slug = self.long_slug try: return url_for(self.URL_NAMESPACE, long_slug=long_slug) except: return url_for(endpoint, long_slug=long_slug) def __unicode__(self): return self.title @property def model_name(self): return self.__class__.__name__.lower() @property def module_name(self): module = self.__module__ module_name = module.replace('quokka.modules.', '').split('.')[0] return module_name def heritage(self): self.model = "{0}.{1}".format(self.module_name, self.model_name) def save(self, *args, **kwargs): self.validate_slug() self.validate_long_slug() self.heritage() super(Content, self).save(*args, **kwargs)
class Comment(Publishable, BaseComment, db.Document): path = db.StringField(max_length=255, required=True, verbose_name=_l('Path')) replies = db.ListField(db.EmbeddedDocumentField(Reply), verbose_name=_l('Replies')) def __unicode__(self): return u"{0} - {1}...".format(self.author_name, self.body[:15]) meta = {"ordering": ['-created_at'], "indexes": ['-created_at', 'path']}
class Owned(object): created_by = db.ReferenceField(User) last_updated_by = db.ReferenceField(User) authors = db.ListField(db.ReferenceField(User)) def get_authors(self, sortedf=None): return set(self.authors + [self.created_by]) @property def has_multiple_authors(self): return len(self.get_authors()) > 1
class HasCustomValue(object): values = db.ListField(db.EmbeddedDocumentField(CustomValue)) def get_values_tuple(self): return [(value.name, value.value, value.formatter) for value in self.values] def clean(self): current_names = [value.name for value in self.values] for name in current_names: if current_names.count(name) > 1: raise Exception(_l("%(name)s already exists", name=name)) super(HasCustomValue, self).clean()
class Comment(Publishable, BaseComment, db.Document): path = db.StringField(max_length=255, required=True) replies = db.ListField(db.EmbeddedDocumentField(Reply)) def __unicode__(self): return u"{0} - {1}...".format(self.author_name, self.body[:15]) def get_canonical_url(self): return "/{0}.{1}".format( self.path, get_setting_value( 'CONTENT_EXTENSION', 'html')) if not self.path.startswith("/") else self.path meta = {"ordering": ['-created_at'], "indexes": ['-created_at', 'path']}
class User(db.DynamicDocument, UserMixin): name = db.StringField(max_length=255) email = db.EmailField(max_length=255, unique=True) password = db.StringField(max_length=255) active = db.BooleanField(default=True) confirmed_at = db.DateTimeField() roles = db.ListField( db.ReferenceField(Role, reverse_delete_rule=db.DENY), default=[] ) last_login_at = db.DateTimeField() current_login_at = db.DateTimeField() last_login_ip = db.StringField(max_length=255) current_login_ip = db.StringField(max_length=255) login_count = db.IntField() username = db.StringField(max_length=50, required=False, unique=True) def clean(self, *args, **kwargs): if not self.username: self.username = User.generate_username(self.email) try: super(User, self).clean(*args, **kwargs) except: pass @classmethod def generate_username(cls, email): username = email.lower() for item in ['@', '.', '-', '+']: username = username.replace(item, '_') return username @classmethod def createuser(cls, name, email, password, active=True, roles=None, username=None): username = username or cls.generate_username(email) return cls.objects.create( name=name, email=email, password=encrypt_password(password), active=active, roles=roles, username=username ) def __unicode__(self): return "{0} <{1}>".format(self.name or '', self.email)
class User(db.Document, UserMixin): name = db.StringField(max_length=255) email = db.StringField(max_length=255) password = db.StringField(max_length=255) active = db.BooleanField(default=True) confirmed_at = db.DateTimeField() roles = db.ListField(db.ReferenceField(Role, reverse_delete_rule=db.DENY), default=[]) last_login_at = db.DateTimeField() current_login_at = db.DateTimeField() last_login_ip = db.StringField(max_length=255) current_login_ip = db.StringField(max_length=255) login_count = db.IntField() def __unicode__(self): return "{} <{}>".format(self.name or '', self.email)
class User(db.DynamicDocument, ThemeChanger, HasCustomValue, UserMixin, HasImages): name = db.StringField(max_length=255) email = db.EmailField(max_length=255, unique=True) password = db.StringField(max_length=255) active = db.BooleanField(default=True) confirmed_at = db.DateTimeField() roles = db.ListField(db.ReferenceField(Role, reverse_delete_rule=db.DENY), default=[]) last_login_at = db.DateTimeField() current_login_at = db.DateTimeField() last_login_ip = db.StringField(max_length=255) current_login_ip = db.StringField(max_length=255) login_count = db.IntField() username = db.StringField(max_length=50, required=False, unique=True) remember_token = db.StringField(max_length=255) authentication_token = db.StringField(max_length=255) tagline = db.StringField(max_length=255) bio = db.StringField() links = db.ListField(db.EmbeddedDocumentField(UserLink)) use_avatar_from = db.StringField(choices=(("gravatar", "gravatar"), ("url", "url"), ("upload", "upload"), ("facebook", "facebook")), default='gravatar') gravatar_email = db.EmailField(max_length=255) avatar_file_path = db.StringField() avatar_url = db.StringField(max_length=255) @property def display_name(self): rtn = None if self.name: rtn = self.name elif self.username: rtn = self.username elif self.email: rtn = self.email else: rtn = str(self) return rtn def get_avatar_url(self, *args, **kwargs): if self.use_avatar_from == 'url': return self.avatar_url elif self.use_avatar_from == 'upload': return url_for('quokka.core.media', filename=self.avatar_file_path) elif self.use_avatar_from == 'facebook': try: return Connection.objects( provider_id='facebook', user_id=self.id, ).first().image_url except Exception as e: logger.warning( '%s use_avatar_from is set to facebook but: Error: %s' % (self.display_name, str(e))) return Gravatar()(self.get_gravatar_email(), *args, **kwargs) @property def summary(self): return (self.bio or self.tagline or '')[:255] def get_gravatar_email(self): return self.gravatar_email or self.email def clean(self, *args, **kwargs): if not self.username: self.username = User.generate_username(self.name) super(User, self).clean(*args, **kwargs) @classmethod def generate_username(cls, name, user=None): name = name or '' username = slugify(name) filters = {"username": username} if user: filters["id__ne"] = user.id if cls.objects.filter(**filters).count(): username = "******".format(username, randint(1, 1000)) return username def set_password(self, password, save=False): self.password = encrypt_password(password) if save: self.save() @classmethod def createuser(cls, name, email, password, active=True, roles=None, username=None, *args, **kwargs): username = username or cls.generate_username(name) if 'links' in kwargs: kwargs['links'] = [UserLink(**link) for link in kwargs['links']] return cls.objects.create(name=name, email=email, password=encrypt_password(password), active=active, roles=roles, username=username, *args, **kwargs) @property def display_name(self): return abbreviate(self.name) or self.email def __unicode__(self): return u"{0} <{1}>".format(self.name or '', self.email) @property def connections(self): return Connection.objects(user_id=str(self.id))
class HasImages(object): images = db.ListField(db.ReferenceField(DBImage))
class MediaGallery(Content): body = db.StringField(required=False) media_type = db.StringField(choices=(('file','File'),('video','Video'),('audio','Audio'),('image','Image')), default=('image','Image')) items = db.ListField(db.ReferenceField(Image))
class Content(HasCustomValue, Publishable, LongSlugged, Channeling, Tagged, ContentFormat, db.DynamicDocument): title = db.StringField(max_length=255, required=True) summary = db.StringField(required=False) template_type = db.ReferenceField(ContentTemplateType, required=False, reverse_delete_rule=db.NULLIFY) contents = db.ListField(db.EmbeddedDocumentField(SubContent)) model = db.StringField() comments_enabled = db.BooleanField(default=True) license = db.EmbeddedDocumentField(License) shortened_url = db.EmbeddedDocumentField(ShortenedURL) image_file = db.ImageField() meta = { 'allow_inheritance': True, 'indexes': ['-created_at', 'slug'], 'ordering': ['-created_at'], } @classmethod def available_objects(cls, **filters): now = datetime.datetime.now() default_filters = { "published": True, 'available_at__lte': now, } default_filters.update(filters) return cls.objects(**default_filters) def get_main_image_url(self, thumb=False, default=None, identifier='mainimage'): """method returns the path (url) of the main image """ if identifier in ['mainimage', 'dbimage', 'postimage' ] and self.image_file is not None: image_file = self.image_file if not hasattr( self.image_file, 'image') else getattr(self.image_file, 'image') #args = helpers.make_thumb_args(image_fil return getattr( self.image_file, 'main_image_path', '') #get_url('{}.api_file_view'.format(identifier), **args) if not isinstance(identifier, (list, tuple)): identifier = [identifier] for item in identifier: try: if not thumb: path = self.contents.get(identifier=item).content.path else: path = self.contents.get(identifier=item).content.thumb return url_for('quokka.core.media', filename=path) except Exception as e: logger.warning('get_main_image_url:' + str(e)) return default def get_main_image_http(self, thumb=False, default=None, identifier='mainimage'): """method returns the path of the main image with http """ site_url = get_site_url() image_url = self.get_main_image_url(thumb=thumb, default=default, identifier=identifier) return u"{}{}".format(site_url, image_url) def get_uid(self): return str(self.id) def get_themes(self): themes = self.channel.get_themes() theme = self.template_type and self.template_type.theme_name if theme: themes.insert(0, theme) return list(set(themes)) def get_http_url(self): site_url = get_site_url() absolute_url = self.get_absolute_url() absolute_url = absolute_url[1:] return u"{0}{1}".format(site_url, absolute_url) def get_absolute_url(self, endpoint='quokka.core.detail'): if self.channel.is_homepage: long_slug = self.slug else: long_slug = self.long_slug try: return url_for(self.URL_NAMESPACE, long_slug=long_slug) except: return url_for(endpoint, long_slug=long_slug) def get_canonical_url(self, *args, **kwargs): return self.get_absolute_url() def get_recommendations(self, limit=3, ordering='-created_at', *a, **k): now = datetime.datetime.now() filters = { 'published': True, 'available_at__lte': now, "id__ne": self.id } contents = Content.objects(**filters).filter(tags__in=self.tags or []) return contents.order_by(ordering)[:limit] def get_summary(self): if self.summary: return self.summary return self.get_text() def get_text(self): if hasattr(self, 'body'): text = self.body elif hasattr(self, 'description'): text = self.description else: text = self.summary or "" if self.content_format == "markdown": return markdown(text) else: return text def __unicode__(self): return self.title @property def short_url(self): return self.shortened_url.short if self.shortened_url else '' @property def model_name(self): return self.__class__.__name__.lower() @property def module_name(self): module = self.__module__ module_name = module.replace('quokka.modules.', '').split('.')[0] return module_name def heritage(self): self.model = "{0}.{1}".format(self.module_name, self.model_name) def save(self, *args, **kwargs): # all those functions should be in a dynamic pipeline self.validate_slug() self.validate_long_slug() self.heritage() self.populate_related_mpath() self.populate_channel_roles() self.populate_shorter_url() super(Content, self).save(*args, **kwargs) def pre_render(self, render_function, *args, **kwargs): return render_function(*args, **kwargs) def populate_shorter_url(self): if not self.published or not get_setting_value('SHORTENER_ENABLED'): return url = self.get_http_url() if not self.shortened_url or url != self.shortened_url.original: shortener = ShorterURL() self.shortened_url = ShortenedURL(original=url, short=shortener.short(url))
class Item(Ordered, Dated, db.EmbeddedDocument): product = db.ReferenceField(Content) reference = db.GenericReferenceField() # customized product """ Must implement all the BaseProduct methods/ its optional if None, "product" will be considered """ uid = db.StringField() title = db.StringField(required=True, max_length=255) description = db.StringField(required=True) link = db.StringField() quantity = db.FloatField(default=1) unity_value = db.FloatField(required=True) total_value = db.FloatField() weight = db.FloatField() dimensions = db.StringField() extra_value = db.FloatField() allowed_to_set = db.ListField(db.StringField(), default=['quantity']) pipeline = db.ListField(db.StringField(), default=[]) def set_status(self, status, *args, **kwargs): kwargs['item'] = self if self.reference and hasattr(self.reference, 'set_status'): self.reference.set_status(status, *args, **kwargs) if self.product and hasattr(self.product, 'set_status'): self.product.set_status(status, *args, **kwargs) def get_main_image_url(self, thumb=False, default=None): try: return self.product.get_main_image_url(thumb, default) except: return None @classmethod def normalize(cls, kwargs): new = {} for k, v in kwargs.items(): field = cls._fields.get(k) if not field: continue new[k] = field.to_python(v) return new def __unicode__(self): return u"{i.title} - {i.total_value}".format(i=self) def get_uid(self): try: return self.product.get_uid() except: return self.uid @property def unity_plus_extra(self): return float(self.unity_value or 0) + float(self.extra_value or 0) @property def total(self): self.clean() self.total_value = self.unity_plus_extra * float(self.quantity or 1) return self.total_value def clean(self): mapping = [ ('title', 'get_title'), ('description', 'get_description'), ('link', 'get_absolute_url'), ('unity_value', 'get_unity_value'), ('weight', 'get_weight'), ('dimensions', 'get_dimensions'), ('extra_value', 'get_extra_value'), ('uid', 'get_uid'), ] references = [self.reference, self.product] for ref in references: if not ref: continue for attr, method in mapping: current = getattr(self, attr, None) if current is not None: continue setattr(self, attr, getattr(ref, method, lambda: None)())
class Cart(Publishable, db.DynamicDocument): STATUS = ( ("pending", _l("Pending")), # not checked out ("checked_out", _l("Checked out")), # not confirmed (payment) ("analysing", _l("Analysing")), # Analysing payment ("confirmed", _l("Confirmed")), # Payment confirmed ("completed", _l("Completed")), # Payment completed (money released) ("refunding", _l("Refunding")), # Buyer asks refund ("refunded", _l("Refunded")), # Money refunded to buyer ("cancelled", _l("Cancelled")), # Cancelled without processing ("abandoned", _l("Abandoned")), # Long time no update ) reference = db.GenericReferenceField() """reference must implement set_status(**kwargs) method arguments: status(str), value(float), date, uid(str), msg(str) and extra(dict). Also reference must implement get_uid() which will return the unique identifier for this transaction""" belongs_to = db.ReferenceField( 'User', # default=get_current_user, reverse_delete_rule=db.NULLIFY) items = db.ListField(db.EmbeddedDocumentField(Item)) payment = db.ListField(db.EmbeddedDocumentField(Payment)) status = db.StringField(choices=STATUS, default='pending') total = db.FloatField(default=0) extra_costs = db.DictField(default=lambda: {}) sender_data = db.DictField(default=lambda: {}) shipping_data = db.DictField(default=lambda: {}) shipping_cost = db.FloatField(default=0) tax = db.FloatField(default=0) processor = db.ReferenceField(Processor, default=Processor.get_default_processor, reverse_delete_rule=db.NULLIFY) reference_code = db.StringField() # Reference code for filtering checkout_code = db.StringField() # The UID for transaction checkout transaction_code = db.StringField() # The UID for transaction requires_login = db.BooleanField(default=True) continue_shopping_url = db.StringField( default=lambda: current_app.config.get('CART_CONTINUE_SHOPPING_URL', '/')) pipeline = db.ListField(db.StringField(), default=[]) log = db.ListField(db.StringField(), default=[]) config = db.DictField(default=lambda: {}) search_helper = db.StringField() meta = {'ordering': ['-created_at']} def send_response(self, response, identifier): if self.reference and hasattr(self.reference, 'get_response'): self.reference.get_response(response, identifier) for item in self.items: if hasattr(item, 'get_response'): item.get_response(response, identifier) def set_tax(self, tax, save=False): """ set tax and send to references """ try: tax = float(tax) self.tax = tax self.set_reference_tax(tax) except Exception as e: self.addlog("impossible to set tax: %s" % str(e)) def set_status(self, status, save=False): """ THis method will be called by the processor which will pass a valid status as in STATUS so, this method will dispatch the STATUS to all the items and also the 'reference' if set """ if self.status != status: self.status = status self.set_reference_statuses(status) if save: self.save() def set_reference_statuses(self, status): if self.reference and hasattr(self.reference, 'set_status'): self.reference.set_status(status, cart=self) for item in self.items: item.set_status(status, cart=self) def set_reference_tax(self, tax): if self.reference and hasattr(self.reference, 'set_tax'): self.reference.set_tax(tax) for item in self.items: if hasattr(item, 'set_tax'): item.set_tax(tax) def addlog(self, msg, save=True): try: self.log.append(u"{0},{1}".format(datetime.datetime.now(), msg)) logger.debug(msg) save and self.save() except UnicodeDecodeError as e: logger.info(msg) logger.error(str(e)) @property def uid(self): return self.get_uid() def get_uid(self): try: return self.reference.get_uid() or str(self.id) except Exception: self.addlog("Using self.id as reference", save=False) return str(self.id) def __unicode__(self): return u"{o.uid} - {o.processor.identifier}".format(o=self) def get_extra_costs(self): if self.extra_costs: return sum(self.extra_costs.values()) @classmethod def get_cart(cls, no_dereference=False, save=True): """ get or create a new cart related to the session if there is a current logged in user it will be set else it will be set during the checkout. """ session.permanent = current_app.config.get("CART_PERMANENT_SESSION", True) try: cart = cls.objects(id=session.get('cart_id'), status='pending') if not cart: raise cls.DoesNotExist('A pending cart not found') if no_dereference: cart = cart.no_dereference() cart = cart.first() save and cart.save() except (cls.DoesNotExist, db.ValidationError): cart = cls(status="pending") cart.save() session['cart_id'] = str(cart.id) session.pop('cart_pipeline_index', None) session.pop('cart_pipeline_args', None) return cart def assign(self): self.belongs_to = self.belongs_to or get_current_user() def save(self, *args, **kwargs): self.total = sum([item.total for item in self.items]) self.assign() self.reference_code = self.get_uid() self.search_helper = self.get_search_helper() if not self.id: self.published = True super(Cart, self).save(*args, **kwargs) self.set_reference_statuses(self.status) def get_search_helper(self): if not self.belongs_to: return "" user = self.belongs_to return " ".join([user.name or "", user.email or ""]) def get_item(self, uid): # MongoEngine/mongoengine#503 return self.items.get(uid=uid) def set_item(self, **kwargs): if 'product' in kwargs: if not isinstance(kwargs['product'], Content): try: kwargs['product'] = Content.objects.get( id=kwargs['product']) except Content.DoesNotExist: kwargs['product'] = None uid = kwargs.get( 'uid', kwargs['product'].get_uid() if kwargs.get('product') else None) if not uid: self.addlog("Cannot add item without an uid %s" % kwargs) return item = self.get_item(uid) kwargs = Item.normalize(kwargs) if not item: # items should only be added if there is a product (for safety) if not kwargs.get('product'): self.addlog("there is no product to add item") return allowed = ['product', 'quantity'] item = self.items.create( **{k: v for k, v in kwargs.items() if k in allowed}) self.addlog("New item created %s" % item, save=False) else: # update only allowed attributes item = self.items.update( {k: v for k, v in kwargs.items() if k in item.allowed_to_set}, uid=item.uid) self.addlog("Item updated %s" % item, save=False) if int(kwargs.get('quantity', "1")) == 0: self.addlog("quantity is 0, removed %s" % kwargs, save=False) self.remove_item(**kwargs) self.save() self.reload() return item def remove_item(self, **kwargs): deleted = self.items.delete(**kwargs) if self.reference and hasattr(self.reference, 'remove_item'): self.reference.remove_item(**kwargs) return deleted def checkout(self, processor=None, *args, **kwargs): self.set_processor(processor) processor_instance = self.processor.get_instance(self, *args, **kwargs) if processor_instance.validate(): response = processor_instance.process() self.status = 'checked_out' self.save() session.pop('cart_id', None) return response else: self.addlog("Cart did not validate") raise Exception("Cart did not validate") # todo: specialize this def get_items_pipeline(self): if not self.items: return [] return reduce(lambda x, y: x + y, [item.pipeline for item in self.items]) def build_pipeline(self): items = ['quokka.modules.cart.pipelines:StartPipeline'] items.extend(current_app.config.get('CART_PIPELINE', [])) items.extend(self.get_items_pipeline()) items.extend(self.pipeline) items.extend(self.processor and self.processor.pipeline or []) return items def process_pipeline(self): if not self.items: return render_template('cart/empty_cart.html', url=self.continue_shopping_url) pipelines = self.build_pipeline() index = session.get('cart_pipeline_index', 0) pipeline = import_string(pipelines[index]) return pipeline(self, pipelines, index)._preprocess() def set_processor(self, processor=None): if not self.processor: self.processor = Processor.get_default_processor() self.save() if not processor: return if isinstance(processor, Processor): self.processor = processor self.save() return try: self.processor = Processor.objects.get(id=processor) except: self.processor = Processor.objects.get(identifier=processor) self.save() def get_available_processors(self): return Processor.objects(published=True)
class Tagged(object): tags = db.ListField(db.StringField(max_length=50))
class Channel(Tagged, HasCustomValue, Publishable, LongSlugged, ChannelConfigs, ContentFormat, db.DynamicDocument): title = db.StringField(max_length=255, required=True) description = db.StringField() show_in_menu = db.BooleanField(default=False) is_homepage = db.BooleanField(default=False) include_in_rss = db.BooleanField(default=True) indexable = db.BooleanField(default=True) canonical_url = db.StringField() order = db.IntField(default=0) parent = db.ReferenceField('self', required=False, default=None, reverse_delete_rule=db.DENY) per_page = db.IntField(default=0) aliases = db.ListField(db.StringField(), default=[]) channel_type = db.ReferenceField(ChannelType, required=False, reverse_delete_rule=db.NULLIFY) redirect_url = db.StringField(max_length=255) render_content = db.ReferenceField(ContentProxy, required=False, reverse_delete_rule=db.NULLIFY) sort_by = db.ListField(db.StringField(), default=[]) meta = { 'ordering': ['order', 'title'] } def get_text(self): if self.content_format == "markdown": return markdown(self.description) else: return self.description def get_content_filters(self): filters = {} if self.channel_type and self.channel_type.content_filters: filters.update(self.channel_type.content_filters) if self.content_filters: filters.update(self.content_filters) return filters def get_ancestors_slugs(self): """return ancestors slugs including self as 1st item >>> channel = Channel(long_slug='articles/technology/programming') >>> channel.get_ancestors_slugs() ['articles/technology/programming', 'articles/technology', 'articles'] """ channel_list = [] channel_slugs = self.long_slug.split('/') while channel_slugs: channel_list.append("/".join(channel_slugs)) channel_slugs.pop() return channel_list def get_ancestors(self, **kwargs): """return all ancestors includind self as 1st item""" channel_list = self.get_ancestors_slugs() ancestors = self.__class__.objects( long_slug__in=channel_list, **kwargs ).order_by('-long_slug') return ancestors def get_children(self, **kwargs): """return direct children 1 level depth""" return self.__class__.objects( parent=self, **kwargs ).order_by('long_slug') def get_descendants(self, **kwargs): """return all descendants including self as 1st item""" return self.__class__.objects( __raw__={'mpath': {'$regex': '^{0}'.format(self.mpath)}} ).order_by('long_slug') def get_themes(self): return list({ c.channel_type.theme_name for c in self.get_ancestors(channel_type__ne=None) if c.channel_type and c.channel_type.theme_name }) @classmethod def get_homepage(cls, attr=None): try: homepage = cls.objects.get(is_homepage=True) except Exception as e: logger.info("There is no homepage: %s" % e.message) return None else: if not attr: return homepage else: return getattr(homepage, attr, homepage) def __unicode__(self): return self.long_slug def get_absolute_url(self, *args, **kwargs): return "/{0}/".format(self.long_slug) def get_canonical_url(self, *args, **kwargs): if self.is_homepage: return "/" return self.get_absolute_url() def clean(self): homepage = Channel.objects(is_homepage=True) if self.is_homepage and homepage and not self in homepage: raise db.ValidationError(_l("Home page already exists")) super(Channel, self).clean() def validate_render_content(self): if self.render_content and \ not isinstance(self.render_content, ContentProxy): self.render_content, created = ContentProxy.objects.get_or_create( content=self.render_content) else: self.render_content = None def heritage(self): """populate inheritance from parent channels""" parent = self.parent if not parent or not self.inherit_parent: return self.content_filters = self.content_filters or parent.content_filters self.include_in_rss = self.include_in_rss or parent.include_in_rss self.show_in_menu = self.show_in_menu or parent.show_in_menu self.indexable = self.indexable or parent.indexable self.channel_type = self.channel_type or parent.channel_type def update_descendants_and_contents(self): """TODO: Detect if self.long_slug and self.mpath has changed. if so, update every descendant using get_descendatns method to query. Also update long_slug and mpath for every Content in this channel This needs to be done by default in araw immediate way, but if current_app.config.get('ASYNC_SAVE_MODE') is True it will delegate all those tasks to celery.""" def save(self, *args, **kwargs): self.validate_render_content() self.validate_slug() self.validate_long_slug() self.heritage() self.update_descendants_and_contents() super(Channel, self).save(*args, **kwargs)
class User(db.DynamicDocument, UserMixin): name = db.StringField(max_length=255, verbose_name=_l('Name')) email = db.EmailField(max_length=255, unique=True, verbose_name=_l('Email')) password = db.StringField(max_length=255, verbose_name=_l('Password')) active = db.BooleanField(default=True, verbose_name=_l('Active')) confirmed_at = db.DateTimeField(verbose_name=_l('Confirmed At')) roles = db.ListField(db.ReferenceField(Role, reverse_delete_rule=db.DENY), default=[], verbose_name=_l('Roles')) last_login_at = db.DateTimeField(verbose_name=_l('Last Login At')) current_login_at = db.DateTimeField(verbose_name=_l('Current Login At')) last_login_ip = db.StringField(max_length=255, verbose_name=_l('Last Login IP')) current_login_ip = db.StringField(max_length=255, verbose_name=_l('Current Login IP')) login_count = db.IntField(verbose_name=_l('Login Count')) username = db.StringField(max_length=50, required=False, unique=True, verbose_name=_l('Username')) remember_token = db.StringField(max_length=255, verbose_name=_l('Remember Token')) authentication_token = db.StringField( max_length=255, verbose_name=_l('Authentication Tokern')) def clean(self, *args, **kwargs): if not self.username: self.username = User.generate_username(self.email) try: super(User, self).clean(*args, **kwargs) except: pass @classmethod def generate_username(cls, email): username = email.lower() for item in ['@', '.', '-', '+']: username = username.replace(item, '_') return username def set_password(self, password, save=False): self.password = encrypt_password(password) if save: self.save() @classmethod def createuser(cls, name, email, password, active=True, roles=None, username=None): username = username or cls.generate_username(email) return cls.objects.create(name=name, email=email, password=encrypt_password(password), active=active, roles=roles, username=username) @property def display_name(self): return self.name or self.email def __unicode__(self): return u"{0} <{1}>".format(self.name or '', self.email) @property def connections(self): return Connection.objects(user_id=str(self.id))
class Content(HasCustomValue, Publishable, LongSlugged, Channeling, Tagged, ContentFormat, db.DynamicDocument): title = db.StringField(max_length=255, required=True) summary = db.StringField(required=False) template_type = db.ReferenceField(ContentTemplateType, required=False, reverse_delete_rule=db.NULLIFY) contents = db.ListField(db.EmbeddedDocumentField(SubContent)) model = db.StringField() comments_enabled = db.BooleanField(default=True) meta = { 'allow_inheritance': True, 'indexes': ['-created_at', 'slug'], 'ordering': ['-created_at'] } @classmethod def available_objects(cls, **filters): now = datetime.datetime.now() default_filters = { "published": True, 'available_at__lte': now, } default_filters.update(filters) return cls.objects(**default_filters) def get_main_image_url(self, thumb=False, default=None, identifier='mainimage'): """ """ if not isinstance(identifier, (list, tuple)): identifier = [identifier] for item in identifier: try: if not thumb: path = self.contents.get(identifier=item).content.path else: path = self.contents.get(identifier=item).content.thumb return url_for('media', filename=path) except Exception as e: logger.warning(str(e)) return default def get_uid(self): return str(self.id) def get_themes(self): themes = self.channel.get_themes() theme = self.template_type and self.template_type.theme_name if theme: themes.insert(0, theme) return list(set(themes)) def get_absolute_url(self, endpoint='detail'): if self.channel.is_homepage: long_slug = self.slug else: long_slug = self.long_slug try: return url_for(self.URL_NAMESPACE, long_slug=long_slug) except: return url_for(endpoint, long_slug=long_slug) def get_canonical_url(self, *args, **kwargs): return self.get_absolute_url() def get_recommendations(self, limit=3, ordering='-created_at', *a, **k): now = datetime.datetime.now() filters = { 'published': True, 'available_at__lte': now, "id__ne": self.id } contents = Content.objects(**filters).filter(tags__in=self.tags or []) return contents.order_by(ordering)[:limit] def get_summary(self): if self.summary: return self.summary return self.get_text() def get_text(self): if hasattr(self, 'body'): text = self.body elif hasattr(self, 'description'): text = self.description else: text = self.summary if self.content_format == "markdown": return markdown(text) else: return text def __unicode__(self): return self.title @property def model_name(self): return self.__class__.__name__.lower() @property def module_name(self): module = self.__module__ module_name = module.replace('quokka.modules.', '').split('.')[0] return module_name def heritage(self): self.model = "{0}.{1}".format(self.module_name, self.model_name) def save(self, *args, **kwargs): self.validate_slug() self.validate_long_slug() self.heritage() super(Content, self).save(*args, **kwargs)
class User(db.DynamicDocument, UserMixin): name = db.StringField(max_length=255) email = db.EmailField(max_length=255, unique=True) password = db.StringField(max_length=255) active = db.BooleanField(default=True) confirmed_at = db.DateTimeField() roles = db.ListField(db.ReferenceField(Role, reverse_delete_rule=db.DENY), default=[]) last_login_at = db.DateTimeField() current_login_at = db.DateTimeField() last_login_ip = db.StringField(max_length=255) current_login_ip = db.StringField(max_length=255) login_count = db.IntField() username = db.StringField(max_length=50, required=False, unique=True) remember_token = db.StringField(max_length=255) authentication_token = db.StringField(max_length=255) tagline = db.StringField(max_length=255) bio = db.StringField() links = db.ListField(db.EmbeddedDocumentField(UserLink)) gravatar_email = db.EmailField(max_length=255) def get_gravatar_email(self): return self.gravatar_email or self.email def clean(self, *args, **kwargs): if not self.username: self.username = User.generate_username(self.name) try: super(User, self).clean(*args, **kwargs) except: pass @classmethod def generate_username(cls, name): # username = email.lower() # for item in ['@', '.', '-', '+']: # username = username.replace(item, '_') # return username username = slugify(name) if cls.objects.filter(username=username).count(): username = "******".format(username, randint(1, 1000)) return username def set_password(self, password, save=False): self.password = encrypt_password(password) if save: self.save() @classmethod def createuser(cls, name, email, password, active=True, roles=None, username=None, *args, **kwargs): username = username or cls.generate_username(name) if "links" in kwargs: kwargs["links"] = [UserLink(**link) for link in kwargs['links']] return cls.objects.create(name=name, email=email, password=encrypt_password(password), active=active, roles=roles, username=username, *args, **kwargs) @property def display_name(self): return abbreviate(self.name) or self.email def __unicode__(self): return u"{0} <{1}>".format(self.name or '', self.email) @property def connections(self): return Connection.objects(user_id=str(self.id))
class User(db.DynamicDocument, UserMixin): name = db.StringField(max_length=255) email = db.EmailField(max_length=255, unique=True) password = db.StringField(max_length=255) active = db.BooleanField(default=True) confirmed_at = db.DateTimeField() roles = db.ListField(db.ReferenceField(Role, reverse_delete_rule=db.DENY), default=[]) last_login_at = db.DateTimeField() current_login_at = db.DateTimeField() last_login_ip = db.StringField(max_length=255) current_login_ip = db.StringField(max_length=255) login_count = db.IntField() username = db.StringField(max_length=50, requied=False, unique=True) @classmethod def generate_username(cls, name, user=None): name = name or '' username = slugify(name) filters = dict(username=username) if user: filters['id__ne'] = user.id if cls.objects.filter(**filters).count(): username = '******'.format(username, random.randint(1, 1000)) return username def set_password(self, password, save=False): self.password = encrypt_password(password) if save: self.save() @classmethod def create_user(cls, name, email, password, active=True, roles=None, username=None, *args, **kwargs): username = username or cls.generate_username(name) if 'links' in kwargs: kwargs['links'] = [UserLink(**link) for link in kwargs['links']] return cls.objects.create(name=name, email=email, password=encrypt_password(password), active=active, roles=roles, username=username, *args, **kwargs) @property def connections(self): return Connection.objects(user_id=str(self.id)) def __unicode__(self): return '{0} <{1}>'.format(self.name or '', self.email)
class Commentable(object): comments = db.ListField(db.EmbeddedDocumentField(Comment))