ContactBridge

Log | Files | Refs | README

commit fb74f22df28e721458179f09f311500f2308b58a
parent 50a36d4ff1592d2f1b3ace5b2f835d71feae512a
Author: William Lindholm <william_lindholm@outlook.com>
Date:   Sat,  4 Nov 2023 19:44:55 +0100

Now using tf model to filter incoming messages.

Diffstat:
DMessageTagging/__init__.py | 0
AMessageTagging/saved_model/fingerprint.pb | 2++
AMessageTagging/saved_model/keras_metadata.pb | 9+++++++++
AMessageTagging/saved_model/saved_model.pb | 0
AMessageTagging/saved_model/variables/variables.data-00000-of-00001 | 0
AMessageTagging/saved_model/variables/variables.index | 0
AMessageTagging/spam_detector.py | 43+++++++++++++++++++++++++++++++++++++++++++
AMessageTagging/tokenizer.pickle | 0
MWebAPI/routes.py | 17+++++++++++------
MWebInterface/routes.py | 10++++++----
Mdbrepository.py | 11++++++-----
Mtemplates/home.html | 3++-
12 files changed, 79 insertions(+), 16 deletions(-)

diff --git a/MessageTagging/__init__.py b/MessageTagging/__init__.py diff --git a/MessageTagging/saved_model/fingerprint.pb b/MessageTagging/saved_model/fingerprint.pb @@ -0,0 +1 @@ +٬ʑi曳؊ƴ D(Е2 +\ No newline at end of file diff --git a/MessageTagging/saved_model/keras_metadata.pb b/MessageTagging/saved_model/keras_metadata.pb @@ -0,0 +1,8 @@ + +*root"_tf_keras_sequential**{"name": "sequential", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": false, "class_name": "Sequential", "config": {"name": "sequential", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 5530]}, "dtype": "float32", "sparse": false, "ragged": false, "name": "embedding_input"}}, {"class_name": "Embedding", "config": {"name": "embedding", "trainable": true, "dtype": "float32", "batch_input_shape": {"class_name": "__tuple__", "items": [null, 5530]}, "input_dim": 57562, "output_dim": 16, "embeddings_initializer": {"class_name": "RandomUniform", "config": {"minval": -0.05, "maxval": 0.05, "seed": null}}, "embeddings_regularizer": null, "activity_regularizer": null, "embeddings_constraint": null, "mask_zero": false, "input_length": 5530}}, {"class_name": "GlobalAveragePooling1D", "config": {"name": "global_average_pooling1d", "trainable": true, "dtype": "float32", "data_format": "channels_last", "keepdims": false}}, {"class_name": "Dense", "config": {"name": "dense", "trainable": true, "dtype": "float32", "units": 16, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Dense", "config": {"name": "dense_1", "trainable": true, "dtype": "float32", "units": 1, "activation": "sigmoid", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}]}, "shared_object_id": 10, "input_spec": [{"class_name": "InputSpec", "config": {"dtype": null, "shape": {"class_name": "__tuple__", "items": [null, 5530]}, "ndim": 2, "max_ndim": null, "min_ndim": null, "axes": {}}}], "build_input_shape": {"class_name": "TensorShape", "items": [null, 5530]}, "is_graph_network": true, "full_save_spec": {"class_name": "__tuple__", "items": [[{"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, 5530]}, "float32", "embedding_input"]}], {}]}, "save_spec": {"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, 5530]}, "float32", "embedding_input"]}, "keras_version": "2.14.0", "backend": "tensorflow", "model_config": {"class_name": "Sequential", "config": {"name": "sequential", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 5530]}, "dtype": "float32", "sparse": false, "ragged": false, "name": "embedding_input"}, "shared_object_id": 0}, {"class_name": "Embedding", "config": {"name": "embedding", "trainable": true, "dtype": "float32", "batch_input_shape": {"class_name": "__tuple__", "items": [null, 5530]}, "input_dim": 57562, "output_dim": 16, "embeddings_initializer": {"class_name": "RandomUniform", "config": {"minval": -0.05, "maxval": 0.05, "seed": null}, "shared_object_id": 1}, "embeddings_regularizer": null, "activity_regularizer": null, "embeddings_constraint": null, "mask_zero": false, "input_length": 5530}, "shared_object_id": 2}, {"class_name": "GlobalAveragePooling1D", "config": {"name": "global_average_pooling1d", "trainable": true, "dtype": "float32", "data_format": "channels_last", "keepdims": false}, "shared_object_id": 3}, {"class_name": "Dense", "config": {"name": "dense", "trainable": true, "dtype": "float32", "units": 16, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 4}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 5}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 6}, {"class_name": "Dense", "config": {"name": "dense_1", "trainable": true, "dtype": "float32", "units": 1, "activation": "sigmoid", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 7}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 8}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 9}]}}, "training_config": {"loss": "binary_crossentropy", "metrics": [[{"class_name": "MeanMetricWrapper", "config": {"name": "accuracy", "dtype": "float32", "fn": "binary_accuracy"}, "shared_object_id": 12}]], "weighted_metrics": null, "loss_weights": null, "optimizer_config": {"class_name": "Custom>Adam", "config": {"name": "Adam", "weight_decay": null, "clipnorm": null, "global_clipnorm": null, "clipvalue": null, "use_ema": false, "ema_momentum": 0.99, "ema_overwrite_frequency": null, "jit_compile": false, "is_legacy_optimizer": false, "learning_rate": 0.0010000000474974513, "beta_1": 0.9, "beta_2": 0.999, "epsilon": 1e-07, "amsgrad": false}}}}2 +root.layer_with_weights-0"_tf_keras_layer*{"name": "embedding", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": {"class_name": "__tuple__", "items": [null, 5530]}, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": false, "class_name": "Embedding", "config": {"name": "embedding", "trainable": true, "dtype": "float32", "batch_input_shape": {"class_name": "__tuple__", "items": [null, 5530]}, "input_dim": 57562, "output_dim": 16, "embeddings_initializer": {"class_name": "RandomUniform", "config": {"minval": -0.05, "maxval": 0.05, "seed": null}, "shared_object_id": 1}, "embeddings_regularizer": null, "activity_regularizer": null, "embeddings_constraint": null, "mask_zero": false, "input_length": 5530}, "shared_object_id": 2, "build_input_shape": {"class_name": "TensorShape", "items": [null, 5530]}}2 + root.layer-1"_tf_keras_layer*{"name": "global_average_pooling1d", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "GlobalAveragePooling1D", "config": {"name": "global_average_pooling1d", "trainable": true, "dtype": "float32", "data_format": "channels_last", "keepdims": false}, "shared_object_id": 3, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 3, "max_ndim": null, "min_ndim": null, "axes": {}}, "shared_object_id": 13}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 5530, 16]}}2 +root.layer_with_weights-1"_tf_keras_layer*{"name": "dense", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Dense", "config": {"name": "dense", "trainable": true, "dtype": "float32", "units": 16, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 4}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 5}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 6, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 2, "axes": {"-1": 16}}, "shared_object_id": 14}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16]}}2 +root.layer_with_weights-2"_tf_keras_layer*{"name": "dense_1", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Dense", "config": {"name": "dense_1", "trainable": true, "dtype": "float32", "units": 1, "activation": "sigmoid", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 7}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 8}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 9, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 2, "axes": {"-1": 16}}, "shared_object_id": 15}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16]}}2 +Xroot.keras_api.metrics.0"_tf_keras_metric*{"class_name": "Mean", "name": "loss", "dtype": "float32", "config": {"name": "loss", "dtype": "float32"}, "shared_object_id": 16}2 +Yroot.keras_api.metrics.1"_tf_keras_metric*{"class_name": "MeanMetricWrapper", "name": "accuracy", "dtype": "float32", "config": {"name": "accuracy", "dtype": "float32", "fn": "binary_accuracy"}, "shared_object_id": 12}2 +\ No newline at end of file diff --git a/MessageTagging/saved_model/saved_model.pb b/MessageTagging/saved_model/saved_model.pb Binary files differ. diff --git a/MessageTagging/saved_model/variables/variables.data-00000-of-00001 b/MessageTagging/saved_model/variables/variables.data-00000-of-00001 Binary files differ. diff --git a/MessageTagging/saved_model/variables/variables.index b/MessageTagging/saved_model/variables/variables.index Binary files differ. diff --git a/MessageTagging/spam_detector.py b/MessageTagging/spam_detector.py @@ -0,0 +1,42 @@ +import pickle +import os +import tensorflow as tf +from keras.preprocessing.sequence import pad_sequences + + +class SpamDetector: + def __init__(self): + current_dir = os.getcwd() + model_save_path = os.path.join(current_dir, 'MessageTagging/saved_model') + tokenizer_save_path = os.path.join(current_dir, 'MessageTagging/tokenizer.pickle') + + self.model = self.load_model(model_save_path) + self.tokenizer = self.load_tokenizer(tokenizer_save_path) + + @staticmethod + def load_model(model_save_path): + return tf.keras.models.load_model(model_save_path) + + @staticmethod + def load_tokenizer(tokenizer_save_path): + with open(tokenizer_save_path, 'rb') as handle: + return pickle.load(handle) + + def detect_spam(self, text): + sequences = self.tokenizer.texts_to_sequences([text]) + padded_sequences = pad_sequences(sequences, padding='post', maxlen=5530) + + return self.model.predict(padded_sequences)[0][0] + + +if __name__ == "__main__": + spam_detector = SpamDetector() + + prediction = spam_detector.detect_spam('Hello, here today at not a scam.cu.uk.rust create your own mlm now') + + if prediction < .05: + print("not spam") + else: + print("spam") + + print(f'Prediction: {prediction:.2f}') +\ No newline at end of file diff --git a/MessageTagging/tokenizer.pickle b/MessageTagging/tokenizer.pickle Binary files differ. diff --git a/WebAPI/routes.py b/WebAPI/routes.py @@ -1,3 +1,4 @@ +from MessageTagging.spam_detector import SpamDetector from . import api from flask_restx import Resource, fields @@ -6,6 +7,7 @@ from .mailer import Mailer mailer = Mailer() db_repo = DatabaseRepository() +spam_detection = SpamDetector() contact_fields = api.model('Contact', { 'email': fields.String(required=True), @@ -13,7 +15,6 @@ contact_fields = api.model('Contact', { 'message_content': fields.String(required=True) }) - @api.route('/contact') class contact(Resource): @api.expect(contact_fields) @@ -26,12 +27,16 @@ class contact(Resource): formatted_subject = mailer.format_subject(email) formatted_message = mailer.format_message(name, email, message_content) - try: - mailer.send_notification(formatted_subject, formatted_message) - except Exception as e: - return {"message": f"An unknown error occurred: {str(e)}"}, 500 + prediction = spam_detection.detect_spam(message_content) + rounded_prediction = float(f'{prediction:.2f}') + + if prediction < .05: + try: + mailer.send_notification(formatted_subject, formatted_message) + except Exception as e: + return {"message": f"An unknown error occurred: {str(e)}"}, 500 - db_repo.save_message(name, email, formatted_subject, message_content) # Save message to database + db_repo.save_message(name, email, formatted_subject, message_content, rounded_prediction) return {"message": "Message sent and saved successfully"}, 200 diff --git a/WebInterface/routes.py b/WebInterface/routes.py @@ -2,12 +2,13 @@ import json from pathlib import Path from flask import Flask, request, redirect, url_for, render_template, session -from dbrepository import DatabaseRepository +from dbrepository import DatabaseRepository from . import web_interface db = DatabaseRepository() +@web_interface.route('/') @web_interface.route('/home') def home(): if not session.get('logged_in'): @@ -22,7 +23,8 @@ def home(): "email": message[2], "subject": message[3], "message_content": message[4], - "timestamp": message[5] + "timestamp": message[5], + "relevance": message[6] } for message in messages ] @@ -49,7 +51,8 @@ def login(): return render_template('login.html') + @web_interface.route('/logout') def logout(): session.pop('logged_in', None) - return redirect(url_for('web_interface.login')) -\ No newline at end of file + return redirect(url_for('web_interface.login')) diff --git a/dbrepository.py b/dbrepository.py @@ -17,21 +17,22 @@ class DatabaseRepository: email TEXT NOT NULL, subject TEXT NOT NULL, message_content TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + relevance NUMERIC ); """ ) conn.commit() conn.close() - def save_message(self, name, email, subject, message_content): + def save_message(self, name, email, subject, message_content, relevance): conn = sqlite3.connect(self.DATABASE_FILE) cursor = conn.cursor() cursor.execute( """ - INSERT INTO messages (name, email, subject, message_content) - VALUES (?, ?, ?, ?) - """, (name, email, subject, message_content) + INSERT INTO messages (name, email, subject, message_content, relevance) + VALUES (?, ?, ?, ?, ?) + """, (name, email, subject, message_content, relevance) ) conn.commit() conn.close() diff --git a/templates/home.html b/templates/home.html @@ -59,7 +59,8 @@ <ul class="list-group mt-3"> {% for message in messages %} <li class="list-group-item"> - <strong>{{ message.subject }}</strong> from {{ message.name }} ({{ message.email }}) at {{ message.timestamp }}: + <strong>{{ message.subject }}</strong> from {{ message.name }} ({{ message.email }})@{{ message.timestamp }} + relevance: {{ message.relevance }}: <p>{{ message.message_content }}</p> </li> {% endfor %}