Compare commits
No commits in common. "dev" and "5662ec7a81c4400310e29bdf6c6f6bb6d1b7a18d" have entirely different histories.
dev
...
5662ec7a81
8
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
6
.idea/misc.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="MarkdownSettingsMigration">
|
||||||
|
<option name="stateVersion" value="1" />
|
||||||
|
</component>
|
||||||
|
</project>
|
11
README.md
@ -1,11 +0,0 @@
|
|||||||
Cat detection
|
|
||||||
|
|
||||||
Quick guide:
|
|
||||||
1. install docker
|
|
||||||
2. build image with `./scripts/build_image.bat`
|
|
||||||
3. run container with `./scripts/start.bat`
|
|
||||||
|
|
||||||
Quick tests guide:
|
|
||||||
1. run unit tests `./scripts/run_tests.bat`
|
|
||||||
|
|
||||||
Have fun!
|
|
39
basics.ipynb
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 7,
|
||||||
|
"id": "initial_id",
|
||||||
|
"metadata": {
|
||||||
|
"collapsed": true,
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2024-01-04T16:38:33.550511800Z",
|
||||||
|
"start_time": "2024-01-04T16:38:33.542353Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython2",
|
||||||
|
"version": "2.7.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
@ -1,58 +0,0 @@
|
|||||||
from io import BytesIO
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
from PIL import Image
|
|
||||||
from keras.src.applications.resnet import preprocess_input, decode_predictions
|
|
||||||
from keras.applications.resnet import ResNet50
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Recognition file.
|
|
||||||
Model is ResNet50. Pretrained model to image recognition.
|
|
||||||
If model recognize cat then returns response with first ten CAT predictions.
|
|
||||||
If first prediction is not a cat then returns False.
|
|
||||||
If prediction is not a cat (is not within list_of_labels) then skips this prediction.
|
|
||||||
Format of response:
|
|
||||||
{
|
|
||||||
'label': {label}
|
|
||||||
'score': {score}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
model = ResNet50(weights='imagenet')
|
|
||||||
|
|
||||||
|
|
||||||
# PRIVATE Preprocess image method
|
|
||||||
def _preprocess_image(image):
|
|
||||||
try:
|
|
||||||
img = Image.open(BytesIO(image.read()))
|
|
||||||
img = img.resize((224, 224))
|
|
||||||
img_array = np.array(img)
|
|
||||||
img_array = np.expand_dims(img_array, axis=0)
|
|
||||||
img_array = preprocess_input(img_array)
|
|
||||||
return img_array
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error preprocessing image: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
# Generate response
|
|
||||||
def _generate_response(decoded_predictions, list_of_labels):
|
|
||||||
results = {}
|
|
||||||
for i, (imagenet_id, label, score) in enumerate(decoded_predictions):
|
|
||||||
if i == 0 and label not in list_of_labels:
|
|
||||||
return None
|
|
||||||
if score < 0.01:
|
|
||||||
break
|
|
||||||
if label in list_of_labels:
|
|
||||||
results[len(results) + 1] = {"label": label, "score": round(float(score), 2)}
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
# Cat detection
|
|
||||||
def detect_cat(image_file, list_of_labels):
|
|
||||||
img_array = _preprocess_image(image_file)
|
|
||||||
prediction = model.predict(img_array)
|
|
||||||
decoded_predictions = decode_predictions(prediction, top=10)[0]
|
|
||||||
return _generate_response(decoded_predictions, list_of_labels)
|
|
@ -1,6 +0,0 @@
|
|||||||
*.md
|
|
||||||
/venv
|
|
||||||
.git
|
|
||||||
__pycache__
|
|
||||||
.pytest_cache
|
|
||||||
/tests
|
|
@ -1,11 +0,0 @@
|
|||||||
FROM python:3.11
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY . /app
|
|
||||||
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
|
||||||
|
|
||||||
EXPOSE 5000
|
|
||||||
|
|
||||||
CMD ["python", "main.py"]
|
|
@ -1,10 +0,0 @@
|
|||||||
version: '3.3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
cat-detection:
|
|
||||||
image: cat-detection
|
|
||||||
build:
|
|
||||||
context: ../
|
|
||||||
dockerfile: ./docker/Dockerfile
|
|
||||||
ports:
|
|
||||||
- "5000:5000"
|
|
@ -1,6 +0,0 @@
|
|||||||
#inzynieriaOprogramowania #semestr3
|
|
||||||
|
|
||||||
# Faza 1 - ogólna architektura projektu
|
|
||||||
Aplikacja oparta na micro-frameworku Flask, w której użytkownik przesyła zdjęcie na serwer i w odpowiedzi dostaje na ile % na zdjęciu znajduje się kot (poszczególne przedziały będą miały inną informację typu 100%-95%: "to *prawie* na pewno jest kot" itd.).
|
|
||||||
|
|
||||||
Wyniki oceny będą przechowywane w sesji - będą zapisane do momentu wyłączenia przeglądarki, przez co nie ma potrzeby stawiania bazy danych.
|
|
53
docs/docs.md
@ -1,53 +0,0 @@
|
|||||||
# Api
|
|
||||||
|
|
||||||
Port -> 5000
|
|
||||||
|
|
||||||
endpoint -> api/v1/detect-cat
|
|
||||||
|
|
||||||
Key -> 'Image'
|
|
||||||
|
|
||||||
Value -> {UPLOADED_FILE}
|
|
||||||
|
|
||||||
Flask Rest API application to cat recognition.
|
|
||||||
If request is valid then send response with results of recognition.
|
|
||||||
If key named 'Image' in body does not occur then returns 400 (BAD REQUEST).
|
|
||||||
Otherwise, returns 200 with results of recognition.
|
|
||||||
Format of response:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"lang": "{users_lang}",
|
|
||||||
"results": {
|
|
||||||
"{filename}": {
|
|
||||||
"isCat": "{is_cat}",
|
|
||||||
"results": {
|
|
||||||
"1": "{result}",
|
|
||||||
"2": "{result}",
|
|
||||||
"3": "{result}",
|
|
||||||
"4": "{result}",
|
|
||||||
"5": "{result}",
|
|
||||||
"6": "{result}",
|
|
||||||
"7": "{result}",
|
|
||||||
"8": "{result}",
|
|
||||||
"9": "{result}",
|
|
||||||
"10": "{result}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
"{error_message}",
|
|
||||||
"{error_message}"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Format of result:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"label": "{label}",
|
|
||||||
"score": "{score}"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Example response:
|
|
||||||
```json
|
|
||||||
|
|
||||||
```
|
|
@ -1,30 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from jproperties import Properties
|
|
||||||
|
|
||||||
"""
|
|
||||||
Translator method.
|
|
||||||
If everything fine then returns translated labels.
|
|
||||||
Else throws an Exception and returns untranslated labels.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def translate(to_translate, lang):
|
|
||||||
try:
|
|
||||||
config = Properties()
|
|
||||||
script_directory = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
resources_path = os.path.join(script_directory, "./resources")
|
|
||||||
|
|
||||||
# Load properties file for given lang
|
|
||||||
with open(os.path.join(resources_path, f"./{lang}.properties"), 'rb') as config_file:
|
|
||||||
config.load(config_file, encoding='UTF-8')
|
|
||||||
|
|
||||||
# Translate labels for given to_translate dictionary
|
|
||||||
for index, label_info in to_translate.items():
|
|
||||||
label = label_info.get("label")
|
|
||||||
to_translate[index]["label"] = config.get(label).data
|
|
||||||
return to_translate, []
|
|
||||||
except Exception as e:
|
|
||||||
error_message = f"Error translating labels: {e}"
|
|
||||||
print(error_message)
|
|
||||||
return to_translate, error_message
|
|
163
main.py
@ -1,108 +1,59 @@
|
|||||||
from flask import Flask, request, Response, json
|
from PIL import Image
|
||||||
from cat_detection import detect_cat
|
import torch
|
||||||
from language_label_mapper import translate
|
import torch.nn.functional as F
|
||||||
from validator import validate
|
from torchvision.models.resnet import resnet50, ResNet50_Weights
|
||||||
from flask_cors import CORS
|
from torchvision.transforms import transforms
|
||||||
|
|
||||||
"""
|
# Load the pre-trained model
|
||||||
Flask Rest API application to cat recognition.
|
model = resnet50(weights=ResNet50_Weights.DEFAULT)
|
||||||
If request is valid then send response with results of recognition.
|
|
||||||
If key named 'Image' in body does not occurred then returns 400 (BAD REQUEST).
|
model.eval()
|
||||||
Otherwise returns 200 with results of recognition.
|
|
||||||
Format of response:
|
# Define the image transformations
|
||||||
{
|
preprocess = transforms.Compose([
|
||||||
"lang": {users_lang},
|
transforms.Resize(256),
|
||||||
"results": {
|
transforms.CenterCrop(224),
|
||||||
{filename}: {
|
transforms.ToTensor(),
|
||||||
"isCat": {is_cat},
|
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||||
"results": {
|
])
|
||||||
"1": {result}
|
|
||||||
"2": {result}
|
|
||||||
"3": {result}
|
def is_cat(image_path):
|
||||||
...
|
# Open the image
|
||||||
"10" {result}
|
img = Image.open(image_path)
|
||||||
|
|
||||||
|
# Preprocess the image
|
||||||
|
img_t = preprocess(img)
|
||||||
|
batch_t = torch.unsqueeze(img_t, 0)
|
||||||
|
|
||||||
|
# Make the prediction
|
||||||
|
out = model(batch_t)
|
||||||
|
|
||||||
|
# Apply softmax to get probabilities
|
||||||
|
probabilities = F.softmax(out, dim=1)
|
||||||
|
|
||||||
|
# Get the maximum predicted class and its probability
|
||||||
|
max_prob, max_class = torch.max(probabilities, dim=1)
|
||||||
|
max_prob = max_prob.item()
|
||||||
|
max_class = max_class.item()
|
||||||
|
|
||||||
|
# Check if the maximum predicted class is within the range 281-285
|
||||||
|
if 281 <= max_class <= 285:
|
||||||
|
return max_class, max_prob
|
||||||
|
else:
|
||||||
|
return max_class, None
|
||||||
|
|
||||||
|
|
||||||
|
image_path = 'wolf.jpg'
|
||||||
|
max_class, max_prob = is_cat(image_path)
|
||||||
|
translator = {
|
||||||
|
281: "tabby cat",
|
||||||
|
282: "tiger cat",
|
||||||
|
283: "persian cat",
|
||||||
|
284: "siamese cat",
|
||||||
|
285: "egyptian cat"
|
||||||
}
|
}
|
||||||
},
|
if max_prob is not None:
|
||||||
...
|
print(f"The image is recognized as '{translator[max_class]}' with a probability of {round(max_prob * 100, 2)}%")
|
||||||
},
|
else:
|
||||||
errors[
|
print(f"The image is not recognized as a class within the range 281-285 ({max_class})")
|
||||||
{error_message},
|
|
||||||
{error_message},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
}
|
|
||||||
To see result format -> cat_detection.py
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
# Define flask app
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.secret_key = 'secret_key'
|
|
||||||
CORS(app)
|
|
||||||
|
|
||||||
# Available cats
|
|
||||||
list_of_labels = [
|
|
||||||
'lynx',
|
|
||||||
'lion',
|
|
||||||
'tiger',
|
|
||||||
'cheetah',
|
|
||||||
'leopard',
|
|
||||||
'jaguar',
|
|
||||||
'tabby',
|
|
||||||
'Egyptian_cat',
|
|
||||||
'cougar',
|
|
||||||
'Persian_cat',
|
|
||||||
'Siamese_cat',
|
|
||||||
'snow_leopard',
|
|
||||||
'tiger_cat'
|
|
||||||
]
|
|
||||||
|
|
||||||
# Available languages
|
|
||||||
languages = {'pl', 'en'}
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/v1/detect-cat', methods=['POST'])
|
|
||||||
def upload_file():
|
|
||||||
# Validate request
|
|
||||||
error_messages = validate(request)
|
|
||||||
|
|
||||||
# If any errors occurred, return 400 (BAD REQUEST)
|
|
||||||
if len(error_messages) > 0:
|
|
||||||
errors = json.dumps(
|
|
||||||
{
|
|
||||||
'errors': error_messages
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return Response(errors, status=400, mimetype='application/json')
|
|
||||||
|
|
||||||
# Get files from request
|
|
||||||
files = request.files.getlist('image')
|
|
||||||
|
|
||||||
# Get user's language (Value in header 'Accept-Language'). Default value is English
|
|
||||||
lang = request.accept_languages.best_match(languages, default='en')
|
|
||||||
|
|
||||||
# Define JSON structure for results
|
|
||||||
results = {
|
|
||||||
'lang': lang,
|
|
||||||
'results': {},
|
|
||||||
'errors': []
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate results
|
|
||||||
for file in files:
|
|
||||||
predictions = detect_cat(file, list_of_labels)
|
|
||||||
if predictions is not None:
|
|
||||||
predictions, error_messages = translate(predictions, lang)
|
|
||||||
results['results'][file.filename] = {
|
|
||||||
'isCat': False if not predictions else True,
|
|
||||||
**({'predictions': predictions} if predictions is not None else {})
|
|
||||||
}
|
|
||||||
if len(error_messages) > 1:
|
|
||||||
results['errors'].append(error_messages)
|
|
||||||
|
|
||||||
# Send response with 200 (Success)
|
|
||||||
return Response(json.dumps(results), status=200, mimetype='application/json')
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app.run(host='0.0.0.0')
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
flask==3.0.0
|
|
||||||
numpy==1.26.3
|
|
||||||
pillow==10.2.0
|
|
||||||
keras==2.15.0
|
|
||||||
jproperties==2.1.1
|
|
||||||
tensorflow==2.15.0
|
|
||||||
werkzeug==3.0.1
|
|
||||||
pytest==7.4.4
|
|
||||||
flask-cors==4.0.0
|
|
@ -1,14 +0,0 @@
|
|||||||
# EN
|
|
||||||
lynx=lynx
|
|
||||||
lion=lion
|
|
||||||
tiger=tiger
|
|
||||||
cheetah=cheetah
|
|
||||||
leopard=leopard
|
|
||||||
jaguar=jaguar
|
|
||||||
tabby=tabby
|
|
||||||
Egyptian_cat=Egyptian cat
|
|
||||||
cougar=cougar
|
|
||||||
Persian_cat=Persian cat
|
|
||||||
Siamese_cat=Siamese cat
|
|
||||||
snow_leopard=snow leopard
|
|
||||||
tiger_cat=tiger cat
|
|
@ -1,14 +0,0 @@
|
|||||||
# PL
|
|
||||||
lynx=ryś
|
|
||||||
lion=lew
|
|
||||||
tiger=tygrys
|
|
||||||
cheetah=gepard
|
|
||||||
leopard=lampart
|
|
||||||
jaguar=jaguar
|
|
||||||
tabby=kot pręgowany
|
|
||||||
Egyptian_cat=kot egipski
|
|
||||||
cougar=puma
|
|
||||||
Persian_cat=kot perski
|
|
||||||
Siamese_cat=kot syjamski
|
|
||||||
snow_leopard=lampart śnieżny
|
|
||||||
tiger_cat=kot tygrysi
|
|
@ -1,24 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001 >nul
|
|
||||||
|
|
||||||
docker -v >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo [31mDocker is not installed.[0m
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
docker info >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo [31mDocker engine is not running.[0m
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
cd %~dp0
|
|
||||||
echo [32mBuilding docker image...[0m
|
|
||||||
docker-compose -f ../docker/docker-compose.yml build
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo [31mBuilding docker image failed.[0m
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [32mThe image was built successfully.[0m
|
|
@ -1,27 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export LC_ALL=C.UTF-8
|
|
||||||
export LANG=C.UTF-8
|
|
||||||
|
|
||||||
docker -v > /dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo -e "\033[31mDocker is not installed.\033[0m"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker info > /dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo -e "\033[31mDocker engine is not running.\033[0m"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd ../docker
|
|
||||||
echo -e "\033[32mBuilding docker image...\033[0m"
|
|
||||||
docker-compose build cat-detection
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo -e "\033[31mBuilding docker image failed.\033[0m"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "\033[32mThe image was built successfully.\033[0m"
|
|
@ -1,12 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo [32mRunning unit tests.[0m
|
|
||||||
|
|
||||||
cd %~dp0
|
|
||||||
pytest ../tests
|
|
||||||
|
|
||||||
if %ERRORLEVEL% equ 0 (
|
|
||||||
echo [32mTests passed successfully.[0m
|
|
||||||
) else (
|
|
||||||
Tests failed.
|
|
||||||
echo [31mTests failed.[0m
|
|
||||||
)
|
|
@ -1,9 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001 >nul
|
|
||||||
|
|
||||||
cd %~dp0
|
|
||||||
docker compose -f ../docker/docker-compose.yml up cat-detection -d
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo [31mStarting docker container failed.[0m
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
@ -1,11 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export LC_ALL=C.UTF-8
|
|
||||||
export LANG=C.UTF-8
|
|
||||||
|
|
||||||
docker-compose -f ../docker/docker-compose.yml up cat-detection -d
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo -e "\033[31mStarting docker container failed.\033[0m"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
Before Width: | Height: | Size: 360 KiB |
Before Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 159 KiB |
Before Width: | Height: | Size: 180 KiB |
Before Width: | Height: | Size: 216 KiB |
Before Width: | Height: | Size: 326 KiB |
Before Width: | Height: | Size: 221 KiB |
Before Width: | Height: | Size: 281 KiB |
Before Width: | Height: | Size: 469 KiB |
Before Width: | Height: | Size: 255 KiB |
Before Width: | Height: | Size: 2.7 MiB |
Before Width: | Height: | Size: 345 KiB |
@ -1,19 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from werkzeug.datastructures import FileStorage
|
|
||||||
|
|
||||||
from main import app
|
|
||||||
|
|
||||||
|
|
||||||
def test_upload_file():
|
|
||||||
with app.test_client() as test_client:
|
|
||||||
script_directory = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
image_path = os.path.join(script_directory, "./img/tiger_cat/cat1.jpg")
|
|
||||||
image = FileStorage(
|
|
||||||
stream=open(image_path, "rb"),
|
|
||||||
filename="cat1.jpg",
|
|
||||||
content_type="image/jpeg",
|
|
||||||
)
|
|
||||||
|
|
||||||
response = test_client.post('/api/v1/detect-cat', data={'image': image}, content_type='multipart/form-data')
|
|
||||||
assert response.status_code == 200
|
|
31
validator.py
@ -1,31 +0,0 @@
|
|||||||
"""
|
|
||||||
Validation method.
|
|
||||||
If everything fine then returns empty list.
|
|
||||||
Else returns list of error messages.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Allowed extensions
|
|
||||||
allowed_extensions = {'jpg', 'jpeg', 'png'}
|
|
||||||
|
|
||||||
|
|
||||||
def validate(request):
|
|
||||||
errors = []
|
|
||||||
try:
|
|
||||||
images = request.files.getlist('image')
|
|
||||||
|
|
||||||
# Case 1 - > request has no 'Image' Key in body
|
|
||||||
if images is None:
|
|
||||||
raise KeyError("'Image' key not found in request.")
|
|
||||||
|
|
||||||
# Case 2 - > if some of the images has no filename
|
|
||||||
if not images or all(img.filename == '' for img in images):
|
|
||||||
raise ValueError("Value of 'Image' key is empty.")
|
|
||||||
|
|
||||||
# Case 3 -> if some of the images has wrong extension
|
|
||||||
for img in images:
|
|
||||||
if not img.filename.lower().endswith(('.png', '.jpg', '.jpeg')):
|
|
||||||
raise ValueError(f"Given file '{img.filename}' has no allowed extension. "
|
|
||||||
f"Allowed extensions: {allowed_extensions}.")
|
|
||||||
except Exception as e:
|
|
||||||
errors.append(e.args[0])
|
|
||||||
return errors
|
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |