diff --git a/web/instance/test.db b/web/instance/test.db index e6b860de..9093c0e8 100644 Binary files a/web/instance/test.db and b/web/instance/test.db differ diff --git a/web/minibase/__pycache__/app.cpython-311.pyc b/web/minibase/__pycache__/app.cpython-311.pyc index fe4b1f25..6df611c3 100644 Binary files a/web/minibase/__pycache__/app.cpython-311.pyc and b/web/minibase/__pycache__/app.cpython-311.pyc differ diff --git a/web/minibase/__pycache__/theme.cpython-311.pyc b/web/minibase/__pycache__/theme.cpython-311.pyc index 95a4fc4f..120340d5 100644 Binary files a/web/minibase/__pycache__/theme.cpython-311.pyc and b/web/minibase/__pycache__/theme.cpython-311.pyc differ diff --git a/web/minibase/app.py b/web/minibase/app.py index efb63720..f2850a04 100644 --- a/web/minibase/app.py +++ b/web/minibase/app.py @@ -73,4 +73,3 @@ def create_app(): # (APP) Returning the initialised app return app - diff --git a/web/minibase/blueprints/company/__pycache__/forms.cpython-311.pyc b/web/minibase/blueprints/company/__pycache__/forms.cpython-311.pyc index 00a63282..de13d23c 100644 Binary files a/web/minibase/blueprints/company/__pycache__/forms.cpython-311.pyc and b/web/minibase/blueprints/company/__pycache__/forms.cpython-311.pyc differ diff --git a/web/minibase/blueprints/company/__pycache__/models.cpython-311.pyc b/web/minibase/blueprints/company/__pycache__/models.cpython-311.pyc index aaae00e5..e4befeed 100644 Binary files a/web/minibase/blueprints/company/__pycache__/models.cpython-311.pyc and b/web/minibase/blueprints/company/__pycache__/models.cpython-311.pyc differ diff --git a/web/minibase/blueprints/company/forms.py b/web/minibase/blueprints/company/forms.py index ad6edccc..81fb665f 100644 --- a/web/minibase/blueprints/company/forms.py +++ b/web/minibase/blueprints/company/forms.py @@ -11,6 +11,13 @@ from minibase.blueprints.company.models import Companies from minibase.blueprints.sensor.models import nbiotDevice from minibase.blueprints.user.models import Users +fields = { + "city_id": SelectField( + 'City', + validators=[DataRequired()], + render_kw={"hx-get": "/geography/get_states", "hx-target": "#state_id"} + ), +} class companyForm(FlaskForm): # Defines the form class to be used for the user update name = StringField('Name', validators=[DataRequired(), Length(min=3, max=100)]) @@ -34,7 +41,6 @@ class companyForm(FlaskForm): # Defines the form class to be used for the user def populate_for_updating(self, company): self.originalModel = company - print(f"company.name={company.name}") self.submit.label.text = "Update" self.country_id.choices = [(row.id, row.name) for row in geoUtils.queryCountryNamesWithDefault(company.country_id)] diff --git a/web/minibase/blueprints/database/__pycache__/utils.cpython-311.pyc b/web/minibase/blueprints/database/__pycache__/utils.cpython-311.pyc index 766b0cf5..4aee1835 100644 Binary files a/web/minibase/blueprints/database/__pycache__/utils.cpython-311.pyc and b/web/minibase/blueprints/database/__pycache__/utils.cpython-311.pyc differ diff --git a/web/minibase/blueprints/database/utils.py b/web/minibase/blueprints/database/utils.py index 2aa93aff..e2750ed9 100644 --- a/web/minibase/blueprints/database/utils.py +++ b/web/minibase/blueprints/database/utils.py @@ -23,6 +23,10 @@ def queryNameWithDefaultId(table,defId): choices = table.query.order_by(case((table.id == defId, 0),else_=1),table.name.asc()) return choices +def queryItemWithId(table,defId): + choices = table.query.order_by(case((table.id == defId, 0),else_=1),table.name.asc()) + return choices + def dbAdd(dbClass): db.session.add(dbClass) diff --git a/web/minibase/blueprints/main/__pycache__/utils.cpython-311.pyc b/web/minibase/blueprints/main/__pycache__/utils.cpython-311.pyc index 2845c87f..44f35ea4 100644 Binary files a/web/minibase/blueprints/main/__pycache__/utils.cpython-311.pyc and b/web/minibase/blueprints/main/__pycache__/utils.cpython-311.pyc differ diff --git a/web/minibase/blueprints/main/utils.py b/web/minibase/blueprints/main/utils.py index d63ada75..78dd6f17 100644 --- a/web/minibase/blueprints/main/utils.py +++ b/web/minibase/blueprints/main/utils.py @@ -18,30 +18,33 @@ class accountInfo: # FORM AND MODEL MANAGEMENT -def fill_model(company, form): +def fill_model(cur_model, form): for item in form: - modelFill(company, item) + modelFill(cur_model, item) -def populate_form(form, company): +def populate_form(form, cur_model): for item in form: - item.data = formFill(item, company) + item.data = formFill(item, cur_model) -def formFill(item, table): +def formFill(item, cur_model): if item.type not in ["SubmitField", "CSRFTokenField"]: - return getattr(table, item.name) + # Cheking if the arrtibute exitst, otherwise ignoring + if hasattr(cur_model, item.name): + return getattr(cur_model, item.name) -def modelFill(table, item): - if item.type not in ["SubmitField", "CSRFTokenField"]: - if item.type == "FileField": - if item.data == "None": - print(f"File filed found with no data -> item name {item.name}") - print(f"It will be filled with the existin tbale data -> item name {getattr(table, item.name)}") - setattr(table, item.name, getattr(table, item.name)) - else: - setattr(table, item.name, item.data) +def modelFill(cur_model, item): + if hasattr(cur_model, item.name): + if item.type not in ["SubmitField", "CSRFTokenField"]: + if item.type == "FileField": + if item.data == "None": + print(f"File field found with no data -> item name {item.name}") + print(f"It will be filled with the existin tbale data -> item name {getattr(cur_model, item.name)}") + setattr(cur_model, item.name, getattr(cur_model, item.name)) + else: + setattr(cur_model, item.name, item.data) # PICTURE MANAGEMENT @@ -73,6 +76,18 @@ If you didn't make this request, then simply ingnore this email and no changes w ''' mail.send(msg) +def send_sensor_email(email, data): + msg = Message('You have recived an ALARM', + sender='noreply@demo.com', + recipients=[email]) + msg.body = f'''The Sensor Has the following DATA: + + {data} + + Please contact your support person as soon as possible + ''' + mail.send(msg) + # QUERIES def queryIndustryNames(): diff --git a/web/minibase/blueprints/sensor/__pycache__/routes.cpython-311.pyc b/web/minibase/blueprints/sensor/__pycache__/routes.cpython-311.pyc index 587e7cbf..bc7cb19c 100644 Binary files a/web/minibase/blueprints/sensor/__pycache__/routes.cpython-311.pyc and b/web/minibase/blueprints/sensor/__pycache__/routes.cpython-311.pyc differ diff --git a/web/minibase/blueprints/sensor/__pycache__/utils.cpython-311.pyc b/web/minibase/blueprints/sensor/__pycache__/utils.cpython-311.pyc index a57fcf86..68d19a68 100644 Binary files a/web/minibase/blueprints/sensor/__pycache__/utils.cpython-311.pyc and b/web/minibase/blueprints/sensor/__pycache__/utils.cpython-311.pyc differ diff --git a/web/minibase/blueprints/sensor/routes.py b/web/minibase/blueprints/sensor/routes.py index 910c785d..dbed622c 100644 --- a/web/minibase/blueprints/sensor/routes.py +++ b/web/minibase/blueprints/sensor/routes.py @@ -6,6 +6,7 @@ import minibase.blueprints.sensor.utils as sensorUtils from minibase.blueprints.sensor.forms import nbiotDeviceForm import minibase.blueprints.database.utils as dbUtils import minibase.blueprints.main.utils as mainUtils +import minibase.blueprints.user.utils as userUtils from minibase.app import db import json import os @@ -47,10 +48,13 @@ def callback(): "srcPort": src_port, "payload": payload } + sensorUtils.decode_payload(payload) - + sensor = sensorUtils.queryByImsi(src_imsi) + mail=userUtils.queryMailById(sensor.device_user_id) + print(f"Sensor Model: {sensor.model}, user e-mail: {mail}") + #mainUtils.send_sensor_email(mail, data) return jsonify(response_data), 200 -#return 'data recieved', 200 @sensor.route('/data', methods=['GET']) @@ -112,10 +116,9 @@ def edit(deviceId): def add(): device = nbiotDevice() form = nbiotDeviceForm() - print(f"Route : manufacturer_id : { form.manufacturer_id.data }") + print(f"Route : manufacturer_id : { form.company_manufacturer_id.data }") form.populate_for_adding(device) - if request.method == 'GET': form.populate_for_adding(device) diff --git a/web/minibase/blueprints/sensor/utils.py b/web/minibase/blueprints/sensor/utils.py index 89f05815..23fda485 100644 --- a/web/minibase/blueprints/sensor/utils.py +++ b/web/minibase/blueprints/sensor/utils.py @@ -7,10 +7,15 @@ def queryImageById(id): selected = nbiotDevice.query.filter_by(id=id).first() return selected.image_file + def queryById(id): return nbiotDevice.query.filter_by(id=id).first() +def queryByImsi(id): + return nbiotDevice.query.filter_by(imsi=id).first() + + def queryStatusNamesWithDefault(defId): choices = dbUtils.queryNameWithDefaultId(nbiotDeviceStatus, defId) return choices diff --git a/web/minibase/blueprints/user/__pycache__/forms.cpython-311.pyc b/web/minibase/blueprints/user/__pycache__/forms.cpython-311.pyc index 4c4b9cd3..d634aae1 100644 Binary files a/web/minibase/blueprints/user/__pycache__/forms.cpython-311.pyc and b/web/minibase/blueprints/user/__pycache__/forms.cpython-311.pyc differ diff --git a/web/minibase/blueprints/user/__pycache__/models.cpython-311.pyc b/web/minibase/blueprints/user/__pycache__/models.cpython-311.pyc index df776b20..4b17b730 100644 Binary files a/web/minibase/blueprints/user/__pycache__/models.cpython-311.pyc and b/web/minibase/blueprints/user/__pycache__/models.cpython-311.pyc differ diff --git a/web/minibase/blueprints/user/__pycache__/routes.cpython-311.pyc b/web/minibase/blueprints/user/__pycache__/routes.cpython-311.pyc index e75c6d08..e148eabe 100644 Binary files a/web/minibase/blueprints/user/__pycache__/routes.cpython-311.pyc and b/web/minibase/blueprints/user/__pycache__/routes.cpython-311.pyc differ diff --git a/web/minibase/blueprints/user/__pycache__/utils.cpython-311.pyc b/web/minibase/blueprints/user/__pycache__/utils.cpython-311.pyc index 35dc0d20..98ed0659 100644 Binary files a/web/minibase/blueprints/user/__pycache__/utils.cpython-311.pyc and b/web/minibase/blueprints/user/__pycache__/utils.cpython-311.pyc differ diff --git a/web/minibase/blueprints/user/forms.py b/web/minibase/blueprints/user/forms.py index f70b0e21..76521eb1 100644 --- a/web/minibase/blueprints/user/forms.py +++ b/web/minibase/blueprints/user/forms.py @@ -1,32 +1,98 @@ from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileAllowed -from wtforms import StringField, PasswordField, SubmitField, BooleanField, URLField +from wtforms import StringField, PasswordField, SubmitField, BooleanField, URLField, IntegerField, SelectField from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError from flask_login import current_user from minibase.blueprints.user.models import Users, User_Roles +import minibase.blueprints.geography.utils as geoUtils class registrationForm(FlaskForm): # Defines the form class to be used for the user registretion # Decalarion of the fields for the form and it's propereties - username = StringField('User Name', validators=[DataRequired(), Length(min=4, max=20)]) - email = StringField('Email', validators=[DataRequired(), Email()]) - password = PasswordField('Password', validators=[DataRequired()]) - password_confirm = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')]) - submit = SubmitField('Sing Up') + username = StringField('User Name', validators=[DataRequired(), Length(min=4, max=20)]) + name = StringField('Name', validators=[DataRequired(), Length(min=4, max=20)]) + surname = StringField('Surname', validators=[DataRequired(), Length(min=4, max=20)]) + email_account = StringField('Email: Account', validators=[DataRequired(), Email()]) + email_comm = StringField('Email: Communication', validators=[DataRequired(), Email()]) + street = StringField('Street', validators=[DataRequired()]) + street_no = IntegerField('No', validators=[DataRequired()]) + post_code = IntegerField('Post', validators=[DataRequired()]) + city_id = SelectField('City', validators=[DataRequired()], render_kw={"hx-get": "/geography/get_states", "hx-target": "#state_id"}) + state_id = SelectField('State', validators=[]) + country_id = SelectField('Country', validators=[DataRequired()], render_kw={"hx-get": "/geography/get_cities", "hx-target": "#city_id"}) + password = PasswordField('Password', validators=[DataRequired()]) + password_confirm= PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')]) + submit = SubmitField() + + def populate_for_adding(self, user): + self.originalModel = user + self.submit.label.text = "Add" + self.country_id.choices = [(row.id, row.name) for row in geoUtils.queryCountryNames()] + + # This is for the htmx implementation. please be careful how of there is data we first take from the database an then from the from(containing th htmx call) + if self.country_id.data: + self.city_id.choices = [(row.id, row.name) for row in geoUtils.queryCityNamesWithCountryId(self.country_id.data)] + self.state_id.choices = [(row.id, row.name) for row in geoUtils.queryStateNamesWithCountryId(self.country_id.data)] + else: + self.city_id.choices = [(row.id, row.name) for row in geoUtils.queryCityNamesWithCountryIdWithDefault(user.city_id, user.country_id)] + self.state_id.choices = [(row.id, row.name) for row in geoUtils.queryStateNamesWithCountryIdWithDefault(user.state_id, user.country_id)] # Queries to be made in order to validate the form : If username exists - def validate_username(self, username): - user = Users.query.filter_by(username=username.data).first() # Database Querry - if user: - raise ValidationError('That username is taken please choose another one') + def validate_username(self, input): + if input.data != self.originalModel.username: + if (Users.query.filter(Users.username.ilike(f'%{input.data}%')).first()): + raise ValidationError('This User Name is alredy registered') - # Queries to be made in order to validate the form : If Email exists - def validate_email(self, email): - email = Users.query.filter_by(email_account=email.data).first() # Database Querry - if email: - raise ValidationError('That email is taken do you have an acocunt ?') + # Queries to be made in order to validate the form : If username exists + def validate_email_account(self, input): + if input.data != self.originalModel.email_account: + if (Users.query.filter(Users.email_account.ilike(f'%{input.data}%')).first()): + raise ValidationError('This E-mail is alredy registered') +class accountUpdateForm(FlaskForm): # Defines the form class to be used for the user registretion + # Decalarion of the fields for the form and it's propereties + username = StringField('User Name', validators=[DataRequired(), Length(min=4, max=20)]) + name = StringField('Name', validators=[DataRequired(), Length(min=4, max=20)]) + surname = StringField('Surname', validators=[DataRequired(), Length(min=4, max=20)]) + email_account = StringField('Email: Account', validators=[DataRequired(), Email()]) + email_comm = StringField('Email: Communication', validators=[DataRequired(), Email()]) + street = StringField('Street', validators=[DataRequired()]) + street_no = IntegerField('No', validators=[DataRequired()]) + post_code = IntegerField('Post', validators=[DataRequired()]) + city_id = SelectField('City', validators=[DataRequired()], render_kw={"hx-get": "/geography/get_states", "hx-target": "#state_id"}) + state_id = SelectField('State', validators=[]) + country_id = SelectField('Country', validators=[DataRequired()], render_kw={"hx-get": "/geography/get_cities", "hx-target": "#city_id"}) + image_file = FileField('Update Avatar', validators=[FileAllowed(['jpg', 'png'])]) + password = PasswordField('Password') + password_confirm= PasswordField('Confirm Password', validators=[EqualTo('password')]) + submit = SubmitField() + + def populate_for_update(self, user): + self.originalModel = user + self.submit.label.text = "Update" + self.country_id.choices = [(row.id, row.name) for row in geoUtils.queryCountryNamesWithDefault(user.country_id)] + + # This is for the htmx implementation. please be careful how of there is data we first take from the database an then from the from(containing th htmx call) + if self.country_id.data: + self.city_id.choices = [(row.id, row.name) for row in geoUtils.queryCityNamesWithCountryId(self.country_id.data)] + self.state_id.choices = [(row.id, row.name) for row in geoUtils.queryStateNamesWithCountryId(self.country_id.data)] + else: + self.city_id.choices = [(row.id, row.name) for row in geoUtils.queryCityNamesWithCountryIdWithDefault(user.city_id, user.country_id)] + self.state_id.choices = [(row.id, row.name) for row in geoUtils.queryStateNamesWithCountryIdWithDefault(user.state_id, user.country_id)] + + # Queries to be made in order to validate the form : If username exists + def validate_username(self, input): + if input.data != self.originalModel.username: + if (Users.query.filter(Users.username.ilike(f'%{input.data}%')).first()): + raise ValidationError('This User Name is alredy registered') + + # Queries to be made in order to validate the form : If username exists + def validate_email_account(self, input): + if input.data != self.originalModel.email_account: + if (Users.query.filter(Users.email_account.ilike(f'%{input.data}%')).first()): + raise ValidationError('This E-mail is alredy registered') + class loginForm(FlaskForm): # Defines the form class to be used for the user login email = StringField('Email', validators=[DataRequired(), Email()]) password = PasswordField('Password', validators=[DataRequired()]) diff --git a/web/minibase/blueprints/user/models.py b/web/minibase/blueprints/user/models.py index fdc2d516..758de6a8 100644 --- a/web/minibase/blueprints/user/models.py +++ b/web/minibase/blueprints/user/models.py @@ -4,7 +4,6 @@ from minibase.app import db, login_manager from flask_login import UserMixin from datetime import datetime - # The Default User Loading proccess @login_manager.user_loader def load_user(user_id): diff --git a/web/minibase/blueprints/user/routes.py b/web/minibase/blueprints/user/routes.py index 5ae22103..6f19a85d 100644 --- a/web/minibase/blueprints/user/routes.py +++ b/web/minibase/blueprints/user/routes.py @@ -5,26 +5,32 @@ import minibase.theme as theme from minibase.blueprints.user.models import Users, User_Roles import minibase.blueprints.database.utils as dbUtils import minibase.blueprints.user.utils as UserUtils -from minibase.blueprints.user.forms import registrationForm, loginForm, updateAccountForm, resetPasswordForm, requestResetForm, updateRoleForm +from minibase.blueprints.user.forms import registrationForm, loginForm, updateAccountForm, resetPasswordForm, requestResetForm, updateRoleForm, accountUpdateForm import minibase.blueprints.main.utils as mainUtils # Declaring a blueprint user = Blueprint('user', __name__, template_folder='templates') - @user.route("/register", methods=['GET', 'POST']) def register(): if current_user.is_authenticated: + flash('You are already logged in', 'success') return redirect(url_for('main.index')) + user = Users() form = registrationForm() + form.populate_for_adding(user) + + if request.method == 'GET': + form.populate_for_adding(user) + if form.validate_on_submit(): - hashed_pw = bcrypt.generate_password_hash(form.password.data).decode('utf-8') - dbUtils.dbAddAndCommit(Users(username=form.username.data, email_account=form.email.data, email_comm=form.email.data, password=hashed_pw)) - flash(f'{"Your account has been created you can now log in!"}', 'success') - return redirect(url_for('user.login')) + mainUtils.fill_model(user, form) + user.password = bcrypt.generate_password_hash(form.password.data).decode('utf-8') + dbUtils.dbAddAndCommit(user) + flash('User has been successfully added', 'success') - return render_template('user/register.html', + return render_template('edit.html', theme=theme, form=form) @@ -57,27 +63,41 @@ def logout(): @user.route("/account", methods=['GET', 'POST']) @login_required def account(): - form = updateAccountForm() + form = accountUpdateForm() + form.populate_for_update(current_user) + _accountInfo = mainUtils.accountInfo( + title=current_user.username, + description=current_user.email_account, + short=current_user.role, + status=current_user.name, + image_file=mainUtils.imageFileLink(current_user.image_file) + ) + if form.validate_on_submit(): - if form.picture.data: - picture_file = mainUtils.save_picture(form.picture.data) + mainUtils.fill_model(current_user, form) + + if form.image_file.data: + picture_file = mainUtils.save_picture(form.image_file.data) current_user.image_file = picture_file - current_user.username = form.username.data - current_user.email_account = form.email_account.data - current_user.email_comm = form.email_comm.data + + if form.password.data: + print(f"Passsword is : {form.password.data}") + print(f"User pass id : {current_user.password}") + hashed = bcrypt.generate_password_hash(form.password.data).decode('utf-8') + current_user.password = hashed + print(f"Hashed is : {hashed}") + print(f"Hashed is : {current_user.password}") db.session.commit() flash('Your account has been updated!', 'success') - return redirect(url_for('user.account')) + return redirect(url_for('user.accountt')) + elif request.method == 'GET': - form.username.data = current_user.username - form.email_account.data = current_user.email_account - form.email_comm.data = current_user.email_comm - image_file = url_for('static', filename='pics/' + current_user.image_file) - return render_template('user/account.html', - theme=theme, - image_file=image_file, - form=form) + mainUtils.populate_form(form, current_user) + return render_template('account.html', + theme=theme, + accountInfo=_accountInfo, + form=form) @user.route("/reset_password", methods=['GET', 'POST']) diff --git a/web/minibase/blueprints/user/utils.py b/web/minibase/blueprints/user/utils.py index a7259424..46408f5b 100644 --- a/web/minibase/blueprints/user/utils.py +++ b/web/minibase/blueprints/user/utils.py @@ -6,6 +6,8 @@ from sqlalchemy import case def dbGetMailFirst(mail): return Users.query.filter_by(email_account=mail).first() +def queryMailById(id): + return (Users.query.filter_by(id=id).first()).email_comm def queryRoleById(id): return User_Roles.query.get_or_404(id) diff --git a/web/minibase/static/pics/aab9913add20a59d.png b/web/minibase/static/pics/aab9913add20a59d.png new file mode 100644 index 00000000..c4f57038 Binary files /dev/null and b/web/minibase/static/pics/aab9913add20a59d.png differ diff --git a/web/minibase/theme.py b/web/minibase/theme.py index 6e122fc8..4d0920cd 100644 --- a/web/minibase/theme.py +++ b/web/minibase/theme.py @@ -61,7 +61,7 @@ class form: date_label_class = default_label_class date_label_style = default_label_style date_class = input_class - date_style = default_field_style + date_style = default_field_style date_type = "date" submit_class = "btn btn-outline-info mb-2 mt-2" @@ -85,7 +85,7 @@ class menu: "sublinks": [ {"text": "List", "url": "sensor.list"}, {"decoration": "line"}, - {"text": "Add", "url": "main.index"}, + {"text": "Add", "url": "sensor.add"}, {"decoration": "line"}, {"text": "Edit", "url": "main.index"}, ],},