src/business/models.py
from django.conf import settings
from fractions import Fraction
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth import get_user_model
from django.contrib.auth import models as auth_models
from django.core.urlresolvers import reverse
from django.db.models import *
from django.db import models as models
from django.utils.translation import ugettext_lazy as _
from django_extensions.db import fields as extension_fields
from django_extensions.db.fields import AutoSlugField
from timezone_field import TimeZoneField
import uuid
from decimal import *
from pyPrintful import pyPrintful
from django.core.exceptions import ObjectDoesNotExist
from business.helper_backend import *
from storemanager.logger import *
logger = StyleAdapter(logging.getLogger("project"))
class commonBusinessModel(models.Model):
id = UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
date_added = DateTimeField(auto_now_add=True, verbose_name=_("Added"))
date_updated = DateTimeField(auto_now=True, verbose_name=_("Updated"))
class Meta:
abstract = True
# Primarily Store App Related
class bzBrand(commonBusinessModel):
# Fields
code = CharField(_("Code"), max_length=2)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
# Relationship Fields
vendor = ForeignKey('business.pfStore', blank=True, null=True, )
outlet = ForeignKey('business.wooStore', blank=True, null=True,)
class Meta:
ordering = ('code',)
verbose_name = _("Brand")
verbose_name_plural = _("Brands")
def __str__(self):
if self.code and self.name:
return "{} - {}".format(self.code, self.name)
elif self.name:
return "{}".format(self.name)
return _("Unknown Brand")
def get_absolute_url(self):
return reverse('business:app_store_brand_detail', args=(self.pk,))
def get_update_url(self):
return reverse('business:app_store_brand_update', args=(self.pk,))
class pfStore(commonBusinessModel):
# Fields
code = CharField(_("Code"), max_length=50,
default="", blank=True, null=True)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
pid = IntegerField(_("Printful ID"), default=0)
website = CharField(_("Website"), max_length=255,
default="", blank=True, null=True)
created = CharField(_("Created"), max_length=255,
default="", blank=True, null=True)
key = CharField(_("API Key"), max_length=64, default="", blank=True)
return_address = ForeignKey("business.pfAddress", verbose_name=_(
"Return Address"), related_name="returnaddress", blank=True, null=True)
billing_address = ForeignKey("business.pfAddress", verbose_name=_(
"Billing Address"), related_name="billingaddress", blank=True, null=True)
payment_type = CharField(_("Payment Card Type"),
max_length=64, default="", blank=True, null=True)
payment_number_mask = CharField(
_("Payment Card Type"), max_length=64, default="", blank=True, null=True)
payment_expires = CharField(
_("Payment Card Type"), max_length=64, default="", blank=True, null=True)
packingslip_email = EmailField(
_("Packing Slip Email"), default="", blank=True, null=True)
packingslip_phone = CharField(
_("Packing Slip Phone"), max_length=64, default="", blank=True, null=True)
packingslip_message = CharField(
_("Packing Slip Message"), max_length=255, default="", blank=True, null=True)
class Meta:
ordering = ('-created',)
verbose_name = _("Printful Store")
verbose_name_plural = _("Printful Stores")
def __str__(self):
rv = []
if self.code and self.name:
return "{} - {}".format(self.code, self.name)
elif self.code:
return "{}".format(self.code)
return _("Unknown Store")
def get_absolute_url(self):
return reverse('business:app_store_pf_detail', args=(self.pk,))
def get_update_url(self):
return reverse('business:app_store_pf_update', args=(self.pk,))
def has_auth(self):
return True if self.key else False
has_auth.short_description = _("Auth?")
has_auth.boolean = True
def save(self, *args, **kwargs):
logger.debug('Method: pfStore.save() Called')
if self.pid == 0:
if pfCountry.objects.all().count() == 0:
pfCountry.api_pull(key=self.key)
self.api_pull()
self.api_push()
self.api_pull()
super(pfStore, self).save(*args, **kwargs)
@staticmethod
def get_store(store=None):
"""
Gets a 'default' Printful store, generally for use with the Printful API
methods on other related objects. If a store is provided, then it is
validated and returned. Otherwise, this method will attempt to grab the
first Printful store object in the database and return that.
If no stores are in the database, then this method will raise an exception.
The wrapping method will need to catch this and respond appropriately.
:param store: Optional. pfStore object. Will validate that it is a valid
pfStore object and return it back.
"""
if type(store) is pfStore and store.has_auth():
return store
else:
store = pfStore.objects.exclude(
key__isnull=True).exclude(key__exact='').first()
if store:
return store
raise ObjectDoesNotExist(
"Either provide a store object or add at least one pfStore with an API key to the database.")
def api_pull(self):
"""
Update current store with data from Printful API.
"""
if not self.has_auth():
raise Exception("This store is missing the API Key.")
# TODO Handle states/countries lookup Exceptions
api = pyPrintful(key=self.key)
sData = api.get_store_info()
print(sData)
print(api._store['last_response_raw'])
self.website = sData['website']
self.name = sData['name']
self.pid = sData['id']
self.created = sData['created']
self.packingslip_phone = sData['packing_slip']['phone']
self.packingslip_email = sData['packing_slip']['email']
self.packingslip_message = sData['packing_slip']['message']
self.payment_type = sData['payment_card']['type']
self.payment_number_mask = sData['payment_card']['number_mask']
self.payment_expires = sData['payment_card']['expires']
if sData['billing_address']:
_state = pfState.objects.get(
code=sData['billing_address']['state_code'])
_country = pfCountry.objects.get(
code=sData['billing_address']['country_code'])
self.billing_address, created = pfAddress.objects.update_or_create(
name=sData['billing_address']['name'],
company=sData['billing_address']['company'],
address1=sData['billing_address']['address1'],
address2=sData['billing_address']['address2'],
city=sData['billing_address']['city'],
zip=sData['billing_address']['zip'],
phone=sData['billing_address']['phone'],
email=sData['billing_address']['email'],
state=_state,
country=_country,
defaults={}
)
if sData['return_address']:
_state = pfState.objects.get(
code=sData['return_address']['state_code'])
_country = pfCountry.objects.get(
code=sData['return_address']['country_code'])
self.return_address, created = pfAddress.objects.update_or_create(
name=sData['return_address']['name'],
company=sData['return_address']['company'],
address1=sData['return_address']['address1'],
address2=sData['return_address']['address2'],
city=sData['return_address']['city'],
zip=sData['return_address']['zip'],
phone=sData['return_address']['phone'],
email=sData['return_address']['email'],
state=_state,
country=_country,
defaults={}
)
def api_push(self):
"""
Pushes the only data available to update via the API: packing slip info.
"""
if not self.has_auth():
raise Exception("This store is missing the API Key.")
data = {
'email': self.packingslip_email,
'phone': self.packingslip_phone,
'message': self.packingslip_message,
}
api = pyPrintful(key=self.key)
api.put_store_packingslip(data)
class wooStore(commonBusinessModel):
# Fields
code = CharField(_("Code"), max_length=2, default="", blank=True, null=True,
help_text=_("Generally, a two-character uppercase code. Used in SKUs."))
base_url = URLField(_("Base URL"), default="", blank=True, null=True, help_text=_(
"Include the schema and FQDN only (e.g., 'https://example.com'). No trailing slash."))
consumer_key = CharField(
_("Consumer Key"), max_length=64, blank=True, null=True)
consumer_secret = CharField(
_("Consumer Secret"), max_length=64, blank=True, null=True)
timezone = TimeZoneField(default='America/New_York')
verify_ssl = BooleanField(_("Verify SSL?"), default=True, help_text=_(
"Uncheck this if you are using a self-signed SSL certificate to disable ssl verification."))
class Meta:
ordering = ('code',)
verbose_name = _("WP Store")
verbose_name_plural = _("WP Stores")
def __str__(self):
rv = []
if self.code and self.base_url:
return "{} - {}".format(self.code, self.base_url)
elif self.code:
return "{}".format(self.code)
return _("Unknown Store")
def get_absolute_url(self):
return reverse('business:app_store_wp_detail', args=(self.pk,))
def get_update_url(self):
return reverse('business:app_store_wp_update', args=(self.pk,))
# Primarily Creative App Related
class bzCreativeCollection(commonBusinessModel):
# Fields
code = CharField(_("Code"), max_length=3)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
# Relationship Fields
bzbrand = ForeignKey('business.bzBrand', verbose_name=_("Brand"))
class Meta:
ordering = ('code',)
verbose_name = _("Creative Collection")
verbose_name_plural = _("Creative Collections")
def __str__(self):
if self.code and self.name:
return "{} - {}".format(self.code, self.name)
elif self.name:
return "{}".format(self.name)
return _("Unknown Collection")
def get_absolute_url(self):
return reverse(
'business:business_bzcreativecollection_detail', args=(self.pk,))
def get_update_url(self):
return reverse(
'business:business_bzcreativecollection_update', args=(self.pk,))
def get_designs(self):
return bzCreativeDesign.objects.filter(bzcreativecollection=self)
get_designs.short_description = _("Designs")
def num_designs(self):
return self.get_designs().count()
num_designs.short_description = _("Designs")
def get_layouts(self):
return bzCreativeLayout.objects.filter(bzcreativecollection=self)
get_designs.short_description = _("Layouts")
def num_layouts(self):
return self.get_layouts().count()
num_layouts.short_description = _("Layouts")
class bzCreativeDesign(commonBusinessModel):
# Fields
code = CharField(_("Code"), max_length=2)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
# Relationship Fields
bzcreativecollection = ForeignKey(
'business.bzCreativeCollection', verbose_name=_("Collection"))
class Meta:
ordering = ('bzcreativecollection__code', 'code',)
verbose_name = _("Creative Design")
verbose_name_plural = _("Creative Designs")
def __str__(self):
rv = []
if self.bzcreativecollection:
if self.bzcreativecollection.code:
rv.append(self.bzcreativecollection.code + "-")
if self.code:
rv.append(self.code)
if self.bzcreativecollection:
if self.bzcreativecollection.code:
rv.append(" / " + self.bzcreativecollection.name)
if self.name:
rv.append(" / " + self.name)
if rv:
return "".join(rv)
return _("Unknown Design")
def get_absolute_url(self):
return reverse('business:business_bzcreativedesign_detail',
args=(self.pk,))
def get_update_url(self):
return reverse('business:business_bzcreativedesign_update',
args=(self.pk,))
def get_products(self):
return bzProduct.objects.filter(bzDesign=self)
get_products.short_description = _("Products")
def num_products(self):
return self.get_products().count()
num_products.short_description = _("Products")
class bzCreativeLayout(commonBusinessModel):
# Fields
code = CharField(_("Code"), max_length=2)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
# Relationship Fields
bzcreativecollection = ForeignKey(
'business.bzCreativeCollection', verbose_name=_("Collection"))
class Meta:
ordering = ('bzcreativecollection__code', 'code',)
verbose_name = _("Creative Layout")
verbose_name_plural = _("Creative Layouts")
def __str__(self):
if self.code and self.name:
return "{} - {}".format(self.code, self.name)
elif self.name:
return "{}".format(self.name)
return _("Unknown Design")
def get_absolute_url(self):
return reverse('business:business_bzcreativelayout_detail',
args=(self.pk,))
def get_update_url(self):
return reverse('business:business_bzcreativelayout_update',
args=(self.pk,))
def get_products(self):
return bzProduct.objects.filter(bzLayout=self)
get_products.short_description = _("Products")
def num_products(self):
return self.get_products().count()
num_products.short_description = _("Products")
class bzCreativeRendering(commonBusinessModel):
# Fields
# Relationship Fields
bzcreativedesign = ForeignKey(
'business.bzCreativeDesign', verbose_name=_("Design"))
bzcreativelayout = ForeignKey(
'business.bzCreativeLayout', verbose_name=_("Layout"))
class Meta:
ordering = ('bzcreativedesign__code', 'bzcreativelayout__code',)
verbose_name = _("Creative Rendering")
verbose_name_plural = _("Creative Renderings")
def __str__(self):
if self.bzcreativedesign and self.bzcreativelayout:
return "{} - {}".format(self.bzcreativedesign.code,
self.bzcreativelayout.code)
return _("Unknown Rendering")
def get_absolute_url(self):
return reverse('business:business_bzcreativerendering_detail',
args=(self.pk,))
def get_update_url(self):
return reverse('business:business_bzcreativerendering_update',
args=(self.pk,))
class bzProduct(commonBusinessModel):
STATUS_DRAFT = "draft"
STATUS_PUBLIC = "public"
STATUS_CHOICES = (
(STATUS_DRAFT, "Draft"),
(STATUS_PUBLIC, "Public"),
)
# Fields
code = CharField(_("Code"), max_length=64,
default="", blank=True, null=True)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
status = CharField(_("Status"), max_length=32,
default=STATUS_DRAFT, choices=STATUS_CHOICES)
# Relationship Fields
bzDesign = ForeignKey('business.bzCreativeDesign',
verbose_name=_("Design"))
bzLayout = ForeignKey('business.bzCreativeLayout',
verbose_name=_("Layout"), null=True, blank=True)
pfProduct = ForeignKey('business.pfCatalogProduct',
verbose_name=_("Vendor Product"),
blank=True, null=True, )
wooProduct = ForeignKey('business.wooProduct',
verbose_name=_("Outlet Product"),
blank=True, null=True, )
pfSyncProduct = ForeignKey('business.pfSyncProduct',
verbose_name=_("Sync Product"),
blank=True, null=True, )
colors = ManyToManyField('business.pfCatalogColor',
blank=True, verbose_name=_("Colors"))
sizes = ManyToManyField('business.pfCatalogSize',
blank=True, verbose_name=_("Sizes"))
class Meta:
ordering = ('code',)
verbose_name = _("Product")
verbose_name_plural = _("Products")
def __str__(self):
return self.get_friendly_name()
def get_friendly_name(self):
if self.code and self.name:
return "{} - {}".format(self.code, self.name)
elif self.name:
return "{}".format(self.name)
return "Unknown Product"
def __unicode__(self):
return self.__str__
def get_absolute_url(self):
return reverse('business:business_bzproduct_detail', args=(self.pk,))
def get_update_url(self):
return reverse('business:business_bzproduct_update', args=(self.pk,))
def get_variants(self):
return bzProductVariant.objects.filter(bzproduct=self)
get_variants.short_description = _("Variants")
def num_variants(self):
return self.get_variants().count()
num_variants.short_description = _("Variants")
def get_colors_as_string(self):
rv = []
for i in self.colors.all():
rv.append(i.__str__())
return ", ".join(rv)
def get_sizes_as_string(self):
rv = []
for i in self.sizes.all():
rv.append(i.__str__())
return ", ".join(rv)
class bzProductVariant(commonBusinessModel):
# Fields
code = CharField(verbose_name=_("Code"), max_length=64,
default="", blank=True, null=True)
is_active = BooleanField(verbose_name=_("Is Active"), default=True)
# Relationship Fields
bzproduct = ForeignKey('business.bzProduct', verbose_name=_("Product"))
pfcatalogvariant = ForeignKey(
'business.pfCatalogVariant', verbose_name=_("Vendor Variant"), )
pfcolor = ForeignKey('business.pfCatalogColor',
verbose_name=_("Color"), blank=True, null=True, )
pfsize = ForeignKey('business.pfCatalogSize',
verbose_name=_("Size"), blank=True, null=True, )
price = DecimalField(_("Price"), max_digits=5,
decimal_places=2, default=Decimal("0"))
class Meta:
ordering = ('bzproduct', 'pfsize', 'pfcolor',)
verbose_name = _("Variant")
verbose_name_plural = _("Variants")
def __str__(self):
rv = []
if self.bzproduct.code:
rv.append(self.bzproduct.code)
if self.bzproduct:
rv.append(self.bzproduct.name)
return " - ".join(rv)
def get_absolute_url(self):
return reverse('business:business_bzproductvariant_detail',
args=(self.pk,))
def get_update_url(self):
return reverse('business:business_bzproductvariant_update',
args=(self.pk,))
class wooAttribute(commonBusinessModel):
TYPE_TEXT = "text"
TYPE_COLORPICKER = "color picker"
TYPE_IMAGESELECT = "image select"
TYPE_TEXTLABEL = "text label"
TYPE_CHOICES = (
(TYPE_TEXT, _("Basic Text")),
(TYPE_COLORPICKER, _("Color Picker")),
(TYPE_IMAGESELECT, _("Image Select")),
(TYPE_TEXTLABEL, _("Text Label")),
)
ORDER_NAME = "name"
ORDER_NAMENUMBER = "name_num"
ORDER_ID = "id"
ORDER_MENU = "menu_order"
ORDER_CHOICES = (
(ORDER_NAME, _("Sort by Name")),
(ORDER_NAMENUMBER, _("Sort by Name (Number)")),
(ORDER_ID, _("Sort by ID")),
(ORDER_MENU, _("Sort by Custom Menu Order")),
)
# Fields
is_active = BooleanField(_("Is Active?"), default=True)
wid = CharField(_("WP ID"), max_length=16,
default="", blank=True, null=True)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
slug = CharField(_("Slug"), max_length=255,
default="", blank=True, null=True)
type = CharField(_("Type"), max_length=255, default="",
blank=True, null=True, choices=TYPE_CHOICES)
has_archives = BooleanField(_("Has Archives?"), default=False)
# Relationship Fields
store = ForeignKey('business.wooStore', blank=True, null=True)
class Meta:
ordering = ('-pk',)
verbose_name = _("WP Attribute")
verbose_name_plural = _("WP Attributes")
def __str__(self):
return u'%s' % self.slug
def get_absolute_url(self):
return reverse('business:business_wooattribute_detail',
args=(self.slug,))
def get_update_url(self):
return reverse('business:business_wooattribute_update',
args=(self.slug,))
class wooCategory(commonBusinessModel):
DISPLAY_DEFAULT = 'default'
DISPLAY_PRODUCTS = 'products'
DISPLAY_SUBCATEGORIES = 'subcategories'
DISPLAY_BOTH = 'both'
DISPLAY_CHOICES = (
(DISPLAY_DEFAULT, _("Default")),
(DISPLAY_PRODUCTS, _("Products")),
(DISPLAY_SUBCATEGORIES, _("Subcategories")),
(DISPLAY_BOTH, _("Display Both"))
)
# Fields
is_active = BooleanField(_("Is Active?"), default=True)
wid = IntegerField(_("WP ID"), default=0, blank=True, null=True)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
slug = CharField(_("Slug"), max_length=255,
default="", blank=True, null=True)
parent = IntegerField(_("Parent ID"), default=0)
description = TextField(
_("Description"), default="", blank=True, null=True)
display = CharField(_("Display"), max_length=255,
default=DISPLAY_DEFAULT, choices=DISPLAY_CHOICES)
count = IntegerField(_("Count"), default=0)
image_id = IntegerField(_("Image ID"), default=0)
image_date_created = CharField(_("Image Created"), max_length=255,
default="", blank=True, null=True)
# Relationship Fields
store = ForeignKey('business.wooStore', blank=True, null=True)
class Meta:
ordering = ('-pk',)
verbose_name = _("WP Category")
verbose_name_plural = _("WP Categories")
def __str__(self):
return u'%s' % self.slug
def get_absolute_url(self):
return reverse('business:business_woocategory_detail',
args=(self.slug,))
def get_update_url(self):
return reverse('business:business_woocategory_update',
args=(self.slug,))
class wooImage(commonBusinessModel):
# Fields
is_active = BooleanField(_("Is Active?"), default=True)
wid = CharField(_("WP ID"), max_length=16, default="", blank=True,
null=True, help_text=_(
"Image ID (attachment ID). In write-mode used to attach pre-existing images."))
date_created = DateField(_("Date Created"), help_text=_(
"READONLY. The date the product was created, in the sites timezone."),
blank=True, null=True)
alt = CharField(_("Alt"), max_length=255,
default="", blank=True, null=True)
position = IntegerField(_("Position"), default=0, help_text=_(
"Image position. 0 means that the image is featured."))
class Meta:
ordering = ('-pk',)
verbose_name = _("WP Image")
verbose_name_plural = _("WP Images")
def __str__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse('business:business_wooimage_detail', args=(self.pk,))
def get_update_url(self):
return reverse('business:business_wooimage_update', args=(self.pk,))
class wooProduct(commonBusinessModel):
# Fields
is_active = BooleanField(_("Is Active?"), default=True)
wid = CharField(_("WP ID"), max_length=16,
default="", blank=True, null=True, )
slug = CharField(_("Slug"), max_length=255,
default="", blank=True, null=True)
permalink = URLField(_("Permalink"), blank=True)
date_created = DateField(_("Date Created"), help_text=_(
"READONLY. The date the product was created, in the sites timezone."),
blank=True, null=True)
dimension_length = DecimalField(
_("Length"), max_digits=10, decimal_places=2, default=0)
dimension_width = DecimalField(
_("Width"), max_digits=10, decimal_places=2, default=0)
dimension_height = DecimalField(
_("Height"), max_digits=10, decimal_places=2, default=0)
weight = DecimalField(_("Weight"), help_text=_(
"Product weight in decimal format."),
max_digits=10, decimal_places=2,
default=0)
reviews_allowed = BooleanField(_("Reviewed Allowed?"), help_text=_(
"Allow reviews. Default is true."), default=True)
# Relationship Fields
woostore = ForeignKey('business.wooStore', verbose_name=_(
"Store"), blank=True, null=True)
shipping_class = ForeignKey(
'business.wooShippingClass', null=True, blank=True)
tags = ManyToManyField(
'business.wooTag', verbose_name=_("Tags"), blank=True)
images = ManyToManyField(
'business.wooImage', verbose_name=_("Images"), blank=True)
class Meta:
ordering = ('-pk',)
verbose_name = _("WP Product")
verbose_name_plural = _("WP Products")
def __str__(self):
return u'%s' % self.slug
def get_absolute_url(self):
return reverse('business:business_wooproduct_detail',
args=(self.slug,))
def get_update_url(self):
return reverse('business:business_wooproduct_update',
args=(self.slug,))
class wooShippingClass(commonBusinessModel):
# Fields
wid = CharField(_("WP ID"), max_length=64,
default="", blank=True, null=True)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
slug = CharField(_("Slug"), max_length=255,
default="", blank=True, null=True)
description = TextField(
_("Description"), default="", blank=True, null=True)
count = IntegerField(_("Count"), default=0)
class Meta:
ordering = ('-pk',)
verbose_name = _("WP Shipping Class")
verbose_name_plural = _("WP Shipping Classes")
def __str__(self):
return u'%s' % self.slug
def get_absolute_url(self):
return reverse(
'business:business_wooshippingclass_detail', args=(self.pk,))
def get_update_url(self):
return reverse(
'business:business_wooshippingclass_update', args=(self.pk,))
class wooTag(commonBusinessModel):
# Fields
is_active = BooleanField(_("Is Active?"), default=True)
wid = IntegerField(_("WP ID"), default=0)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
slug = CharField(_("Slug"), max_length=255,
default="", blank=True, null=True)
description = TextField(
_("Description"), default="", blank=True, null=True)
count = IntegerField(_("Count"), default=0)
# Relationship Fields
store = ForeignKey('business.wooStore', blank=True, null=True)
class Meta:
ordering = ('-pk',)
verbose_name = _("WP Tag")
verbose_name_plural = _("WP Tags")
def __str__(self):
return u'%s' % self.slug
def get_absolute_url(self):
return reverse('business:business_wootag_detail', args=(self.slug,))
def get_update_url(self):
return reverse('business:business_wootag_update', args=(self.slug,))
class wooTerm(commonBusinessModel):
# Fields
wid = CharField(_("WP ID"), max_length=16,
default="", blank=True, null=True)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
slug = CharField(_("Slug"), max_length=255,
default="", blank=True, null=True)
menu_order = IntegerField(_("Menu Order"), default=0)
count = IntegerField(_("Count"), default=0)
wr_tooltip = CharField(_("WR Tooltip"), max_length=255,
default="", blank=True, null=True)
wr_label = CharField(_("WR Label"), max_length=255,
default="", blank=True, null=True)
# Relationship Fields
productattribute = ForeignKey('business.wooAttribute', verbose_name=_(
"Product Attribute"), blank=True, null=True)
class Meta:
ordering = ('-pk',)
verbose_name = _("WP Term")
verbose_name_plural = _("WP Terms")
def __str__(self):
return u'%s' % self.slug
def get_absolute_url(self):
return reverse('business:business_wooterm_detail', args=(self.slug,))
def get_update_url(self):
return reverse('business:business_wooterm_update', args=(self.slug,))
class wooVariant(commonBusinessModel):
# Fields
is_active = BooleanField(_("Is Active?"), default=True)
wid = CharField(_("WP ID"), max_length=16,
default="", blank=True, null=True, )
date_created = DateField(_("Date Created"), help_text=_(
"READONLY. The date the product was created, in the sites timezone."), blank=True, null=True)
permalink = URLField(_("Permalink"), blank=True)
sku = CharField(_("SKU"), help_text=_("Unique identifier."),
max_length=255, default="", blank=True, null=True)
price = CharField(_("Price"), help_text=_(
"READONLY. Current product price. This is set from regular_price and sale_price."), max_length=255, default="", blank=True, null=True)
dimension_length = DecimalField(
_("Length"), max_digits=10, decimal_places=2, default=0)
dimension_width = DecimalField(
_("Width"), max_digits=10, decimal_places=2, default=0)
dimension_height = DecimalField(
_("Height"), max_digits=10, decimal_places=2, default=0)
weight = DecimalField(_("Weight"), help_text=_(
"Product weight in decimal format."), max_digits=10, decimal_places=2, default=0)
# Relationship Fields
shipping_class = ForeignKey(
'business.wooShippingClass', null=True, blank=True)
images = ManyToManyField(
'business.wooImage', verbose_name=_("Images"), blank=True)
class Meta:
ordering = ('-pk',)
verbose_name = _("WP Variant")
verbose_name_plural = _("WP Variants")
def __str__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse('business:business_woovariant_detail', args=(self.pk,))
def get_update_url(self):
return reverse('business:business_woovariant_update', args=(self.pk,))
class wpMedia(commonBusinessModel):
STATUSBOOL_OPEN = 'open'
STATUSBOOL_CLOSED = 'closed'
STATUSBOOL_CHOICES = (
(STATUSBOOL_OPEN, _("Open")),
(STATUSBOOL_CLOSED, _("Closed")),
)
MEDIATYPE_IMAGE = "image"
MEDIATYPE_FILE = "file"
MEDIATYPE_CHOICES = (
(MEDIATYPE_IMAGE, _("Image")),
(MEDIATYPE_FILE, )
)
# Fields
is_active = BooleanField(_("Is Active?"), default=True)
alt_text = CharField(_("Alternate Text"), max_length=255,
default="", blank=True, null=True)
width = IntegerField(_("Width"), default=0)
height = IntegerField(_("Height"), default=0)
file = CharField(_("File"), max_length=255,
default="", blank=True, null=True)
author = IntegerField(_("Author"), default=0)
mime_type = CharField(_("MIME Type"), max_length=255,
default="", blank=True, null=True)
comment_status = CharField(_("Comment Status"), max_length=255,
default=STATUSBOOL_OPEN, choices=STATUSBOOL_CHOICES)
wid = CharField(_("ID"), max_length=16, default="", blank=True, null=True)
source_url = URLField(_("Source URL"), blank=True, null=True)
template = CharField(_("Template"), max_length=255,
default="", blank=True, null=True)
ping_status = CharField(_("Ping Status"), max_length=255,
default=STATUSBOOL_OPEN, choices=STATUSBOOL_CHOICES)
caption = CharField(_("Caption"), max_length=255,
default="", blank=True, null=True)
link = URLField(_("Link"), default="", blank=True, null=True)
slug = CharField(_("Slug"), max_length=255, blank=True, null=True)
modified = DateTimeField(_("Modified"), blank=True, null=True)
guid = CharField(_("GUID"), max_length=255,
default="", blank=True, null=True)
description = TextField(
_("Description"), default="", blank=True, null=True)
modified_gmt = DateTimeField(_("Modified GMT"), blank=True, null=True)
title = CharField(_("Title"), max_length=255,
default="", blank=True, null=True)
date_gmt = DateTimeField(_("Date GMT"), blank=True, null=True)
type = CharField(_("Type"), max_length=64,
default="", blank=True, null=True)
# Relationship Fields
woostore = ForeignKey('business.woostore', blank=True, null=True)
class Meta:
ordering = ('-pk',)
verbose_name = _("WP Media")
verbose_name_plural = _("WP Media")
def __str__(self):
return u'%s' % self.slug
def get_absolute_url(self):
return reverse('business:business_wpmedia_detail', args=(self.slug,))
def get_update_url(self):
return reverse('business:business_wpmedia_update', args=(self.slug,))
class wpMediaSize(commonBusinessModel):
# Fields
is_active = BooleanField(_("Is Active?"), default=True)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
file = CharField(_("File"), max_length=255,
default="", blank=True, null=True)
mime_type = CharField(_("MIME Type"), max_length=255,
default="", blank=True, null=True)
width = IntegerField(_("Width"), default=0)
height = IntegerField(_("Height"), default=0)
source_url = URLField(_("Source URL"), default="", blank=True, null=True)
# Relationship Fields
wpmedia = ForeignKey('business.wpMedia', verbose_name=_("Media"))
class Meta:
ordering = ('-pk',)
verbose_name = _("WP Media Size")
verbose_name_plural = _("WP Media Sizes")
def __str__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse('business:business_wpmediasize_detail', args=(self.pk,))
def get_update_url(self):
return reverse('business:business_wpmediasize_update', args=(self.pk,))
class pfCountry(commonBusinessModel):
# Fields
code = CharField(_("Code"), max_length=50,
default="", blank=True, null=True)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
class Meta:
ordering = ('code',)
verbose_name = _("Country")
verbose_name_plural = _("Countries")
def __str__(self):
if self.code and self.name:
return "{} - {}".format(self.code, self.name)
elif self.name:
return "{}".format(self.name)
return _("Unknown Country")
def get_absolute_url(self):
return reverse('business:business_pfcountry_detail', args=(self.pk,))
def get_update_url(self):
return reverse('business:business_pfcountry_update', args=(self.pk,))
@staticmethod
def api_pull(store=None, key=None):
"""
Update the Country and State objects from the Printful API.
:param store: Optional bzStore object. If not provided, method will
attempt to use the first store from the database if it exists.
:param key: If a key is provided, then it is used instead of store.
This is especially useful for when you're first creating a
store, and so avoids a race condition.
"""
if key:
api = pyPrintful(key=key)
else:
_storeObj = pfStore.get_store(store)
api = pyPrintful(key=_storeObj.key)
countries = api.get_countries_list()
for c in countries:
cObj, cCreated = pfCountry.objects.update_or_create(
code=c['code'],
defaults={
'name': c['name']
}
)
if c['states']:
for s in c['states']:
sObj, sCreated = pfState.objects.update_or_create(
code=s['code'],
pfcountry=cObj,
defaults={
'name': s['name'],
}
)
def get_states(self):
return pfState.objects.filter(pfcountry=self)
get_states.short_description = _("States")
def num_states(self):
return self.get_states().count()
num_states.short_description = _("States")
class pfState(commonBusinessModel):
# Fields
code = CharField(_("Code"), max_length=50,
default="", blank=True, null=True)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
# Relationship Fields
pfcountry = ForeignKey('business.pfCountry', verbose_name=_("Country"))
class Meta:
ordering = ('pfcountry__code', 'code',)
verbose_name = _("State")
verbose_name_plural = _("States")
def __str__(self):
if self.code and self.name:
return "{} - {}".format(self.code, self.name)
elif self.name:
return "{}".format(self.name)
return _("Unknown State")
def get_absolute_url(self):
return reverse('business:business_pfstate_detail', args=(self.pk,))
def get_update_url(self):
return reverse('business:business_pfstate_update', args=(self.pk,))
class pfSyncProduct(commonBusinessModel):
# Fields
pid = CharField(_("Printful ID"), max_length=200,
default="", blank=True, null=True)
external_id = CharField(_("External ID"), max_length=200,
default="", blank=True, null=True)
variants = IntegerField(_("Variant Count"), default=0)
synced = IntegerField(_("Synced"), default=0)
# Relationship Fields
pfstore = ForeignKey('business.pfStore', verbose_name=_("Store"))
class Meta:
ordering = ('-pk',)
verbose_name = _("Sync Product")
verbose_name_plural = _("Sync Products")
def __str__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse('business:business_pfsyncproduct_detail',
args=(self.pk,))
def get_update_url(self):
return reverse('business:business_pfsyncproduct_update',
args=(self.pk,))
class pfSyncVariant(commonBusinessModel):
# Fields
pid = CharField(_("Printful ID"), max_length=200,
default="", blank=True, null=True)
external_id = CharField(_("External ID"), max_length=200,
default="", blank=True, null=True)
synced = BooleanField(_("Synced"), default=False)
# Relationship Fields
pfsyncproduct = ForeignKey(
'business.pfSyncProduct', verbose_name=_("Sync Product"))
files = ManyToManyField('business.pfPrintFile', blank=True)
class Meta:
ordering = ('-pk',)
verbose_name = _("Sync Variant")
verbose_name_plural = _("Sync Variants")
def __str__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse('business:business_pfsyncvariant_detail',
args=(self.pk,))
def get_update_url(self):
return reverse('business:business_pfsyncvariant_update',
args=(self.pk,))
class pfSyncItemOption(commonBusinessModel):
# Fields
pid = CharField(_("Printful ID"), max_length=200,
default="", blank=True, null=True)
value = CharField(_("Value"), max_length=255,
default="", blank=True, null=True)
# Relationship Fields
pfsyncvariant = ForeignKey('business.pfSyncVariant', )
class Meta:
ordering = ('-pk',)
verbose_name = _("Sync Item Option")
verbose_name_plural = _("Sync Item Options")
def __str__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse(
'business:business_pfsyncitemoption_detail', args=(self.pk,))
def get_update_url(self):
return reverse(
'business:business_pfsyncitemoption_update', args=(self.pk,))
class pfCatalogColor(commonBusinessModel):
# Fields
code = CharField(_("Code"), max_length=3,
default="", blank=True, null=True)
name = CharField(_("Color"), max_length=255,
default="", blank=True, null=True)
label_clean = CharField(_("Clean Label"), max_length=255,
default="", blank=True, null=True)
hex_code = CharField(_("Color Hex Code"), max_length=255,
default="", blank=True, null=True)
class Meta:
ordering = ('-pk',)
verbose_name = _("Printful Color")
verbose_name_plural = _("Printful Colors")
def __str__(self):
rv = []
if self.code:
rv.append(self.code)
if self.label_clean:
rv.append(self.label_clean)
elif self.name:
rv.append(self.name)
if rv:
return " - ".join(rv)
return _("Unknown Color")
def get_absolute_url(self):
return reverse(
'business:business_pfcatalogcolor_detail', args=(self.pk,))
def get_update_url(self):
return reverse(
'business:business_pfcatalogcolor_update', args=(self.pk,))
def get_hex_code_clean(self):
return self.hex_code.replace("#", "")
class pfCatalogSize(commonBusinessModel):
# Fields
code = CharField(_("Code"), max_length=3,
default="", blank=True, null=True)
name = CharField(_("Size"), max_length=255,
default="", blank=True, null=True)
label_clean = CharField(_("Clean Label"), max_length=255,
default="", blank=True, null=True)
sort_group = CharField(_("Sort Group"), max_length=2,
default="", blank=True, null=True)
sort_order = CharField(_("Sort Order"), max_length=16,
default="", blank=True, null=True)
class Meta:
ordering = ('sort_group', 'sort_order',)
verbose_name = _("Printful Size")
verbose_name_plural = _("Printful Sizes")
def __str__(self):
rv = []
if self.code:
rv.append(self.code)
if self.label_clean:
rv.append(self.label_clean)
elif self.name:
rv.append(self.name)
if rv:
return " - ".join(rv)
return _("Unknown Size")
def get_absolute_url(self):
return reverse('business:business_pfcatalogsize_detail',
args=(self.pk,))
def get_update_url(self):
return reverse('business:business_pfcatalogsize_update',
args=(self.pk,))
class pfCatalogFileSpec(commonBusinessModel):
COLORSYSTEM_RGB = 'R'
COLORSYSTEM_CMYK = 'Y'
COLORSYSTEM_CHOICES = (
(COLORSYSTEM_RGB, "RGB"),
(COLORSYSTEM_CMYK, "CMYK"),
)
# Fields
name = CharField(_("Name"), max_length=5,
default="", blank=True, null=True)
note = TextField(_("Note"), default="", blank=True, null=True)
width = IntegerField(_("Width"), default=0)
height = IntegerField(_("Height"), default=0)
width_in = DecimalField(_("Width (in)"), default=0,
decimal_places=2, max_digits=4)
height_in = DecimalField(_("Height (in)"), default=0,
decimal_places=2, max_digits=4)
ratio = CharField(_("Ratio"), max_length=32,
default="", blank=True, null=True)
colorsystem = CharField(_("Color System"), max_length=1,
default="R", choices=COLORSYSTEM_CHOICES)
class Meta:
ordering = ('name',)
verbose_name = _("Printful File Spec")
verbose_name_plural = _("Printful File Specs")
def __str__(self):
if self.name:
return self.name
return _("Unknown File Spec")
def get_absolute_url(self):
return reverse(
'business:business_pfcatalogfilespec_detail', args=(self.pk,))
def get_update_url(self):
return reverse(
'business:business_pfcatalogfilespec_update', args=(self.pk,))
def save(self, *args, **kwargs):
if self.width and not self.width_in:
self.width_in = int(self.width / 300)
elif self.width_in and not self.width:
self.width = self.width_in * 300
if self.height and not self.height_in:
self.height_in = int(self.height / 300)
elif self.height_in and not self.height:
self.height = self.height_in * 300
# This should prevent ZeroDivisionError exceptions.
if not self.ratio and self.width and self.height:
_fraction = Fraction(int(self.width), int(self.height))
self.ratio = "{}:{}".format(
_fraction.numerator, _fraction.denominator)
super(pfCatalogFileSpec, self).save(*args, **kwargs)
class pfCatalogFileType(commonBusinessModel):
# Fields
pid = CharField(_("Printful ID"), max_length=255,
default="", blank=True, null=True)
title = CharField(_("Title"), max_length=255,
default="", blank=True, null=True)
additional_price = CharField(_("Additional Price"), max_length=100,
default="", blank=True, null=True)
# Relationship Fields
pfcatalogvariant = ForeignKey(
'business.pfCatalogVariant', verbose_name=_("Variant"))
class Meta:
ordering = ('-pk',)
verbose_name = _("Printful File Type")
verbose_name_plural = _("Printful File Types")
def __str__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse(
'business:business_pfcatalogfiletype_detail', args=(self.pk,))
def get_update_url(self):
return reverse(
'business:business_pfcatalogfiletype_update', args=(self.pk,))
class pfCatalogOptionType(commonBusinessModel):
# Fields
pid = CharField(_("Printful ID"), max_length=255,
default="", blank=True, null=True)
title = CharField(_("Title"), max_length=255,
default="", blank=True, null=True)
type = CharField(_("Type"), max_length=255,
default="", blank=True, null=True)
additional_price = CharField(_("Additional Price"), max_length=100,
default="", blank=True, null=True)
# Relationship Fields
pfcatalogvariant = ForeignKey('business.pfCatalogVariant', )
class Meta:
ordering = ('-pk',)
verbose_name = _("Printful Option Type")
verbose_name_plural = _("Printful Option Types")
def __str__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse(
'business:business_pfcatalogoptiontype_detail', args=(self.pk,))
def get_update_url(self):
return reverse(
'business:business_pfcatalogoptiontype_update', args=(self.pk,))
class pfCatalogBrand(commonBusinessModel):
name = CharField(_("Name"), max_length=128,
null=True, blank=True, default="")
def __str__(self):
if self.name:
return self.name
return "Unknown Brand"
class Meta:
ordering = ('-pk',)
verbose_name = _("Catalog Brand")
verbose_name_plural = _("Catalog Brands")
class pfCatalogType(commonBusinessModel):
name = CharField(_("Name"), max_length=128,
null=True, blank=True, default="")
def __str__(self):
if self.name:
return self.name
return "Unknown Type"
class Meta:
ordering = ('-pk',)
verbose_name = _("Catalog Product Type")
verbose_name_plural = _("Catalog Product Types")
class pfCatalogProduct(commonBusinessModel):
# Fields
is_active = BooleanField(_("Is Active?"), default=True)
pid = CharField(_("Printful ID"), max_length=255,
default="", blank=True, null=True)
ptype = ForeignKey('business.pfCatalogType', blank=True, null=True)
brand = ForeignKey('business.pfCatalogBrand', blank=True, null=True)
model = CharField(_("Model"), max_length=255,
default="", blank=True, null=True)
image = CharField(_("Image"), max_length=255,
default="", blank=True, null=True)
variant_count = IntegerField(_("Variants"), default=0)
class Meta:
ordering = ('brand', 'model')
verbose_name = _("Printful Product")
verbose_name_plural = _("Printful Products")
def __str__(self):
return self.get_friendly_name()
def get_friendly_name(self):
if self.pid and self.brand and self.brand:
return "{} / {} ({})".format(self.brand, self.model, self.pid)
return "Unknown Product"
def get_absolute_url(self):
return reverse(
'business:business_pfcatalogproduct_detail', args=(self.pk,))
def get_update_url(self):
return reverse(
'business:business_pfcatalogproduct_update', args=(self.pk,))
def get_variants(self):
return pfCatalogVariant.objects.filter(pfcatalogproduct=self)
get_variants.short_description = _("Variants")
def get_colors(self):
"""
Get all color objects associated with this product's variants.
"""
return pfCatalogColor.objects.filter(pfcatalogvariant__in=self.get_variants()).distinct()
get_colors.short_description = _("Colors")
def get_colors_as_string(self):
c = self.get_colors()
if c:
rv = ", ".join([v.label for v in c])
else:
rv = "-"
return rv
get_colors_as_string.short_description = _("Available Colors")
def num_colors(self):
return self.get_colors().count()
num_colors.short_description = _("Colors")
def get_sizes(self):
return pfCatalogSize.objects.filter(pfcatalogvariant__in=self.get_variants()).distinct()
get_sizes.short_description = _("Sizes")
def get_sizes_as_string(self):
s = self.get_sizes()
if s:
rv = ", ".join([v.get_name() for v in s])
else:
rv = "-"
return rv
get_sizes_as_string.short_description = _("Available Sizes")
def num_sizes(self):
return self.get_sizes().count()
num_sizes.short_description = _("Sizes")
def get_out_of_stock(self):
return pfCatalogVariant.objects.filter(pfcatalogproduct=self, in_stock=False)
def num_out_of_stock(self):
return self.get_out_of_stock().count()
num_out_of_stock.short_description = _("Out of Stock")
@staticmethod
def api_pull(store=None, key=None):
"""
Update the product objects from the Printful API.
:param store: Optional bzStore object. If not provided, method will
attempt to use the first store from the database if it exists.
:param key: If a key is provided, then it is used instead of store.
This is especially useful for when you're first creating a
store, and so avoids a race condition.
"""
if key:
api = pyPrintful(key=key)
else:
_storeObj = pfStore.get_store(store)
api = pyPrintful(key=_storeObj.key)
logger.debug("pfCatalogProduct.api_pull / Making API Call")
products = api.get_product_list()
logger.debug("pfCatalogProduct.api_pull / All: is_active=False")
pfCatalogProduct.objects.all().update(is_active=False)
for p in products:
# {
# 'dimensions': {
# '16×20': '16×20',
# },
# 'options': [],
# 'files': [
# {'id': 'preview', 'title': 'Mockup', 'type': 'mockup', 'additional_price': None}
# ]}
pType, tCreated = pfCatalogType.objects.update_or_create(
name=p['type'],
defaults={}
)
pBrand, bCreated = pfCatalogBrand.objects.update_or_create(
name=p['brand'],
defaults={}
)
pObj, pCreated = pfCatalogProduct.objects.update_or_create(
pid=p['id'],
defaults={
'brand': pBrand,
'variant_count': cleanValue(p['variant_count']),
'ptype': pType,
'model': cleanValue(p['model']),
'image': cleanValue(p['image']),
'is_active': True,
}
)
logger.debug("pfCatalogProduct.api_pull / {} {}", pCreated, pObj)
# Handle 'files'
# Handle 'dimensions'
if pObj.variant_count:
variants = api.get_product_info(pObj.pid)
pfCatalogVariant.objects.all().update(is_active=False)
for p in variants['variants']:
colorObj = None
if p['color']:
colorObj, colCreated = pfCatalogColor.objects.update_or_create(
name=cleanValue(p['color']),
defaults={
'hex_code': cleanValue(p['color_code']),
}
)
sizeObj = None
if p['size']:
sizeObj, szCreated = pfCatalogSize.objects.update_or_create(
name=cleanValue(p['size']),
defaults={}
)
vObj, vCreated = pfCatalogVariant.objects.update_or_create(
pid=p['id'],
pfcatalogproduct=pObj,
defaults={
'name': cleanValue(p['name']),
'image': cleanValue(p['image']),
'in_stock': cleanValue(p['in_stock'], False),
'price': Decimal(str(cleanValue(p['price']))),
'pfcolor': colorObj,
'pfsize': sizeObj,
}
)
# Handle 'options' (Attach to variants)
@staticmethod
def get_avail_sizes(obj):
return obj.get_sizes()
@staticmethod
def get_avail_colors(obj):
return obj.get_colors()
class pfCatalogVariant(commonBusinessModel):
# Fields
is_active = BooleanField(verbose_name=_("Is Active"), default=True)
pid = CharField(_("Printful ID"), max_length=16,
default="", blank=True, null=True)
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
image = CharField(_("Image"), max_length=255,
default="", blank=True, null=True)
price = CharField(_("Price"), max_length=255,
default="", blank=True, null=True)
in_stock = BooleanField(_("In Stock"), default=False)
weight = DecimalField(_("Weight (oz)"), default=0, blank=True,
null=True, decimal_places=2, max_digits=5)
# Relationship Fields
pfsize = ForeignKey('business.pfCatalogSize', blank=True,
null=True, verbose_name=_("Size"))
pfcolor = ForeignKey('business.pfCatalogColor', blank=True,
null=True, verbose_name=_("Color"))
pfcatalogproduct = ForeignKey('business.pfCatalogProduct', blank=True,
null=True, verbose_name=_("Catalog Product"))
class Meta:
ordering = ('-pk',)
verbose_name = _("Printful Variant")
verbose_name_plural = _("Printful Variants")
def __str__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse(
'business:business_pfcatalogvariant_detail', args=(self.pk,))
def get_update_url(self):
return reverse(
'business:business_pfcatalogvariant_update', args=(self.pk,))
class MimeType(commonBusinessModel):
name = CharField(_("Name"), max_length=255,
null=True, blank=True, default="")
clean_name = CharField(_("Friendly Name"), max_length=255,
null=True, blank=True, default="")
def __str__(self):
if self.clean_name:
return self.clean_name
if self.name:
return self.name
return "Unknown"
class Meta:
ordering = ('clean_name', 'name',)
verbose_name = _("MIME Type")
verbose_name_plural = _("MIME Types")
class pfFileType(commonBusinessModel):
name = CharField(_("Name"), max_length=255,
null=True, blank=True, default="")
clean_name = CharField(_("Friendly Name"), max_length=255,
null=True, blank=True, default="")
def __str__(self):
if self.clean_name:
return self.clean_name
if self.name:
return self.name
return "Unknown"
class Meta:
ordering = ('clean_name', 'name',)
verbose_name = _("File Type")
verbose_name_plural = _("File Types")
class pfFileStatus(commonBusinessModel):
name = CharField(_("Name"), max_length=255,
null=True, blank=True, default="")
clean_name = CharField(_("Friendly Name"), max_length=255,
null=True, blank=True, default="")
def __str__(self):
if self.clean_name:
return self.clean_name
if self.name:
return self.name
return "Unknown"
class Meta:
ordering = ('clean_name', 'name',)
verbose_name = _("File Status")
verbose_name_plural = _("File Statuses")
class pfPrintFile(commonBusinessModel):
# Fields
pid = IntegerField(_("Printful ID"), default=0)
phash = CharField(_("Hash"), max_length=255,
default="", blank=True, null=True)
url = CharField(_("URL"), max_length=255,
default="", blank=True, null=True)
filename = CharField(_("Filename"), max_length=255,
default="", blank=True, null=True)
size = IntegerField(_("Size"), default=0)
width = IntegerField(_("Width"), default=0)
height = IntegerField(_("Height"), default=0)
dpi = IntegerField(_("DPI"), default=0)
created = CharField(_("Created"), max_length=255,
default="", blank=True, null=True)
thumbnail_url = CharField(
_("Thumbnail URL"), max_length=255, default="", blank=True, null=True)
preview_url = CharField(
_("Preview URL"), max_length=255, default="", blank=True, null=True)
visible = BooleanField(_("Visible"), default=False)
is_active = BooleanField(_("Active"), default=True)
# Relationship Fields
mime_type = ForeignKey("business.MimeType",
verbose_name="MIME Type", blank=True, null=True)
ptype = ForeignKey("business.pfFileType",
verbose_name="File Type", blank=True, null=True)
status = ForeignKey("business.pfFileStatus",
verbose_name="File Status", blank=True, null=True)
pfstore = models.ForeignKey('business.pfStore', verbose_name="Store")
filespec = models.ForeignKey(
'business.pfCatalogFileSpec', verbose_name="File Spec", blank=True, null=True)
class Meta:
ordering = ('-created',)
verbose_name = _("Printful File")
verbose_name_plural = _("Printful Files")
def __str__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse('business:business_pfprintfile_detail', args=(self.pk,))
def get_update_url(self):
return reverse('business:business_pfprintfile_update', args=(self.pk,))
def dimensions(self, dpi=300):
if self.width and self.height:
rv = '{}" x {}" / {}dpi'.format(
str(int(self.width / 300)),
str(int(self.height / 300)),
dpi,
)
else:
rv = "Unknown"
return rv
dimensions.short_description = "Dimensions"
@staticmethod
def api_pull(store=None):
"""
Update the file list from your Printful store.
:param store: pfStore object.
"""
if store:
_storeObj = pfStore.get_store(store)
api = pyPrintful(key=_storeObj.key)
else:
raise("A pfStore object is required.")
# TODO Implement paging in this call.
logger.debug("pfPrintFile.api_pull / Making API Call")
files = api.get_file_list()
logger.debug("pfPrintFile.api_pull / All: is_active=False")
pfPrintFile.objects.all().update(is_active=False)
for p in files:
if p['mime_type']:
_mimetype, c = MimeType.objects.update_or_create(
name=p['mime_type'],
defaults={}
)
else:
_mimetype = None
if p['status']:
_status, c = pfFileStatus.objects.update_or_create(
name=p['status'],
defaults={}
)
else:
_status = None
if p['type']:
_ptype, c = pfFileType.objects.update_or_create(
name=p['type'],
defaults={}
)
else:
_ptype = None
pObj, pCreated = pfPrintFile.objects.update_or_create(
pid=p['id'],
pfstore=_storeObj,
defaults={
'is_active': True,
'phash': cleanValue(p['hash']),
'url': cleanValue(p['url']),
'filename': cleanValue(p['filename']),
'size': cleanValue(p['size'], 0),
'width': cleanValue(p['width'], 0),
'height': cleanValue(p['height'], 0),
'dpi': cleanValue(p['dpi'], 0),
'created': cleanValue(p['created']),
'thumbnail_url': cleanValue(p['thumbnail_url']),
'preview_url': cleanValue(p['preview_url']),
'visible': cleanValue(p['visible'], True),
'mime_type': _mimetype,
'status': _status,
'ptype': _ptype,
}
)
logger.debug("pfPrintFile.api_pull / {} {}", pCreated, pObj)
class pfAddress(commonBusinessModel):
# Fields
name = CharField(_("Name"), max_length=255,
default="", blank=True, null=True)
company = CharField(_("Company"), max_length=255,
default="", blank=True, null=True)
address1 = CharField(_("Address 1"), max_length=255,
default="", blank=True, null=True)
address2 = CharField(_("Address 2"), max_length=255,
default="", blank=True, null=True)
city = CharField(_("City"), max_length=255,
default="", blank=True, null=True)
state = ForeignKey("business.pfState", verbose_name=_(
"State"), blank=True, null=True)
country = ForeignKey("business.pfCountry", verbose_name=_(
"Country"), blank=True, null=True)
zip = CharField(_("Postal Code"), max_length=24,
default="", blank=True, null=True)
phone = CharField(_("Phone"), max_length=24,
default="", blank=True, null=True)
email = EmailField(_("Email"), default="", blank=True, null=True)
class Meta:
ordering = ('name',)
verbose_name = _("Address")
verbose_name_plural = _("Addresses")
def __str__(self):
if self.name and self.company:
return ", ".join([self.name, self.company])
return "Unnamed Address"
def asHTML(self):
"""
Returns an HTML div, formatted in a 'standard' way:
Name
Company
Address1
Address2
Zip City, State
Country
Tel: <Phone>
E-Mail: <Email>
"""
rv = []
rv.append("<div class='element-address'>")
if self.name:
rv.append(self.name + "<br/>")
if self.company:
rv.append(self.company + "<br/>")
if self.address1:
rv.append(self.address1 + "<br/>")
if self.address2:
rv.append(self.address2 + "<br/>")
if self.zip:
rv.append(self.zip + " ")
if self.city:
rv.append(self.city + ", ")
if self.state:
rv.append(self.state.code)
if self.country:
rv.append("<br/>" + self.country.name)
if self.phone:
rv.append("<br/>Tel: " + self.phone)
if self.email:
rv.append(
"<br/>Email: <a href='mailto:[]'>[]</a>".replace('[]', self.email))
rv.append("</div>")
return "".join(rv)