Exemple #1
0
class BaseModel(CommonModel):
    __abstract__ = True

    is_active = db.Column(db.Boolean(),
                          nullable=False,
                          default=True,
                          server_default='1')
    is_deleted = db.Column(db.Boolean(),
                           nullable=False,
                           default=False,
                           server_default='0')
class CommonIsDeletedModel(db.Model):
    __abstract__ = True

    is_deleted = db.Column(db.Boolean(),
                           nullable=False,
                           default=False,
                           server_default='1')
Exemple #3
0
class DocReceiveRecord(db.Model):
    __tablename__ = 'doc_receive_records'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    predefined_comment = db.Column(
        db.String(255),
        info={
            'label':
            'Predefined Comment',
            'choices':
            [(c, c) for c in
             [u'แจ้งเพื่อทราบ', u'แจ้งเพื่อพิจารณา', u'ขอความร่วมมือเข้าร่วม']]
        })
    comment = db.Column(db.Text, info={'label': 'Additional Comment'})
    sent_at = db.Column(db.DateTime(timezone=True))
    sender_id = db.Column(db.ForeignKey('staff_account.id'))
    round_org_id = db.Column(db.ForeignKey('doc_round_orgs.id'))
    round_org = db.relationship(DocRoundOrg,
                                backref=db.backref('sent_records',
                                                   lazy='dynamic'))
    doc_id = db.Column(db.ForeignKey('doc_documents.id'))
    rejected = db.Column(db.Boolean(), default=False)
    members = db.relationship(StaffPersonalInfo, secondary=receipt_receivers)
    sender = db.relationship(StaffAccount)
    doc = db.relationship(DocDocument,
                          backref=db.backref('doc_receipts',
                                             lazy='dynamic',
                                             cascade='all, delete-orphan'))
Exemple #4
0
class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.BigInteger, primary_key=True)
    username = db.Column(db.String(255), unique=True, nullable=False)
    password = db.Column(db.UnicodeText, nullable=False)
    first_name = db.Column(db.UnicodeText, nullable=False)
    last_name = db.Column(db.UnicodeText, nullable=False)
    title = db.Column(db.UnicodeText, nullable=False)
    email = db.Column(db.String(255), unique=True, nullable=False)
    enabled = db.Column(db.Boolean(),
                        nullable=False,
                        default=True,
                        server_default='1')

    @property
    def full_name(self):
        return ' '.join([self.first_name, self.last_name])

    # Required for flask-login
    @property
    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return str(self.id)
Exemple #5
0
class Article(Base):
    aid = db.Column(db.Integer, primary_key=True)
    uid = db.Column(db.Integer)
    cid = db.Column(db.Integer)
    title = db.Column(db.String(255))
    content = db.Column(db.Text)
    picture = db.Column(db.String(2048))
    can_show = db.Column(db.Boolean())

    @classmethod
    def create_new(cls, uid, cid, title, content, picture):
        article = cls()
        article.uid = uid
        article.cid = cid
        article.title = title
        article.content = content
        article.picture = picture
        article.can_show = 1

        db.session.add(article)
        db.session.commit()

    @classmethod
    def update(cls, article):
        cls.query.filter_by(aid=article.aid).update({
            "title": article.title,
            "content": article.content,
        })
        db.session.commit()

    @classmethod
    def delete(cls, id):
        cls.query.filter_by(aid=id).delete()
        db.session.commit()

    @classmethod
    def get_last_by_limit(cls, limit):
        articles = cls.query.filter_by(can_show=1).order_by(
            desc(cls.date_created)).limit(limit).all()
        return articles

    @classmethod
    def get_article_by_id(cls, id):
        article = cls.query.filter_by(aid=id).first()
        if article.can_show:
            return article
        else:
            return None

    @classmethod
    def get_all_article_of_club(cls, clubid):
        articles = cls.query.filter_by(cid=clubid, can_show=1).all()
        if articles:
            return articles
        else:
            return []
Exemple #6
0
class VehicleBooking(db.Model):
    __tablename__ = 'scheduler_vehicle_bookings'
    id = db.Column('id', db.Integer(), primary_key=True, autoincrement=True)
    vehicle_id = db.Column('vehicle_id',
                           db.ForeignKey('scheduler_vehicle_resources.id'),
                           nullable=False)
    vehicle = db.relationship('VehicleResource',
                              backref=db.backref('bookings'))
    title = db.Column('title', db.String(255), nullable=False)
    init_milage = db.Column('init_milage', db.Integer, nullable=True)
    end_milage = db.Column('end_milage', db.Integer, nullable=True)
    toll_fee = db.Column('toll_fee', db.Float(), default=0.0)
    distance = db.Column('distance', db.Integer, nullable=True)
    init_location = db.Column('init_location', db.String(255), nullable=True)
    destination = db.Column('destination', db.String(255), nullable=True)
    start = db.Column('start', db.DateTime(timezone=True), nullable=False)
    end = db.Column('end', db.DateTime(timezone=True), nullable=False)
    iocode_id = db.Column('iocode_id', db.ForeignKey('iocodes.id'))
    iocode = db.relationship('IOCode', backref=db.backref('vehicle_bookings'))
    org_id = db.Column('org_id', db.ForeignKey('orgs.id'))
    org = db.relationship('Org', backref=db.backref('vehicle_bookings'))
    num_passengers = db.Column('num_passengers', db.Integer())
    approved = db.Column('approved', db.Boolean(), default=False)
    closed = db.Column('closed', db.Boolean(), default=False)
    created_at = db.Column('created_at',
                           db.DateTime(timezone=True),
                           server_default=func.now())
    created_by = db.Column('created_by', db.ForeignKey('staff_account.id'))
    updated_at = db.Column('updated_at',
                           db.DateTime(timezone=True),
                           server_default=None)
    updated_by = db.Column('updated_by', db.ForeignKey('staff_account.id'))
    cancelled_at = db.Column('cancelled_at',
                             db.DateTime(timezone=True),
                             server_default=None)
    cancelled_by = db.Column('cancelled_by', db.ForeignKey('staff_account.id'))
    approved_by = db.Column('approved_by', db.ForeignKey('staff_account.id'))
    approved_at = db.Column('approved_at',
                            db.DateTime(timezone=True),
                            server_default=None)
    desc = db.Column('desc', db.Text())
    google_event_id = db.Column('google_event_id', db.String(64))
    google_calendar_id = db.Column('google_calendar_id', db.String(255))
class Alumni(db.Model):
    __tablename__ = "alumni"

    alumni_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    alumni_uuid = db.Column(db.String(50), unique=True)
    odoo_contact_id = db.Column(db.String(50), unique=True, nullable=False)
    email = db.Column(db.String(100), unique=True)
    password = db.Column(db.String(100))
    user_confirmed = db.Column(db.Boolean())
    allow_show_contacts = db.Column(db.Boolean())

    update_form = db.relationship("UpdateForm", back_populates="alumni")

    def __init__(self, odoo_contact_id, email, password, allow_show_contacts):
        self.alumni_uuid = str(uuid.uuid4())
        self.odoo_contact_id = odoo_contact_id
        self.email = email
        self.password = flask_bcrypt.generate_password_hash(password).decode()
        self.user_confirmed = False
        self.allow_show_contacts = allow_show_contacts

    def check_password(self, password):
        return flask_bcrypt.check_password_hash(self.password, password)
Exemple #8
0
class SmartClassOnlineAccountEvent(db.Model):
    __tablename__ = 'smartclass_scheduler_online_account_events'
    id = db.Column('id', db.Integer(), primary_key=True, autoincrement=True)
    account_id = db.Column(
        'account_id',
        db.ForeignKey('smartclass_scheduler_online_accounts.id'),
        nullable=False)
    title = db.Column('title',
                      db.String(255),
                      nullable=False,
                      info={'label': u'กิจกรรม'})
    start = db.Column('start',
                      db.DateTime(timezone=True),
                      nullable=False,
                      info={'label': u'เริ่ม'})
    end = db.Column('end',
                    db.DateTime(timezone=True),
                    nullable=False,
                    info={'label': u'สิ้นสุด'})
    occupancy = db.Column('occupancy',
                          db.Integer(),
                          info={'label': u'ผู้เข้าร่วม'})
    approved = db.Column('approved', db.Boolean(), default=True)
    created_at = db.Column('created_at',
                           db.DateTime(timezone=True),
                           server_default=func.now())
    created_by = db.Column('created_by', db.ForeignKey('staff_account.id'))
    creator = db.Column('creator',
                        db.String(),
                        nullable=True,
                        info={'label': u'ชื่อผู้ลงทะเบียน'})
    updated_at = db.Column('updated_at',
                           db.DateTime(timezone=True),
                           server_default=None)
    updated_by = db.Column('updated_by', db.ForeignKey('staff_account.id'))
    cancelled_at = db.Column('cancelled_at',
                             db.DateTime(timezone=True),
                             server_default=None)
    cancelled_by = db.Column('cancelled_by', db.ForeignKey('staff_account.id'))
    approved_by = db.Column('approved_by', db.ForeignKey('staff_account.id'))
    approved_at = db.Column('approved_at',
                            db.DateTime(timezone=True),
                            server_default=None)
    note = db.Column('note', db.Text(), info={'label': u'หมายเหตุ'})
    account = db.relationship(SmartClassOnlineAccount,
                              backref=db.backref('events'))
Exemple #9
0
class Admin(db.Model):
    __tablename__ = "admin"

    admin_id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.String(100))
    last_name = db.Column(db.String(100)) 
    username = db.Column(db.String(100), unique=True, nullable=False)
    photo_url = db.Column(db.String(100))
    is_superadmin = db.Column(db.Boolean())

    repetition =  db.relationship("Repetition", back_populates="admin")

    def __init__(self, admin_id, first_name, last_name, username, photo_url, is_superadmin=False):
        self.admin_id = admin_id
        self.first_name = first_name
        self.last_name = last_name
        self.username = username
        self.photo_url = photo_url
        self.is_superadmin = is_superadmin
Exemple #10
0
class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.BigInteger, primary_key=True)
    username = db.Column(db.String(255), unique=True, nullable=False)
    password = db.Column(db.UnicodeText, nullable=False)
    first_name = db.Column(db.UnicodeText, nullable=False)
    last_name = db.Column(db.UnicodeText, nullable=False)
    role = db.Column(db.UnicodeText, nullable=False)
    email = db.Column(db.String(255), unique=True, nullable=False)
    enabled = db.Column(db.Boolean(),
                        nullable=False,
                        default=True,
                        server_default='1')

    client_services = db.relationship('ClientService',
                                      cascade='all, delete-orphan',
                                      backref='users')
    worker_services = db.relationship('WorkerService',
                                      cascade='all, delete-orphan',
                                      backref='users')

    @property
    def full_name(self):
        return ' '.join([self.first_name, self.last_name])

    @property
    def is_admin(self):
        return self.role == 'admin'

    # Required for flask-login
    @property
    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return str(self.id)
Exemple #11
0
class JobDetails(db.Model):
    """
    [summary]
    Args:
        UserMixin ([type]): [description]
        db ([type]): [description]
    """
    __tablename__ = "jobs"
    job_id = db.Column(db.Integer(),
                       nullable=False,
                       autoincrement=True,
                       primary_key=True)
    job_title = db.Column(db.String(250), nullable=True)
    company_name = db.Column(db.String(250), nullable=True)
    payscale = db.Column(db.String(250), nullable=True)
    location = db.Column(db.String(250), nullable=True)
    parent_source = db.Column(db.String(250), nullable=True)
    job_type = db.Column(db.String(250), nullable=True)
    company_type = db.Column(db.String(250), nullable=True)
    description = db.Column(db.String(250), nullable=True)
    date_posted = db.Column(db.String(250), nullable=True)
    active = db.Column(db.Boolean(), nullable=True)
Exemple #12
0
class RoomEvent(db.Model):
    __tablename__ = 'scheduler_room_reservations'
    id = db.Column('id', db.Integer(), primary_key=True, autoincrement=True)
    room_id = db.Column('room_id',
                        db.ForeignKey('scheduler_room_resources.id'),
                        nullable=False)
    category_id = db.Column('category_id',
                            db.ForeignKey('scheduler_event_categories.id'))
    category = db.relationship('EventCategory', backref=db.backref('events'))
    title = db.Column('title', db.String(255), nullable=False)
    start = db.Column('start', db.DateTime(timezone=True), nullable=False)
    end = db.Column('end', db.DateTime(timezone=True), nullable=False)
    iocode_id = db.Column('iocode_id', db.ForeignKey('iocodes.id'))
    occupancy = db.Column('occupancy', db.Integer())
    # number of sets of food/refreshment requested
    refreshment = db.Column('refreshment', db.Integer(), default=0)
    request = db.Column('request', db.Text())  # comma separated list of things
    approved = db.Column('approved', db.Boolean(), default=True)
    created_at = db.Column('created_at',
                           db.DateTime(timezone=True),
                           server_default=func.now())
    created_by = db.Column('created_by', db.ForeignKey('staff_account.id'))
    updated_at = db.Column('updated_at',
                           db.DateTime(timezone=True),
                           server_default=None)
    updated_by = db.Column('updated_by', db.ForeignKey('staff_account.id'))
    cancelled_at = db.Column('cancelled_at',
                             db.DateTime(timezone=True),
                             server_default=None)
    cancelled_by = db.Column('cancelled_by', db.ForeignKey('staff_account.id'))
    approved_by = db.Column('approved_by', db.ForeignKey('staff_account.id'))
    approved_at = db.Column('approved_at',
                            db.DateTime(timezone=True),
                            server_default=None)
    extra_items = db.Column('extra_items', db.JSON)
    note = db.Column('note', db.Text())
    google_event_id = db.Column('google_event_id', db.String(64))
    google_calendar_id = db.Column('google_calendar_id', db.String(255))
Exemple #13
0
class DocDocumentReach(db.Model):
    __tablename__ = 'doc_document_reaches'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    reached_at = db.Column(db.DateTime(timezone=True))
    reacher_id = db.Column(db.ForeignKey('staff_account.id'))
    reacher = db.relationship(StaffAccount,
                              backref=db.backref('doc_reaches',
                                                 lazy='dynamic'))
    doc_id = db.Column(db.ForeignKey('doc_documents.id'))
    starred = db.Column(db.Boolean(False))
    note = db.Column(db.Text())
    doc = db.relationship(DocDocument,
                          backref=db.backref('reaches',
                                             lazy='dynamic',
                                             cascade='all, delete-orphan'))
    round_org_id = db.Column(db.ForeignKey('doc_round_orgs.id'))
    round_org = db.relationship(DocRoundOrg,
                                backref=db.backref('doc_reaches',
                                                   lazy='dynamic'))
    sender_comment = db.Column(db.Text())
    receiver_comment = db.Column(db.Text())
    receiver_commented_at = db.Column(db.DateTime(timezone=True))
    created_at = db.Column(db.DateTime(timezone=True))
class UpdateForm(db.Model):
    """Namings as in Odoo.
    """
    __tablename__ = "update_form"

    form_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    form_status = db.Column(db.String(100))

    name = db.Column(db.String(100))  # full name in ukrainian
    birth_date = db.Column(db.Date())
    image_1920 = db.Column(
        db.String(500000)
    )  # Odoo saves image as base64 encoded string, f*cking large str
    # email = db.Column(db.String(100))  # Do not allow to update email here, because we use email for login

    contact_country = db.Column(db.String(100))  # selection field
    contact_city = db.Column(db.String(100))

    mobile = db.Column(db.String(15))
    skype = db.Column(db.String(100))
    telegram = db.Column(db.String(100))
    viber = db.Column(db.String(100))

    facebook_link = db.Column(db.String(100))
    linkedin_link = db.Column(db.String(100))

    diploma_naukma = db.Column(db.Boolean)

    bachelor_degree = db.Column(db.Boolean())
    bachelor_faculty = db.Column(db.String(100))  # selection field
    bachelor_speciality = db.Column(db.String(100))  # selection field
    bachelor_year_in = db.Column(db.String(100))  # selection field
    bachelor_year_out = db.Column(db.String(100))  # selection field

    master_degree = db.Column(db.Boolean())
    master_faculty = db.Column(db.String(100))  # selection field
    master_speciality = db.Column(db.String(100))  # selection field
    master_year_in = db.Column(db.String(100))  # selection field
    master_year_out = db.Column(db.String(100))  # selection field

    parent_id = db.Column(
        db.Integer)  # company id in Odoo, many2one field in Odoo
    company_name = db.Column(db.String(100))
    function = db.Column(db.String(100))  # job position

    # foreign keys
    alumni_id = db.Column(db.Integer,
                          db.ForeignKey('alumni.alumni_id',
                                        onupdate="CASCADE",
                                        ondelete="NO ACTION"),
                          nullable=False)
    alumni = db.relationship("Alumni", back_populates="update_form")

    operator_id = db.Column(db.Integer,
                            db.ForeignKey('operator.operator_id',
                                          onupdate="CASCADE",
                                          ondelete="NO ACTION"),
                            nullable=True)
    operator = db.relationship("Operator", back_populates="update_form")

    def __init__(self, name, birth_date, image_1920, contact_country,
                 contact_city, mobile, skype, telegram, viber, facebook_link,
                 linkedin_link, diploma_naukma, bachelor_degree,
                 bachelor_faculty, bachelor_speciality, bachelor_year_in,
                 bachelor_year_out, master_degree, master_faculty,
                 master_speciality, master_year_in, master_year_out, parent_id,
                 company_name, function, alumni_id, operator_id):

        self.form_status = 'new'  # TODO: create enum for the form statuses
        self.name = name
        self.birth_date = datetime.strptime(birth_date, '%Y-%m-%d').date()
        self.image_1920 = image_1920

        self.contact_country = contact_country
        self.contact_city = contact_city

        self.mobile = mobile
        self.skype = skype
        self.telegram = telegram
        self.viber = viber
        self.facebook_link = facebook_link
        self.linkedin_link = linkedin_link

        self.diploma_naukma = diploma_naukma

        self.bachelor_degree = bachelor_degree
        self.bachelor_faculty = bachelor_faculty
        self.bachelor_speciality = bachelor_speciality
        self.bachelor_year_in = bachelor_year_in
        self.bachelor_year_out = bachelor_year_out

        self.master_degree = master_degree
        self.master_faculty = master_faculty
        self.master_speciality = master_speciality
        self.master_year_in = master_year_in
        self.master_year_out = master_year_out

        self.parent_id = parent_id
        self.company_name = company_name
        self.function = function

        self.alumni_id = alumni_id
        self.operator_id = operator_id

    def update(self, data):
        for key, item in data.items():
            if hasattr(self, key):
                setattr(self, key, item)
            db.session.commit()
Exemple #15
0
class UserEntity(db.Model, UserMixin, CRUDMixin):
    """ Stores the basic information about the user.
    Implements the functions as required by:
        https://flask-login.readthedocs.org/en/latest/
    """
    __tablename__ = 'User'

    id = db.Column("usrID", db.Integer, primary_key=True)
    email = db.Column("usrEmail", db.String(255), nullable=False, unique=True)
    first = db.Column("usrFirst", db.String(255), nullable=False)
    last = db.Column("usrLast", db.String(255), nullable=False)
    minitial = db.Column("usrMI", db.String(1), nullable=False)
    added_at = db.Column("usrAddedAt",
                         db.DateTime,
                         nullable=False,
                         server_default='0000-00-00 00:00:00')
    modified_at = db.Column("usrModifiedAt", db.TIMESTAMP, nullable=False)
    email_confirmed_at = db.Column("usrEmailConfirmedAt",
                                   db.DateTime,
                                   nullable=False,
                                   server_default='0000-00-00 00:00:00')
    active = db.Column("usrIsActive",
                       db.Boolean(),
                       nullable=False,
                       server_default='1')

    access_expires_at = db.Column("usrAccessExpiresAt",
                                  db.DateTime,
                                  nullable=False,
                                  server_default='0000-00-00 00:00:00')
    password_hash = db.Column("usrPasswordHash",
                              db.String(255),
                              nullable=False,
                              server_default='')

    # @OneToMany
    roles = db.relationship(RoleEntity,
                            secondary=UserRoleEntity.__tablename__,
                            backref=db.backref('users'),
                            lazy='dynamic')
    """
    `lazy` defines when SQLAlchemy will load the data from the:
        'select' (which is the default) means that SQLAlchemy will load the
        data as necessary in one go using a standard select statement.
    'joined' tells SQLAlchemy to load the relationship in the same query as the
        parent using a JOIN statement.
    'subquery' works like 'joined' but instead SQLAlchemy will use a subquery.
    'dynamic' is special and useful if you have may items. Instead of loading
        the items SQLAlchemy will return another query object which you can
        further refine before loading them items. This is usually what you want
        if you expect more than a handful of items for this relationship.
    """
    def is_active(self):
        """ An user can be blocked by setting a flag in the """
        return self.active

    def is_expired(self):
        """ An user can be blocked by setting expiration date to yesterday"""
        return self.access_expires_at < datetime.today()

    def is_anonymous(self):
        """ Flag instances of valid users """
        return False

    def is_authenticated(self):
        """ Returns True if the user is authenticated, i.e. they have provided
        valid credentials.
        (Only authenticated users will fulfill the criteria of login_required.)
        """
        return True

    def get_id(self):
        """ The id encrypted in the session """
        return unicode(self.id)

    def get_roles(self):
        """ Return text representation of user roles """
        return [role.name for role in self.roles]

    def get_name(self):
        """
        :rtype string
        :return concat(first, ' ', last)
        """
        return "{} {}".format(self.first.encode('utf-8'),
                              self.last.encode('utf-8'))

    def get_email_verification_token(self, salt, secret):
        """
        :rtype string
        :return the email verification token stored in the
        """
        return utils.get_email_token(self.email, salt, secret)

    def serialize(self):
        """Return object data for jsonification"""
        return {
            'id':
            self.id,
            'email':
            self.email,
            'roles': [r.name for r in self.roles],
            'first':
            self.first,
            'last':
            self.last,
            'minitial':
            self.minitial,
            'is_active':
            True if self.active else False,
            'is_expired':
            True if self.is_expired() else False,
            'added_at':
            utils.localize_est_date(self.added_at),
            'email_confirmed_at':
            utils.localize_est_datetime(self.email_confirmed_at),
            'access_expires_at':
            utils.localize_est_datetime(self.access_expires_at)
        }

    def __repr__(self):
        return "<UserEntity (usrID: {0.id}, usrEmail: {0.email}, " \
               "usrIsActive: {0.active})>".format(self)
Exemple #16
0
class Resource(TimestampMixin, db.Model):
    __tablename__ = 'resources'
    id = db.Column(db.BigInteger, primary_key=True)
    category_id = db.Column(db.BigInteger,
                            db.ForeignKey('categories.id',
                                          onupdate='CASCADE',
                                          ondelete='RESTRICT'),
                            nullable=False)
    category = db.relationship('Category', backref='resources')
    user_id = db.Column(db.BigInteger,
                        db.ForeignKey('users.id',
                                      onupdate='CASCADE',
                                      ondelete='CASCADE'),
                        nullable=False)
    user = db.relationship('User', backref='resources')
    description = db.Column(db.UnicodeText(), nullable=True)
    type = db.Column(sau.ChoiceType(USER_RESOURCE_TYPES), index=True)
    quantity_available = db.Column(db.BigInteger)
    quantity_needed = db.Column(db.BigInteger)
    fulfilled = db.Column(db.Boolean,
                          nullable=False,
                          default=False,
                          server_default='0')
    name = db.Column(db.UnicodeText(), nullable=False)
    picture = db.Column(db.UnicodeText())
    requested = db.Column(db.Boolean(), nullable=True, default=False)

    def __setattr__(self, name, value):
        if name == 'picture':
            if value:
                filename = '{}.{}'.format(self.id,
                                          value.filename.split('.')[-1])
                super().__setattr__(
                    'picture', upload_file(value, 'resource_pictures',
                                           filename))

        else:
            super().__setattr__(name, value)

    @property
    def picture_url(self):
        if not self.picture:
            return None
        return file_url(self.picture)

    @property
    def quantity_remaining(self):
        if self.type != 'HAVE':
            return None

        fulfillment = ResourceFulfillment.query.filter(
            ResourceFulfillment.fulfilling_resource == self,
            ResourceFulfillment.confirmed_by_recipient == True,
        )
        return self.quantity_available - sum(
            [f.fulfilled_quantity for f in fulfillment])

    @property
    def quantity_fulfilled(self):
        if self.type != 'NEED':
            return None

        fulfillment = ResourceFulfillment.query.filter(
            ResourceFulfillment.fulfilled_resource == self,
            ResourceFulfillment.confirmed_by_recipient == True,
        )
        return self.quantity_needed - sum(
            [f.fulfilled_quantity for f in fulfillment])

    def serialize(self):
        return {
            'id': self.id,
            'name': self.name,
            'category': self.category.name,
            'user_id': self.user.id,
            'type': self.type.code,
            'quantity_available': self.quantity_available,
            'quantity_needed': self.quantity_needed,
            'fulfilled': self.fulfilled,
        }