Kuberwastaken commited on
Commit
b9604a1
·
0 Parent(s):

Initial commit for TREAT project

Browse files
README.md ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ![Treat_Banner](static/images/readme-images/New_Treat_Banner.png)
2
+
3
+ <h1 align="center">
4
+ Trigger Recognition for Enjoyable and Appropriate Television
5
+ </h1>
6
+
7
+ <p align="center">
8
+ <img src="https://img.shields.io/static/v1?label=Kuberwastaken&message=TREAT&color=blue&logo=github" alt="Kuberwastaken - TREAT">
9
+ <img src="https://img.shields.io/badge/version-2.0-blue" alt="Version 2.0">
10
+ <img src="https://img.shields.io/badge/License-Apache_2.0-blue" alt="License Apache 2.0">
11
+ </p>
12
+
13
+ I was tired of getting grossed out watching unexpected scenes in movies and TV and losing my appetite, that's why I created TREAT.
14
+
15
+ The goal of this project is to empower viewers by forewarning them about potential triggers in the content they watch, making the viewing experience more enjoyable, inclusive, and appropriate for everyone.
16
+
17
+ TREAT is a web application that uses natural language processing to analyze movie and TV show scripts, identifying potential triggers to help viewers make informed choices.
18
+
19
+ ## Installation Instructions
20
+ ### Prerequisites
21
+ - Star the Repository to Show Your Support.
22
+ - Clone the Repository to Your Local Machine:
23
+
24
+ ```bash
25
+ git clone https://github.com/Kuberwastaken/TREAT.git
26
+ ```
27
+
28
+ ### Hugging Face Login Instructions for Llama-3.2-1B Model
29
+ To use the [Llama-3.2-1B model](https://huggingface.co/meta-llama/Llama-3.2-1B), which provides a 35% increase in accuracy and efficiency over the [previous model](https://github.com/Kuberwastaken/TREAT-CS50), you must request access for it, as it is a gated model.
30
+
31
+ ![Request_Accesss_Page](static/images/readme-images/Request-Access-Page.png)
32
+
33
+ 1. **Login to Hugging Face in Your Environment:**
34
+
35
+ Run the following command in your terminal:
36
+
37
+ ```bash
38
+ huggingface-cli login
39
+ ```
40
+
41
+ Enter your Hugging Face access token when prompted.
42
+
43
+ 2. **Download the Llama-3.2-1B Model:**
44
+
45
+ The model will be downloaded automatically when running the script analysis for the first time, provided you have received access.
46
+
47
+ ### Environment Setup
48
+ To set up the development environment, you will need to create a virtual environment and install the necessary dependencies.
49
+
50
+ 1. Create a Virtual Environment:
51
+
52
+ ```bash
53
+ python3 -m venv treat-env
54
+ ```
55
+
56
+ 2. Activate the Virtual Environment:
57
+
58
+ ```bash
59
+ source treat-env/bin/activate # On Unix or MacOS
60
+ treat-env\Scriptsctivate # On Windows
61
+ ```
62
+
63
+ 3. Install Dependencies:
64
+
65
+ Navigate to the project directory and run:
66
+
67
+ ```bash
68
+ pip install -r requirements.txt
69
+ ```
70
+
71
+ ## Project Usage
72
+ 1. **Start the Flask Server:**
73
+
74
+ ```bash
75
+ python run.py
76
+ ```
77
+
78
+ 2. **Open Your Browser:**
79
+
80
+ Navigate to `http://127.0.0.1:5000` to access the TREAT web interface.
81
+
82
+ 3. **Analyze Scripts:**
83
+
84
+ You can manually enter a script in the provided text area and click "Analyze Script."
85
+
86
+ ## File Descriptions
87
+ - **app.py:** The main Flask application file that handles routing.
88
+
89
+ - **app/routes.py:** Contains the Flask routes for handling script uploads.
90
+
91
+ - **app/model.py:** Includes the script analysis functions using the Llama-3.2-1B model.
92
+
93
+ - **templates/index.html:** The main HTML file for the web interface.
94
+
95
+ - **static/css/style.css:** Custom CSS for styling the web interface.
96
+
97
+ - **static/js/app.js:** JavaScript for handling client-side interactions.
98
+
99
+ ## Types of Triggers Detected
100
+ The TREAT application focuses on identifying a variety of potential triggers in scripts, including but not limited to:
101
+
102
+ - **Violence:** Scenes of physical aggression or harm.
103
+
104
+ - **Self-Harm:** Depictions of self-inflicted injury.
105
+
106
+ - **Death:** Depictions of death or dying characters.
107
+
108
+ - **Sexual Content:** Any depiction or mention of sexual activity, intimacy, or behavior.
109
+
110
+ - **Sexual Abuse:** Instances of sexual violence or exploitation.
111
+
112
+ - **Gun Use:** Depictions of firearms and their usage.
113
+
114
+ - **Gore:** Graphic depiction of injury, blood, or dismemberment.
115
+
116
+ - **Vomit:** Depictions of vomiting or nausea-inducing content.
117
+
118
+ - **Mental Health Issues:** Depictions of mental health struggles, including anxiety, depression, or disorders.
119
+
120
+ - **Animal Cruelty:** Depictions of harm or abuse towards animals.
121
+
122
+ These categories help address a very real-world problem by forewarning viewers about potentially distressing content, enhancing their viewing experience.
123
+
124
+ Adding new categories is as simple as specifying a new category under model.py and utils.py
125
+
126
+ ## Design Choices
127
+
128
+ - **Inspiration:** I aimed for a simple and intuitive user experience, focusing on simplicity and ease of use. This decision stemmed from the need to create a tool that is easy to navigate for all users, regardless of background or age.
129
+
130
+ - **Theme and Color Scheme:** The chosen theme and color scheme create a visually appealing and engaging environment. The chocolate and sweets theme is intended to stick to the TREAT theme and make the experience enjoyable and pleasant.
131
+
132
+ - **Script Analysis:** The Llama-3.2-1B model by Meta was chosen for its increased accuracy (about 35% better) compared to the prior FLAN-T5 version. The decision was based on its ability to provide precise trigger recognition while being open source. As a new and advanced model, it enhances the script analysis capabilities significantly.
133
+
134
+ ## How to Edit Sensitivity Settings or Prompts
135
+
136
+ To adjust the sensitivity or modify the prompts used during script analysis, you can edit the `model.py` file located under the `treat/app` directory. This file contains various parameters and settings that control how triggers are identified and how the model processes the script. Here’s how you can adjust key settings:
137
+
138
+ ### Key Parameters to Edit:
139
+
140
+
141
+ - **max_new_tokens**: Controls the maximum number of tokens (words or characters) the model will generate in response to a prompt. A higher number can lead to more detailed results but may also increase processing time.
142
+
143
+ - **temperature**: Controls the randomness of the model's responses. A value of 1.0 means standard behavior, while lower values (e.g., 0.2) make the model's output more deterministic and focused. Higher values (e.g., 1.5) allow for more creative or varied responses.
144
+
145
+ - **top_p** (Nucleus Sampling): Controls how many of the top predicted tokens are considered during text generation. A value of 0.9 means the model will only consider the top 90% of predictions, cutting off the least likely options. A lower value can make the output more coherent but less creative.
146
+
147
+ To adjust these, look for `max_new_tokens`, `temperature`, and `top_p` in the `model.py` file and set them to your desired values. For example:
148
+ ```python
149
+ max_new_tokens = 10 # Set the maximum number of tokens
150
+ temperature = 0.7 # A moderate level for balanced responses
151
+ top_p = 0.9 # Adjust the diversity of the output
152
+ ```
153
+
154
+ ### Chunk Size and Overlap:
155
+ To handle long scripts effectively, the text is divided into chunks before being analyzed. Adjusting the chunk size and overlap helps control how much of the script is processed at once and ensures that the model doesn't miss context between chunks.
156
+
157
+ - **chunk_size**: This parameter defines the length of each chunk in tokens. A larger chunk size can help the model capture more context, but may result in higher memory usage and processing time. A smaller chunk size may process faster but could lose context.
158
+
159
+ - **overlap**: This parameter defines the number of overlapping tokens between consecutive chunks. Adding overlap ensures that context from the end of one chunk is carried over to the next chunk, preventing the model from losing important information.
160
+
161
+ To adjust these, look for `chunk_size` and `overlap` in `model.py` and set them to your desired values:
162
+ ```python
163
+ chunk_size = 1000 # Set the length of each chunk (tokens)
164
+ overlap = 200 # Set the number of overlapping tokens between chunks
165
+ ```
166
+
167
+ ### Adjusting Prompts:
168
+ To modify the types of triggers detected by the model, you can edit the prompts under the `trigger_categories` section in `model.py`. This section allows you to adjust how the model recognizes various types of content in the script. Simply modify the prompts to suit your needs.
169
+
170
+ ### Summary of Editable Parameters:
171
+ - **max_new_tokens, temperature, top_p**: Control the length, randomness, and diversity of the model's output.
172
+ - **chunk_size, overlap**: Control how the script is divided into chunks and how context is maintained between chunks.
173
+ - **trigger_categories**: Adjust the prompts to change how triggers are identified in the script.
174
+
175
+
176
+ ## To-Do List
177
+ - Integration with an API to Directly Search Scripts by Name of Movies/Shows
178
+
179
+ - Introduce multiple themes to allow users to customize the appearance of the application according to their preferences
180
+
181
+ - Increasing speed and efficiency
182
+
183
+ - Potentially host this online
184
+
185
+ - Make the application mobile-friendly
186
+
187
+ ## Open Source Contribution
188
+ This repository is completely open source and free to contribute. I intend to keep this project alive and evolve it into a tool that's extremely usable for all. Contributions are welcome and highly encouraged to add new features, improve the user interface, or enhance the script analysis.
189
+
190
+ ## Acknowledgements
191
+ I would like to thank:
192
+
193
+ - Meta AI: For developing and allowing me access to the Llama-3.2-1B model, a very critical component of this project.
194
+
195
+ - Parasite (2019): For that unexpected jumpscare that ruined my appetite and ultimately inspired this project.
196
+
197
+ ## License
198
+ This project is licensed under the [Apache 2.0 License](https://github.com/Kuberwastaken/TREAT/blob/main/LICENSE).
app/__init__.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask
2
+ from flask_cors import CORS
3
+ import os
4
+
5
+ # Get the absolute path for the template and static folders
6
+ base_dir = os.path.abspath(os.path.dirname(__file__))
7
+ template_dir = os.path.join(base_dir, "../../templates") # Path to the templates folder
8
+ static_dir = os.path.join(base_dir, "../../static") # Path to the static folder
9
+
10
+ # Create an instance of the Flask class with the specified template and static folders
11
+ app = Flask(
12
+ __name__,
13
+ template_folder=template_dir,
14
+ static_folder=static_dir
15
+ )
16
+
17
+ # Enable Cross-Origin Resource Sharing (CORS) for the app
18
+ CORS(app)
19
+
20
+ # Import routes after initializing the Flask app to avoid circular import issues
21
+ from app import routes
app/routes.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify, render_template
2
+ from app import app
3
+ from app.model import analyze_script
4
+
5
+ # Define the home route which renders the index.html template
6
+ @app.route('/')
7
+ def home():
8
+ return render_template('index.html')
9
+
10
+ # Define the upload route to handle POST requests for script analysis
11
+ @app.route('/upload', methods=['POST'])
12
+ def upload_script():
13
+ try:
14
+ # Get the JSON data from the request
15
+ data = request.get_json()
16
+ # Extract the text content from the JSON data
17
+ content = data.get('text', '')
18
+ # Analyze the script for triggers
19
+ triggers = analyze_script(content)
20
+ # Return the triggers in a JSON response
21
+ return jsonify({"triggers": triggers})
22
+ except Exception as e:
23
+ # Handle any exceptions and return an error message
24
+ return jsonify({"error": str(e)}), 500
app/run.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ from os.path import dirname, abspath
3
+
4
+ # Add the directory of the 'treat' folder to the system path
5
+ sys.path.append(abspath(dirname(__file__)) + "/treat")
6
+
7
+ # Import the Flask app from the app module
8
+ from app import app
9
+
10
+ if __name__ == '__main__':
11
+ # Run the Flask app with debug mode enabled
12
+ app.run(debug=True)
gradio_app.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from model.model import get_detailed_analysis
3
+
4
+ def analyze_script(script):
5
+ return get_detailed_analysis(script)
6
+
7
+ iface = gr.Interface(fn=analyze_script, inputs="text", outputs="json")
8
+ iface.launch()
model/model.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoTokenizer, AutoModelForCausalLM
2
+ import torch
3
+ from datetime import datetime
4
+
5
+ def analyze_script(script):
6
+ # Starting the script analysis
7
+ print("\n=== Starting Analysis ===")
8
+ print(f"Time: {datetime.now()}") # Outputting the current timestamp
9
+ print("Loading model and tokenizer...")
10
+
11
+ try:
12
+ # Load the tokenizer and model, selecting the appropriate device (CPU or CUDA)
13
+ tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-1B", use_fast=True)
14
+ device = "cuda" if torch.cuda.is_available() else "cpu" # Use CUDA if available, else use CPU
15
+ print(f"Using device: {device}")
16
+
17
+ model = AutoModelForCausalLM.from_pretrained(
18
+ "meta-llama/Llama-3.2-1B",
19
+ torch_dtype=torch.float16 if device == "cuda" else torch.float32, # Use 16-bit precision for CUDA, 32-bit for CPU
20
+ device_map="auto" # Automatically map model to available device
21
+ )
22
+ print("Model loaded successfully")
23
+
24
+ # Define trigger categories with their descriptions
25
+ trigger_categories = {
26
+ "Violence": {
27
+ "mapped_name": "Violence",
28
+ "description": (
29
+ "Any act involving physical force or aggression intended to cause harm, injury, or death to a person, animal, or object. "
30
+ "Includes direct physical confrontations (e.g., fights, beatings, or assaults), implied violence (e.g., very graphical threats or descriptions of injuries), "
31
+ "or large-scale events like wars, riots, or violent protests."
32
+ )
33
+ },
34
+ "Death": {
35
+ "mapped_name": "Death References",
36
+ "description": (
37
+ "Any mention, implication, or depiction of the loss of life, including direct deaths of characters, including mentions of deceased individuals, "
38
+ "or abstract references to mortality (e.g., 'facing the end' or 'gone forever'). This also covers depictions of funerals, mourning, "
39
+ "grieving, or any dialogue that centers around death, do not take metaphors into context that don't actually lead to death."
40
+ )
41
+ },
42
+ "Substance Use": {
43
+ "mapped_name": "Substance Use",
44
+ "description": (
45
+ "Any explicit or implied reference to the consumption, misuse, or abuse of drugs, alcohol, or other intoxicating substances. "
46
+ "Includes scenes of drinking, smoking, or drug use, whether recreational or addictive. May also cover references to withdrawal symptoms, "
47
+ "rehabilitation, or substance-related paraphernalia (e.g., needles, bottles, pipes)."
48
+ )
49
+ },
50
+ "Gore": {
51
+ "mapped_name": "Gore",
52
+ "description": (
53
+ "Extremely detailed and graphic depictions of highly severe physical injuries, mutilation, or extreme bodily harm, often accompanied by descriptions of heavy blood, exposed organs, "
54
+ "or dismemberment. This includes war scenes with severe casualties, horror scenarios involving grotesque creatures, or medical procedures depicted with excessive detail."
55
+ "only answer yes if you're completely certain."
56
+ )
57
+ },
58
+ "Vomit": {
59
+ "mapped_name": "Vomit",
60
+ "description": (
61
+ "Any explicit reference to vomiting, whether directly described, implied, or depicted. This includes detailed sounds, visual descriptions, mentions of nausea explicitly leading to vomiting, or any aftermath involving vomit."
62
+ "Respond 'yes' only if the scene unambiguously and clearly involves vomiting, with no room for doubt."
63
+ )
64
+ },
65
+ "Sexual Content": {
66
+ "mapped_name": "Sexual Content",
67
+ "description": (
68
+ "Any depiction or mention of sexual activity, intimacy, or sexual behavior, ranging from implied scenes to explicit descriptions. "
69
+ "This includes romantic encounters, physical descriptions of characters in a sexual context, sexual dialogue, or references to sexual themes (e.g., harassment, innuendos)."
70
+ )
71
+ },
72
+ "Sexual Abuse": {
73
+ "mapped_name": "Sexual Abuse",
74
+ "description": (
75
+ "Any form of non-consensual sexual act, behavior, or interaction, involving coercion, manipulation, or physical force. "
76
+ "This includes incidents of sexual assault, molestation, exploitation, harassment, and any acts where an individual is subjected to sexual acts against their will or without their consent. "
77
+ "It also covers discussions or depictions of the aftermath of such abuse, such as trauma, emotional distress, legal proceedings, or therapy. "
78
+ "References to inappropriate sexual advances, groping, or any other form of sexual misconduct are also included, as well as the psychological and emotional impact on survivors. "
79
+ "Scenes where individuals are placed in sexually compromising situations, even if not directly acted upon, may also fall under this category."
80
+ "only answer yes if you're completely certain of it's presence."
81
+ )
82
+ },
83
+ "Self-Harm": {
84
+ "mapped_name": "Self-Harm",
85
+ "description": (
86
+ "Any mention or depiction of behaviors where an individual intentionally causes harm to themselves. This includes cutting, burning, or other forms of physical injury, "
87
+ "as well as suicidal ideation, suicide attempts, or discussions of self-destructive thoughts and actions. References to scars, bruises, or other lasting signs of self-harm are also included."
88
+ "only answer yes if you're completely certain."
89
+ )
90
+ },
91
+ "Gun Use": {
92
+ "mapped_name": "Gun Use",
93
+ "description": (
94
+ "Any explicit or implied mention of firearms being handled, fired, or used in a threatening manner. This includes scenes of gun violence, references to shootings, "
95
+ "gun-related accidents, or the presence of firearms in a tense or dangerous context (e.g., holstered weapons during an argument)."
96
+ )
97
+ },
98
+ "Animal Cruelty": {
99
+ "mapped_name": "Animal Cruelty",
100
+ "description": (
101
+ "Any act of harm or abuse toward animals, whether intentional or accidental. This includes physical abuse (e.g., hitting, injuring, or killing animals), "
102
+ "mental or emotional mistreatment (e.g., starvation, isolation), and scenes where animals are subjected to pain or suffering for human entertainment or experimentation."
103
+ "Respond 'yes' only if the scene unambiguously and clearly involves Animal Cruelty, with no room for doubt"
104
+ )
105
+ },
106
+ "Mental Health Issues": {
107
+ "mapped_name": "Mental Health Issues",
108
+ "description": (
109
+ "Any reference to mental health struggles, disorders, or psychological distress. This includes mentions of depression, anxiety, PTSD, bipolar disorder, schizophrenia, "
110
+ "or other conditions. Scenes depicting destructive coping mechanisms are also included."
111
+ "like a character expressing feelings of worthlessness, hopelessness, or detachment from reality."
112
+ )
113
+ }
114
+ }
115
+
116
+ print("\nProcessing text...") # Output indicating the text is being processed
117
+ chunk_size = 256 # Set the chunk size for text processing
118
+ overlap = 15 # Overlap between chunks for context preservation
119
+ script_chunks = [] # List to store script chunks
120
+
121
+ # Split the script into smaller chunks
122
+ for i in range(0, len(script), chunk_size - overlap):
123
+ chunk = script[i:i + chunk_size]
124
+ script_chunks.append(chunk)
125
+
126
+ print(f"Split into {len(script_chunks)} chunks with {overlap} token overlap") # Inform about the chunking
127
+
128
+ identified_triggers = {} # Dictionary to store the identified triggers
129
+
130
+ # Process each chunk of the script
131
+ for chunk_idx, chunk in enumerate(script_chunks, 1):
132
+ print(f"\n--- Processing Chunk {chunk_idx}/{len(script_chunks)} ---")
133
+ print(f"Chunk text (preview): {chunk[:50]}...") # Preview of the current chunk
134
+
135
+ # Check each category for triggers
136
+ for category, info in trigger_categories.items():
137
+ mapped_name = info["mapped_name"]
138
+ description = info["description"]
139
+
140
+ print(f"\nAnalyzing for {mapped_name}...")
141
+ prompt = f"""
142
+ Check this text for any indication of {mapped_name} ({description}).
143
+ Be sensitive to subtle references or implications, make sure the text is not metaphorical.
144
+ Respond concisely with: YES, NO, or MAYBE.
145
+ Text: {chunk}
146
+ Answer:
147
+ """
148
+
149
+ print(f"Sending prompt to model...") # Indicate that prompt is being sent to the model
150
+ inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512) # Tokenize the prompt
151
+ inputs = {k: v.to(device) for k, v in inputs.items()} # Send inputs to the chosen device
152
+
153
+ with torch.no_grad(): # Disable gradient calculation for inference
154
+ print("Generating response...") # Indicate that the model is generating a response
155
+ outputs = model.generate(
156
+ **inputs,
157
+ max_new_tokens=10, # Limit response length
158
+ do_sample=True, # Enable sampling for more diverse output
159
+ temperature=0.5, # Control randomness of the output
160
+ top_p=0.9, # Use nucleus sampling
161
+ pad_token_id=tokenizer.eos_token_id # Pad token ID
162
+ )
163
+
164
+ response_text = tokenizer.decode(outputs[0], skip_special_tokens=True).strip().upper() # Decode and format the response
165
+ first_word = response_text.split("\n")[-1].split()[0] if response_text else "NO" # Get the first word of the response
166
+ print(f"Model response for {mapped_name}: {first_word}")
167
+
168
+ # Update identified triggers based on model response
169
+ if first_word == "YES":
170
+ print(f"Detected {mapped_name} in this chunk!") # Trigger detected
171
+ identified_triggers[mapped_name] = identified_triggers.get(mapped_name, 0) + 1
172
+ elif first_word == "MAYBE":
173
+ print(f"Possible {mapped_name} detected, marking for further review.") # Possible trigger detected
174
+ identified_triggers[mapped_name] = identified_triggers.get(mapped_name, 0) + 0.5
175
+ else:
176
+ print(f"No {mapped_name} detected in this chunk.") # No trigger detected
177
+
178
+ print("\n=== Analysis Complete ===") # Indicate that analysis is complete
179
+ print("Final Results:")
180
+ final_triggers = [] # List to store final triggers
181
+
182
+ # Filter and output the final trigger results
183
+ for mapped_name, count in identified_triggers.items():
184
+ if count > 0.5:
185
+ final_triggers.append(mapped_name)
186
+ print(f"- {mapped_name}: found in {count} chunks")
187
+
188
+ if not final_triggers:
189
+ print("No triggers detected") # No triggers detected
190
+ final_triggers = ["None"]
191
+
192
+ print("\nReturning results...")
193
+ return final_triggers # Return the list of detected triggers
194
+
195
+ except Exception as e:
196
+ # Handle errors and provide stack trace
197
+ print(f"\nERROR OCCURRED: {str(e)}")
198
+ print("Stack trace:")
199
+ import traceback
200
+ traceback.print_exc()
201
+ return {"error": str(e)}
202
+
203
+ def get_detailed_analysis(script):
204
+ print("\n=== Starting Detailed Analysis ===")
205
+ triggers = analyze_script(script) # Call the analyze_script function
206
+
207
+ if isinstance(triggers, list) and triggers != ["None"]:
208
+ result = {
209
+ "detected_triggers": triggers,
210
+ "confidence": "High - Content detected",
211
+ "model": "Llama-3.2-1B",
212
+ "analysis_timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
213
+ }
214
+ else:
215
+ result = {
216
+ "detected_triggers": ["None"],
217
+ "confidence": "High - No concerning content detected",
218
+ "model": "Llama-3.2-1B",
219
+ "analysis_timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
220
+ }
221
+
222
+ print("\nFinal Result Dictionary:", result) # Output the final result dictionary
223
+ return result
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ flask
2
+ flask_cors
3
+ torch
4
+ transformers
5
+ accelerate
static/css/style.css ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Chocolate and sweets theme for TREAT */
2
+
3
+ /* Set up the main styles for the body of the document */
4
+ body {
5
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; /* font */
6
+ margin: 0; /* Remove default margin */
7
+ padding: 0; /* Remove default padding */
8
+ background-color: #836853; /* Background color resembling chocolate */
9
+ color: #FFFFFF; /* White text color for contrast */
10
+ height: 100vh; /* Full viewport height */
11
+ display: flex; /* Flexbox layout for centering content */
12
+ flex-direction: column; /* Column layout */
13
+ justify-content: center; /* Center content vertically */
14
+ align-items: center; /* Center content horizontally */
15
+ overflow: hidden; /* Hide overflow content */
16
+ }
17
+
18
+ /* Style the container for the main content */
19
+ .choco-container {
20
+ background: rgba(78, 52, 46, 0.9); /* Semi-transparent dark chocolate background */
21
+ box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37); /* Soft shadow effect */
22
+ border-radius: 20px; /* Rounded corners */
23
+ padding: 40px; /* Padding inside the container */
24
+ width: 90%; /* 90% width of the viewport */
25
+ max-width: 1000px; /* Maximum width */
26
+ height: 80%; /* 80% height of the viewport */
27
+ animation: fadeIn 1s ease-in-out; /* Fade-in animation */
28
+ border: 1px solid #5d4037; /* Slightly lighter chocolate border */
29
+ display: flex; /* Flexbox layout for centering content */
30
+ flex-direction: column; /* Column layout */
31
+ align-items: center; /* Center content horizontally */
32
+ justify-content: center; /* Center content vertically */
33
+ }
34
+
35
+ /* Define the fade-in animation for the container */
36
+ @keyframes fadeIn {
37
+ from {
38
+ opacity: 0; /* Start with no opacity */
39
+ transform: scale(0.95); /* Start slightly scaled down */
40
+ }
41
+ to {
42
+ opacity: 1; /* Fade to full opacity */
43
+ transform: scale(1); /* Scale to full size */
44
+ }
45
+ }
46
+
47
+ /* Style the header of the document */
48
+ .header {
49
+ text-align: center; /* Center-align text */
50
+ margin-bottom: 20px; /* Margin below the header */
51
+ }
52
+
53
+ /* Style the main heading */
54
+ .header h1 {
55
+ font-size: 3rem; /* Large font size */
56
+ color: #d7ccc8; /* Light creamy color */
57
+ }
58
+
59
+ /* Style the paragraph under the heading */
60
+ .header p {
61
+ font-size: 1.5rem; /* Medium font size */
62
+ color: #ffab91; /* Candy-like color */
63
+ }
64
+
65
+ /* Highlight certain text */
66
+ .highlight {
67
+ color: #ff7043; /* Sweet orange color */
68
+ }
69
+
70
+ /* Style the main content area */
71
+ .main-content {
72
+ width: 100%; /* Full width */
73
+ height: 100%; /* Full height */
74
+ display: flex; /* Flexbox layout */
75
+ flex-direction: column; /* Column layout */
76
+ align-items: center; /* Center content horizontally */
77
+ justify-content: center; /* Center content vertically */
78
+ }
79
+
80
+ /* Style the form */
81
+ .sweet-form {
82
+ width: 100%; /* Full width */
83
+ height: 100%; /* Full height */
84
+ display: flex; /* Flexbox layout */
85
+ flex-direction: column; /* Column layout */
86
+ align-items: center; /* Center content horizontally */
87
+ justify-content: center; /* Center content vertically */
88
+ }
89
+
90
+ /* Style the textarea element */
91
+ textarea {
92
+ width: 100%; /* Full width */
93
+ height: 70%; /* 70% height of the container */
94
+ padding: 20px; /* Padding inside the textarea */
95
+ font-size: 1.2rem; /* Medium font size */
96
+ border: none; /* Remove default border */
97
+ border-radius: 10px; /* Rounded corners */
98
+ resize: none; /* Disable resizing */
99
+ background: #3e2723; /* Chocolate color background */
100
+ color: #ffecb3; /* Creamy color text */
101
+ box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5); /* Inset shadow */
102
+ transition: all 0.3s ease-in-out; /* Smooth transition for all properties */
103
+ }
104
+
105
+ /* Style the textarea when it is focused */
106
+ textarea:focus {
107
+ outline: none; /* Remove default outline */
108
+ box-shadow: 0 0 15px #ffab91; /* Light glow when focused */
109
+ }
110
+
111
+ /* Style the button element */
112
+ button {
113
+ width: 50%; /* 50% width of the container */
114
+ padding: 20px; /* Padding inside the button */
115
+ font-size: 1.5rem; /* Large font size */
116
+ margin-top: 20px; /* Margin above the button */
117
+ background: linear-gradient(45deg, #8d6e63, #d7ccc8); /* Chocolate gradient background */
118
+ color: white; /* White text color */
119
+ border: none; /* Remove default border */
120
+ border-radius: 10px; /* Rounded corners */
121
+ cursor: pointer; /* Pointer cursor on hover */
122
+ text-transform: uppercase; /* Uppercase text */
123
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5); /* Soft shadow */
124
+ transition: all 0.3s ease; /* Smooth transition for all properties */
125
+ position: relative; /* Relative positioning for overlay effects */
126
+ }
127
+
128
+ /* Style the button on hover */
129
+ button:hover {
130
+ background: linear-gradient(45deg, #d7ccc8, #8d6e63); /* Reversed gradient on hover */
131
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.6); /* Stronger shadow on hover */
132
+ }
133
+
134
+ /* Create an overlay effect for the button */
135
+ button::after {
136
+ content: ''; /* Empty content */
137
+ position: absolute; /* Absolute positioning */
138
+ top: 0; /* Position at the top */
139
+ left: 0; /* Position at the left */
140
+ width: 100%; /* Full width */
141
+ height: 100%; /* Full height */
142
+ background: rgba(255, 255, 255, 0.1); /* Light overlay */
143
+ border-radius: 10px; /* Rounded corners */
144
+ opacity: 0; /* Initially hidden */
145
+ transition: opacity 0.3s ease; /* Smooth transition for opacity */
146
+ }
147
+
148
+ /* Show the overlay effect on hover */
149
+ button:hover::after {
150
+ opacity: 1; /* Show overlay on hover */
151
+ }
152
+
153
+ /* Style the loading bar element */
154
+ .loading-bar {
155
+ display: none; /* Initially hidden */
156
+ width: 100%; /* Full width */
157
+ height: 5px; /* Fixed height */
158
+ background: linear-gradient(90deg, #8d6e63, #d7ccc8); /* Chocolate gradient background */
159
+ margin-top: 20px; /* Margin above the loading bar */
160
+ border-radius: 2px; /* Slightly rounded corners */
161
+ overflow: hidden; /* Hide overflow content */
162
+ position: relative; /* Relative positioning */
163
+ }
164
+
165
+ /* Create an animation for the loading bar */
166
+ .loading-bar::before {
167
+ content: ''; /* Empty content */
168
+ position: absolute; /* Absolute positioning */
169
+ width: 100%; /* Full width */
170
+ height: 100%; /* Full height */
171
+ background: linear-gradient(90deg, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0)); /* Gradient background */
172
+ animation: loading-animation 2s infinite; /* Infinite loading animation */
173
+ }
174
+
175
+ /* Define the loading animation */
176
+ @keyframes loading-animation {
177
+ 0% {
178
+ transform: translateX(-100%); /* Start outside on the left */
179
+ }
180
+ 50% {
181
+ transform: translateX(0); /* Move to the center */
182
+ }
183
+ 100% {
184
+ transform: translateX(100%); /* Move outside on the right */
185
+ }
186
+ }
187
+
188
+ /* Style the results container */
189
+ #results {
190
+ overflow-x: auto; /* Add horizontal scrollbar only if needed */
191
+ overflow-y: hidden; /* Hide vertical overflow */
192
+ margin-top: 30px; /* Margin above the results */
193
+ color: #ff7043; /* Sweet orange color */
194
+ font-size: 1.5rem; /* Large font size */
195
+ font-weight: bold; /* Bold text */
196
+ text-shadow: 0 0 5px #ff7043, 0 0 10px #ff7043; /* Reduced shininess */
197
+ text-align: center; /* Center-align text */
198
+ word-wrap: break-word; /* Ensure text wraps appropriately */
199
+ overflow-wrap: break-word; /* Compatibility with other browsers */
200
+ padding: 10px; /* Padding inside the results container */
201
+ box-sizing: border-box; /* Include padding in width calculations */
202
+ white-space: nowrap; /* Prevent line breaks so horizontal scrolling is possible */
203
+ }
204
+
205
+ /* Style the horizontal scrollbar */
206
+ #results::-webkit-scrollbar {
207
+ height: 10px; /* Horizontal scrollbar height */
208
+ }
209
+
210
+ /* Style the scrollbar thumb */
211
+ #results::-webkit-scrollbar-thumb {
212
+ background: linear-gradient(45deg, #8d6e63, #d7ccc8); /* Chocolate gradient for scrollbar thumb */
213
+ border-radius: 5px; /* Rounded corners */
214
+ }
215
+
216
+ /* Style the scrollbar track */
217
+ #results::-webkit-scrollbar-track {
218
+ background: rgba(78, 52, 46, 0.5); /* Lighter chocolate track */
219
+ border-radius: 5px;
220
+ }
221
+
222
+ /* To Fix: Title moving upwards when Output is shown*/
static/images/android-chrome-192x192.png ADDED
static/images/apple-touch-icon.png ADDED
static/images/favicon-16x16.png ADDED
static/images/favicon-32x32.png ADDED
static/images/favicon.ico ADDED
static/images/readme-images/Request-Access-Page.png ADDED
static/images/site.webmanifest ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "",
3
+ "short_name": "",
4
+ "icons": [
5
+ {
6
+ "src": "/android-chrome-192x192.png",
7
+ "sizes": "192x192",
8
+ "type": "image/png"
9
+ }
10
+ ],
11
+ "theme_color": "#FFFFFF",
12
+ "background_color": "#FFFFFF",
13
+ "display": "standalone"
14
+ }
static/js/app.js ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Add an event listener for the form submission
2
+ document.getElementById('text-form').addEventListener('submit', async function(event) {
3
+ event.preventDefault(); // Prevent the default form submission behavior
4
+
5
+ const text = document.getElementById('text-input').value; // Get the text input value
6
+ const loadingBar = document.getElementById('loading-bar'); // Get the loading bar element
7
+ const resultsDiv = document.getElementById('results'); // Get the results div element
8
+ resultsDiv.textContent = ''; // Clear previous results
9
+ loadingBar.style.display = 'block'; // Display the loading bar
10
+
11
+ try {
12
+ // Make a POST request to the server with the text input
13
+ const response = await fetch('/upload', {
14
+ method: 'POST', // Use the POST method
15
+ headers: {
16
+ 'Content-Type': 'application/json' // Set the Content-Type header
17
+ },
18
+ body: JSON.stringify({ text: text }) // Convert the text to JSON and send it in the request body
19
+ });
20
+
21
+ // Check if the response is not ok (status code outside the range 200-299)
22
+ if (!response.ok) {
23
+ throw new Error(`HTTP error! status: ${response.status}`); // Throw an error with the status code
24
+ }
25
+
26
+ // Parse the JSON response from the server
27
+ const result = await response.json();
28
+ resultsDiv.innerHTML = `<span>Triggers:</span> ${result.triggers.join(', ')}`; // Display the triggers in the results div
29
+ } catch (error) {
30
+ // Handle any errors that occurred during the request
31
+ console.error('Error analyzing text:', error); // Log the error to the console
32
+ resultsDiv.innerHTML = '<span>Error:</span> Unable to analyze text.'; // Display an error message in the results div
33
+ } finally {
34
+ loadingBar.style.display = 'none'; // Hide the loading bar once the request is complete
35
+ }
36
+ });
templates/index.html ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Ensure proper scaling on mobile devices -->
6
+ <title>TREAT</title> <!-- Page title -->
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> <!-- Link to external stylesheet -->
8
+
9
+ <!-- Favicon and Apple Touch Icons -->
10
+ <link rel="apple-touch-icon" sizes="180x180" href="./static/images/apple-touch-icon.png">
11
+ <link rel="icon" type="image/png" sizes="32x32" href="./static/images/favicon-32x32.png">
12
+ <link rel="icon" type="image/png" sizes="16x16" href="./static/images/favicon-16x16.png">
13
+ <link rel="manifest" href="./static/images/site.webmanifest">
14
+
15
+ </head>
16
+ <body>
17
+ <div class="choco-container"> <!-- Main container with chocolate theme -->
18
+ <header class="header"> <!-- Header section -->
19
+ <h1>TREAT</h1> <!-- Main heading -->
20
+ <p> <!-- Subheading with acronym explanation -->
21
+ <span class="highlight">T</span>rigger
22
+ <span class="highlight">R</span>ecognition for
23
+ <span class="highlight">E</span>njoyable and
24
+ <span class="highlight">A</span>ppropriate
25
+ <span class="highlight">T</span>elevision
26
+ </p>
27
+ </header>
28
+ <main class="main-content"> <!-- Main content section -->
29
+ <form id="text-form" method="POST" class="sweet-form"> <!-- Form for text input -->
30
+ <textarea id="text-input" name="text" rows="20" placeholder="Enter your text or script here..."></textarea> <!-- Textarea for user input -->
31
+ <button type="submit">Analyze Script</button> <!-- Submit button -->
32
+ </form>
33
+ <div id="loading-bar" class="loading-bar"></div> <!-- Loading bar displayed during analysis -->
34
+ <div id="results"></div> <!-- Div to display results -->
35
+ </main>
36
+ </div>
37
+ <script src="{{ url_for('static', filename='js/app.js') }}"></script> <!-- Link to external JavaScript file -->
38
+ </body>
39
+ </html>
utils/utils.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def identify_triggers(outputs):
2
+ # Initialize a set to store identified triggers
3
+ identified_triggers = set()
4
+
5
+ # Iterate over the model outputs
6
+ for response_text in outputs:
7
+ # Check if the response contains a positive indication of the category
8
+ if "yes" in response_text.lower():
9
+ # Extract the category from the response_text
10
+ category = extract_category(response_text)
11
+ if category:
12
+ identified_triggers.add(category)
13
+
14
+ # Convert the set of identified triggers to a list and return it
15
+ return list(identified_triggers)
16
+
17
+ def extract_category(response_text):
18
+ # Define trigger categories
19
+ trigger_categories = [
20
+ "Violence",
21
+ "Self-Harm",
22
+ "Death",
23
+ "Substance Use"
24
+ "Sexual Content"
25
+ "Sexual Abuse",
26
+ "Gun Use",
27
+ "Gore",
28
+ "Vomit",
29
+ "Mental Health Issues"
30
+ "Animal Cruelty"
31
+ ]
32
+
33
+ # Check if any category is present in the response_text
34
+ for category in trigger_categories:
35
+ if category.lower() in response_text.lower():
36
+ return category
37
+ # Return None if no category is found
38
+ return None