Compare commits

...

15 Commits

Author SHA1 Message Date
Mustafa Yontar 73a5820535 docker compose run with .env file 4 years ago
Mustafa Yontar 24b5ebc40a Merge branch 'master' into autogenerate_flutter 4 years ago
Mustafa Yontar 739497f824 form generator , reference fields, list embeded fields , embeded fields created. 4 years ago
Mustafa Yontar 5994f2a155 generate flutter forms and models 4 years ago
Mustafa Yontar 539ff5e2e5 Merge pull request 'Update Docker Dev environment' (#9) from mrtmrcbr/Adunatio:updateDockerDevEnv into master 4 years ago
Mustafa Yontar 7e15e570db Merge pull request 'Fix typo at AuthMethots.py' (#11) from ooguz/Adunatio:master into master 4 years ago
Özcan Oğuz 5207e712bf
Fix typo at AuthMethods.py 4 years ago
Özcan Oğuz b57590b32f
Add .env to .gitignore 4 years ago
Mustafa Yontar ca49799d68 Merge pull request 'Update installation section' (#8) from mrtmrcbr/Adunatio:updateReadme into master 4 years ago
Mustafa Yontar 89aeb025f3 Merge pull request 'Configuration support via .env file' (#10) from ooguz/Adunatio:master into master 4 years ago
Özcan Oğuz 75d04c4268 Merge branch 'master' into master 4 years ago
Özcan Oğuz 1e90dec29e
Add configuration via .env file 4 years ago
murat emir cabaroğlu a1a11b2dd4 update mongo data mount point to volume 4 years ago
murat emir cabaroğlu 62c82d933c remove copy command in development dockerfile 4 years ago
murat emir cabaroğlu 74fcc75ee9 update instalation steps and add basic usage of this project 4 years ago
  1. 8
      .env.example
  2. 2
      .gitignore
  3. 2
      Dockerfile
  4. 67
      README.md
  5. 38
      docker-compose-dev.yaml
  6. 0
      internal_lib/AuthMethods.py
  7. 3
      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

@ -0,0 +1,8 @@
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,3 +25,5 @@ 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,20 +2,73 @@
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 https://git.oyd.org.tr/oyd/Adunatio.git git clone --recurse-submodules git@git.oyd.org.tr:oyd/Adunatio.git Adunatio
# Go to project directory
cd Adunatio cd Adunatio
git submodule init
git submodule update # Create a encrytion key
docker-compose -f docker-compose-dev.yaml up 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>
...
``` ```
You can access Adunatio from <http://localhost:5000> - login with your union and get your Berear token. Your username is combination of your `Legal Registration Number` and `@root`
```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,31 +1,33 @@
version: "3.3" version: "3"
services: services:
mongo: mongo:
image: mongo image: mongo
restart: always env_file:
environment: .env
MONGO_INITDB_ROOT_USERNAME: xcoder
MONGO_INITDB_ROOT_PASSWORD: 4dun4710
volumes: volumes:
- ./mongo_data_dir/:/data/db/ - mongo_data:/data/db/
mongo-express: mongo-express:
image: mongo-express image: mongo-express
restart: always
ports: ports:
- 8081:8081 - 8081:8081
environment: env_file:
ME_CONFIG_MONGODB_ADMINUSERNAME: xcoder .env
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
environment: env_file:
ADUNATIO_PRIV_KEY: /code/privkey.pem .env
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,7 +13,10 @@ 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,22 +2,39 @@ 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 from werkzeug.security import check_password_hash ,generate_password_hash
from internal_lib.AuthMethots import AuthJWT from models.Account import Account
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', username="xcoder", password="4dun4710", authentication_source='admin') connect('adunatio', host=MONGO_HOST, username=MONGO_USER, password=MONGO_PASSWORD, authentication_source='admin')
app = Flask(__name__) app = Flask(__name__)
app.secret_key = "secret_key+secret_key" app.secret_key = environ.get('APP_SECRET')
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"
@ -31,8 +48,12 @@ 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
@ -66,7 +87,13 @@ 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
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 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,9 +1,24 @@
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,22 +1,52 @@
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() name = StringField(required=True)
rights = ListField(StringField()) rights = ListField(StringField(), required=True)
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() name = StringField(required=True)
deleted = BooleanField(default=False) deleted = BooleanField(default=False)
discount_percent = IntField() discount_percent = IntField(required=True)
def __unicode__(self): def __unicode__(self):
return "{} {}".format(self.union.name, self.name) return "{} {}".format(self.union.name, self.name)

@ -4,26 +4,41 @@ 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) user = ReferenceField(User, required=True)
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() price = DecimalField(required=True)
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) account = ReferenceField(Account, required=True)
due_date = DateTimeField() due_date = DateTimeField()
is_paid = BooleanField(default=False) is_paid = BooleanField(default=False, required=True)
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,7 +55,8 @@ 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))
current_app.logger.info("New union password : {}".format(password)) passhash = generate_password_hash(password,salt_length=20)
user.password = generate_password_hash(password) current_app.logger.info("New union password : '{}'".format(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,7 +29,9 @@ 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,3 +7,4 @@ flask_views
flask_login flask_login
pillow pillow
flask-admin flask-admin
python-dotenv

@ -0,0 +1,331 @@
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/")

@ -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<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