Compare commits

..

No commits in common. 'master' and 'master' have entirely different histories.

  1. 8
      .env.example
  2. 2
      .gitignore
  3. 2
      Dockerfile
  4. 69
      README.md
  5. 38
      docker-compose-dev.yaml
  6. 0
      internal_lib/AuthMethots.py
  7. 5
      internal_lib/EncryptedField.py
  8. 41
      main.py
  9. 15
      models/Account.py
  10. 40
      models/Group.py
  11. 29
      models/Payment.py
  12. 7
      models/Union.py
  13. 4
      models/User.py
  14. 1
      requirements.txt
  15. 0
      scripts/__init__.py
  16. 331
      scripts/generate_flutter_forms.py
  17. 212
      scripts/generate_flutter_model.py

@ -1,8 +0,0 @@
APP_SECRET=s3cr3tk3yh3r3
FLASK_DEBUG=1
MONGO_HOST=mongo
MONGO_USER=username
MONGO_PASSWORD=Pa$$w0rD
ADUNATIO_PRIV_KEY=privkey.pem

2
.gitignore vendored

@ -25,5 +25,3 @@ var/
trash trash
mongo_data_dir/* mongo_data_dir/*
.directory .directory
.env
flutter_out/

@ -15,5 +15,5 @@ RUN apt-get update && apt-get install -y \
COPY requirements.txt requirements.txt COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt RUN pip install -r requirements.txt
EXPOSE 5000 EXPOSE 5000
COPY . .
CMD ["flask", "run","--debugger"] CMD ["flask", "run","--debugger"]

@ -2,73 +2,20 @@
Association software by [Özgür Yazılım Derneği](https://oyd.org.tr) Association software by [Özgür Yazılım Derneği](https://oyd.org.tr)
## Dev install ### Dev install
```bash ```bash
# clone the repository and get submodules # clone the repository and get submodules
git clone --recurse-submodules git@git.oyd.org.tr:oyd/Adunatio.git Adunatio git clone https://git.oyd.org.tr/oyd/Adunatio.git
cd Adunatio
# Go to project directory git submodule init
cd Adunatio git submodule update
docker-compose -f docker-compose-dev.yaml up
# Create a encrytion key
ssh-keygen -t rsa -b 4096 -f ./privkey.pem
# Run all system with docker
docker-compose -f docker-compose-dev.yaml up --build -d
```
You can access Adunatio from <http://localhost:5000/admin>
## How to use
- First, You must create a union from here <http://localhost:5000/admin/union/>
- Check your union password in logs. With this command, you should see this kind of line in logs
```sh
docker-compose -f docker-compose-dev.yaml logs web
...
...
[2021-02-04 20:39:16,685] INFO in Union: New union password : <PASSWORD>
...
``` ```
- login with your union and get your Berear token. Your username is combination of your `Legal Registration Number` and `@root` You can access Adunatio from <http://localhost:5000>
```sh
# Example Request with curl
curl --request POST \
--url http://localhost:5000/auth/login \
--header 'Content-Type: application/json' \
--data '{
"username":"<Legal Registration Number>@root",
"password":"<PASSWORD>"
}'
# Response
{
"access_token": "<Your Access Token>",
"status": true
}
```
- Test avaliable endpoints with your Berear token. Note that, header title must be `Adunation_Session_Token`
```sh
export ACCESS="<Your Access Token>"
```
```sh
curl -H "Adunation_Session_Token: Bearer $ACCESS" http://localhost:5000/api/user/
```
```sh
curl -H "Adunation_Session_Token: Bearer $ACCESS" http://localhost:5000/api/union/
```
## License ### License
Copyright (C) 2021 Özgür Yazılım Derneği Copyright (C) 2021 Özgür Yazılım Derneği

@ -1,33 +1,31 @@
version: "3" version: "3.3"
services: services:
mongo: mongo:
image: mongo image: mongo
env_file: restart: always
.env environment:
MONGO_INITDB_ROOT_USERNAME: xcoder
MONGO_INITDB_ROOT_PASSWORD: 4dun4710
volumes: volumes:
- mongo_data:/data/db/ - ./mongo_data_dir/:/data/db/
mongo-express: mongo-express:
image: mongo-express image: mongo-express
restart: always
ports: ports:
- 8081:8081 - 8081:8081
env_file: environment:
.env ME_CONFIG_MONGODB_ADMINUSERNAME: xcoder
ME_CONFIG_MONGODB_ADMINPASSWORD: 4dun4710
ME_CONFIG_OPTIONS_EDITORTHEME: ambiance
ME_CONFIG_BASICAUTH_USERNAME: xcoder
ME_CONFIG_BASICAUTH_PASSWORD: 4dun4710
web: web:
build: . build: .
ports: ports:
- 5000:5000 - "5000:5000"
env_file: environment:
.env ADUNATIO_PRIV_KEY: /code/privkey.pem
FLASK_DEBUG: 1
volumes: volumes:
- .:/code - .:/code
volumes:
mongo_data:
driver_opts:
device: /home/john/PycharmProjects/Adunatio/mongo_data_dir/
type: xfs
o: bind

@ -13,10 +13,7 @@ class EncryptedStringField(BaseField):
def __init__(self, **kwargs): def __init__(self, **kwargs):
import os import os
priv_key = os.environ.get('ADUNATIO_PRIV_KEY') priv_key = os.environ.get('ADUNATIO_PRIV_KEY')
try: self.keyPair = RSA.importKey(open(priv_key).read())
self.keyPair = RSA.importKey(open(priv_key).read())
except:
pass
super().__init__(**kwargs) super().__init__(**kwargs)

@ -2,39 +2,22 @@ from flask import Flask, request, jsonify
from flask_admin.contrib.mongoengine import ModelView from flask_admin.contrib.mongoengine import ModelView
from flask_jwt_extended import JWTManager, create_access_token from flask_jwt_extended import JWTManager, create_access_token
from mongoengine import connect from mongoengine import connect
from werkzeug.security import check_password_hash ,generate_password_hash from werkzeug.security import check_password_hash
from models.Account import Account from internal_lib.AuthMethots import AuthJWT
from models.Group import Group, PaymentGroup
from models.Payment import Payments
from os import environ, path
from dotenv import load_dotenv
from internal_lib.AuthMethods import AuthJWT
from models.Group import Group from models.Group import Group
from models.Union import Union from models.Union import Union
from models.User import User from models.User import User
from restapi import MongoApi from restapi import MongoApi
from flask_admin import Admin from flask_admin import Admin
"""
Load .env file and get variables
"""
base_path = path.abspath(path.dirname(__file__))
load_dotenv(path.join(base_path, '.env'))
MONGO_HOST = environ.get('MONGO_HOST')
MONGO_USER = environ.get('MONGO_USER')
MONGO_PASSWORD = environ.get('MONGO_PASSWORD')
""" """
Mongodb connection string Mongodb connection string
""" """
connect('adunatio', host=MONGO_HOST, username=MONGO_USER, password=MONGO_PASSWORD, authentication_source='admin') connect('adunatio', host='mongo', username="xcoder", password="4dun4710", authentication_source='admin')
app = Flask(__name__) app = Flask(__name__)
app.secret_key = environ.get('APP_SECRET') app.secret_key = "secret_key+secret_key"
app.config["JWT_TOKEN_LOCATION"] = "headers" app.config["JWT_TOKEN_LOCATION"] = "headers"
app.config["JWT_HEADER_NAME"] = "Adunation_Session_Token" app.config["JWT_HEADER_NAME"] = "Adunation_Session_Token"
app.config["JWT_HEADER_TYPE"] = "Bearer" app.config["JWT_HEADER_TYPE"] = "Bearer"
@ -48,12 +31,8 @@ jwt = JWTManager(app)
flask mongorester register flask mongorester register
""" """
api = MongoApi(app, authentication_methods=[AuthJWT]) api = MongoApi(app, authentication_methods=[AuthJWT])
api.register_model(User, uri="/api/user/") api.register_model(User, uri="/api/user")
api.register_model(Union, uri="/api/union/") 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 flask admin register
@ -87,13 +66,7 @@ def login():
app.logger.error(e) app.logger.error(e)
return jsonify({"message": "Bad username or password", "error": "Unauthorized", "status": False}), 401 return jsonify({"message": "Bad username or password", "error": "Unauthorized", "status": False}), 401
app.logger.error(password) if not check_password_hash(user.password,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 return jsonify({"message": "Bad username or password", "error": "Unauthorized", "status":False}), 401
# Identity can be any data that is json serializable # Identity can be any data that is json serializable

@ -1,24 +1,9 @@
from mongoengine import * from mongoengine import *
from models.Union import Union from models.Union import Union
from restapi import Methods
class Account(Document): class Account(Document):
meta = {
'index_background': True,
'index_cls': False,
'auto_create_index': True,
'can_query': True,
'with_sub_docs': True,
'methods': [Methods.Get, Methods.List, Methods.Create],
"indexes": [
('bank'),
("-bank"),
("union"),
('union', 'bank', "deleted")
],
}
union = ReferenceField(Union) union = ReferenceField(Union)
bank = StringField() bank = StringField()
number = StringField() number = StringField()

@ -1,52 +1,22 @@
from mongoengine import * from mongoengine import *
from models.Union import Union from models.Union import Union
from restapi import Methods
class Group(Document): class Group(Document):
meta = {
'index_background': True,
'index_cls': False,
'auto_create_index': True,
'can_query': True,
'with_sub_docs': True,
'methods': [Methods.Get, Methods.List, Methods.Create],
"indexes": [
('name'),
("-name"),
("union","deleted"),
("union","deleted","name"),
],
}
union = ReferenceField(Union) union = ReferenceField(Union)
name = StringField(required=True) name = StringField()
rights = ListField(StringField(), required=True) rights = ListField(StringField())
deleted = BooleanField(default=False) deleted = BooleanField(default=False)
def __unicode__(self): def __unicode__(self):
return "{} {}".format(self.union.name, self.name) return "{} {}".format(self.union.name,self.name)
class PaymentGroup(Document): class PaymentGroup(Document):
meta = {
'index_background': True,
'index_cls': False,
'auto_create_index': True,
'can_query': True,
'with_sub_docs': True,
'methods': [Methods.Get, Methods.List, Methods.Create],
"indexes": [
('name'),
("-name"),
("union"),
('union','name',"deleted")
],
}
union = ReferenceField(Union) union = ReferenceField(Union)
name = StringField(required=True) name = StringField()
deleted = BooleanField(default=False) deleted = BooleanField(default=False)
discount_percent = IntField(required=True) discount_percent = IntField()
def __unicode__(self): def __unicode__(self):
return "{} {}".format(self.union.name, self.name) return "{} {}".format(self.union.name, self.name)

@ -4,41 +4,26 @@ from models.Account import Account
from models.File import File from models.File import File
from models.Union import Union from models.Union import Union
from models.User import User from models.User import User
from restapi import Methods
class Payments(Document): class Payments(Document):
meta = {
'index_background': True,
'index_cls': False,
'auto_create_index': True,
'can_query': True,
'with_sub_docs': True,
'methods': [Methods.Get, Methods.List, Methods.Create],
"indexes": [
("union","deleted"),
("union","deleted","account"),
('user',"union"),
('user',"union","deleted"),
('union', "user","income","deleted"),
('union', "user","deleted")
],
}
""" """
all payments income and outcome together! all payments income and outcome together!
""" """
union = ReferenceField(Union) union = ReferenceField(Union)
user = ReferenceField(User, required=True) user = ReferenceField(User)
income = BooleanField(default=False) income = BooleanField(default=False)
date = DateTimeField() date = DateTimeField()
regular = BooleanField(default=False) regular = BooleanField(default=False)
regular_type = StringField(choices=('Weekly', 'Monthly', 'Yearly')) regular_type = StringField(choices=('Weekly','Monthly','Yearly'))
price = DecimalField(required=True) price = DecimalField()
deleted = BooleanField(default=False) deleted = BooleanField(default=False)
description = StringField() description = StringField()
reference_no = StringField() reference_no = StringField()
file = ReferenceField(File) file = ReferenceField(File)
account = ReferenceField(Account, required=True) account = ReferenceField(Account)
due_date = DateTimeField() due_date = DateTimeField()
is_paid = BooleanField(default=False, required=True) is_paid = BooleanField(default=False)
is_donate = BooleanField(default=False) is_donate = BooleanField(default=False)

@ -20,7 +20,7 @@ class Union(Document):
"indexes": [ "indexes": [
('name'), ('name'),
("-name"), ("-name"),
], ]
} }
name = StringField(required=True) name = StringField(required=True)
logo = ImageField(thumbnail_size=(120, 120)) logo = ImageField(thumbnail_size=(120, 120))
@ -55,8 +55,7 @@ class Union(Document):
user.union = self user.union = self
from flask import current_app from flask import current_app
password = ''.join(choices(ascii_letters + digits, k=10)) password = ''.join(choices(ascii_letters + digits, k=10))
passhash = generate_password_hash(password,salt_length=20) current_app.logger.info("New union password : {}".format(password))
current_app.logger.info("New union password : '{}'".format(password)) user.password = generate_password_hash(password)
user.password = passhash
# TODO: send password via mail or sms or nothing # TODO: send password via mail or sms or nothing
user.save() user.save()

@ -29,9 +29,7 @@ class User(Document):
('union'), ('union'),
('username', 'union'), ('username', 'union'),
('accept_date') ('accept_date')
], ]
"auto_generated": ['union','deleted','member_no']
} }
deleted = BooleanField(default=False) deleted = BooleanField(default=False)
union = ReferenceField(Union) union = ReferenceField(Union)

@ -7,4 +7,3 @@ flask_views
flask_login flask_login
pillow pillow
flask-admin flask-admin
python-dotenv

@ -1,331 +0,0 @@
from random import randint, choice
from string import ascii_lowercase
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]
configlistNames = []
for classItem in data[0]:
saveAction = ""
imports = "import 'package:flutter/material.dart';\n"
imports += "import 'package:flutter/services.dart';\n"
imports += "import 'package:dio/dio.dart';\n"
imports += "import 'package:horde/widgets/HorusBox.dart';\n"
imports += "import 'package:dropdown_search/dropdown_search.dart';\n"
imports += "import '../Models.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/models/{}.dart';\n".format(field.get('class_name'))
if field.get('islist'):
if field.get('embeded') == False:
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'))
saveAction += "\t\tnew{classname}.{name} = item{name};\n".format(name=field.get('name'),
classname=classItem.get(
'class'))
else:
imports += "import 'Form{}.dart';\n".format(
field.get('class_name'))
if field.get('class_name') not in configlistNames:
clsname = field.get('class_name')
configlistNames.append(field.get('class_name'))
else:
clsname = field.get('class_name') + ''.join(
[choice(ascii_lowercase) for i in range(randint(3, 6))])
while clsname in configlistNames:
clsname = field.get('class_name') + ''.join(
[choice(ascii_lowercase) for i in range(randint(3, 6))])
configlistNames.append(clsname)
saveAction += "\t\tnew{classname}.{name} = item{clist}List;\n".format(clist=clsname,
name=field.get(
'name'),
classname=classItem.get(
'class'))
controller += """
List<{originclass}> item{classname}List = [];
""".format(originclass=field.get("class_name"), classname=clsname)
textFields += """
Padding(padding: EdgeInsets.only(left:40,right: 40),
child: Column(
children: [
Text('{cname} list'),
Column(
children: List.generate(item{clsname}List.length, (index) {{
return HorusBox(
child:
Row(
children: [
Expanded(child: Text(item{clsname}List[index].name)),
GestureDetector(
onTap: () {{
item{clsname}List.removeAt(index);
setState(() {{
}});
}},
child: Icon(Icons.delete),
)
],
)
,
);
}}),
)
],
),
),
ElevatedButton(onPressed: ()
{{
_showMaterialDialog(SingleChildScrollView(child:Container(width:500,height:500,child:new Form{classname}(onSave: ({classname} item) {{
item{clsname}List.add(item);
Navigator.pop(context);
setState(() {{
}});
}}))),"Create {cname}");
}}, child: Text('Add {cname}')),
SizedBox(
height: 15,
),
""".format(classname=field.get('class_name'), cname=field.get('name'), clsname=clsname)
elif field.get("embeded") == False:
controller += "\t\t{classname} item{name};\n".format(classname=field.get('class_name'),
name=field.get('name'))
saveAction += "\t\tnew{classname}.{name} = item{name};\n".format(name=field.get('name'),
classname=classItem.get(
'class'))
controllerInit += "\t\t\titem{name} = {classname}();\n".format(
classname=field.get('class_name'),
name=field.get('name'))
controller += "\t\tList<{classname}> {cname}List = [];".format(
classname=field.get('class_name'), cname=field.get('name'))
controller += """
Future<List<{classname}>> getData{classname}(filter) async {{
print(filter);
var response = await Dio().get(
"${{apiurl}}/{cname}",
queryParameters: {{"{search}__contains": filter}},
);
List<{classname}> models = [];
response.data.forEach((element) {{
{classname} cn = {classname}();
cn.name = element['{search}'];
models.add(cn);
}});
return models;
}}
""".format(classname=field.get('class_name'), cname=field.get('name'),
search=field.get('search_field'))
textFields += """
DropdownSearch<{classname}>(
mode: Mode.BOTTOM_SHEET,
showSearchBox: true,
onFind: (String filter) => getData{classname}(filter),
items: [],
itemAsString: ({classname} u) => u.{search},
isFilteredOnline: true,
autoValidateMode: AutovalidateMode.onUserInteraction,
validator: ({classname} u) =>
u == null ? "field is required " : null,
hint: "{cname} in menu mode",
onChanged: ({classname} selected) {{
item{cname} = selected;
setState(() {{
}});
}}),
SizedBox(
height: 15,
),
""".format(classname=field.get('class_name'), cname=field.get('name'),
search=field.get('search_field'))
elif field.get("embeded"):
imports += "import 'Form{}.dart';\n".format(
field.get('class_name'))
saveAction += "\t\tif(!_key{classname}.currentState.build{classname}()) return false;\n".format(
classname=field.get('class_name'))
saveAction += "\t\tnew{cname}.{name} = _key{classname}.currentState.new{classname};\n".format(
classname=field.get('class_name'), name=field.get('name'), cname=classItem.get('class'))
textFields += "Form{classname}(key:_key{classname}),".format(classname=field.get('class_name'))
controller += "\tGlobalKey<Form{}State> _key{} = GlobalKey();\n".format(field.get('class_name'),
field.get('class_name'))
elif field.get('choices'):
saveAction += "\t\tnew{classname}.{name} = _{name}.text;\n".format(name=field.get('name'),
classname=classItem.get('class'))
imports += "import 'package:dropdown_search/dropdown_search.dart';\n"
textFields += """
DropdownSearch<String>(
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':
saveAction += "\t\tnew{classname}.{name} = _{name}.text;\n".format(name=field.get('name'),
classname=classItem.get('class'))
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':
saveAction += "\t\tnew{classname}.{name} = int.tryParse(_{name}.text);\n".format(
name=field.get('name'),
classname=classItem.get('class'))
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 {{
final Function onSave;
const Form{classname}({{Key key, this.onSave}}) : super(key: key);
@override
Form{classname}State createState() => Form{classname}State();
}}
class Form{classname}State extends State<Form{classname}> {{
{controller}
var apiurl = "";
{classname} new{classname} = new {classname}();
bool build{classname}() {{
if (_{classname}.currentState.validate()) {{
{saveact}
widget.onSave(new{classname});
return true;
}}
return false;
}}
_showMaterialDialog(content, title) {{
showDialog(
context: context,
builder: (_) => new AlertDialog(
title: new Text(title),
content: content,
));
}}
final _{classname} = GlobalKey<FormState>();
@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: <Widget>[
{textfield}
Row(
children: [
widget.onSave !=null ? ElevatedButton(
child: Text('Save'),
onPressed: () {{
build{classname}();
}},
): Center()
],
)
])
);
}}
}}
""".format(imports=imports, classname=classItem.get('class'), controller=controller,
initdata=controllerInit, dispose=controllerDispose, textfield=textFields, saveact=saveAction)
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])
os.system("flutter format flutter_out/")

@ -1,212 +0,0 @@
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<String,dynamic>'
}
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<String,dynamic>"
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<String,dynamic> toJson() {")
print("\t\tMap<String,dynamic> 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])
Loading…
Cancel
Save