-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 78546d3
Showing
10 changed files
with
18,712 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from flask import Flask, render_template, request | ||
import json | ||
from models import * | ||
from js import * | ||
|
||
app = Flask(__name__) | ||
sql_debug(True) | ||
|
||
@app.route('/') | ||
@db_session | ||
def index(): | ||
return render_template("index.html") | ||
|
||
@app.route('/departments') | ||
@db_session | ||
def get_departments(): | ||
departments = Department.select().order_by(Department.number) | ||
return to_json(db, departments, include=[Department.groups, Department.courses]) | ||
|
||
@app.route('/course-students/<name>/<semester>') | ||
@db_session | ||
def get_course_students(name, semester): | ||
students = Course[name, semester].students | ||
return to_json(db, {'students': students}) | ||
|
||
@app.route('/group-students/<number>') | ||
@db_session | ||
def get_group_students(number): | ||
students = Group[number].students | ||
return to_json(db, {'students': students}) | ||
|
||
@app.route('/update', methods=['POST']) | ||
@db_session | ||
def update(): | ||
ormdata = request.form['ormdata'] | ||
save_changes(db, ormdata) | ||
return json.dumps({'status': 'ok'}) | ||
|
||
if __name__ == '__main__': | ||
app.debug = True | ||
app.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
from __future__ import absolute_import, print_function, division | ||
from pony.py23compat import int_types, basestring, imap, iteritems | ||
|
||
import json | ||
from operator import attrgetter | ||
from collections import defaultdict | ||
from datetime import date, datetime | ||
from decimal import Decimal | ||
|
||
from pony.orm.core import Attribute, Set, Entity, EntityMeta, TransactionError, db_session, flush | ||
# PermissionError, get_current_user, get_current_user_groups | ||
# can_view, can_edit, can_delete | ||
from pony.utils import throw, cut_traceback | ||
|
||
__all__ = 'basic_converter', 'get_schema_dict', 'get_schema_json', 'to_json', 'save_changes' | ||
|
||
def basic_converter(x): | ||
if isinstance(x, (datetime, date, Decimal)): | ||
return str(x) | ||
if isinstance(x, dict): | ||
return dict(x) | ||
if isinstance(x, Entity): | ||
pkval = x._get_raw_pkval_() | ||
return pkval[0] if len(pkval) == 1 else pkval | ||
try: iter(x) | ||
except: raise TypeError(x) | ||
return list(x) | ||
|
||
def get_schema_dict(db): | ||
result = [] | ||
for entity in sorted(db.entities.values(), key=attrgetter('_id_')): | ||
# if not can_view(entity): continue | ||
attrs = [] | ||
for attr in entity._new_attrs_: | ||
d = dict(name = attr.name, type = attr.py_type.__name__, kind = attr.__class__.__name__) | ||
if attr.auto: d['auto'] = True | ||
if attr.reverse: | ||
# if not can_view(attr.reverse.entity): continue | ||
d['reverse'] = attr.reverse.name | ||
if attr.lazy: d['lazy'] = True | ||
if attr.nullable: d['nullable'] = True | ||
if attr.default and issubclass(type(attr.default), (int_types, basestring)): | ||
d['defaultValue'] = attr.default | ||
attrs.append(d) | ||
d = dict(name=entity.__name__, newAttrs=attrs, pkAttrs=[ attr.name for attr in entity._pk_attrs_ ]) | ||
if entity._all_bases_: | ||
d['bases'] = [ base.__name__ for base in entity._all_bases_ ] | ||
if entity._simple_keys_: | ||
d['simpleKeys'] = [ attr.name for attr in entity._simple_keys_ ] | ||
if entity._composite_keys_: | ||
d['compositeKeys'] = [ [ attr.name for attr in attrs ] for attrs in entity._composite_keys_ ] | ||
result.append(d) | ||
return result | ||
|
||
def get_schema_json(db): | ||
return json.dumps(get_schema_dict(db), default=basic_converter) | ||
|
||
@cut_traceback | ||
def to_json(database, data, include=(), exclude=(), converter=None, with_schema=True): | ||
for attrs, param_name in ((include, 'include'), (exclude, 'exclude')): | ||
for attr in attrs: | ||
if not isinstance(attr, Attribute): throw(TypeError, | ||
"Each item of '%s' list should be attribute. Got: %s" % (param_name, attr)) | ||
include, exclude = set(include), set(exclude) | ||
if converter is None: converter = basic_converter | ||
|
||
# def user_has_no_rights_to_see(obj, attr=None): | ||
# user_groups = get_current_user_groups() | ||
# throw(PermissionError, 'The current user %s which belongs to groups %s ' | ||
# 'has no rights to see the object %s on the frontend' | ||
# % (get_current_user(), sorted(user_groups), obj)) | ||
|
||
object_set = set() | ||
caches = set() | ||
def obj_converter(obj): | ||
if not isinstance(obj, Entity): return converter(obj) | ||
caches.add(obj._session_cache_) | ||
if len(caches) > 1: throw(TransactionError, | ||
'An attempt to serialize objects belonging to different transactions') | ||
# if not can_view(obj): | ||
# user_has_no_rights_to_see(obj) | ||
object_set.add(obj) | ||
pkval = obj._get_raw_pkval_() | ||
if len(pkval) == 1: pkval = pkval[0] | ||
return { 'class': obj.__class__.__name__, 'pk': pkval } | ||
|
||
data_json = json.dumps(data, default=obj_converter) | ||
|
||
objects = {} | ||
if caches: | ||
cache = caches.pop() | ||
if cache.database is not database: | ||
throw(TransactionError, 'An object does not belong to specified database') | ||
object_list = list(object_set) | ||
objects = {} | ||
for obj in object_list: | ||
if obj in cache.seeds[obj._pk_attrs_]: obj._load_() | ||
entity = obj.__class__ | ||
# if not can_view(obj): | ||
# user_has_no_rights_to_see(obj) | ||
d = objects.setdefault(entity.__name__, {}) | ||
for val in obj._get_raw_pkval_(): d = d.setdefault(val, {}) | ||
assert not d, d | ||
for attr in obj._attrs_: | ||
if attr in exclude: continue | ||
if attr in include: pass | ||
# if attr not in entity_perms.can_read: user_has_no_rights_to_see(obj, attr) | ||
elif attr.is_collection: continue | ||
elif attr.lazy: continue | ||
# elif attr not in entity_perms.can_read: continue | ||
|
||
if attr.is_collection: | ||
if not isinstance(attr, Set): throw(NotImplementedError) | ||
value = [] | ||
for item in attr.__get__(obj): | ||
if item not in object_set: | ||
object_set.add(item) | ||
object_list.append(item) | ||
pkval = item._get_raw_pkval_() | ||
value.append(pkval[0] if len(pkval) == 1 else pkval) | ||
value.sort() | ||
else: | ||
value = attr.__get__(obj) | ||
if value is not None and attr.is_relation: | ||
if attr in include and value not in object_set: | ||
object_set.add(value) | ||
object_list.append(value) | ||
pkval = value._get_raw_pkval_() | ||
value = pkval[0] if len(pkval) == 1 else pkval | ||
|
||
d[attr.name] = value | ||
objects_json = json.dumps(objects, default=converter) | ||
if not with_schema: | ||
return '{"data": %s, "objects": %s}' % (data_json, objects_json) | ||
schema_json = get_schema_json(database) | ||
return '{"data": %s, "objects": %s, "schema": %s}' % (data_json, objects_json, schema_json) | ||
|
||
@cut_traceback | ||
@db_session | ||
def save_changes(db, changes, observer=None): | ||
changes = json.loads(changes) | ||
|
||
import pprint; pprint.pprint(changes) | ||
|
||
objmap = {} | ||
for diff in changes['objects']: | ||
if diff['_status_'] == 'c': continue | ||
pk = diff['_pk_'] | ||
pk = (pk,) if type(pk) is not list else tuple(pk) | ||
entity_name = diff['class'] | ||
entity = db.entities[entity_name] | ||
obj = entity._get_by_raw_pkval_(pk, from_db=False) | ||
oid = diff['_id_'] | ||
objmap[oid] = obj | ||
|
||
def id2obj(attr, val): | ||
return objmap[val] if attr.reverse and val is not None else val | ||
|
||
# def user_has_no_rights_to(operation, obj): | ||
# user_groups = get_current_user_groups() | ||
# throw(PermissionError, 'The current user %s which belongs to groups %s ' | ||
# 'has no rights to %s the object %s on the frontend' | ||
# % (get_current_user(), sorted(user_groups), operation, obj)) | ||
|
||
for diff in changes['objects']: | ||
entity_name = diff['class'] | ||
entity = db.entities[entity_name] | ||
dbvals = {} | ||
newvals = {} | ||
for name, val in diff.items(): | ||
if name not in ('class', '_pk_', '_id_', '_status_'): | ||
attr = entity._adict_[name] | ||
if not attr.is_collection: | ||
if type(val) is dict: | ||
if 'old' in val: dbvals[attr] = attr.validate(id2obj(attr, val['old'])) | ||
if 'new' in val: newvals[attr.name] = attr.validate(id2obj(attr, val['new'])) | ||
else: newvals[attr.name] = attr.validate(id2obj(attr, val)) | ||
oid = diff['_id_'] | ||
status = diff['_status_'] | ||
if status == 'c': | ||
assert not dbvals | ||
obj = entity(**newvals) | ||
if observer: | ||
flush() # in order to get obj.id | ||
observer('create', obj, newvals) | ||
objmap[oid] = obj | ||
# if not can_edit(obj): user_has_no_rights_to('create', obj) | ||
else: | ||
obj = objmap[oid] | ||
if status == 'd': | ||
# if not can_delete(obj): user_has_no_rights_to('delete', obj) | ||
if observer: observer('delete', obj) | ||
obj.delete() | ||
elif status == 'u': | ||
# if not can_edit(obj): user_has_no_rights_to('update', obj) | ||
if newvals: | ||
assert dbvals | ||
if observer: | ||
oldvals = dict((attr.name, val) for attr, val in iteritems(dbvals)) | ||
observer('update', obj, newvals, oldvals) | ||
obj._db_set_(dbvals) # dbvals can be modified here | ||
for attr in dbvals: attr.__get__(obj) | ||
obj.set(**newvals) | ||
else: assert not dbvals | ||
objmap[oid] = obj | ||
flush() | ||
for diff in changes['objects']: | ||
if diff['_status_'] == 'd': continue | ||
obj = objmap[diff['_id_']] | ||
entity = obj.__class__ | ||
for name, val in diff.items(): | ||
if name not in ('class', '_pk_', '_id_', '_status_'): | ||
attr = entity._adict_[name] | ||
if attr.is_collection and attr.reverse.is_collection and attr < attr.reverse: | ||
removed = [ objmap[oid] for oid in val.get('removed', ()) ] | ||
added = [ objmap[oid] for oid in val.get('added', ()) ] | ||
collection = attr.__get__(obj) | ||
if removed: | ||
observer('remove', obj, {name: removed}) | ||
collection.remove(removed) | ||
if added: | ||
observer('add', obj, {name: added}) | ||
collection.add(added) | ||
flush() | ||
|
||
def deserialize(x): | ||
t = type(x) | ||
if t is list: return list(imap(deserialize, x)) | ||
if t is dict: | ||
if '_id_' not in x: | ||
return dict((key, deserialize(val)) for key, val in iteritems(x)) | ||
obj = objmap.get(x['_id_']) | ||
if obj is None: | ||
entity_name = x['class'] | ||
entity = db.entities[entity_name] | ||
pk = x['_pk_'] | ||
obj = entity[pk] | ||
return obj | ||
return x | ||
|
||
return deserialize(changes['data']) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
from __future__ import absolute_import, print_function | ||
|
||
from decimal import Decimal | ||
from datetime import date | ||
|
||
from pony.orm.core import * | ||
|
||
db = Database() | ||
|
||
class Department(db.Entity): | ||
number = PrimaryKey(int, auto=True) | ||
name = Required(str, unique=True) | ||
groups = Set("Group") | ||
courses = Set("Course") | ||
|
||
class Group(db.Entity): | ||
number = PrimaryKey(int) | ||
major = Required(str) | ||
dept = Required("Department") | ||
students = Set("Student") | ||
|
||
class Course(db.Entity): | ||
name = Required(str) | ||
semester = Required(int) | ||
lect_hours = Required(int) | ||
lab_hours = Required(int) | ||
credits = Required(int) | ||
dept = Required(Department) | ||
students = Set("Student") | ||
PrimaryKey(name, semester) | ||
|
||
class Student(db.Entity): | ||
# _table_ = "public", "Students" # Schema support | ||
id = PrimaryKey(int, auto=True) | ||
name = Required(str) | ||
dob = Required(date) | ||
tel = Optional(str) | ||
picture = Optional(buffer, lazy=True) | ||
gpa = Required(float, default=0) | ||
group = Required(Group) | ||
courses = Set(Course) | ||
|
||
sql_debug(True) # Output all SQL queries to stdout | ||
|
||
db.bind('sqlite', 'university.sqlite', create_db=True) | ||
#db.bind('mysql', host="localhost", user="presentation", passwd="pony", db="presentation") | ||
#db.bind('postgres', user='presentation', password='pony', host='localhost', database='presentation') | ||
#db.bind('oracle', 'presentation/pony@localhost') | ||
|
||
db.generate_mapping(create_tables=True) | ||
|
||
@db_session | ||
def populate_database(): | ||
if select(s for s in Student).count() > 0: | ||
return | ||
|
||
d1 = Department(name="Department of Computer Science") | ||
d2 = Department(name="Department of Mathematical Sciences") | ||
d3 = Department(name="Department of Applied Physics") | ||
|
||
c1 = Course(name="Web Design", semester=1, dept=d1, | ||
lect_hours=30, lab_hours=30, credits=3) | ||
c2 = Course(name="Data Structures and Algorithms", semester=3, dept=d1, | ||
lect_hours=40, lab_hours=20, credits=4) | ||
|
||
c3 = Course(name="Linear Algebra", semester=1, dept=d2, | ||
lect_hours=30, lab_hours=30, credits=4) | ||
c4 = Course(name="Statistical Methods", semester=2, dept=d2, | ||
lect_hours=50, lab_hours=25, credits=5) | ||
|
||
c5 = Course(name="Thermodynamics", semester=2, dept=d3, | ||
lect_hours=25, lab_hours=40, credits=4) | ||
c6 = Course(name="Quantum Mechanics", semester=3, dept=d3, | ||
lect_hours=40, lab_hours=30, credits=5) | ||
|
||
g101 = Group(number=101, major='B.E. in Computer Engineering', dept=d1) | ||
g102 = Group(number=102, major='B.S./M.S. in Computer Science', dept=d1) | ||
g103 = Group(number=103, major='B.S. in Applied Mathematics and Statistics', dept=d2) | ||
g104 = Group(number=104, major='B.S./M.S. in Pure Mathematics', dept=d2) | ||
g105 = Group(number=105, major='B.E in Electronics', dept=d3) | ||
g106 = Group(number=106, major='B.S./M.S. in Nuclear Engineering', dept=d3) | ||
|
||
s1 = Student(name='John Smith', dob=date(1991, 3, 20), tel='123-456', gpa=3, group=g101, | ||
courses=[c1, c2, c4, c6]) | ||
s2 = Student(name='Matthew Reed', dob=date(1990, 11, 26), gpa=3.5, group=g101, | ||
courses=[c1, c3, c4, c5]) | ||
s3 = Student(name='Chuan Qin', dob=date(1989, 2, 5), gpa=4, group=g101, | ||
courses=[c3, c5, c6]) | ||
s4 = Student(name='Rebecca Lawson', dob=date(1990, 4, 18), tel='234-567', gpa=3.3, group=g102, | ||
courses=[c1, c4, c5, c6]) | ||
s5 = Student(name='Maria Ionescu', dob=date(1991, 4, 23), gpa=3.9, group=g102, | ||
courses=[c1, c2, c4, c6]) | ||
s6 = Student(name='Oliver Blakey', dob=date(1990, 9, 8), gpa=3.1, group=g102, | ||
courses=[c1, c2, c5]) | ||
s7 = Student(name='Jing Xia', dob=date(1988, 12, 30), gpa=3.2, group=g102, | ||
courses=[c1, c3, c5, c6]) | ||
|
||
|
||
populate_database() |
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Oops, something went wrong.