Ejemplo n.º 1
0
class WikiPage(db.Model):
    """Model for a page on the wiki"""
    __tablename__ = 'wiki_page'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), index=True, unique=True)
    content = db.Column(db.Text)
    modtime = db.Column(db.String(32))
    editor = db.Column(db.String(64))
    views = db.Column(db.Integer, index=True)

    def __init__(self, name, content):
        """Initiialize a WikiPage model"""
        self.name = name
        self.content = content
        self.modtime = datetime.datetime.now().strftime('%m/%d/%Y %I:%M %p')
        if current_user:
            self.editor = current_user.name
        else:
            self.editor = 'DLI'
        self.views = 0

    def __repr__(self):
        """Return a descriptive representation of a WikiPage"""
        return '<WikiPage %r>' % self.name

    def with_toc(self):
        """Return the page contents with a Table of Contents header"""
        full_text = """
        [TOC]

        {content}
        """.format(content=self.content)
        return full_text
Ejemplo n.º 2
0
class PasswordReset(db.Model):
    """Model for password reset key"""
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    key = db.Column(db.String(64))
    expiration = db.Column(db.DateTime)

    def __init__(self, user, key=None):
        """Initialize a  model"""
        if key is None:
            key = ''.join(
                random.choice(string.ascii_letters + string.digits)
                for _ in range(60))
        self.user = user
        self.key = key
        # Add one additional day so the user can potentially reset their password at midnight
        self.expiration = datetime.datetime.now() + datetime.timedelta(days=8)

    def __repr__(self):
        """Return a descriptive representation of password reset"""
        return '<Reset password for user %r>' % self.user

    @classmethod
    def get_by_key(cls, key):
        """Retrieve a user by the associated password reset key"""
        pw_reset = PasswordReset.query.filter_by(key=key).first()
        if pw_reset is not None:
            return pw_reset.user
        else:
            return None
Ejemplo n.º 3
0
class RegisterCandidate(db.Model):
    """Model for users who are allowed to register on the site"""
    __tablename__ = 'register_candidate'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), index=True, unique=True)
    send_time = db.Column(db.String(32))
    registration_key = db.Column(db.String(64))

    def __init__(self, email, registration_key=None):
        """Initialize a RegisterCandidate model"""
        if registration_key is None:
            registration_key = ''.join(
                random.choice(string.ascii_letters + string.digits)
                for _ in range(60))

        self.email = email
        self.registration_key = registration_key
        self.send_time = datetime.datetime.now().strftime('%m/%d/%Y %I:%M %p')

    def __repr__(self):
        """Return a descriptive representation of a RegisterCandidate"""
        return '<Register Candidate %r>' % self.email

    def send_link(self):
        """Send a registration link to this user"""
        key = self.registration_key
        title = 'Activate your account'
        url = 'http://{site}/auth/register/{key}'.format(
            site=os.environ['DLI_REPORTS_SITE_URL'],
            key=key,
        )
        recipient = self.email
        msg = Message(title, recipients=[recipient])
        msg.html = """
            <p>Hello</p>
            <p>You are invited to register at DLI Reports!
            Please go to this link to activate your account:</p>
            <a href="{url}">{url}</a>
            <p>Thank you!</p>
        """.format(url=url)
        mail.send(msg)
Ejemplo n.º 4
0
class FieldType(db.Model):
    """Model for the type of a Field"""
    __tablename__ = "field_type"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32), index=True)

    def __init__(self, name):
        """Initialize a FieldType model"""
        self.name = name

    def __repr__(self):
        """Return a descriptive representation of a FieldType"""
        return '<Field Type %r>' % self.name

    def __eq__(self, other):
        """Determine if two FieldTypes are equal"""
        return other is not None and self.id == other.id
Ejemplo n.º 5
0
class ChartType(db.Model):
    """Model for a ChartType (eg. Line, Bar, etc.)"""
    __tablename__ = 'chart_type'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32), index=True)

    def __init__(self, name):
        """Initialize a ChartType model"""
        self.name = name

    def __repr__(self):
        """Return a descriptive representation of a ChartType"""
        return '<Chart Type %r>' % self.name

    def __eq__(self, other):
        """Determine if two ChartTypes are equal"""
        return other is not None and self.id == other.id
Ejemplo n.º 6
0
class Location(db.Model):
    """Model for DLI's physical locations"""
    __tablename__ = "location"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), index=True, unique=True)
    users = db.relationship(
        User,
        backref="location",
    )

    def __init__(self, name):
        """Initialize a Location model"""
        self.name = name

    def __repr__(self):
        """Return a descriptive representation of a Location"""
        return '<Location %r>' % self.name
Ejemplo n.º 7
0
class Department(db.Model):
    """Model for DLI's departments"""
    __tablename__ = "department"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), index=True, unique=True)
    users = db.relationship(
        User,
        backref="department",
        lazy="dynamic",
    )
    fields = db.relationship(
        Field,
        backref="department",
    )

    def __init__(self, name):
        """Initialize a Department model"""
        self.name = name

    def __repr__(self):
        """Return a descriptive representation of a Department"""
        return '<Department %r>' % self.name
Ejemplo n.º 8
0
class Field(db.Model):
    """Model for a Field within a Report"""
    __tablename__ = "field"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))
    ftype_id = db.Column(db.Integer, db.ForeignKey("field_type.id"))
    ftype = db.relationship(FieldType)
    department_id = db.Column(db.Integer, db.ForeignKey("department.id"))
    data_points = db.relationship(
        FieldData,
        backref='field',
        lazy='dynamic',
    )

    def __init__(self, name, ftype, department):
        """Initialize a Field model"""
        self.name = name
        self.ftype = ftype
        self.department = department

    def __repr__(self):
        """Return a descriptive representation of a Field"""
        return '<Field %r>' % self.name

    def get_data_for_date(self, ds, pretty=False):
        """Retrieve the FieldData instance for the given date stamp"""
        data_point = self.data_points.filter_by(ds=ds).first()
        if pretty:
            if data_point is not None:
                data_point = data_point.pretty_value
            else:
                data_point = ""
        return data_point

    @property
    def identifier(self):
        """Property to uniquely identify this Field"""
        return '{}: {}'.format(self.department.name, self.name)
Ejemplo n.º 9
0
class Tag(db.Model):
    """Model for a Tag associated with a Report"""
    __tablename__ = "tag"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), index=True, unique=True)

    def __init__(self, name):
        """Initialize a Tag model"""
        self.name = name

    def __repr__(self):
        """Return a descriptive representation of a Tag"""
        return '<Tag %r>' % self.name

    @classmethod
    def get_or_create(cls, name):
        """Either retrieve a tag or create it if it doesn't exist"""
        tag = Tag.query.filter_by(name=name).first()
        if tag is None:
            tag = Tag(name)
            db.session.add(tag)
            db.session.commit()
        return tag
Ejemplo n.º 10
0
class Chart(db.Model):
    """Model for a DLI Chart"""
    __tablename__ = "chart"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), index=True)
    with_table = db.Column(db.Boolean)
    owner_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True)
    ctype_id = db.Column(db.Integer, db.ForeignKey("chart_type.id"))
    ctype = db.relationship(ChartType, backref="charts")
    fields = db.relationship(
        Field,
        secondary=chart_fields,
        backref='charts',
    )
    tags = db.relationship(
        Tag,
        secondary=chart_tags,
        backref='charts',
    )

    def __init__(self, name, with_table, user, ctype, fields, tags):
        """Initialize a Chart model"""
        self.name = name
        self.with_table = with_table
        self.user = user
        self.ctype = ctype
        self.fields = fields
        self.tags = tags

    def __repr__(self):
        """Return a descriptive representation of a Chart"""
        return '<Chart %r>' % self.name

    @property
    def is_pie_chart(self):
        """Return whether or not this chart is a Pie Chart"""
        ChartTypeConstants.reload()
        return self.ctype == ChartTypeConstants.PIE

    def data_points(self, min_date, max_date=None, ds_format=False):
        """Retrieve the data points needed for this chart"""
        if ds_format:
            min_ds = min_date
            max_ds = max_date
        else:
            min_ds = min_date.strftime('%Y-%m-%d')
            max_ds = datetime.datetime.now().strftime('%Y-%m-%d')
            if max_date:
                max_ds = max_date.strftime('%Y-%m-%d')

        return {
            field.identifier: {
                str(fdata.ds): str(fdata.value)
                for fdata in field.data_points.filter(
                    FieldData.ds >= min_ds).filter(FieldData.ds <= max_ds)
            }
            for field in self.fields
        }

    @property
    def tagnames(self):
        """Helper function to get the names of the Report's tags"""
        return [tag.name for tag in self.tags]

    def generated_js(self):
        """Property that represents this chart generated as C3 JavaScript"""
        min_date = datetime.datetime.now()
        ChartTypeConstants.reload()
        if self.ctype != ChartTypeConstants.PIE:
            min_date = min_date - datetime.timedelta(days=14)

        generate = 'true'
        if self.ctype == ChartTypeConstants.TABLE_ONLY:
            generate = 'false'

        return """
            var data_points = {data_points};
            var time_series = {time_series};
            var chart_type = "{chart_type}";
            var generate = {should_generate};
        """.format(
            time_series=get_time_series_sequence(min_date),
            data_points=self.data_points(min_date),
            chart_type=self.ctype.name,
            should_generate=generate,
        )
Ejemplo n.º 11
0
class Report(db.Model):
    """Model for a DLI Report"""
    __tablename__ = "report"
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True)
    name = db.Column(db.String(128))
    fields = db.relationship(
        Field,
        secondary=report_fields,
        backref='reports',
    )
    tags = db.relationship(
        Tag,
        secondary=report_tags,
        backref='reports',
    )

    def __init__(self, user, name, fields, tags):
        """Initialize a Report model"""
        self.user = user
        self.name = name
        self.fields = fields
        self.tags = tags

    def __repr__(self):
        """Return a descriptive representation of a Report"""
        return '<Report %r>' % self.name

    @property
    def tagnames(self):
        """Helper function to get the names of the Report's tags"""
        return [tag.name for tag in self.tags]

    def generate_filename(self, start_ds, end_ds):
        """Generate the filename for the Excel sheet for downloads"""
        return "{filename}-{start}-to-{end}.xlsx".format(
            filename=self.name,
            start=start_ds,
            end=end_ds,
        )

    def collect_dept_data_for_template(self, ds):
        """Collect all of the department data for this Report

        Collect department data for this Report on a given day in a format
        that is easy to template for render_template functions in Jinja2
        """

        dept_data = collections.defaultdict(list)
        for field in self.fields:
            dept_data[field.department.name].append({
                'name':
                field.name,
                'value':
                field.get_data_for_date(ds, pretty=True),
            })
        return dept_data

    def excel_filepath_for_ds(self, start_ds, end_ds):
        """Return the absolute filepath for the Excel sheet on the given ds"""
        return os.path.join(
            os.path.abspath(os.path.dirname(__file__)),
            EXCEL_FILE_DIR,
            self.generate_filename(start_ds, end_ds),
        )

    def excel_file_exists(self, start_ds, end_ds):
        """Determine whether or not an Excel file for this ds exists"""
        return os.path.exists(self.excel_filepath_for_ds(start_ds, end_ds))

    def create_excel_file(self, start_ds, end_ds):
        """Generate an Excel sheet with this Report's data

        Arguments:
        start_ds - Date stamp for the start day of Report data to generate
        end_ds - Date stamp for the end day of Report data to generate
        """

        excel_helper = ExcelSheetHelper(
            filepath=self.excel_filepath_for_ds(start_ds, end_ds),
            report=self,
            date_list=generate_date_list(
                datetime.datetime.strptime(start_ds, '%Y-%m-%d'),
                datetime.datetime.strptime(end_ds, '%Y-%m-%d'),
                step=1,
            ),
        )
        excel_helper.write_all(self.collect_dept_fields(start_ds, end_ds))
        excel_helper.finalize()

    def remove_excel_files(self):
        """Delete the Excel files for this Report"""
        basepath = os.path.join(
            os.path.abspath(os.path.dirname(__file__)),
            EXCEL_FILE_DIR,
        )
        globpath = os.path.join(basepath, self.name + '*.xlsx')

        for filename in glob.glob(globpath):
            os.remove(os.path.join(basepath, filename))

    def collect_dept_fields(self, start_ds, end_ds):
        """Collect all of the department data for this Report

        The best way we can do this is to create a complex dict:
        {dept : {field : {ds : value}}}

        Arguments:
        start_ds - the beginning ds for data to collect
        end_ds - the ending ds for data to collect
        """
        dept_data = {}
        for field in self.fields:
            field_data = {
                pt.ds: pt.value
                for pt in field.data_points.filter(
                    FieldData.ds >= start_ds).filter(FieldData.ds <= end_ds)
            }
            if not dept_data.get(field.department.name):
                dept_data[field.department.name] = {}
            dept_data[field.department.name][field] = field_data
        return dept_data
Ejemplo n.º 12
0
class FieldData(db.Model):
    """Model for the actual data stored in a Field"""
    __tablename__ = "field_data"
    id = db.Column(db.Integer, primary_key=True)
    ds = db.Column(db.String(16), index=True)
    field_id = db.Column(db.Integer, db.ForeignKey("field.id"))
    ivalue = db.Column(db.BigInteger)
    dvalue = db.Column(db.Float)
    svalue = db.Column(db.String(128))

    def __init__(self, ds, field, value):
        """Initialize a FieldData model"""
        FieldTypeConstants.reload()
        self.ds = ds
        self.field = field

        # Type checking should have already been done from the form
        if self.field.ftype == FieldTypeConstants.CURRENCY:
            parts = value.replace(',', '').replace('$', '').split('.')
            # Convert the value into cents to avoid any floating-point issues
            self.ivalue = int(parts[0]) * 100
            if len(parts) == 2:
                self.ivalue += int(parts[1])
        elif self.field.ftype == FieldTypeConstants.DOUBLE:
            self.dvalue = value
        elif self.field.ftype == FieldTypeConstants.INTEGER:
            self.ivalue = value
        elif self.field.ftype == FieldTypeConstants.STRING:
            self.svalue = value
        elif self.field.ftype == FieldTypeConstants.TIME:
            # Convert the value into seconds for convenience
            if ':' in value:
                parts = value.split(':')
            elif '.' in value:
                # Some people use '.' to denote minutes/seconds
                parts = value.split('.')
            else:
                # If no : or ., assume the value listed is seconds
                parts = ['0', value]

            # If the user listed something like ':00', make sure we can still parse
            parts = [x or '0' for x in parts]
            self.ivalue = int(parts[0]) * 60
            if len(parts) == 2:
                self.ivalue += int(parts[1])

    def __repr__(self):
        """Return a descriptive representation of a FieldData"""
        return '<FieldData of %r>' % self.field.name

    @property
    def value(self):
        """Property to easily retrieve the FieldData's value"""
        FieldTypeConstants.reload()
        ftype = self.field.ftype
        if ftype == FieldTypeConstants.CURRENCY:
            return float(self.ivalue) / 100
        elif ftype == FieldTypeConstants.DOUBLE:
            return self.dvalue
        elif ftype == FieldTypeConstants.INTEGER:
            return self.ivalue
        elif ftype == FieldTypeConstants.STRING:
            return self.svalue
        elif ftype == FieldTypeConstants.TIME:
            return self.ivalue
        else:
            raise NotImplementedError("ERROR: Type %s not supported!" % ftype)

    @property
    def pretty_value(self):
        """Property to easily retrieve a human-readable FieldData model"""
        FieldTypeConstants.reload()
        ftype = self.field.ftype
        if ftype == FieldTypeConstants.CURRENCY:
            dollars = self.ivalue / 100
            cents = self.ivalue % 100
            return "${dollars}.{cents:02d}".format(
                dollars=dollars,
                cents=cents,
            )
        elif ftype == FieldTypeConstants.DOUBLE:
            return self.dvalue
        elif ftype == FieldTypeConstants.INTEGER:
            return self.ivalue
        elif ftype == FieldTypeConstants.STRING:
            return self.svalue
        elif ftype == FieldTypeConstants.TIME:
            mins = self.ivalue / 60
            secs = self.ivalue % 60
            return "{mins}:{secs:02d}".format(
                mins=mins,
                secs=secs,
            )
        else:
            raise NotImplementedError("ERROR: Type %s not supported!" % ftype)
Ejemplo n.º 13
0
class User(db.Model, UserMixin):
    """Model for users of the site"""
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64))
    email = db.Column(db.String(64), index=True, unique=True)
    password = db.Column(db.String(128))
    is_admin = db.Column(db.Boolean)
    location_id = db.Column(db.Integer, db.ForeignKey("location.id"))
    dept_id = db.Column(db.Integer, db.ForeignKey("department.id"))
    pw_reset = db.relationship(
        PasswordReset,
        backref="user",
    )
    reports = db.relationship(
        Report,
        backref="user",
    )
    charts = db.relationship(
        Chart,
        backref="user",
    )
    error_reports = db.relationship(
        ErrorReport,
        backref="user",
    )
    favorite_reports = db.relationship(
        Report,
        secondary=report_users,
        backref='favorite_users',
    )
    favorite_charts = db.relationship(
        Chart,
        secondary=chart_users,
        backref='favorite_users',
    )

    def __init__(self, name, email, password, location, department):
        """Initialize a User model"""
        UserMixin.__init__(self)
        self.name = name
        self.email = email
        self.password = generate_password_hash(password)
        self.location = location
        self.department = department
        self.is_admin = False

    def set_password(self, new_password):
        """Change the user's password to the new password"""
        self.password = generate_password_hash(new_password)

    def check_password(self, password):
        """Check the user's password against the given value"""
        return check_password_hash(self.password, password)

    def favorite(self, report):
        """Add a report to the user's list of favorite reports"""
        if report not in self.favorite_reports:
            self.favorite_reports.append(report)

    def unfavorite(self, report):
        """Remove a report from the user's list of favorite reports"""
        if report in self.favorite_reports:
            self.favorite_reports.remove(report)

    def favorite_chart(self, chart):
        """Add a chart to the user's list of favorite charts"""
        if chart not in self.favorite_charts:
            self.favorite_charts.append(chart)

    def unfavorite_chart(self, chart):
        """Remove a chart from the user's list of favorite charts"""
        if chart in self.favorite_charts:
            self.favorite_charts.remove(chart)

    def __repr__(self):
        """Return a descriptive representation of a User"""
        return '<User %r>' % self.email

    @classmethod
    def get_by_email(cls, email):
        """Retrieve a user by their email address"""
        return User.query.filter_by(email=email).first()