diff --git a/.gitignore b/.gitignore index 56268a6..b75006b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ var/ *test.py trash mongo_data_dir/* -.directory \ No newline at end of file +.directory +flutter_out/ \ No newline at end of file diff --git a/internal_lib/EncryptedField.py b/internal_lib/EncryptedField.py index b6efac9..a6a3620 100644 --- a/internal_lib/EncryptedField.py +++ b/internal_lib/EncryptedField.py @@ -13,7 +13,10 @@ class EncryptedStringField(BaseField): def __init__(self, **kwargs): import os priv_key = os.environ.get('ADUNATIO_PRIV_KEY') - self.keyPair = RSA.importKey(open(priv_key).read()) + try: + self.keyPair = RSA.importKey(open(priv_key).read()) + except: + pass super().__init__(**kwargs) diff --git a/main.py b/main.py index 88c0145..6009a43 100644 --- a/main.py +++ b/main.py @@ -2,10 +2,12 @@ from flask import Flask, request, jsonify from flask_admin.contrib.mongoengine import ModelView from flask_jwt_extended import JWTManager, create_access_token from mongoengine import connect -from werkzeug.security import check_password_hash +from werkzeug.security import check_password_hash ,generate_password_hash from internal_lib.AuthMethots import AuthJWT -from models.Group import Group +from models.Account import Account +from models.Group import Group, PaymentGroup +from models.Payment import Payments from models.Union import Union from models.User import User from restapi import MongoApi @@ -31,8 +33,12 @@ jwt = JWTManager(app) flask mongorester register """ api = MongoApi(app, authentication_methods=[AuthJWT]) -api.register_model(User, uri="/api/user") -api.register_model(Union, uri="/api/union") +api.register_model(User, uri="/api/user/") +api.register_model(Union, uri="/api/union/") +api.register_model(Group, uri="/api/group/") +api.register_model(PaymentGroup, uri="/api/payment_group/") +api.register_model(Account, uri="/api/accounts/") +api.register_model(Payments, uri="/api/payments/") """ flask admin register @@ -66,7 +72,13 @@ def login(): app.logger.error(e) return jsonify({"message": "Bad username or password", "error": "Unauthorized", "status": False}), 401 - if not check_password_hash(user.password,password): + app.logger.error(password) + app.logger.error(username) + app.logger.error(generate_password_hash(password)) + app.logger.error(user.password) + app.logger.error(check_password_hash(pwhash=user.password,password=password)) + + if check_password_hash(pwhash=user.password,password=password) == False: return jsonify({"message": "Bad username or password", "error": "Unauthorized", "status":False}), 401 # Identity can be any data that is json serializable diff --git a/models/Group.py b/models/Group.py index e0a2d61..c44bd2e 100644 --- a/models/Group.py +++ b/models/Group.py @@ -5,8 +5,8 @@ from models.Union import Union class Group(Document): union = ReferenceField(Union) - name = StringField() - rights = ListField(StringField()) + name = StringField(required=True) + rights = ListField(StringField(),required=True) deleted = BooleanField(default=False) def __unicode__(self): return "{} {}".format(self.union.name,self.name) @@ -14,9 +14,9 @@ class Group(Document): class PaymentGroup(Document): union = ReferenceField(Union) - name = StringField() + name = StringField(required=True) deleted = BooleanField(default=False) - discount_percent = IntField() + discount_percent = IntField(required=True) def __unicode__(self): return "{} {}".format(self.union.name, self.name) \ No newline at end of file diff --git a/models/Payment.py b/models/Payment.py index cd871b7..9409a40 100644 --- a/models/Payment.py +++ b/models/Payment.py @@ -11,19 +11,17 @@ class Payments(Document): all payments income and outcome together! """ union = ReferenceField(Union) - user = ReferenceField(User) + user = ReferenceField(User, required=True) income = BooleanField(default=False) date = DateTimeField() regular = BooleanField(default=False) - regular_type = StringField(choices=('Weekly','Monthly','Yearly')) - price = DecimalField() + regular_type = StringField(choices=('Weekly', 'Monthly', 'Yearly')) + price = DecimalField(required=True) deleted = BooleanField(default=False) description = StringField() reference_no = StringField() file = ReferenceField(File) - account = ReferenceField(Account) + account = ReferenceField(Account, required=True) due_date = DateTimeField() - is_paid = BooleanField(default=False) + is_paid = BooleanField(default=False, required=True) is_donate = BooleanField(default=False) - - diff --git a/models/Union.py b/models/Union.py index 68846ac..8b21073 100644 --- a/models/Union.py +++ b/models/Union.py @@ -20,7 +20,7 @@ class Union(Document): "indexes": [ ('name'), ("-name"), - ] + ], } name = StringField(required=True) logo = ImageField(thumbnail_size=(120, 120)) @@ -55,7 +55,8 @@ class Union(Document): user.union = self from flask import current_app password = ''.join(choices(ascii_letters + digits, k=10)) - current_app.logger.info("New union password : {}".format(password)) - user.password = generate_password_hash(password) + passhash = generate_password_hash(password,salt_length=20) + current_app.logger.info("New union password : '{}'".format(password)) + user.password = passhash # TODO: send password via mail or sms or nothing user.save() diff --git a/models/User.py b/models/User.py index 2edd094..7e97562 100644 --- a/models/User.py +++ b/models/User.py @@ -29,7 +29,9 @@ class User(Document): ('union'), ('username', 'union'), ('accept_date') - ] + ], + "auto_generated": ['union','deleted','member_no'] + } deleted = BooleanField(default=False) union = ReferenceField(Union) diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/generate_flutter_forms.py b/scripts/generate_flutter_forms.py new file mode 100644 index 0000000..bf539d4 --- /dev/null +++ b/scripts/generate_flutter_forms.py @@ -0,0 +1,146 @@ +from models.Account import Account +from models.Payment import Payments +from models.User import User +from scripts.generate_flutter_model import build_class + + +def mainBuilder(classlist): + blist = [] + for cls in classlist: + data = build_class(cls,blist) + blist = blist + data[1] + clstypeList = [i.__class__.__name__ for i in blist] + + for classItem in data[0]: + imports = "import 'package:flutter/material.dart';\n" + imports += "import 'package:flutter/services.dart';\n" + controller = "" + controllerInit = "" + controllerDispose = "" + textFields = "" + for field in classItem.get('fields'): + if field.get('name') == "id": + continue + if field.get('autogen'): + continue + controller += "\tTextEditingController _{};\n".format(field.get('name')) + controllerInit += "\t\t_{} = new TextEditingController();\n".format(field.get('name')) + controllerDispose += "\t\t_{}.dispose();\n".format(field.get('name')) + req = "" + if field.get('required'): + req = """ + validator: (value) { + if (value.isEmpty) { + return 'Please enter some text'; + } + return null; + }, + """ + if field.get("is_class"): + + imports += "import 'package:adunationfe/data/forms/Form{}.dart';\n".format(field.get('class_name')) + imports += "import 'package:adunationfe/data/models/{}.dart';\n".format(field.get('class_name')) + if field.get('islist'): + controller += "\t\tList<{classname}> item{name};\n".format(classname=field.get('class_name'), + name=field.get('name')) + controllerInit += "\t\t\titem{name} = [];\n".format(name=field.get('name')) + else: + controller += "\t\t{classname} item{name};\n".format(classname=field.get('class_name'), + name=field.get('name')) + controllerInit += "\t\t\titem{name} = {classname}();\n".format(classname=field.get('class_name'), + name=field.get('name')) + elif field.get('choices'): + imports += "import 'package:dropdown_search/dropdown_search.dart';\n" + textFields += """ + DropdownSearch( + mode: Mode.BOTTOM_SHEET, + showClearButton: true, + showSearchBox: true, + label: '{name}', + showSelectedItem: true, + items: ['{items}'], + hint: "country in menu mode", + onChanged: print, + selectedItem: "") + , + SizedBox( + height: 15, + ), + """.format(items = "','".join(field.get('choices')),name=field.get('name')) + elif field.get('type') == 'String': + textFields +=""" + new TextFormField( + decoration: new InputDecoration(labelText: "Enter your {name}"), + controller: _{name}, + {req} + ), + SizedBox( + height: 15, + ), + """.format(name=field.get('name'),req=req) + elif field.get('type') == 'int': + textFields += """ + new TextFormField( + decoration: new InputDecoration(labelText: "Enter your {name}"), + controller: _{name}, + keyboardType: TextInputType.number, + inputFormatters: < TextInputFormatter > [ + FilteringTextInputFormatter.digitsOnly + ], + {req} + ), + SizedBox( + height: 15, + ), + """.format(name=field.get('name'),req=req) + # elif field.get('is_class'): + # print(field) + # # exit() + + + + + cls_text = """ +{imports} +class Form{classname} extends StatefulWidget {{ + + @override + _Form{classname} createState() => _Form{classname}(); +}} +class _Form{classname} extends State {{ +{controller} + + final _{classname} = GlobalKey(); + @override + void initState() {{ +{initdata} + super.initState(); + }} + @override + void dispose() {{ +{dispose} + super.dispose(); + }} + @override + Widget build(BuildContext context) {{ + return new Form( + key:_{classname}, + child:new Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + {textfield} + ]) + ); + }} + +}} + + """.format(imports=imports,classname=classItem.get('class'),controller=controller,initdata=controllerInit,dispose=controllerDispose,textfield=textFields) + + f = open("flutter_out/Form{}.dart".format(classItem.get('class')),"w+") + f.write(cls_text) + f.close() +import os +os.chdir('../') +print(os.getcwd()) +mainBuilder([User, Payments, Account]) \ No newline at end of file diff --git a/scripts/generate_flutter_model.py b/scripts/generate_flutter_model.py new file mode 100644 index 0000000..15182b8 --- /dev/null +++ b/scripts/generate_flutter_model.py @@ -0,0 +1,212 @@ +import mongoengine +import os + +from models.Account import Account +from models.Payment import Payments +from models.User import * +from mongoengine import fields +types = { + mongoengine.fields.StringField: "String", + IntField: 'int', + BooleanField: 'bool', + FloatField: 'Float', + DictField: 'Map' +} + + +def to_camel_case(snake_str): + components = snake_str.split('_') + # We capitalize the first letter of each component except the first one + # with the 'title' method and join them together. + return components[0] + ''.join(x.title() for x in components[1:]) + + +def buildReferenceField(field_object, builded): + if isinstance(field_object, (ReferenceField, EmbeddedDocumentField)): + if field_object.document_type not in builded: + builded.append(field_object.document_type) + return build_class(field_object.document_type, builded) + return None, builded + + else: + return None, builded + + +def buildFields(field_object): + type = "" + if isinstance(field_object, mongoengine.fields.LongField): + type = "long" + elif isinstance(field_object, EncryptedStringField): + type = "String" + elif isinstance(field_object, fields.ImageField): + type = "String" + elif isinstance(field_object, EncryptedStringField): + type = "String" + elif isinstance(field_object, StringField): + type = "String" + elif isinstance(field_object, ObjectIdField): + type = "String" + elif isinstance(field_object, DateTimeField): + type = "DateTime" + elif isinstance(field_object, IntField): + type = "int" + elif isinstance(field_object, BooleanField): + type = "bool" + + elif isinstance(field_object, FloatField): + type = "double" + elif isinstance(field_object, DictField): + type = "Map" + elif isinstance(field_object, DynamicField): + type = "dynamic" + else: + type = "String" + return type + + +def build_class(mongoClass, builded): + if builded is None: + builded = [] + mClass = mongoClass + builded.append(mClass) + rClass = mongoClass() + builded.append(rClass) + class_list = [] + field_list = [] + for field in rClass._fields_ordered: + field_object = getattr(mClass, field) + ignore_fields = mClass._meta.get('ignore_fields') + autogen_fields = mClass._meta.get('auto_generated') + if not ignore_fields: + ignore_fields = [] + if not autogen_fields: + autogen_fields = [] + + if isinstance(field_object, (ReferenceField, EmbeddedDocumentField)): + _, builded = buildReferenceField(field_object, builded) + if _ is not None: + class_list = class_list + list(_) + pn = field_object.document_type() + if field not in ignore_fields: + field_list.append( + {"type": pn._class_name, "origin_name": field, "name": to_camel_case(field), "islist": False, + "is_class": True, "required":field_object.required, "class_name": pn._class_name, "autogen": field in autogen_fields}) + + elif isinstance(field_object, ListField): + if isinstance(field_object.field, (ReferenceField, EmbeddedDocumentField)): + _, builded = buildReferenceField(field_object.field, builded) + + if _ is not None: + class_list = class_list + list(_) + pn = field_object.field.document_type() + if field not in ignore_fields: + + field_list.append( + {"type": "List<{}>".format(pn._class_name), "origin_name": field, "name": to_camel_case(field), + "islist": True, "is_class": True, "required":field_object.required, "class_name": pn._class_name, "autogen": field in autogen_fields}) + + + else: + type = buildFields(field_object.field) + if field not in ignore_fields: + + field_list.append({"type": "List<{}>".format(type), "name": to_camel_case(field), "origin_name": field, + "islist": True, "originType": field_object.field, "required":field_object.required, "autogen": field in autogen_fields,"choices":field_object.field.choices}) + + else: + type = buildFields(field_object) + if field not in ignore_fields: + chc = None + req = False + if field_object is not None: + chc = field_object.choices + req = field_object.required + field_list.append( + {"type": "{}".format(type), "name": to_camel_case(field), "origin_name": field, "islist": False, + "originType": field_object, "autogen": field in autogen_fields,"choices":chc,"required":req}) + class_list.append({'class': rClass._class_name, "fields": field_list}) + return class_list, builded + +f = None + +def print(item): + if f: + f.writelines(str(item) + "\n") + +def buildFunctionFromJson(fields): + print("\t void fromJson(data) {") + + for i in fields: + if i.get('type') != "DateTime": + if not i.get('islist') and not i.get('is_class'): + print("\t\tthis.{} = data[\"{}\"];".format(i.get('name'), i.get("origin_name"))) + elif i.get('islist') and not i.get('is_class'): + print("\t\tthis.{}=[];".format(i.get('name'))); + print("\t\tdata[\"{}\"].forEach((element)".format(i.get("origin_name"))) + print("\t\t{") + print("\t\t\tthis.{}.add(element);".format(i.get('name'))) + print("\t\t}" + ");") + elif not i.get('islist') and i.get('is_class'): + print("\t\tthis.{} = {}();".format(i.get('name'), i.get('class_name'))) + print("\t\tthis.{}.fromJson(data[\"{}\"]);".format(i.get('name'), i.get('origin_name'))) + else: + print("\t\tthis.{}=[];".format(i.get('name'))); + print("\t\tdata[\"{}\"].forEach((element)".format(i.get("origin_name"))) + print("\t\t{") + print("\t\t\t{cname} cls = {cname}();".format(cname=i.get('class_name'))) + print("\t\t\tcls.fromJson(data[\"{}\"]);".format(i.get('origin_name'))) + + print("\t\t\tthis.{}.add(cls);".format(i.get('name'))) + print("\t\t}" + ");") + + print("\t}") + + +def exportJson(fields): + print("\tMap toJson() {") + print("\t\tMap output = {};") + for i in fields: + if i.get('type') != "DateTime": + if not i.get('islist') and not i.get('is_class'): + print("\t\toutput[\"{}\"] = this.{};".format(i.get('origin_name'), i.get("name"))) + elif i.get('islist') and not i.get('is_class'): + print("\t\toutput[\"{}\"] = [];".format(i.get('origin_name'))) + print("\t\tthis.{}.forEach((element) => output[\"{}\"].add(element));".format(i.get('name'),i.get('origin_name'))) + elif i.get('islist') and i.get('is_class'): + print("\t\toutput[\"{}\"] = [];".format(i.get('origin_name'))) + print("\t\tthis.{}.forEach((element) => output[\"{}\"].add(element.toJson()));".format(i.get('name'), + i.get('origin_name'))) + elif not i.get('islist') and i.get('is_class'): + print("\t\toutput[\"{}\"] = this.{}.toJson();".format(i.get('origin_name'),i.get('name'))) + print("\t\treturn output;") + print("\t}") + + + + +def mainBuilder(classlist): + global f + blist = [] + for cls in classlist: + data = build_class(cls,blist) + blist = blist + data[1] + for i in data[0]: + f = open('flutter_out/{}.dart'.format(i.get('class')),"w") + print("import 'package:scoped_model/scoped_model.dart';") + print("class {} extends Model ".format(i.get('class'))) + print("{") + for field in i.get('fields'): + print("\t {} {};".format(field.get('type'), field.get('name'))) + print("") + print("\t// Build Functions") + print("\t// ") + buildFunctionFromJson(i.get('fields')) + exportJson(i.get('fields')) + print("}") +if __name__ == '__main__': + import os + os.chdir('../') + print(os.getcwd()) + mainBuilder([User, Payments, Account]) \ No newline at end of file