Added Flask-Demo and Flask AppBuilder tutorial
This commit is contained in:
17
Flask-Demo/app.py
Normal file
17
Flask-Demo/app.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def hello_world():
|
||||||
|
return "<p>Hello, world!</p>"
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/haha")
|
||||||
|
def second_example():
|
||||||
|
return "<p>Go dukes!</p>"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(debug=True)
|
||||||
51
Flask-Demo/world.py
Normal file
51
Flask-Demo/world.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
"""Example web app that connects to PostgreSQL."""
|
||||||
|
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
import psycopg
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
con = psycopg.connect(
|
||||||
|
host="data.cs.jmu.edu", user="demo", password="demo", dbname="world"
|
||||||
|
)
|
||||||
|
cur = con.cursor()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def hello_world():
|
||||||
|
return "<p>Hello, World!</p>"
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/country/<code>")
|
||||||
|
def country_info(code: str):
|
||||||
|
_ = cur.execute(
|
||||||
|
"SELECT name, continent, region, population FROM country WHERE code = %s",
|
||||||
|
(code.upper(),),
|
||||||
|
)
|
||||||
|
row = cur.fetchone()
|
||||||
|
if not row:
|
||||||
|
return "<p>Country not found</p>"
|
||||||
|
name, cont, reg, pop = row
|
||||||
|
out = StringIO()
|
||||||
|
_ = out.write(
|
||||||
|
f"<p><strong>{name}</strong> is in <strong>{cont}</strong> in the <strong>{reg}</strong> region.</p>"
|
||||||
|
)
|
||||||
|
_ = out.write(
|
||||||
|
f"<p><strong>{name}</strong>'s population <strong>{pop:,d}</strong>.</p>"
|
||||||
|
)
|
||||||
|
return out.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/codes")
|
||||||
|
def multi_country():
|
||||||
|
_ = cur.execute("SELECT name, code population FROM country")
|
||||||
|
|
||||||
|
return f"<ul>{"".join(
|
||||||
|
f'<li><a href="/country/{code}">{name} {code}</a></li>'
|
||||||
|
for (name, code) in cur.fetchall()
|
||||||
|
)}</ul>"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(debug=True)
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
python-dotenv==1.1.1
|
python-dotenv==1.1.1
|
||||||
psycopg==3.2.11
|
psycopg==3.2.11
|
||||||
psycopg-binary==3.2.11
|
psycopg-binary==3.2.11
|
||||||
|
Flask-AppBuilder==5.0.1
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
30
fab/README.md
Normal file
30
fab/README.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# webapp
|
||||||
|
|
||||||
|
This application is built using [Flask-AppBuilder](https://github.com/dpgaspar/Flask-AppBuilder) version 5.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
* [app.py](app.py) -- Initialize the Flask application, database session, and appbuilder object.
|
||||||
|
* [config.py](config.py) -- Configure application settings such as the database URI, secret key, etc.
|
||||||
|
* [models.py](models.py) -- SQLAlchemy model classes (database tables) generated by `sqlacodegen`.
|
||||||
|
* [views.py](views.py) -- Flask-AppBuilder views that provide CRUD web interfaces for the models.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Generate a random SECRET_KEY for use in config.py:
|
||||||
|
``` sh
|
||||||
|
python -c 'import secrets; print(secrets.token_hex())'
|
||||||
|
```
|
||||||
|
|
||||||
|
Create an admin user before running for the first time:
|
||||||
|
``` sh
|
||||||
|
flask fab create-admin
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
Start a local dev server with debugging and reloading:
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
flask run --debug
|
||||||
|
```
|
||||||
17
fab/app.py
Normal file
17
fab/app.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
"""Initialize the Flask application, database session, and appbuilder object."""
|
||||||
|
|
||||||
|
from flask import Flask
|
||||||
|
from flask_appbuilder import AppBuilder
|
||||||
|
from flask_appbuilder.utils.legacy import get_sqla_class
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_object("config")
|
||||||
|
|
||||||
|
db = get_sqla_class()()
|
||||||
|
appbuilder = AppBuilder()
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
db.init_app(app)
|
||||||
|
appbuilder.init_app(app, db.session) # type: ignore
|
||||||
|
|
||||||
|
import views # noqa
|
||||||
23
fab/config.py
Normal file
23
fab/config.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
"""Configure application settings such as the database URI, secret key, etc."""
|
||||||
|
|
||||||
|
import socket
|
||||||
|
|
||||||
|
# Determine whether connecting from on/off campus
|
||||||
|
try:
|
||||||
|
socket.gethostbyname("data.cs.jmu.edu")
|
||||||
|
HOST = "data.cs.jmu.edu"
|
||||||
|
except socket.gaierror:
|
||||||
|
HOST = "localhost"
|
||||||
|
|
||||||
|
|
||||||
|
# See https://flask-appbuilder.readthedocs.io/en/latest/config.html
|
||||||
|
|
||||||
|
SQLALCHEMY_DATABASE_URI = "postgresql+psycopg://tamassno:113880616@localhost/sec2"
|
||||||
|
|
||||||
|
SECRET_KEY = "c46096d07cfae2ef311332eb98bc8336acaacf162427122b285ceb388a1cade1"
|
||||||
|
|
||||||
|
AUTH_TYPE = 1 # Database style (user/password)
|
||||||
|
|
||||||
|
APP_NAME = "Conference Review System"
|
||||||
|
|
||||||
|
APP_THEME = "readable.css"
|
||||||
275
fab/models.py
Normal file
275
fab/models.py
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
import datetime
|
||||||
|
from typing import Optional, override
|
||||||
|
|
||||||
|
from flask_appbuilder import Model
|
||||||
|
from sqlalchemy import (
|
||||||
|
Column,
|
||||||
|
Date,
|
||||||
|
DateTime,
|
||||||
|
Enum,
|
||||||
|
ForeignKeyConstraint,
|
||||||
|
Identity,
|
||||||
|
Integer,
|
||||||
|
PrimaryKeyConstraint,
|
||||||
|
Table,
|
||||||
|
Text,
|
||||||
|
)
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
|
|
||||||
|
class Affiliation(Model):
|
||||||
|
__tablename__ = "affiliation"
|
||||||
|
__table_args__ = (PrimaryKeyConstraint("org_name", name="affiliation_pkey"),)
|
||||||
|
|
||||||
|
org_name: Mapped[str] = mapped_column(Text, primary_key=True)
|
||||||
|
website: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
country: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
|
||||||
|
person_affiliation: Mapped[list["PersonAffiliation"]] = relationship(
|
||||||
|
"PersonAffiliation", back_populates="affiliation"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Conference(Model):
|
||||||
|
__tablename__ = "conference"
|
||||||
|
__table_args__ = (PrimaryKeyConstraint("year", name="conference_pkey"),)
|
||||||
|
|
||||||
|
year: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||||
|
location: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
|
||||||
|
papers: Mapped[list["Paper"]] = relationship("Paper", back_populates="conference")
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"{self.location} {self.year}"
|
||||||
|
|
||||||
|
|
||||||
|
class Person(Model):
|
||||||
|
__tablename__ = "person"
|
||||||
|
__table_args__ = (PrimaryKeyConstraint("email", name="person_pkey"),)
|
||||||
|
|
||||||
|
email: Mapped[str] = mapped_column(Text, primary_key=True)
|
||||||
|
first_name: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
last_name: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
|
||||||
|
paper: Mapped[list["Paper"]] = relationship("Paper", back_populates="person")
|
||||||
|
person_affiliation: Mapped[list["PersonAffiliation"]] = relationship(
|
||||||
|
"PersonAffiliation", back_populates="person"
|
||||||
|
)
|
||||||
|
paper_author: Mapped[list["PaperAuthor"]] = relationship(
|
||||||
|
"PaperAuthor", back_populates="person"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Topic(Model):
|
||||||
|
__tablename__ = "topic"
|
||||||
|
__table_args__ = (PrimaryKeyConstraint("topic_id", name="topic_pkey"),)
|
||||||
|
|
||||||
|
topic_id: Mapped[int] = mapped_column(
|
||||||
|
Integer,
|
||||||
|
Identity(
|
||||||
|
start=1, increment=1, minvalue=1, maxvalue=2147483647, cycle=False, cache=1
|
||||||
|
),
|
||||||
|
primary_key=True,
|
||||||
|
)
|
||||||
|
topic_name: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
|
||||||
|
papers: Mapped[list["Paper"]] = relationship(
|
||||||
|
"Paper", secondary="paper_topic", back_populates='topics'
|
||||||
|
)
|
||||||
|
reviewer: Mapped[list["Reviewer"]] = relationship(
|
||||||
|
"Reviewer", secondary="expertise", back_populates="topic"
|
||||||
|
)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"{self.topic_name}"
|
||||||
|
|
||||||
|
|
||||||
|
class Paper(Model):
|
||||||
|
__tablename__ = "paper"
|
||||||
|
__table_args__ = (
|
||||||
|
ForeignKeyConstraint(
|
||||||
|
["contact_email"], ["person.email"], name="paper_contact_email_fkey"
|
||||||
|
),
|
||||||
|
ForeignKeyConstraint(["year"], ["conference.year"], name="paper_year_fkey"),
|
||||||
|
PrimaryKeyConstraint("paper_id", name="paper_pkey"),
|
||||||
|
)
|
||||||
|
|
||||||
|
paper_id: Mapped[int] = mapped_column(
|
||||||
|
Integer,
|
||||||
|
Identity(
|
||||||
|
start=1, increment=1, minvalue=1, maxvalue=2147483647, cycle=False, cache=1
|
||||||
|
),
|
||||||
|
primary_key=True,
|
||||||
|
)
|
||||||
|
title: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
abstract: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
filename: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
contact_email: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
year: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
|
||||||
|
person: Mapped["Person"] = relationship("Person", back_populates="paper")
|
||||||
|
conference: Mapped["Conference"] = relationship(
|
||||||
|
"Conference", back_populates='papers'
|
||||||
|
)
|
||||||
|
topics: Mapped[list["Topic"]] = relationship(
|
||||||
|
"Topic", secondary="paper_topic", back_populates='papers'
|
||||||
|
)
|
||||||
|
history: Mapped[list["History"]] = relationship("History", back_populates="paper")
|
||||||
|
paper_author: Mapped[list["PaperAuthor"]] = relationship(
|
||||||
|
"PaperAuthor", back_populates="paper"
|
||||||
|
)
|
||||||
|
review: Mapped[list["Review"]] = relationship("Review", back_populates="paper")
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"Paper #{self.paper_id}: {self.title}"
|
||||||
|
|
||||||
|
|
||||||
|
class PersonAffiliation(Model):
|
||||||
|
__tablename__ = "person_affiliation"
|
||||||
|
__table_args__ = (
|
||||||
|
ForeignKeyConstraint(
|
||||||
|
["email"], ["person.email"], name="person_affiliation_email_fkey"
|
||||||
|
),
|
||||||
|
ForeignKeyConstraint(
|
||||||
|
["org_name"],
|
||||||
|
["affiliation.org_name"],
|
||||||
|
name="person_affiliation_org_name_fkey",
|
||||||
|
),
|
||||||
|
PrimaryKeyConstraint("email", "org_name", name="person_affiliation_pkey"),
|
||||||
|
)
|
||||||
|
|
||||||
|
email: Mapped[str] = mapped_column(Text, primary_key=True)
|
||||||
|
org_name: Mapped[str] = mapped_column(Text, primary_key=True)
|
||||||
|
from_date: Mapped[Optional[datetime.date]] = mapped_column(Date)
|
||||||
|
to_date: Mapped[Optional[datetime.date]] = mapped_column(Date)
|
||||||
|
|
||||||
|
person: Mapped["Person"] = relationship(
|
||||||
|
"Person", back_populates="person_affiliation"
|
||||||
|
)
|
||||||
|
affiliation: Mapped["Affiliation"] = relationship(
|
||||||
|
"Affiliation", back_populates="person_affiliation"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Reviewer(Person):
|
||||||
|
__tablename__ = "reviewer"
|
||||||
|
__table_args__ = (
|
||||||
|
ForeignKeyConstraint(["email"], ["person.email"], name="reviewer_email_fkey"),
|
||||||
|
PrimaryKeyConstraint("email", name="reviewer_pkey"),
|
||||||
|
)
|
||||||
|
|
||||||
|
email: Mapped[str] = mapped_column(Text, primary_key=True)
|
||||||
|
phone: Mapped[Optional[str]] = mapped_column(Text)
|
||||||
|
|
||||||
|
topic: Mapped[list["Topic"]] = relationship(
|
||||||
|
"Topic", secondary="expertise", back_populates="reviewer"
|
||||||
|
)
|
||||||
|
review: Mapped[list["Review"]] = relationship("Review", back_populates="reviewer")
|
||||||
|
|
||||||
|
|
||||||
|
t_expertise = Table(
|
||||||
|
"expertise",
|
||||||
|
Model.metadata,
|
||||||
|
Column("email", Text, primary_key=True),
|
||||||
|
Column("topic_id", Integer, primary_key=True),
|
||||||
|
ForeignKeyConstraint(["email"], ["reviewer.email"], name="expertise_email_fkey"),
|
||||||
|
ForeignKeyConstraint(
|
||||||
|
["topic_id"], ["topic.topic_id"], name="expertise_topic_id_fkey"
|
||||||
|
),
|
||||||
|
PrimaryKeyConstraint("email", "topic_id", name="expertise_pkey"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class History(Model):
|
||||||
|
__tablename__ = "history"
|
||||||
|
__table_args__ = (
|
||||||
|
ForeignKeyConstraint(
|
||||||
|
["paper_id"], ["paper.paper_id"], name="history_paper_id_fkey"
|
||||||
|
),
|
||||||
|
PrimaryKeyConstraint("paper_id", "timestamp", name="history_pkey"),
|
||||||
|
)
|
||||||
|
|
||||||
|
paper_id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||||
|
timestamp: Mapped[datetime.datetime] = mapped_column(DateTime, primary_key=True)
|
||||||
|
paper_status: Mapped[str] = mapped_column(
|
||||||
|
Enum(
|
||||||
|
"SUBMITTED",
|
||||||
|
"UNDER_REVIEW",
|
||||||
|
"REVISION",
|
||||||
|
"RESUBMITTED",
|
||||||
|
"REJECTED",
|
||||||
|
"ACCEPTED",
|
||||||
|
"PUBLISHED",
|
||||||
|
name="status",
|
||||||
|
),
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
|
notes: Mapped[Optional[str]] = mapped_column(Text)
|
||||||
|
|
||||||
|
paper: Mapped["Paper"] = relationship("Paper", back_populates="history")
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"History for #{self.paper_id} at {self.timestamp}"
|
||||||
|
|
||||||
|
|
||||||
|
class PaperAuthor(Model):
|
||||||
|
__tablename__ = "paper_author"
|
||||||
|
__table_args__ = (
|
||||||
|
ForeignKeyConstraint(
|
||||||
|
["email"], ["person.email"], name="paper_author_email_fkey"
|
||||||
|
),
|
||||||
|
ForeignKeyConstraint(
|
||||||
|
["paper_id"], ["paper.paper_id"], name="paper_author_paper_id_fkey"
|
||||||
|
),
|
||||||
|
PrimaryKeyConstraint("paper_id", "email", name="paper_author_pkey"),
|
||||||
|
)
|
||||||
|
|
||||||
|
paper_id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||||
|
email: Mapped[str] = mapped_column(Text, primary_key=True)
|
||||||
|
rank: Mapped[int] = mapped_column(Integer, nullable=False, comment="author order")
|
||||||
|
|
||||||
|
person: Mapped["Person"] = relationship("Person", back_populates="paper_author")
|
||||||
|
paper: Mapped["Paper"] = relationship("Paper", back_populates="paper_author")
|
||||||
|
|
||||||
|
|
||||||
|
t_paper_topic = Table(
|
||||||
|
"paper_topic",
|
||||||
|
Model.metadata,
|
||||||
|
Column("paper_id", Integer, primary_key=True),
|
||||||
|
Column("topic_id", Integer, primary_key=True),
|
||||||
|
ForeignKeyConstraint(
|
||||||
|
["paper_id"], ["paper.paper_id"], name="paper_topic_paper_id_fkey"
|
||||||
|
),
|
||||||
|
ForeignKeyConstraint(
|
||||||
|
["topic_id"], ["topic.topic_id"], name="paper_topic_topic_id_fkey"
|
||||||
|
),
|
||||||
|
PrimaryKeyConstraint("paper_id", "topic_id", name="paper_topic_pkey"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Review(Model):
|
||||||
|
__tablename__ = "review"
|
||||||
|
__table_args__ = (
|
||||||
|
ForeignKeyConstraint(["email"], ["reviewer.email"], name="review_email_fkey"),
|
||||||
|
ForeignKeyConstraint(
|
||||||
|
["paper_id"], ["paper.paper_id"], name="review_paper_id_fkey"
|
||||||
|
),
|
||||||
|
PrimaryKeyConstraint("paper_id", "email", name="review_pkey"),
|
||||||
|
{"comment": "Scores range from 1 to 5"},
|
||||||
|
)
|
||||||
|
|
||||||
|
paper_id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||||
|
email: Mapped[str] = mapped_column(Text, primary_key=True)
|
||||||
|
merit: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
relevance: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
readability: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
originality: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
author_comments: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
|
committee_comments: Mapped[Optional[str]] = mapped_column(Text)
|
||||||
|
|
||||||
|
reviewer: Mapped["Reviewer"] = relationship("Reviewer", back_populates="review")
|
||||||
|
paper: Mapped["Paper"] = relationship("Paper", back_populates="review")
|
||||||
54
fab/mvstubs.py
Normal file
54
fab/mvstubs.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
"""Generate a basic ModelView class for each table in the database."""
|
||||||
|
|
||||||
|
import socket
|
||||||
|
|
||||||
|
import psycopg
|
||||||
|
|
||||||
|
# Determine whether connecting from on/off campus
|
||||||
|
try:
|
||||||
|
socket.gethostbyname("data.cs.jmu.edu")
|
||||||
|
HOST = "data.cs.jmu.edu"
|
||||||
|
except socket.gaierror:
|
||||||
|
HOST = "localhost"
|
||||||
|
|
||||||
|
# Get all tables and their columns
|
||||||
|
with psycopg.connect(
|
||||||
|
host=HOST, user="tamassno", dbname="sec2", password="113880616"
|
||||||
|
) as conn:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
SELECT table_name, column_name
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'tamassno'
|
||||||
|
AND table_name NOT LIKE 'ab_%'
|
||||||
|
ORDER BY table_name, ordinal_position;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
schema = cur.fetchall()
|
||||||
|
|
||||||
|
# Build a dictionary of results by table
|
||||||
|
tables: dict[str, list[str]] = {}
|
||||||
|
for table_name, column_name in schema:
|
||||||
|
tables.setdefault(table_name, []).append(column_name)
|
||||||
|
|
||||||
|
# Generate Flask-AppBuilder ModelView classes
|
||||||
|
for table, columns in tables.items():
|
||||||
|
name = table.capitalize()
|
||||||
|
print(f"class {name}(ModelView):")
|
||||||
|
print(f" datamodel = SQLAInterface(models.{name})")
|
||||||
|
print(f" route_base = '/{table}'")
|
||||||
|
print(f" list_title = '{name}s'")
|
||||||
|
print(f" list_columns = {columns}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Generate code to add each view to the app
|
||||||
|
for table in tables:
|
||||||
|
name = table.capitalize()
|
||||||
|
print("appbuilder.add_view(")
|
||||||
|
print(f" {name},")
|
||||||
|
print(f' "{name}s",')
|
||||||
|
print(' icon="fa-database",')
|
||||||
|
print(' category="Admin",')
|
||||||
|
print(")")
|
||||||
|
print()
|
||||||
79
fab/views.py
Normal file
79
fab/views.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
from flask_appbuilder import ModelView
|
||||||
|
from flask_appbuilder.models.sqla.interface import SQLAInterface
|
||||||
|
|
||||||
|
import models
|
||||||
|
from app import appbuilder
|
||||||
|
|
||||||
|
|
||||||
|
class History(ModelView):
|
||||||
|
datamodel = SQLAInterface(models.History)
|
||||||
|
route_base = "/history"
|
||||||
|
list_title = "History"
|
||||||
|
list_columns = ["paper_id", "timestamp", "paper_status", "notes"]
|
||||||
|
|
||||||
|
|
||||||
|
class Topic(ModelView):
|
||||||
|
datamodel = SQLAInterface(models.Topic)
|
||||||
|
route_base = "/topic"
|
||||||
|
list_title = "Topics"
|
||||||
|
list_columns = ["topic_id", "topic_name"]
|
||||||
|
|
||||||
|
|
||||||
|
class Paper(ModelView):
|
||||||
|
datamodel = SQLAInterface(models.Paper)
|
||||||
|
route_base = "/paper"
|
||||||
|
list_title = "Papers"
|
||||||
|
list_columns = [
|
||||||
|
"paper_id",
|
||||||
|
"title",
|
||||||
|
"abstract",
|
||||||
|
"filename",
|
||||||
|
"contact_email",
|
||||||
|
"year",
|
||||||
|
]
|
||||||
|
|
||||||
|
add_exclude_columns = edit_exclude_columns = show_exclude_columns = [
|
||||||
|
"topics",
|
||||||
|
"history",
|
||||||
|
]
|
||||||
|
|
||||||
|
related_views = [Topic, History]
|
||||||
|
|
||||||
|
|
||||||
|
class Conference(ModelView):
|
||||||
|
datamodel = SQLAInterface(models.Conference)
|
||||||
|
route_base = "/conference"
|
||||||
|
list_title = "Conferences"
|
||||||
|
list_columns = ["year", "location"]
|
||||||
|
|
||||||
|
add_exclude_columns = edit_exclude_columns = show_exclude_columns = ["papers"]
|
||||||
|
related_views = [Paper]
|
||||||
|
|
||||||
|
|
||||||
|
appbuilder.add_view(
|
||||||
|
Conference,
|
||||||
|
"Conferences",
|
||||||
|
icon="fa-database",
|
||||||
|
category="Admin",
|
||||||
|
)
|
||||||
|
|
||||||
|
appbuilder.add_view(
|
||||||
|
History,
|
||||||
|
"History",
|
||||||
|
icon="fa-database",
|
||||||
|
category="Admin",
|
||||||
|
)
|
||||||
|
|
||||||
|
appbuilder.add_view(
|
||||||
|
Paper,
|
||||||
|
"Papers",
|
||||||
|
icon="fa-database",
|
||||||
|
category="Admin",
|
||||||
|
)
|
||||||
|
|
||||||
|
appbuilder.add_view(
|
||||||
|
Topic,
|
||||||
|
"Topics",
|
||||||
|
icon="fa-database",
|
||||||
|
category="Admin",
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user