mirror of
				https://github.com/ivanch/tcc.git
				synced 2025-11-04 03:07:36 +00:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
			FlaskAPI
			...
			05dad7ade5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 05dad7ade5 | |||
| cd1e9da710 | |||
| 1dbd8c238f | 
							
								
								
									
										10
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +1,14 @@
 | 
			
		||||
.vs
 | 
			
		||||
__pycache__
 | 
			
		||||
bin
 | 
			
		||||
obj
 | 
			
		||||
*.vscode
 | 
			
		||||
__pycache__
 | 
			
		||||
.idea
 | 
			
		||||
 | 
			
		||||
*/target
 | 
			
		||||
 | 
			
		||||
*.png
 | 
			
		||||
*.csv
 | 
			
		||||
*.jpg
 | 
			
		||||
*.mp4
 | 
			
		||||
							
								
								
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,8 +0,0 @@
 | 
			
		||||
# Default ignored files
 | 
			
		||||
/shelf/
 | 
			
		||||
/workspace.xml
 | 
			
		||||
# Editor-based HTTP Client requests
 | 
			
		||||
/httpRequests/
 | 
			
		||||
# Datasource local storage ignored files
 | 
			
		||||
/dataSources/
 | 
			
		||||
/dataSources.local.xml
 | 
			
		||||
							
								
								
									
										6
									
								
								.idea/inspectionProfiles/profiles_settings.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								.idea/inspectionProfiles/profiles_settings.xml
									
									
									
										generated
									
									
									
								
							@@ -1,6 +0,0 @@
 | 
			
		||||
<component name="InspectionProjectProfileManager">
 | 
			
		||||
  <settings>
 | 
			
		||||
    <option name="USE_PROJECT_PROFILE" value="false" />
 | 
			
		||||
    <version value="1.0" />
 | 
			
		||||
  </settings>
 | 
			
		||||
</component>
 | 
			
		||||
							
								
								
									
										4
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							@@ -1,4 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
								
							@@ -1,8 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="ProjectModuleManager">
 | 
			
		||||
    <modules>
 | 
			
		||||
      <module fileurl="file://$PROJECT_DIR$/.idea/tcc.iml" filepath="$PROJECT_DIR$/.idea/tcc.iml" />
 | 
			
		||||
    </modules>
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										8
									
								
								.idea/tcc.iml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								.idea/tcc.iml
									
									
									
										generated
									
									
									
								
							@@ -1,8 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<module type="PYTHON_MODULE" version="4">
 | 
			
		||||
  <component name="NewModuleRootManager">
 | 
			
		||||
    <content url="file://$MODULE_DIR$" />
 | 
			
		||||
    <orderEntry type="inheritedJdk" />
 | 
			
		||||
    <orderEntry type="sourceFolder" forTests="false" />
 | 
			
		||||
  </component>
 | 
			
		||||
</module>
 | 
			
		||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
								
							@@ -1,6 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="VcsDirectoryMappings">
 | 
			
		||||
    <mapping directory="" vcs="Git" />
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
@@ -23,6 +23,7 @@ namespace TCC.Controllers
 | 
			
		||||
            mstream.Position = 0;
 | 
			
		||||
 | 
			
		||||
            var result = ImageService.BoxBlurImage(mstream, radius);
 | 
			
		||||
            mstream.Close();
 | 
			
		||||
 | 
			
		||||
            var blurredImageStream = new MemoryStream();
 | 
			
		||||
            result.Write(blurredImageStream);
 | 
			
		||||
@@ -31,28 +32,16 @@ namespace TCC.Controllers
 | 
			
		||||
            return File(blurredImageStream, "image/png");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpGet("load-image")]
 | 
			
		||||
        [HttpGet("load-small-image")]
 | 
			
		||||
        public async Task<IActionResult> GetSimpleImage()
 | 
			
		||||
        {
 | 
			
		||||
            var result = ImageService.GetSimpleImage();
 | 
			
		||||
 | 
			
		||||
            var imageStream = new MemoryStream();
 | 
			
		||||
            result.Write(imageStream);
 | 
			
		||||
            imageStream.Position = 0;
 | 
			
		||||
 | 
			
		||||
            return File(imageStream, "image/png");
 | 
			
		||||
            return File(ImageService.GetSimpleImage(), "image/png");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpGet("load-big-image")]
 | 
			
		||||
        public async Task<IActionResult> GetBigImage()
 | 
			
		||||
        {
 | 
			
		||||
            var result = ImageService.GetBigImage();
 | 
			
		||||
 | 
			
		||||
            var imageStream = new MemoryStream();
 | 
			
		||||
            result.Write(imageStream);
 | 
			
		||||
            imageStream.Position = 0;
 | 
			
		||||
 | 
			
		||||
            return File(imageStream, "image/png");
 | 
			
		||||
            return File(ImageService.GetBigImage(), "image/png");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpPost("save-big-image")]
 | 
			
		||||
@@ -61,6 +50,7 @@ namespace TCC.Controllers
 | 
			
		||||
            MemoryStream mstream = new MemoryStream();
 | 
			
		||||
            await HttpContext.Request.Body.CopyToAsync(mstream);
 | 
			
		||||
            mstream.Position = 0;
 | 
			
		||||
            mstream.Close();
 | 
			
		||||
 | 
			
		||||
            return Ok();
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim AS build-env
 | 
			
		||||
WORKDIR /App
 | 
			
		||||
 | 
			
		||||
# Copy everything
 | 
			
		||||
RUN apt update && apt install wget -y
 | 
			
		||||
COPY * .
 | 
			
		||||
 | 
			
		||||
# Restore as distinct layers
 | 
			
		||||
@@ -10,15 +11,21 @@ RUN dotnet restore
 | 
			
		||||
# Build a release
 | 
			
		||||
RUN dotnet build -c Release -o out
 | 
			
		||||
 | 
			
		||||
RUN cd out && \
 | 
			
		||||
    wget https://files.ivanch.me/api/public/dl/iFuXSNhw/small-image.png && \
 | 
			
		||||
    wget https://files.ivanch.me/api/public/dl/81Bkht5C/big-image.png && \
 | 
			
		||||
    wget https://files.ivanch.me/api/public/dl/nAndfAjK/video.mp4 && \
 | 
			
		||||
    rm -rf runtimes && \
 | 
			
		||||
    mkdir -p ./static && \
 | 
			
		||||
    mv small-image.png ./static && \
 | 
			
		||||
    mv big-image.png ./static && \
 | 
			
		||||
    mv video.mp4 ./static
 | 
			
		||||
 | 
			
		||||
# Build runtime image
 | 
			
		||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0-bullseye-slim
 | 
			
		||||
 | 
			
		||||
WORKDIR /App
 | 
			
		||||
 | 
			
		||||
RUN wget https://files.ivanch.me/api/public/dl/QFCLgtrG/simpleimage.png && \
 | 
			
		||||
    wget https://files.ivanch.me/api/public/dl/E0VLgWbx/bigimage.png && \
 | 
			
		||||
    rm -rf runtimes
 | 
			
		||||
 | 
			
		||||
COPY --from=build-env /App/out .
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["dotnet", "/App/TCC.APP.dll"]
 | 
			
		||||
@@ -4,13 +4,13 @@ namespace tcc_app
 | 
			
		||||
{
 | 
			
		||||
    public static class ImageHelper
 | 
			
		||||
    {
 | 
			
		||||
        public static MagickImage SimpleImage;
 | 
			
		||||
        public static MagickImage BigImage;
 | 
			
		||||
        public static byte[] SimpleImage;
 | 
			
		||||
        public static byte[] BigImage;
 | 
			
		||||
 | 
			
		||||
        static ImageHelper()
 | 
			
		||||
        {
 | 
			
		||||
            SimpleImage = new MagickImage("simpleimage.png");
 | 
			
		||||
            BigImage = new MagickImage("bigimage.png");
 | 
			
		||||
            SimpleImage = File.ReadAllBytes("static/small-image.png");
 | 
			
		||||
            BigImage = File.ReadAllBytes("static/big-image.png");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
 | 
			
		||||
using Microsoft.Extensions.FileProviders;
 | 
			
		||||
using TCC.Services;
 | 
			
		||||
 | 
			
		||||
namespace TCC
 | 
			
		||||
@@ -18,9 +19,13 @@ namespace TCC
 | 
			
		||||
                options.Limits.MaxRequestBodySize = int.MaxValue; // if don't set default value is: 30 MB
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            var app = builder.Build();
 | 
			
		||||
 | 
			
		||||
            app.UseStaticFiles(new StaticFileOptions
 | 
			
		||||
            {
 | 
			
		||||
                FileProvider = new PhysicalFileProvider(Path.Combine(builder.Environment.ContentRootPath, "static")),
 | 
			
		||||
                RequestPath = "/static"
 | 
			
		||||
            });
 | 
			
		||||
            app.MapControllers();
 | 
			
		||||
 | 
			
		||||
            app.Run();
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
      "dotnetRunMessages": true,
 | 
			
		||||
      "launchBrowser": false,
 | 
			
		||||
      "launchUrl": "weatherforecast",
 | 
			
		||||
      "applicationUrl": "http://0.0.0.0:5100",
 | 
			
		||||
      "applicationUrl": "http://0.0.0.0:9090",
 | 
			
		||||
      "environmentVariables": {
 | 
			
		||||
        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -62,14 +62,16 @@ namespace TCC.Services
 | 
			
		||||
            file.Close();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public MagickImage GetSimpleImage()
 | 
			
		||||
        public byte[] GetSimpleImage()
 | 
			
		||||
        {
 | 
			
		||||
            return ImageHelper.SimpleImage;
 | 
			
		||||
            return File.ReadAllBytes("static/small-image.png");
 | 
			
		||||
            //return ImageHelper.SimpleImage;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public MagickImage GetBigImage()
 | 
			
		||||
        public byte[] GetBigImage()
 | 
			
		||||
        {
 | 
			
		||||
            return ImageHelper.BigImage;
 | 
			
		||||
            return File.ReadAllBytes("static/big-image.png");
 | 
			
		||||
            //return ImageHelper.BigImage;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								ASP.NET/static/nginx.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								ASP.NET/static/nginx.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <title>Welcome to nginx!</title>
 | 
			
		||||
    <style>
 | 
			
		||||
        html {
 | 
			
		||||
            color-scheme: light dark;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        body {
 | 
			
		||||
            width: 35em;
 | 
			
		||||
            margin: 0 auto;
 | 
			
		||||
            font-family: Tahoma, Verdana, Arial, sans-serif;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <h1>Welcome to nginx!</h1>
 | 
			
		||||
    <p>
 | 
			
		||||
        If you see this page, the nginx web server is successfully installed and
 | 
			
		||||
        working. Further configuration is required.
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <p>
 | 
			
		||||
        For online documentation and support please refer to
 | 
			
		||||
        <a href="http://nginx.org/">nginx.org</a>.<br />
 | 
			
		||||
        Commercial support is available at
 | 
			
		||||
        <a href="http://nginx.com/">nginx.com</a>.
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <p><em>Thank you for using nginx.</em></p>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										1266
									
								
								ActixAPI/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1266
									
								
								ActixAPI/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								ActixAPI/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								ActixAPI/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "ActixAPI"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
actix-web = "4"
 | 
			
		||||
actix-files = "0.6.2"
 | 
			
		||||
							
								
								
									
										25
									
								
								ActixAPI/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								ActixAPI/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
FROM rust:slim-bullseye AS build-env
 | 
			
		||||
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
RUN apt update && apt install wget -y && \
 | 
			
		||||
    wget https://files.ivanch.me/api/public/dl/Dj0gkp-m/small-image.png && \
 | 
			
		||||
    wget https://files.ivanch.me/api/public/dl/FqHEPM1Q/big-image.png && \
 | 
			
		||||
    wget https://files.ivanch.me/api/public/dl/nTAYqZwD/video.mp4 && \
 | 
			
		||||
    mv small-image.png ./static && \
 | 
			
		||||
    mv big-image.png ./static && \
 | 
			
		||||
    mv video.mp4 ./static
 | 
			
		||||
 | 
			
		||||
RUN cargo build --release
 | 
			
		||||
 | 
			
		||||
FROM debian:bullseye-slim
 | 
			
		||||
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
 | 
			
		||||
COPY --from=build-env /app/target/release .
 | 
			
		||||
 | 
			
		||||
COPY --from=build-env /app/static .
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["./ActixAPI"]
 | 
			
		||||
							
								
								
									
										40
									
								
								ActixAPI/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								ActixAPI/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder, HttpRequest, Result};
 | 
			
		||||
use actix_files::NamedFile;
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
 | 
			
		||||
#[get("/status/ok")]
 | 
			
		||||
async fn hello() -> impl Responder {
 | 
			
		||||
    HttpResponse::Ok().body("{\"status\": 200}")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[post("/echo")]
 | 
			
		||||
async fn echo(req_body: String) -> impl Responder {
 | 
			
		||||
    HttpResponse::Ok().body(req_body)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn manual_hello() -> impl Responder {
 | 
			
		||||
    HttpResponse::Ok().body("Hey there!")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn static_serve(req: HttpRequest) -> Result<NamedFile> {
 | 
			
		||||
    let path: &str = req.path();
 | 
			
		||||
    let real_path = &path[1..];
 | 
			
		||||
 | 
			
		||||
    Ok(NamedFile::open(real_path)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[actix_web::main]
 | 
			
		||||
async fn main() -> std::io::Result<()> {
 | 
			
		||||
    println!("Hello, world!");
 | 
			
		||||
 | 
			
		||||
    HttpServer::new(|| {
 | 
			
		||||
        App::new()
 | 
			
		||||
            .route("/static/{filename:.*}", web::get().to(static_serve))
 | 
			
		||||
            .service(hello)
 | 
			
		||||
            .service(echo)
 | 
			
		||||
            .route("/hey", web::get().to(manual_hello))
 | 
			
		||||
    })
 | 
			
		||||
    .bind(("0.0.0.0", 9090))?
 | 
			
		||||
    .run()
 | 
			
		||||
    .await
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								ActixAPI/static/nginx.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								ActixAPI/static/nginx.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <title>Welcome to nginx!</title>
 | 
			
		||||
    <style>
 | 
			
		||||
        html {
 | 
			
		||||
            color-scheme: light dark;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        body {
 | 
			
		||||
            width: 35em;
 | 
			
		||||
            margin: 0 auto;
 | 
			
		||||
            font-family: Tahoma, Verdana, Arial, sans-serif;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <h1>Welcome to nginx!</h1>
 | 
			
		||||
    <p>
 | 
			
		||||
        If you see this page, the nginx web server is successfully installed and
 | 
			
		||||
        working. Further configuration is required.
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <p>
 | 
			
		||||
        For online documentation and support please refer to
 | 
			
		||||
        <a href="http://nginx.org/">nginx.org</a>.<br />
 | 
			
		||||
        Commercial support is available at
 | 
			
		||||
        <a href="http://nginx.com/">nginx.com</a>.
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <p><em>Thank you for using nginx.</em></p>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
# Include any files or directories that you don't want to be copied to your
 | 
			
		||||
# container here (e.g., local build artifacts, temporary files, etc.).
 | 
			
		||||
#
 | 
			
		||||
# For more help, visit the .dockerignore file reference guide at
 | 
			
		||||
# https://docs.docker.com/engine/reference/builder/#dockerignore-file
 | 
			
		||||
 | 
			
		||||
**/.DS_Store
 | 
			
		||||
**/__pycache__
 | 
			
		||||
**/.venv
 | 
			
		||||
**/.classpath
 | 
			
		||||
**/.dockerignore
 | 
			
		||||
**/.env
 | 
			
		||||
**/.git
 | 
			
		||||
**/.gitignore
 | 
			
		||||
**/.project
 | 
			
		||||
**/.settings
 | 
			
		||||
**/.toolstarget
 | 
			
		||||
**/.vs
 | 
			
		||||
**/.vscode
 | 
			
		||||
**/*.*proj.user
 | 
			
		||||
**/*.dbmdl
 | 
			
		||||
**/*.jfm
 | 
			
		||||
**/bin
 | 
			
		||||
**/charts
 | 
			
		||||
**/docker-compose*
 | 
			
		||||
**/compose*
 | 
			
		||||
**/Dockerfile*
 | 
			
		||||
**/node_modules
 | 
			
		||||
**/npm-debug.log
 | 
			
		||||
**/obj
 | 
			
		||||
**/secrets.dev.yaml
 | 
			
		||||
**/values.dev.yaml
 | 
			
		||||
LICENSE
 | 
			
		||||
README.md
 | 
			
		||||
							
								
								
									
										8
									
								
								FlaskAPI/.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								FlaskAPI/.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,8 +0,0 @@
 | 
			
		||||
# Default ignored files
 | 
			
		||||
/shelf/
 | 
			
		||||
/workspace.xml
 | 
			
		||||
# Editor-based HTTP Client requests
 | 
			
		||||
/httpRequests/
 | 
			
		||||
# Datasource local storage ignored files
 | 
			
		||||
/dataSources/
 | 
			
		||||
/dataSources.local.xml
 | 
			
		||||
							
								
								
									
										19
									
								
								FlaskAPI/.idea/FlaskAPI.iml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										19
									
								
								FlaskAPI/.idea/FlaskAPI.iml
									
									
									
										generated
									
									
									
								
							@@ -1,19 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<module type="PYTHON_MODULE" version="4">
 | 
			
		||||
  <component name="Flask">
 | 
			
		||||
    <option name="enabled" value="true" />
 | 
			
		||||
  </component>
 | 
			
		||||
  <component name="NewModuleRootManager">
 | 
			
		||||
    <content url="file://$MODULE_DIR$" />
 | 
			
		||||
    <orderEntry type="inheritedJdk" />
 | 
			
		||||
    <orderEntry type="sourceFolder" forTests="false" />
 | 
			
		||||
  </component>
 | 
			
		||||
  <component name="TemplatesService">
 | 
			
		||||
    <option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
 | 
			
		||||
    <option name="TEMPLATE_FOLDERS">
 | 
			
		||||
      <list>
 | 
			
		||||
        <option value="$MODULE_DIR$/../FlaskAPI\templates" />
 | 
			
		||||
      </list>
 | 
			
		||||
    </option>
 | 
			
		||||
  </component>
 | 
			
		||||
</module>
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
<component name="InspectionProjectProfileManager">
 | 
			
		||||
  <settings>
 | 
			
		||||
    <option name="USE_PROJECT_PROFILE" value="false" />
 | 
			
		||||
    <version value="1.0" />
 | 
			
		||||
  </settings>
 | 
			
		||||
</component>
 | 
			
		||||
							
								
								
									
										4
									
								
								FlaskAPI/.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								FlaskAPI/.idea/misc.xml
									
									
									
										generated
									
									
									
								
							@@ -1,4 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (FlaskAPI)" project-jdk-type="Python SDK" />
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										8
									
								
								FlaskAPI/.idea/modules.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								FlaskAPI/.idea/modules.xml
									
									
									
										generated
									
									
									
								
							@@ -1,8 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="ProjectModuleManager">
 | 
			
		||||
    <modules>
 | 
			
		||||
      <module fileurl="file://$PROJECT_DIR$/.idea/FlaskAPI.iml" filepath="$PROJECT_DIR$/.idea/FlaskAPI.iml" />
 | 
			
		||||
    </modules>
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										17
									
								
								FlaskAPI/.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								FlaskAPI/.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							@@ -1,17 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "version": "0.0.1",
 | 
			
		||||
  "configurations": [
 | 
			
		||||
    
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Python: Flask",
 | 
			
		||||
      "type": "python",
 | 
			
		||||
      "request": "launch",
 | 
			
		||||
      "module": "flask",
 | 
			
		||||
      "env": { "FLASK_APP": "app.py", "FLASK_DEBUG": "1" },
 | 
			
		||||
      "args": ["run", "--no-debugger", "--no-reload"],
 | 
			
		||||
      "jinja": true,
 | 
			
		||||
      "justMyCode": true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								FlaskAPI/.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										0
									
								
								FlaskAPI/.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@@ -1,45 +0,0 @@
 | 
			
		||||
# syntax=docker/dockerfile:1
 | 
			
		||||
 | 
			
		||||
# Comments are provided throughout this file to help you get started.
 | 
			
		||||
# If you need more help, visit the Dockerfile reference guide at
 | 
			
		||||
# https://docs.docker.com/engine/reference/builder/
 | 
			
		||||
 | 
			
		||||
ARG PYTHON_VERSION=3.10.12
 | 
			
		||||
FROM python:${PYTHON_VERSION}-slim as base
 | 
			
		||||
 | 
			
		||||
# Prevents Python from writing pyc files.
 | 
			
		||||
ENV PYTHONDONTWRITEBYTECODE=1
 | 
			
		||||
 | 
			
		||||
# Keeps Python from buffering stdout and stderr to avoid situations where
 | 
			
		||||
# the application crashes without emitting any logs due to buffering.
 | 
			
		||||
ENV PYTHONUNBUFFERED=1
 | 
			
		||||
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
 | 
			
		||||
# Copy the source code into the container.
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
RUN apt-get update && apt-get install -y imagemagick && apt-get install -y wget && ls
 | 
			
		||||
 | 
			
		||||
RUN wget https://files.ivanch.me/api/public/dl/iFuXSNhw/small-image.png && \
 | 
			
		||||
    wget https://files.ivanch.me/api/public/dl/81Bkht5C/big-image.png && \
 | 
			
		||||
    wget https://files.ivanch.me/api/public/dl/nAndfAjK/video.mp4 && \
 | 
			
		||||
    rm -rf runtimes && \
 | 
			
		||||
    mkdir -p ./static && \
 | 
			
		||||
    mv small-image.png ./static && \
 | 
			
		||||
    mv big-image.png ./static && \
 | 
			
		||||
    mv video.mp4 ./static
 | 
			
		||||
 | 
			
		||||
# Download dependencies as a separate step to take advantage of Docker's caching.
 | 
			
		||||
# Leverage a cache mount to /root/.cache/pip to speed up subsequent builds.
 | 
			
		||||
# Leverage a bind mount to requirements.txt to avoid having to copy them into
 | 
			
		||||
# into this layer.
 | 
			
		||||
RUN --mount=type=cache,target=/root/.cache/pip \
 | 
			
		||||
    --mount=type=bind,source=requirements.txt,target=requirements.txt \
 | 
			
		||||
    python -m pip install -r requirements.txt
 | 
			
		||||
 | 
			
		||||
# Expose the port that the application listens on.
 | 
			
		||||
EXPOSE 5000
 | 
			
		||||
 | 
			
		||||
# Run the application.
 | 
			
		||||
CMD gunicorn 'app:app' --bind=0.0.0.0:5000
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -1,12 +0,0 @@
 | 
			
		||||
from flask import Flask
 | 
			
		||||
from controllers.status import status_blueprint
 | 
			
		||||
from controllers.image import image_blueprint
 | 
			
		||||
 | 
			
		||||
app = Flask(__name__)
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    app.run()
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
app.register_blueprint(status_blueprint)
 | 
			
		||||
app.register_blueprint(image_blueprint)
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 36 KiB  | 
@@ -1,49 +0,0 @@
 | 
			
		||||
# Comments are provided throughout this file to help you get started.
 | 
			
		||||
# If you need more help, visit the Docker compose reference guide at
 | 
			
		||||
# https://docs.docker.com/compose/compose-file/
 | 
			
		||||
 | 
			
		||||
# Here the instructions define your application as a service called "server".
 | 
			
		||||
# This service is built from the Dockerfile in the current directory.
 | 
			
		||||
# You can add other services your application may depend on here, such as a
 | 
			
		||||
# database or a cache. For examples, see the Awesome Compose repository:
 | 
			
		||||
# https://github.com/docker/awesome-compose
 | 
			
		||||
services:
 | 
			
		||||
  server:
 | 
			
		||||
    build:
 | 
			
		||||
      context: .
 | 
			
		||||
    ports:
 | 
			
		||||
      - 5000:5000
 | 
			
		||||
 | 
			
		||||
# The commented out section below is an example of how to define a PostgreSQL
 | 
			
		||||
# database that your application can use. `depends_on` tells Docker Compose to
 | 
			
		||||
# start the database before your application. The `db-data` volume persists the
 | 
			
		||||
# database data between container restarts. The `db-password` secret is used
 | 
			
		||||
# to set the database password. You must create `db/password.txt` and add
 | 
			
		||||
# a password of your choosing to it before running `docker compose up`.
 | 
			
		||||
#     depends_on:
 | 
			
		||||
#       db:
 | 
			
		||||
#         condition: service_healthy
 | 
			
		||||
#   db:
 | 
			
		||||
#     image: postgres
 | 
			
		||||
#     restart: always
 | 
			
		||||
#     user: postgres
 | 
			
		||||
#     secrets:
 | 
			
		||||
#       - db-password
 | 
			
		||||
#     volumes:
 | 
			
		||||
#       - db-data:/var/lib/postgresql/data
 | 
			
		||||
#     environment:
 | 
			
		||||
#       - POSTGRES_DB=example
 | 
			
		||||
#       - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
 | 
			
		||||
#     expose:
 | 
			
		||||
#       - 5432
 | 
			
		||||
#     healthcheck:
 | 
			
		||||
#       test: [ "CMD", "pg_isready" ]
 | 
			
		||||
#       interval: 10s
 | 
			
		||||
#       timeout: 5s
 | 
			
		||||
#       retries: 5
 | 
			
		||||
# volumes:
 | 
			
		||||
#   db-data:
 | 
			
		||||
# secrets:
 | 
			
		||||
#   db-password:
 | 
			
		||||
#     file: db/password.txt
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -1,41 +0,0 @@
 | 
			
		||||
from services.image import ImageService
 | 
			
		||||
from static.image_helper import ImageHelper
 | 
			
		||||
from flask import request, Response, Blueprint, jsonify, send_file
 | 
			
		||||
 | 
			
		||||
image_blueprint = Blueprint('image_blueprint', __name__)
 | 
			
		||||
image_service = ImageService()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@image_blueprint.route('/image/blur', methods=['POST'])
 | 
			
		||||
def blur_image():
 | 
			
		||||
    radius = int(request.form.get('radius'))
 | 
			
		||||
    image = request.files.get('file')
 | 
			
		||||
 | 
			
		||||
    if radius and image:
 | 
			
		||||
        return send_file(image_service.box_blur_image(image, radius),
 | 
			
		||||
                         mimetype='image/jpeg',
 | 
			
		||||
                         as_attachment=True,
 | 
			
		||||
                         download_name='blurred_image.jpeg')
 | 
			
		||||
 | 
			
		||||
    return "Bad request", 400
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@image_blueprint.route('/image/load-small-image', methods=['GET'])
 | 
			
		||||
def get_simple_image():
 | 
			
		||||
    return send_file(image_service.get_simple_image(),
 | 
			
		||||
                     mimetype='image/png',
 | 
			
		||||
                     as_attachment=True,
 | 
			
		||||
                     download_name='small-image.png')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@image_blueprint.route('/image/load-big-image', methods=['GET'])
 | 
			
		||||
def get_big_image():
 | 
			
		||||
    return send_file(image_service.get_big_image(),
 | 
			
		||||
                     mimetype='image/png',
 | 
			
		||||
                     as_attachment=True,
 | 
			
		||||
                     download_name='big-image.png')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@image_blueprint.route('/image/save-big-image', methods=['POST'])
 | 
			
		||||
def save_image():
 | 
			
		||||
    pass
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
from flask import jsonify, Blueprint
 | 
			
		||||
 | 
			
		||||
status_blueprint = Blueprint('status_blueprint', __name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StatusController:
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def return_ok(self):
 | 
			
		||||
        return jsonify(status=200)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
status_controller = StatusController()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@status_blueprint.route('/status/ok', methods=['GET'])
 | 
			
		||||
def return_ok():
 | 
			
		||||
    return jsonify(status=200)
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
Flask>=1.0
 | 
			
		||||
gunicorn>=19.9.0
 | 
			
		||||
Wand
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -1,69 +0,0 @@
 | 
			
		||||
from wand.image import Image
 | 
			
		||||
 | 
			
		||||
from static.image_helper import ImageHelper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def box_blur_image_separable(image, blurred_image, radius_x, radius_y):
 | 
			
		||||
    pixels = image.get_pixels()
 | 
			
		||||
    blurred_pixels = blurred_image.get_pixels()
 | 
			
		||||
 | 
			
		||||
    for pixel in pixels:
 | 
			
		||||
        x, y = pixel[0], pixel[1]
 | 
			
		||||
 | 
			
		||||
        r_total, g_total, b_total = 0, 0, 0
 | 
			
		||||
        pixel_count = 0
 | 
			
		||||
        for offset_y in range(-radius_y, radius_y + 1):
 | 
			
		||||
            for offset_x in range(-radius_x, radius_x + 1):
 | 
			
		||||
                new_x = x + offset_x
 | 
			
		||||
                new_y = y + offset_y
 | 
			
		||||
 | 
			
		||||
                if 0 <= new_x < image.width and 0 <= new_y < image.height:
 | 
			
		||||
                    pixel_color = pixels[new_y * image.width + new_x]
 | 
			
		||||
                    r_total += pixel_color.red
 | 
			
		||||
                    g_total += pixel_color.green
 | 
			
		||||
                    b_total += pixel_color.blue
 | 
			
		||||
                    pixel_count += 1
 | 
			
		||||
 | 
			
		||||
        blurred_pixel = blurred_pixels[y * image.width + x]
 | 
			
		||||
        blurred_pixel.red = int(r_total / pixel_count)
 | 
			
		||||
        blurred_pixel.green = int(g_total / pixel_count)
 | 
			
		||||
        blurred_pixel.blue = int(b_total / pixel_count)
 | 
			
		||||
 | 
			
		||||
    return blurred_image
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def save_image(file_stream):
 | 
			
		||||
    with open("image.png", "wb") as file:
 | 
			
		||||
        file.write(file_stream.read())
 | 
			
		||||
        file.close()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImageService:
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def box_blur_image(self, img, radius):
 | 
			
		||||
        temp_path = 'temp_image.png'
 | 
			
		||||
        img.save(temp_path)
 | 
			
		||||
 | 
			
		||||
        with Image(filename=temp_path) as img:
 | 
			
		||||
            img.blur(radius, 2)
 | 
			
		||||
            blurred_temp_path = 'blurred_temp_image.png'
 | 
			
		||||
            img.save(filename='blurred_temp_image.png')
 | 
			
		||||
            return blurred_temp_path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def get_simple_image(self):
 | 
			
		||||
        with ImageHelper.SimpleImage as img:
 | 
			
		||||
            img = ImageHelper.SimpleImage
 | 
			
		||||
            simple_image = 'simple_image.png'
 | 
			
		||||
            img.save(filename='simple_image.png')
 | 
			
		||||
            return simple_image
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def get_big_image(self):
 | 
			
		||||
        with ImageHelper.BigImage as img:
 | 
			
		||||
            img = ImageHelper.BigImage
 | 
			
		||||
            big_image = 'big_image.png'
 | 
			
		||||
            img.save(filename='big_image.png')
 | 
			
		||||
            return big_image
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -1,17 +0,0 @@
 | 
			
		||||
from wand.image import Image
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImageHelper:
 | 
			
		||||
    SimpleImage = None
 | 
			
		||||
    BigImage = None
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def load_images():
 | 
			
		||||
        ImageHelper.SimpleImage = Image(filename="./static/small-image.png")
 | 
			
		||||
        #ImageHelper.SimpleImage.save(filename="./static/small-image.png")
 | 
			
		||||
 | 
			
		||||
        ImageHelper.BigImage = Image(filename="./static/big-image.png")
 | 
			
		||||
        #ImageHelper.BigImage.save(filename="./static/big-image.png")
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
ImageHelper.load_images()
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 44 KiB  | 
							
								
								
									
										89
									
								
								scripts/graph.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								scripts/graph.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
import numpy as np
 | 
			
		||||
import matplotlib.pyplot as plt
 | 
			
		||||
 | 
			
		||||
def plot_graph(x, y, title, x_label, y_label, filename):
 | 
			
		||||
    plt.plot(x, y, 'ro', markersize=1, linewidth=0.5, linestyle='solid')
 | 
			
		||||
    plt.title(title)
 | 
			
		||||
    plt.xlabel(x_label)
 | 
			
		||||
    plt.ylabel(y_label)
 | 
			
		||||
    plt.savefig(f'{filename}.png')
 | 
			
		||||
 | 
			
		||||
    plt.clf()
 | 
			
		||||
    plt.close('all')
 | 
			
		||||
 | 
			
		||||
def plot_resource_graph(x_data, y_data, title, x_label, y_label, filename):
 | 
			
		||||
    requests = x_data
 | 
			
		||||
    resource = {
 | 
			
		||||
        'CPU': [p[0] for p in y_data],
 | 
			
		||||
        'RAM': [p[1] for p in y_data],
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    x = np.arange(len(requests))
 | 
			
		||||
    width = 0.25
 | 
			
		||||
    multiplier = 0
 | 
			
		||||
 | 
			
		||||
    fig, ax = plt.subplots(layout='constrained')
 | 
			
		||||
 | 
			
		||||
    for attribute, measurement in resource.items():
 | 
			
		||||
        offset = width * multiplier
 | 
			
		||||
 | 
			
		||||
        rects = ax.bar(x + offset, measurement, width, label=attribute)
 | 
			
		||||
        ax.bar_label(rects, padding=3)
 | 
			
		||||
        multiplier += 1
 | 
			
		||||
 | 
			
		||||
    # Add some text for labels, title and custom x-axis tick labels, etc.
 | 
			
		||||
    ax.set_xlabel(x_label)
 | 
			
		||||
    ax.set_ylabel(y_label)
 | 
			
		||||
    ax.set_title(title)
 | 
			
		||||
    ax.set_xticks(x + (width/2), requests)
 | 
			
		||||
    ax.legend(loc='upper left', ncols=len(resource.items()))
 | 
			
		||||
    ax.set_ylim(0, 100)
 | 
			
		||||
 | 
			
		||||
    plt.savefig(f'{filename}.png')
 | 
			
		||||
 | 
			
		||||
    plt.clf()
 | 
			
		||||
    plt.close('all')
 | 
			
		||||
 | 
			
		||||
def get_data(filename):
 | 
			
		||||
    lines = []
 | 
			
		||||
    with open(filename, 'r') as f:
 | 
			
		||||
        lines = f.readlines()
 | 
			
		||||
 | 
			
		||||
    x = []
 | 
			
		||||
    y = []
 | 
			
		||||
    for line in lines:
 | 
			
		||||
        line = line.strip().split(',')
 | 
			
		||||
        if line:
 | 
			
		||||
            x.append(int(line[0]))
 | 
			
		||||
            y.append(float(line[1]))
 | 
			
		||||
 | 
			
		||||
    return x, y
 | 
			
		||||
 | 
			
		||||
def get_resource_data(filename):
 | 
			
		||||
    lines = []
 | 
			
		||||
    with open(filename, 'r') as f:
 | 
			
		||||
        lines = f.readlines()
 | 
			
		||||
 | 
			
		||||
    x = []
 | 
			
		||||
    y = []
 | 
			
		||||
    for line in lines:
 | 
			
		||||
        line = line.strip().split(',')
 | 
			
		||||
        if line:
 | 
			
		||||
            x.append(int(line[0])) # requests
 | 
			
		||||
            y.append([float(v)*100 for v in line[1:]]) # cpu, ram
 | 
			
		||||
 | 
			
		||||
    return x, y
 | 
			
		||||
 | 
			
		||||
def generate_req_graph(filename, framework_name, endpoint_name):
 | 
			
		||||
    x, y = get_data(filename)
 | 
			
		||||
 | 
			
		||||
    filename = filename.split('/')[-1]
 | 
			
		||||
    new_filename = filename.replace('.csv', '')
 | 
			
		||||
    plot_graph(x, y, f'{framework_name} - {endpoint_name}', 'Número de requisições', 'Requisições por segundo', new_filename)
 | 
			
		||||
 | 
			
		||||
def generate_resource_graph(filename, framework_name, endpoint_name):
 | 
			
		||||
    x, y = get_resource_data(filename)
 | 
			
		||||
 | 
			
		||||
    filename = filename.split('/')[-1]
 | 
			
		||||
    new_filename = filename.replace('.csv', '')
 | 
			
		||||
    plot_resource_graph(x, y, f'{framework_name} - {endpoint_name}', 'Uso de recursos', 'Uso (%)', new_filename)
 | 
			
		||||
							
								
								
									
										17
									
								
								scripts/init.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								scripts/init.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import requests
 | 
			
		||||
 | 
			
		||||
def download_file(url):
 | 
			
		||||
    local_filename = url.split('/')[-1]
 | 
			
		||||
    with requests.get(url, stream=True) as r:
 | 
			
		||||
        r.raise_for_status()
 | 
			
		||||
        with open(local_filename, 'wb') as f:
 | 
			
		||||
            for chunk in r.iter_content(chunk_size=8192):
 | 
			
		||||
                # If you have chunk encoded response uncomment if
 | 
			
		||||
                # and set chunk_size parameter to None.
 | 
			
		||||
                #if chunk:
 | 
			
		||||
                f.write(chunk)
 | 
			
		||||
    return local_filename
 | 
			
		||||
 | 
			
		||||
def init():
 | 
			
		||||
    download_file('https://files.ivanch.me/api/public/dl/Dj0gkp-m/small-image.png')
 | 
			
		||||
    download_file('https://files.ivanch.me/api/public/dl/FqHEPM1Q/big-image.png')
 | 
			
		||||
@@ -1,33 +1,155 @@
 | 
			
		||||
import requests
 | 
			
		||||
import docker
 | 
			
		||||
import concurrent.futures
 | 
			
		||||
import time
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
from graph import generate_req_graph, generate_resource_graph
 | 
			
		||||
from math import floor
 | 
			
		||||
from init import init
 | 
			
		||||
 | 
			
		||||
URL_BASE = 'http://localhost:5100'
 | 
			
		||||
if len(sys.argv) < 2 or len(sys.argv) > 3 or sys.argv[1] == '-h' or sys.argv[1] == '--help':
 | 
			
		||||
    print("Usage: python testes.py <framework name> [container name]")
 | 
			
		||||
    sys.exit(1)
 | 
			
		||||
 | 
			
		||||
num_requests = [1000, 5000, 10_000, 50_000, 100_000, 500_000, 1_000_000]
 | 
			
		||||
init()
 | 
			
		||||
 | 
			
		||||
def send_request(session, url):
 | 
			
		||||
    response = session.get(url)
 | 
			
		||||
    return response.status_code
 | 
			
		||||
THREADS = 10
 | 
			
		||||
FRAMEWORK_NAME = sys.argv[1]
 | 
			
		||||
CONTAINER_NAME = sys.argv[2] if len(sys.argv) > 2 else ""
 | 
			
		||||
URL_BASE = 'http://localhost:9090'
 | 
			
		||||
BLUR_RADIUS = 5
 | 
			
		||||
API_REQUESTS = [
 | 
			
		||||
    ('/image/save-big-image', 'POST', range(0, 10_000, 1_000), open('big-image.png', 'rb').read()),
 | 
			
		||||
    (f'/image/blur?radius={BLUR_RADIUS}', 'POST', range(0, 1_000, 50), open('small-image.png', 'rb').read()),
 | 
			
		||||
    ('/status/ok', 'GET', range(0, 30_000, 5000), None),
 | 
			
		||||
    ('/image/load-small-image', 'GET', range(0, 30_000, 5000), None),
 | 
			
		||||
    ('/static/small-image.png', 'GET', range(0, 30_000, 5000), None),
 | 
			
		||||
    ('/image/load-big-image', 'GET', range(0, 500, 50), None),
 | 
			
		||||
    ('/static/big-image.png', 'GET', range(0, 500, 50), None),
 | 
			
		||||
    ('/static/video.mp4', 'GET', range(0, 10_000, 1_000), None),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
def send_request(url, method = 'GET', payload = None):
 | 
			
		||||
    success = False
 | 
			
		||||
    responses = {
 | 
			
		||||
        2: 0, # OK
 | 
			
		||||
        4: 0, # Bad Request
 | 
			
		||||
        5: 0, # Server Error
 | 
			
		||||
    }
 | 
			
		||||
    while not success:
 | 
			
		||||
        try:
 | 
			
		||||
            response = None
 | 
			
		||||
            if method == 'GET':
 | 
			
		||||
                response = requests.get(url)
 | 
			
		||||
            elif method == 'POST':
 | 
			
		||||
                response = requests.post(url, data=payload, headers={'Content-Type': 'image/png'})
 | 
			
		||||
        except:
 | 
			
		||||
            continue
 | 
			
		||||
        success = response.status_code == 200
 | 
			
		||||
        responses[floor(response.status_code/100)] += 1
 | 
			
		||||
    return responses
 | 
			
		||||
 | 
			
		||||
def getFileNames(endpoint):
 | 
			
		||||
    endpoint = endpoint.replace('/', '')
 | 
			
		||||
 | 
			
		||||
    files = [
 | 
			
		||||
        f"data/req_{FRAMEWORK_NAME}_{endpoint}.csv",
 | 
			
		||||
        f"data/resource_{FRAMEWORK_NAME}_{endpoint}.csv",
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    return files
 | 
			
		||||
 | 
			
		||||
def record(filename, requests, reqpersec):
 | 
			
		||||
    with open(filename, "a") as file:
 | 
			
		||||
        file.write(f"{requests},{reqpersec}\n")
 | 
			
		||||
 | 
			
		||||
def record_resource(filename, requests, cpu, ram):
 | 
			
		||||
    with open(filename, "a") as file:
 | 
			
		||||
        file.write(f"{requests},{cpu},{ram}\n")
 | 
			
		||||
 | 
			
		||||
def run_tests(endpoint, method, num_requests, metadata):
 | 
			
		||||
    files = getFileNames(endpoint)
 | 
			
		||||
    for filename in files:
 | 
			
		||||
        if os.path.exists(filename):
 | 
			
		||||
            os.remove(filename)
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    for num_request in num_requests:
 | 
			
		||||
        with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
 | 
			
		||||
            url = f'{URL_BASE}/status/ok'
 | 
			
		||||
        if num_request <= 0: continue
 | 
			
		||||
 | 
			
		||||
        ok_responses = 0
 | 
			
		||||
        bad_responses = 0
 | 
			
		||||
        server_errors = 0
 | 
			
		||||
        cpu, ram = 0, 0
 | 
			
		||||
 | 
			
		||||
        with concurrent.futures.ThreadPoolExecutor(max_workers=THREADS) as executor:
 | 
			
		||||
            url = f'{URL_BASE}{endpoint}'
 | 
			
		||||
 | 
			
		||||
            start_time = time.time()
 | 
			
		||||
 | 
			
		||||
            futures = []
 | 
			
		||||
            with requests.Session() as session:
 | 
			
		||||
                futures = [executor.submit(send_request, session, url) for _ in range(num_request)]
 | 
			
		||||
            #with requests.Session() as session:
 | 
			
		||||
            #    futures = [executor.submit(send_request, session, url) for _ in range(num_request)]
 | 
			
		||||
 | 
			
		||||
            half = floor(num_request/2)
 | 
			
		||||
            for i in range(num_request):
 | 
			
		||||
                futures.append(executor.submit(send_request, url, method, metadata))
 | 
			
		||||
 | 
			
		||||
                if i == half:
 | 
			
		||||
                    cpu, ram = get_resource_usage()
 | 
			
		||||
 | 
			
		||||
            concurrent.futures.wait(futures)
 | 
			
		||||
 | 
			
		||||
            elapsed_time = time.time() - start_time
 | 
			
		||||
 | 
			
		||||
            successful_responses = sum(1 for future in futures if future.result() == 200)
 | 
			
		||||
            for future in futures:
 | 
			
		||||
                responses = future.result()
 | 
			
		||||
                ok_responses += responses[2]
 | 
			
		||||
                bad_responses += responses[4]
 | 
			
		||||
                server_errors += responses[5]
 | 
			
		||||
 | 
			
		||||
        print(f"All requests completed in {elapsed_time:.2f} seconds. {elapsed_time/num_request:.4f} seconds per request. {num_request/elapsed_time:.2f} requests per second.")
 | 
			
		||||
        print(f"Successful responses: {successful_responses}/{num_request}")
 | 
			
		||||
        print(f"{num_request}: {elapsed_time:.2f} seconds. {elapsed_time/num_request:.4f} seconds per request. {num_request/elapsed_time:.2f} requests per second. [OK: {ok_responses}, Bad Request: {bad_responses}, Server Error: {server_errors}]]")
 | 
			
		||||
        record(files[0], num_request, f"{num_request/elapsed_time:.2f}")
 | 
			
		||||
        record_resource(files[1], num_request, cpu, ram)
 | 
			
		||||
 | 
			
		||||
main()
 | 
			
		||||
        generate_req_graph(files[0], FRAMEWORK_NAME, endpoint)
 | 
			
		||||
        generate_resource_graph(files[1], FRAMEWORK_NAME, endpoint)
 | 
			
		||||
 | 
			
		||||
        time.sleep(3)
 | 
			
		||||
 | 
			
		||||
def get_resource_usage():
 | 
			
		||||
    if CONTAINER_NAME == "": return 0, 0
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        client = docker.from_env()
 | 
			
		||||
        stats = client.containers.get(CONTAINER_NAME).stats(stream=False)
 | 
			
		||||
    except:
 | 
			
		||||
        return 0, 0 # unable to get stats
 | 
			
		||||
 | 
			
		||||
    return get_cpu_usage(stats), get_ram_usage(stats)
 | 
			
		||||
 | 
			
		||||
def get_cpu_usage(stats):
 | 
			
		||||
    UsageDelta = stats['cpu_stats']['cpu_usage']['total_usage'] - stats['precpu_stats']['cpu_usage']['total_usage']
 | 
			
		||||
    SystemDelta = stats['cpu_stats']['system_cpu_usage'] - stats['precpu_stats']['system_cpu_usage']
 | 
			
		||||
    len_cpu = stats['cpu_stats']['online_cpus']
 | 
			
		||||
    percentage = (UsageDelta / SystemDelta) * len_cpu
 | 
			
		||||
    return f"{percentage:.2f}"
 | 
			
		||||
 | 
			
		||||
def get_ram_usage(stats):
 | 
			
		||||
    usage = stats['memory_stats']['usage']
 | 
			
		||||
    limit = stats['memory_stats']['limit']
 | 
			
		||||
 | 
			
		||||
    percentage = (usage / limit)
 | 
			
		||||
 | 
			
		||||
    # percent = round(percentage, 2)
 | 
			
		||||
    return f"{percentage:.2f}"
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    if not os.path.exists("data"):
 | 
			
		||||
        os.mkdir("data")
 | 
			
		||||
    else:
 | 
			
		||||
        os.system("rm -rf data/*")
 | 
			
		||||
 | 
			
		||||
    for endpoint, method, num_requests, metadata in API_REQUESTS:
 | 
			
		||||
        print(f"# {endpoint}")
 | 
			
		||||
        run_tests(endpoint, method, num_requests, metadata)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user