diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..7f47431c1ae2d5bd999d2a97882ea74a239498f2 --- /dev/null +++ b/.clang-format @@ -0,0 +1,6 @@ +--- +Language: Cpp +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 120 +AccessModifierOffset: -3 \ No newline at end of file diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..10be693d187eed44b2c816d12e486e251e5f036e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +/cmake-build-debug/ +/build/ +/build1/ +.vscode +.idea +CMakeUserPresets.json \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 0000000000000000000000000000000000000000..5ec041491649a9ae124227604f2dc0c7120db944 --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +PGHOST=db +PGPORT=5432 +PGDATABASE=mydb +PGUSER=temp +PGPASSWORD=temp \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb53bfb98b8acc6ea96465e6171f9e4892326bf6..98d52f2ec3862474d6730a3c78cdaa5f571c473f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,45 +11,25 @@ defaults: shell: bash jobs: - build: + Format: runs-on: ubuntu-latest container: raiden454/cpp-app steps: - uses: actions/checkout@v3 - - name: Installing - run: | - apt update - git clone https://github.com/google/googletest.git -b release-1.11.0 - cd googletest - mkdir build - cd build - cmake .. -DBUILD_GMOCK=ON - make - make install - cd ../.. - - name: Building - run: | - mkdir build - cd build - cmake .. - make - # tests: - # runs-on: ubuntu-latest - # container: raiden454/cpp-app - # needs: [build] - # steps: - # - uses: actions/checkout@v3 - # - name: Run-Tests - # run: | - # find / -name "test_service" - linters: + - run: make format + Lint: + runs-on: ubuntu-latest + container: raiden454/cpp-app + needs: [Format] + steps: + - uses: actions/checkout@v3 + - run: make lint + build: runs-on: ubuntu-latest container: raiden454/cpp-app steps: - uses: actions/checkout@v3 - - name: Cppcheck - run: | - cppcheck server --std=c++17 --enable=all - - name: Cpplint - run: | - cpplint --extensions=cpp,hpp,h --recursive ./server/* + - name: Building + run: make rebuild + - name: Run-Tests + run: make test \ No newline at end of file diff --git a/.gitignore b/.gitignore index 870ca9cbdc136f4823abee3dce85671fc5ccd26c..3106a351a4595c172e30c1cab21ec872980d0903 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,11 @@ /cmake-build-debug/ /build/ +/build1/ .vscode .idea +.vscode +server/build/ +log.txt +/server/cpp-dotenv CMakeUserPresets.json +/metrics/cmake-build-debug" diff --git a/CMakeLists.txt b/CMakeLists.txt index 5054100d4d9df7e09087c90a11fdac68ac7ce761..28f21487b7982736bf72b93bea72d7856de31846 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,41 @@ -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) cmake_minimum_required(VERSION 3.16) -set(CMAKE_PREFIX_PATH build) -project(SourcedOut CXX) -# find_package(antlr4-runtime REQUIRED) -find_package(Boost 1.8.1 REQUIRED) -# find_package(libpqxx REQUIRED) -find_package(GTest REQUIRED) -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) -message(STATUS ${Boost_LIBRARIES}) -set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-lpthread -pthread") -add_executable(${PROJECT_NAME} src/main.cpp) -#add_executable(${PROJECT_NAME} text-basic-metrics/tbm_main.cpp text-basic-metrics/tbm_main.cpp) строка для запуска моей части в text-basic-metrics -target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${antlr4-runtime_LIBRARIES} ${libpqxx_LIBRARIES} ${GTest_LIBRARIES}) \ No newline at end of file + + +set(PROJECT_NAME "SourcedOut") + +project(${PROJECT_NAME}) + + +set(BUILD_DEV TRUE CACHE BOOL "build dev version") +set(SANITIZE_BUILD TRUE CACHE BOOL "build with sanitizers") + +if(BUILD_DEV) + enable_testing() + message("Building dev version") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -O0 -fprofile-arcs -ftest-coverage") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -pedantic -Wformat=2 -Wfloat-equal -Wconversion \ + -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -lpq -lpqxx") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -coverage -lgcov") + + if(SANITIZE_BUILD) + message("Sanitizers ON") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined -fno-sanitize-recover=all -fsanitize-undefined-trap-on-error") + endif(SANITIZE_BUILD) +endif(BUILD_DEV) + +set(BUILD_SERVER "BUILD_SERVER") +set(BUILD_CLIENT "BUILD_CLIENT") +set(BUILD_ALL "BUILD_ALL") + + +message("${BUILD_DEV} ${SANITIZE_BUILD} ${CMAKE_EXE_LINKER_FLAGS}") +set(BUILD_MODE ${BUILD_SERVER}) + +if((BUILD_MODE STREQUAL ${BUILD_CLIENT}) OR (BUILD_MODE STREQUAL ${BUILD_ALL})) + add_subdirectory(client) +endif() + +if((BUILD_MODE STREQUAL ${BUILD_SERVER}) OR (BUILD_MODE STREQUAL ${BUILD_ALL})) + add_subdirectory(server) +endif() \ No newline at end of file diff --git a/CPPLINT.cfg b/CPPLINT.cfg new file mode 100644 index 0000000000000000000000000000000000000000..9d70c29edbf629903407f904102a5a0ae7ae67f7 --- /dev/null +++ b/CPPLINT.cfg @@ -0,0 +1,13 @@ +headers=h,hpp +linelength=120 +filter=-whitespace/tab +filter=-runtime/int +filter=-legal/copyright +filter=-build/include_subdir +filter=-build/include +filter=-readability/casting +filter=-runtime/references +filter=-runtime/string +filter=-build/header_guard +filter=-build/c++11 +filter=-build/namespaces diff --git a/Docker/db.Dockerfile b/Docker/db.Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..03d32ce703411a9b575edd6a36582992d116f0f4 --- /dev/null +++ b/Docker/db.Dockerfile @@ -0,0 +1,3 @@ +FROM postgres:15 +RUN apt-get update && apt-get -y install pgagent +COPY init.sql /docker-entrypoint-initdb.d/init.sql \ No newline at end of file diff --git a/Docker/init.sql b/Docker/init.sql new file mode 100644 index 0000000000000000000000000000000000000000..8a1e8fd7e925b9dd2600729392697e42b756ce22 --- /dev/null +++ b/Docker/init.sql @@ -0,0 +1,671 @@ +-- +-- PostgreSQL database cluster dump +-- + +-- Started on 2023-05-15 22:49:08 + +SET default_transaction_read_only = off; + +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; + +-- +-- Roles +-- + +CREATE ROLE postgres; +ALTER ROLE postgres WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN REPLICATION BYPASSRLS PASSWORD 'SCRAM-SHA-256$4096:nOmjHrNPdaIcnCeed7FgbQ==$U+pukPiYK1tBvbNsPjn4d9zV4IXLekf/OMW5+ZDzgrY=:1f9vBcPgoAcX5hmUUOkjkXLp0FRCqsnlQRmYs2U29pU='; + +-- +-- User Configurations +-- + + + + + + + + +-- +-- Databases +-- + +-- +-- Database "template1" dump +-- + +\connect template1 + +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 15.2 +-- Dumped by pg_dump version 15.2 + +-- Started on 2023-05-15 22:49:08 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- Completed on 2023-05-15 22:49:08 + +-- +-- PostgreSQL database dump complete +-- + +-- +-- Database "mydb" dump +-- + +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 15.2 +-- Dumped by pg_dump version 15.2 + +-- Started on 2023-05-15 22:49:08 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- TOC entry 3362 (class 1262 OID 16565) +-- Name: mydb; Type: DATABASE; Schema: -; Owner: postgres +-- + +CREATE DATABASE mydb WITH TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'en_US.utf8'; + + +ALTER DATABASE mydb OWNER TO postgres; + +\connect mydb + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- TOC entry 215 (class 1259 OID 16574) +-- Name: users; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.users ( + id integer NOT NULL, + login character varying(255), + password character varying(255), + username character varying(255) +); + + +ALTER TABLE public.users OWNER TO postgres; + +-- +-- TOC entry 214 (class 1259 OID 16573) +-- Name: Users_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public."Users_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public."Users_id_seq" OWNER TO postgres; + +-- +-- TOC entry 3363 (class 0 OID 0) +-- Dependencies: 214 +-- Name: Users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public."Users_id_seq" OWNED BY public.users.id; + + +-- +-- TOC entry 219 (class 1259 OID 16613) +-- Name: metricstat; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.metricstat ( + id integer NOT NULL, + solution_id integer, + text_based_res real, + token_based_res real, + tree_based_res real, + verdict boolean, + mean_res real +); + + +ALTER TABLE public.metricstat OWNER TO postgres; + +-- +-- TOC entry 221 (class 1259 OID 16626) +-- Name: metricstat_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.metricstat_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + MAXVALUE 2147483647 + CACHE 1; + + +ALTER TABLE public.metricstat_id_seq OWNER TO postgres; + +-- +-- TOC entry 3364 (class 0 OID 0) +-- Dependencies: 221 +-- Name: metricstat_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.metricstat_id_seq OWNED BY public.metricstat.id; + + +-- +-- TOC entry 217 (class 1259 OID 16583) +-- Name: solutions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.solutions ( + id integer NOT NULL, + send_date date NOT NULL, + sender_id integer, + source character varying, + task_id integer, + result character varying, + tokens character varying, + asttree character varying, + original_solution_id integer, + language character varying(255) +); + + +ALTER TABLE public.solutions OWNER TO postgres; + +-- +-- TOC entry 216 (class 1259 OID 16582) +-- Name: solutions_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.solutions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.solutions_id_seq OWNER TO postgres; + +-- +-- TOC entry 3365 (class 0 OID 0) +-- Dependencies: 216 +-- Name: solutions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.solutions_id_seq OWNED BY public.solutions.id; + + +-- +-- TOC entry 218 (class 1259 OID 16598) +-- Name: tasks; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.tasks ( + id integer NOT NULL, + description text, + treshold real, + name character varying(255) +); + + +ALTER TABLE public.tasks OWNER TO postgres; + +-- +-- TOC entry 220 (class 1259 OID 16623) +-- Name: tasks_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.tasks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + MAXVALUE 2147483647 + CACHE 1; + + +ALTER TABLE public.tasks_id_seq OWNER TO postgres; + +-- +-- TOC entry 3366 (class 0 OID 0) +-- Dependencies: 220 +-- Name: tasks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.tasks_id_seq OWNED BY public.tasks.id; + + +-- +-- TOC entry 3191 (class 2604 OID 16628) +-- Name: metricstat id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metricstat ALTER COLUMN id SET DEFAULT nextval('public.metricstat_id_seq'::regclass); + + +-- +-- TOC entry 3189 (class 2604 OID 16586) +-- Name: solutions id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solutions ALTER COLUMN id SET DEFAULT nextval('public.solutions_id_seq'::regclass); + + +-- +-- TOC entry 3190 (class 2604 OID 16625) +-- Name: tasks id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.tasks ALTER COLUMN id SET DEFAULT nextval('public.tasks_id_seq'::regclass); + + +-- +-- TOC entry 3188 (class 2604 OID 16577) +-- Name: users id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.users ALTER COLUMN id SET DEFAULT nextval('public."Users_id_seq"'::regclass); + + +-- +-- TOC entry 3354 (class 0 OID 16613) +-- Dependencies: 219 +-- Data for Name: metricstat; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +COPY public.metricstat (id, solution_id, text_based_res, token_based_res, tree_based_res, verdict, mean_res) FROM stdin; +0 0 1 1 1 t 1 +\. + + +-- +-- TOC entry 3352 (class 0 OID 16583) +-- Dependencies: 217 +-- Data for Name: solutions; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +COPY public.solutions (id, send_date, sender_id, source, task_id, result, tokens, asttree, original_solution_id, language) FROM stdin; +0 2023-05-02 0 0.1 0 0.1 0.1 0.1 0 unknown +\. + + +-- +-- TOC entry 3353 (class 0 OID 16598) +-- Dependencies: 218 +-- Data for Name: tasks; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +COPY public.tasks (id, description, treshold, name) FROM stdin; +2 other_description 0.1 second_task +1 description 0.5 first_task +0 zero 0 zero +\. + + +-- +-- TOC entry 3350 (class 0 OID 16574) +-- Dependencies: 215 +-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +COPY public.users (id, login, password, username) FROM stdin; +0 deleted_user 0000 DELETED_USER +1 qwerty200468@gmail.com 123 tolik +\. + + +-- +-- TOC entry 3367 (class 0 OID 0) +-- Dependencies: 214 +-- Name: Users_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public."Users_id_seq"', 1, true); + + +-- +-- TOC entry 3368 (class 0 OID 0) +-- Dependencies: 221 +-- Name: metricstat_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public.metricstat_id_seq', 1, true); + + +-- +-- TOC entry 3369 (class 0 OID 0) +-- Dependencies: 216 +-- Name: solutions_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public.solutions_id_seq', 1, true); + + +-- +-- TOC entry 3370 (class 0 OID 0) +-- Dependencies: 220 +-- Name: tasks_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public.tasks_id_seq', 1, true); + + +-- +-- TOC entry 3193 (class 2606 OID 16581) +-- Name: users Users_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT "Users_pkey" PRIMARY KEY (id); + + +-- +-- TOC entry 3202 (class 2606 OID 16617) +-- Name: metricstat metricStat_pk; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metricstat + ADD CONSTRAINT "metricStat_pk" PRIMARY KEY (id); + + +-- +-- TOC entry 3198 (class 2606 OID 16588) +-- Name: solutions solutions_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solutions + ADD CONSTRAINT solutions_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3200 (class 2606 OID 16604) +-- Name: tasks tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.tasks + ADD CONSTRAINT tasks_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3194 (class 1259 OID 16637) +-- Name: fki_original_solution_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX fki_original_solution_id ON public.solutions USING btree (original_solution_id); + + +-- +-- TOC entry 3195 (class 1259 OID 16594) +-- Name: fki_sender_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX fki_sender_id ON public.solutions USING btree (sender_id); + + +-- +-- TOC entry 3196 (class 1259 OID 16610) +-- Name: fki_task_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX fki_task_id ON public.solutions USING btree (task_id); + + +-- +-- TOC entry 3203 (class 2606 OID 16632) +-- Name: solutions original_solution_id; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solutions + ADD CONSTRAINT original_solution_id FOREIGN KEY (original_solution_id) REFERENCES public.solutions(id) ON UPDATE CASCADE ON DELETE CASCADE NOT VALID; + + +-- +-- TOC entry 3204 (class 2606 OID 16589) +-- Name: solutions sender_id; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solutions + ADD CONSTRAINT sender_id FOREIGN KEY (sender_id) REFERENCES public.users(id) ON UPDATE RESTRICT ON DELETE CASCADE NOT VALID; + + +-- +-- TOC entry 3206 (class 2606 OID 16618) +-- Name: metricstat solutions_id; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metricstat + ADD CONSTRAINT solutions_id FOREIGN KEY (solution_id) REFERENCES public.solutions(id); + + +-- +-- TOC entry 3205 (class 2606 OID 16605) +-- Name: solutions task_id; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solutions + ADD CONSTRAINT task_id FOREIGN KEY (task_id) REFERENCES public.tasks(id) NOT VALID; + + +-- Completed on 2023-05-15 22:49:09 + +-- +-- PostgreSQL database dump complete +-- + +-- +-- Database "postgres" dump +-- + +\connect postgres + +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 15.2 +-- Dumped by pg_dump version 15.2 + +-- Started on 2023-05-15 22:49:09 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- TOC entry 8 (class 2615 OID 16398) +-- Name: pgagent; Type: SCHEMA; Schema: -; Owner: postgres +-- + +CREATE SCHEMA pgagent; + + +ALTER SCHEMA pgagent OWNER TO postgres; + +-- +-- TOC entry 3437 (class 0 OID 0) +-- Dependencies: 8 +-- Name: SCHEMA pgagent; Type: COMMENT; Schema: -; Owner: postgres +-- + +COMMENT ON SCHEMA pgagent IS 'pgAgent system tables'; + + +-- +-- TOC entry 2 (class 3079 OID 16384) +-- Name: adminpack; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS adminpack WITH SCHEMA pg_catalog; + + +-- +-- TOC entry 3438 (class 0 OID 0) +-- Dependencies: 2 +-- Name: EXTENSION adminpack; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION adminpack IS 'administrative functions for PostgreSQL'; + + +-- +-- TOC entry 3 (class 3079 OID 16399) +-- Name: pgagent; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pgagent WITH SCHEMA pgagent; + + +-- +-- TOC entry 3439 (class 0 OID 0) +-- Dependencies: 3 +-- Name: EXTENSION pgagent; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION pgagent IS 'A PostgreSQL job scheduler'; + + +-- +-- TOC entry 3218 (class 0 OID 16400) +-- Dependencies: 219 +-- Data for Name: pga_jobagent; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_jobagent (jagpid, jaglogintime, jagstation) FROM stdin; +7380 2023-05-09 17:32:50.44529+03 DESKTOP-CLI5MDC +\. + + +-- +-- TOC entry 3219 (class 0 OID 16409) +-- Dependencies: 221 +-- Data for Name: pga_jobclass; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_jobclass (jclid, jclname) FROM stdin; +\. + + +-- +-- TOC entry 3220 (class 0 OID 16419) +-- Dependencies: 223 +-- Data for Name: pga_job; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_job (jobid, jobjclid, jobname, jobdesc, jobhostagent, jobenabled, jobcreated, jobchanged, jobagentid, jobnextrun, joblastrun) FROM stdin; +\. + + +-- +-- TOC entry 3222 (class 0 OID 16467) +-- Dependencies: 227 +-- Data for Name: pga_schedule; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_schedule (jscid, jscjobid, jscname, jscdesc, jscenabled, jscstart, jscend, jscminutes, jschours, jscweekdays, jscmonthdays, jscmonths) FROM stdin; +\. + + +-- +-- TOC entry 3223 (class 0 OID 16495) +-- Dependencies: 229 +-- Data for Name: pga_exception; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_exception (jexid, jexscid, jexdate, jextime) FROM stdin; +\. + + +-- +-- TOC entry 3224 (class 0 OID 16509) +-- Dependencies: 231 +-- Data for Name: pga_joblog; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_joblog (jlgid, jlgjobid, jlgstatus, jlgstart, jlgduration) FROM stdin; +\. + + +-- +-- TOC entry 3221 (class 0 OID 16443) +-- Dependencies: 225 +-- Data for Name: pga_jobstep; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_jobstep (jstid, jstjobid, jstname, jstdesc, jstenabled, jstkind, jstcode, jstconnstr, jstdbname, jstonerror, jscnextrun) FROM stdin; +\. + + +-- +-- TOC entry 3225 (class 0 OID 16525) +-- Dependencies: 233 +-- Data for Name: pga_jobsteplog; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_jobsteplog (jslid, jsljlgid, jsljstid, jslstatus, jslresult, jslstart, jslduration, jsloutput) FROM stdin; +\. + + +-- Completed on 2023-05-15 22:49:09 + +-- +-- PostgreSQL database dump complete +-- + +-- Completed on 2023-05-15 22:49:09 + +-- +-- PostgreSQL database cluster dump complete +-- diff --git a/Dockerfile b/Dockerfile index fd9957b926a8c123392a74501b8176fc2f51c710..d6343d8e32c3d6d17120dc6f8a53311b0ce05a6d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,44 +1,57 @@ -FROM ubuntu:20.04 AS base +FROM ubuntu:latest + +ENV TZ=Europe/Moscow +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt update -y +RUN apt install -y gcc +RUN apt install -y libpqxx-dev +RUN apt install -y clang-tidy +RUN apt-get install -y wget +RUN apt install -y g++ +RUN apt install -y cmake + +RUN wget https://github.com/jtv/libpqxx/archive/refs/tags/7.7.5.tar.gz +RUN tar -zxvf 7.7.5.tar.gz +WORKDIR libpqxx-7.7.5 +RUN ./configure CXXFLAGS=-std=c++17 --disable-dependency-tracking +RUN make +RUN make install + +RUN apt install -y python3-pip +RUN apt install -y cppcheck +RUN apt-get update +RUN apt install -y nlohmann-json3-dev +RUN apt install -y git +RUN apt-get update +RUN apt install -y default-jre +RUN apt -y install curl + +RUN pwd +WORKDIR /usr/local/lib +RUN curl -O https://www.antlr.org/download/antlr-4.12.0-complete.jar + +RUN apt install -y xvfb +RUN pip install gcovr +RUN pip install cpplint -ENV TZ=Europe/Moscow -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN apt update -y -RUN apt install -y gcc -RUN apt install -y libpqxx-dev -RUN apt install -y clang-tidy -RUN apt install -y nlohmann-json3-dev -RUN apt install -y python3-pip -RUN apt install -y cppcheck -RUN apt install -y git -RUN apt-get update -y -RUN apt install -y xvfb -RUN pip install gcovr -RUN pip install cpplint - -RUN apt-get install wget -RUN apt-get install libssl-dev - -RUN wget https://github.com/Kitware/CMake/releases/download/v3.26.3/cmake-3.26.3.tar.gz -RUN tar -zxvf cmake-3.26.3.tar.gz -WORKDIR cmake-3.26.3 -RUN ./bootstrap -RUN make -RUN make install -RUN cd .. - -RUN wget https://boostorg.jfrog.io/artifactory/main/release/1.82.0/source/boost_1_82_0.tar.gz -RUN tar xvf boost_1_82_0.tar.gz -WORKDIR boost_1_82_0 -RUN ./bootstrap.sh --prefix=/usr/ -RUN ./b2 install RUN git clone https://github.com/google/googletest.git -b release-1.11.0 WORKDIR googletest/build -RUN cmake .. -DBUILD_GMOCK=OFF +RUN cmake .. -DBUILD_GMOCK=ON RUN make -RUN make install - +RUN make install + +RUN wget https://boostorg.jfrog.io/artifactory/main/release/1.82.0/source/boost_1_82_0.tar.gz +RUN tar xvf boost_1_82_0.tar.gz +WORKDIR boost_1_82_0 +RUN ./bootstrap.sh --prefix=/usr/ +RUN ./b2 install + +RUN apt install -y clang-format + WORKDIR /project -COPY . . \ No newline at end of file +COPY . . + +EXPOSE 8080 \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..64180d7aad6f729778b8695441c7603f2b080519 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +generate: + mkdir build + cmake -B build/ + +build-project: + cd ./build && cmake .. && make --no-print-directory + +clean: + rm -rf build + +rebuild: clean generate build-project + +server-run: + ./build/server/cmd/Server + +test: + ctest --verbose --test-dir build/ + +check: + cppcheck server --std=c++17 --enable=all + +lint: + ./run_linters.sh + +format: + ./run_format.sh + +dev: + docker run --rm -it \ + -v $(PWD):/project \ + --name app \ + -p 8080:8080 \ + raiden454/cpp-app + +stop-docker: + docker stop app + +run-docker-compose: + docker compose up -d --build + diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..4147519820083ff249ac5749021a75b22602430d --- /dev/null +++ b/client/CMakeLists.txt @@ -0,0 +1,15 @@ +set(CMAKE_PREFIX_PATH build) +project(SourcedOut_client CXX) + +set(CMAKE_CXX_STANDARD 20) + +find_package(Boost 1.8.1 REQUIRED) +find_package(GTest REQUIRED) + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + +add_subdirectory(internal) +add_subdirectory(cmd) + +set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-lpthread -pthread") \ No newline at end of file diff --git a/client/cmd/CMakeLists.txt b/client/cmd/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/client/internal/CMakeLists.txt b/client/internal/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b5403669e61b8f7e3b480e846cbb51aa358cf07e --- /dev/null +++ b/client/internal/CMakeLists.txt @@ -0,0 +1,16 @@ +add_subdirectory(entities) +add_subdirectory(httpClient) +add_subdirectory(core) +add_subdirectory(gui) + +set(libClientEntities_LIB ${libClientEntities_LIB} PARENT_SCOPE) +set(libClientEntities_INCLUDE_DIRS ${libClientEntities_INCLUDE_DIRS} PARENT_SCOPE) + +set(HTTPCLIENT_lib_LIB ${HTTPCLIENT_lib_LIB} PARENT_SCOPE) +set(HTTPCLIENT_lib_INCLUDE_DIRS ${HTTPCLIENT_lib_INCLUDE_DIRS} PARENT_SCOPE) + +set(CORE_lib_LIB ${CORE_lib_LIB} PARENT_SCOPE) +set(CORE_lib_INCLUDE_DIRS ${CORE_lib_INCLUDE_DIRS} PARENT_SCOPE) + +set(GUI_lib_LIB ${GUI_lib_LIB} PARENT_SCOPE) +set(GUI_lib_INCLUDE_DIRS ${GUI_lib_INCLUDE_DIRS} PARENT_SCOPE) \ No newline at end of file diff --git a/client/internal/core/CMakeLists.txt b/client/internal/core/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a493bebffe9d6c3c3afdda10485001c78a04876d --- /dev/null +++ b/client/internal/core/CMakeLists.txt @@ -0,0 +1,18 @@ +project("CoreLib") + +set(CMAKE_CXX_STANDARD 20) + +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) +file(GLOB INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) + +include_directories(${INCLUDE_DIRS} ${libClientEntities_INCLUDE_DIRS} ${HTTPCLIENT_lib_INCLUDE_DIRS} ${Boost_INCLUDE_DIR}) + +add_library(${PROJECT_NAME} ${SOURCES} ${INCLUDES}) +target_link_libraries(${PROJECT_NAME} ${libClientEntities_LIB} ${HTTPCLIENT_lib_LIB} ${Boost_LIBRARIES}) + +set(CORE_lib_LIB ${PROJECT_NAME}) +set(CORE_lib_LIB ${CORE_lib_LIB} PARENT_SCOPE) + +set(CORE_lib_INCLUDE_DIRS ${LIB_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(CORE_lib_INCLUDE_DIRS ${CORE_lib_INCLUDE_DIRS} PARENT_SCOPE) \ No newline at end of file diff --git a/client/internal/core/include/Core.h b/client/internal/core/include/Core.h new file mode 100644 index 0000000000000000000000000000000000000000..c837a25f5451bcd819596d0f9c5a7629d4c5c98c --- /dev/null +++ b/client/internal/core/include/Core.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include "Task.h" +#include "Solution.h" + +class Core { +public: + static unsigned signUp(const std::string &login, const std::string &username, const std::string &pass); + + static unsigned login(const std::string &login, const std::string &pass); + + static std::vector getAllTasks(); + + static Solution submitSolution(const int& task_id, const std::string& filename, const std::string& path_to_file); + + static unsigned int createTask(const std::string &desc); + + static void logout(); + +private: + static std::size_t user_id; +}; diff --git a/client/internal/core/src/Core.cpp b/client/internal/core/src/Core.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e200e633d3a350271d6c77f0f8ad4f70d4189282 --- /dev/null +++ b/client/internal/core/src/Core.cpp @@ -0,0 +1,40 @@ +// +// Created by Николай Степанов on 11.05.2023. +// +#include "Core.h" +#include "HttpClientManager.h" + +const std::string CLIENT_IP = "0.0.0.0"; +const std::string CLIENT_PORT = "8080"; +HttpClientManager client(CLIENT_IP, CLIENT_PORT); + +std::size_t Core::user_id = -1; + +unsigned Core::signUp(const std::string &login, const std::string &username, const std::string &pass) { + auto res = client.registerUser(login, username, pass); + Core::user_id = res.second.id; + return res.first; +} + +unsigned Core::login(const std::string &login, const std::string &pass) { + auto res = client.loginUser(login, pass); + Core::user_id = res.second.id; + return res.first; +} + +void Core::logout() { + Core::user_id = -1; +} + +std::vector Core::getAllTasks() { + return client.getAllTasks(); +} + +Solution Core::submitSolution(const int &task_id, const std::string& filename, const std::string &path_to_file) { + return client.submitSolution(user_id, task_id, filename, path_to_file); +} + +unsigned int Core::createTask(const std::string &desc) { + return client.createTask(desc); +} + diff --git a/client/internal/entities/CMakeLists.txt b/client/internal/entities/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..40875190d755991243c507b304a1b64c3d67d28e --- /dev/null +++ b/client/internal/entities/CMakeLists.txt @@ -0,0 +1,19 @@ +project("ClientEntitiesLib") + +set(LIB_NAME libClientEntities) + +file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -O2 -pedantic -Wformat=2 -Wfloat-equal -Wconversion \ +-Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align") + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lboost_filesystem") + +add_library(${LIB_NAME} ${SOURCES} ${HEADERS}) +target_include_directories(${LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + +set(libClientEntities_LIB ${LIB_NAME}) +set(libClientEntities_LIB ${libClientEntities_LIB} PARENT_SCOPE) +set(libClientEntities_INCLUDE_DIRS ${LIB_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(libClientEntities_INCLUDE_DIRS ${libClientEntities_INCLUDE_DIRS} PARENT_SCOPE) \ No newline at end of file diff --git a/client/internal/entities/include/Metric.h b/client/internal/entities/include/Metric.h new file mode 100644 index 0000000000000000000000000000000000000000..db320cf9732f1a3b1031e4ff370f717ae99f4ff0 --- /dev/null +++ b/client/internal/entities/include/Metric.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct Metric { + std::string name; + unsigned int value; +}; diff --git a/client/internal/entities/include/Solution.h b/client/internal/entities/include/Solution.h new file mode 100644 index 0000000000000000000000000000000000000000..4cf7061a36531ecf61daf2c070eed8a569958042 --- /dev/null +++ b/client/internal/entities/include/Solution.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include + +struct Solution { + size_t id; + std::string source; + std::string result; +}; \ No newline at end of file diff --git a/client/internal/entities/include/Task.h b/client/internal/entities/include/Task.h new file mode 100644 index 0000000000000000000000000000000000000000..de9c48848fffdc53ce7675dcf5112077f6ecffe9 --- /dev/null +++ b/client/internal/entities/include/Task.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +struct Task{ + std::size_t id; + std::string description; + + Task(std::size_t id, std::string_view desc); +}; diff --git a/client/internal/entities/include/User.h b/client/internal/entities/include/User.h new file mode 100644 index 0000000000000000000000000000000000000000..94825402abfefa1f9518fd38f9f35389aef7eae7 --- /dev/null +++ b/client/internal/entities/include/User.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +struct User { + size_t id; + std::string login; + std::string password; + std::string username; +}; \ No newline at end of file diff --git a/client/internal/entities/src/Task.cpp b/client/internal/entities/src/Task.cpp new file mode 100644 index 0000000000000000000000000000000000000000..38e9e6e73437acd5846fdca098ce1f251d024254 --- /dev/null +++ b/client/internal/entities/src/Task.cpp @@ -0,0 +1,3 @@ +#include "Task.h" + +Task::Task(std::size_t id, std::string_view desc) : id(id), description(desc) {} diff --git a/client/internal/gui/CMakeLists.txt b/client/internal/gui/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..419c4dd5e925504b3455a42a991af42ab5c7bb12 --- /dev/null +++ b/client/internal/gui/CMakeLists.txt @@ -0,0 +1,30 @@ +project("GuiLib") + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) +file(GLOB INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) + +set(CMAKE_PREFIX_PATH "~/Qt/6.5.0/macos/lib/cmake") +find_package(Qt6 REQUIRED COMPONENTS Widgets Core Gui) + +include_directories(${INCLUDE_DIRS} ${libClientEntities_INCLUDE_DIRS} ${CORE_lib_INCLUDE_DIRS}) + +add_library(${PROJECT_NAME} ${SOURCES} ${INCLUDES}) +target_link_libraries(${PROJECT_NAME} ${libClientEntities_LIB} ${CORE_lib_LIB} Qt6::Widgets Qt6::Core Qt6::Gui) + +set(GUI_lib_LIB ${PROJECT_NAME}) +set(GUI_lib_LIB ${GUI_lib_LIB} PARENT_SCOPE) + +set(GUI_lib_INCLUDE_DIRS ${INCLUDE_DIRS}) +set(GUI_lib_INCLUDE_DIRS ${GUI_lib_INCLUDE_DIRS} PARENT_SCOPE) + +add_executable(gui_run main.cpp) +target_link_libraries(gui_run ${GUI_lib_LIB}) + +enable_testing() +add_subdirectory(tests) \ No newline at end of file diff --git a/client/internal/gui/include/AddTaskDialog.h b/client/internal/gui/include/AddTaskDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..c5b6547938e1d78a2b6b6e47afa745dcd32702ab --- /dev/null +++ b/client/internal/gui/include/AddTaskDialog.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class AddTaskDialog : public QDialog { +Q_OBJECT + +public: + explicit AddTaskDialog(QWidget *parent = nullptr); + +public slots: + void on_createButton_clicked(); + void on_backButton_clicked(); + +private: + QVBoxLayout* verticalLayout = nullptr; + QLabel* label = nullptr; + QTextEdit* editor = nullptr; + QPushButton *createButton = nullptr; + QPushButton *backButton = nullptr; + + void setupUi(QDialog *AddTaskDialog); +}; diff --git a/client/internal/gui/include/AuthDialog.h b/client/internal/gui/include/AuthDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..b8917b88db833d72a7d6fe91250dd5f809bac228 --- /dev/null +++ b/client/internal/gui/include/AuthDialog.h @@ -0,0 +1,37 @@ +#ifndef AUTH_DIALOG_H +#define AUTH_DIALOG_H + +#include +#include +#include +#include +#include +#include +#include + +class AuthDialog : public QDialog { + Q_OBJECT + +public: + explicit AuthDialog(QWidget *parent = nullptr); + +public slots: + void on_loginButton_clicked(); + void on_backButton_clicked(); + +private: + QVBoxLayout *verticalLayout = nullptr; + QWidget *fields = nullptr; + QFormLayout* fieldsLayout = nullptr; + QLabel *loginLabel = nullptr; + QLineEdit *login = nullptr; + QLabel *passwordLabel = nullptr; + QLineEdit *password = nullptr; + QPushButton *loginButton = nullptr; + QPushButton *backButton = nullptr; + + void setupUi(QDialog *AuthDialog); + void retranslateUi(QDialog *AuthDialog); +}; + +#endif // AUTH_DIALOG_H diff --git a/client/internal/gui/include/EntryWindow.h b/client/internal/gui/include/EntryWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..4bc4f111548a879970b81fdf6a855b1a64f9bb2d --- /dev/null +++ b/client/internal/gui/include/EntryWindow.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include + +#include "AuthDialog.h" +#include "SignUpDialog.h" + +class EntryWindow : public QMainWindow { +Q_OBJECT + +public: + EntryWindow(QWidget *parent = nullptr); + +signals: + void showTasksWindow(); + +public slots: + void on_exitButton_clicked(); + void on_loginButton_clicked(); + void on_signUpButton_clicked(); + +private: + + QWidget *centralwidget = nullptr; + QVBoxLayout *verticalLayout = nullptr; + QLabel *introLabel = nullptr; + QPushButton *loginButton = nullptr; + QSpacerItem *horizontalSpacer = nullptr; + QPushButton *signUpButton = nullptr; + QSpacerItem *horizontalSpacer_2 = nullptr; + QPushButton *exitButton = nullptr; + QMenuBar *menubar = nullptr; + QStatusBar *statusbar = nullptr; + + void setupUi(QMainWindow *EntryWindow); + void retranslateUi(QMainWindow *EntryWindow); +}; + +#endif // MAINWINDOW_H diff --git a/client/internal/gui/include/SignUpDialog.h b/client/internal/gui/include/SignUpDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..f9da4f50d8f2438ddce831cb3c4b9ce949ce1061 --- /dev/null +++ b/client/internal/gui/include/SignUpDialog.h @@ -0,0 +1,40 @@ +#ifndef SIGN_UP_DIALOG_H +#define SIGN_UP_DIALOG_H + +#include +#include +#include +#include +#include +#include + +class SignUpDialog : public QDialog { +Q_OBJECT + +public: + explicit SignUpDialog(QWidget *parent = nullptr); + +private slots: + void on_signUpButton_clicked(); + void on_backButton_clicked(); + +private: + QVBoxLayout *verticalLayout = nullptr; + QWidget *fields = nullptr; + QFormLayout* fieldsLayout = nullptr; + QLabel *loginLabel = nullptr; + QLineEdit *login = nullptr; + QLabel* usernameLabel = nullptr; + QLineEdit *username = nullptr; + QLabel *passwordLabel = nullptr; + QLineEdit *password = nullptr; + QLabel *passwordRepeatLabel = nullptr; + QLineEdit *passwordRepeat = nullptr; + QPushButton *signUpButton = nullptr; + QPushButton *backButton = nullptr; + + void setupUi(QDialog *SignUpDialog); + void retranslateUi(QDialog *SignUpDialog); +}; + +#endif // SIGN_UP_DIALOG_H diff --git a/client/internal/gui/include/SolutionsWindow.h b/client/internal/gui/include/SolutionsWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..3107c18d87dc5c0a8a58b5f3198ac51b2f2b810c --- /dev/null +++ b/client/internal/gui/include/SolutionsWindow.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Task.h" +#include "TasksWindow.h" + +class SolutionsWindow : public QMainWindow { +Q_OBJECT + +public: + explicit SolutionsWindow(Task task, QWidget *parent = nullptr); + +private slots: + void on_backButton_clicked(); + void on_chooseFileButton_clicked(); + void on_sendButton_clicked(); + +private: + + Task task; + std::string path_to_file; + + QWidget *centralwidget = nullptr; + QGroupBox* taskBox = nullptr; + QVBoxLayout* taskLayout = nullptr; + QVBoxLayout* verticalLayout = nullptr; + QLabel* taskDescription = nullptr; + QPushButton* chooseFileButton = nullptr; + QLabel* filename = nullptr; + QPushButton* sendButton = nullptr; + QTextEdit* result = nullptr; + QPushButton* backButton = nullptr; + + void setupUi(QMainWindow *UserWindow); +}; diff --git a/client/internal/gui/include/TasksWindow.h b/client/internal/gui/include/TasksWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..3ba92af7eb7eb95cd43e7349026b8005b406ec1b --- /dev/null +++ b/client/internal/gui/include/TasksWindow.h @@ -0,0 +1,49 @@ +#ifndef USERWINDOW_H +#define USERWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Task.h" + +class TasksWindow : public QMainWindow { +Q_OBJECT + +public: + explicit TasksWindow(QWidget *parent = nullptr); + +public slots: + void on_backButton_clicked(); + void on_addTaskButton_clicked(); + void on_goToTaskButton_clicked(); + void indexChanged(); + +private: + + QWidget *centralwidget = nullptr; + QVBoxLayout* verticalLayout = nullptr; + QGroupBox *taskChooseGroupBox = nullptr; + QVBoxLayout* taskVerticalLayout = nullptr; + QLabel* label = nullptr; + QComboBox* tasks = nullptr; + QTextEdit* taskDescription = nullptr; + QPushButton* goToTaskButton = nullptr; + QPushButton* addTaskButton = nullptr; + QPushButton* backButton = nullptr; + QWidget* buttonsWidget = nullptr; + QHBoxLayout* buttonsLayout = nullptr; + + std::vector tasks_vector; + + void setupUi(QMainWindow *UserWindow); + void updateTasks(); +}; + +#endif // USERWINDOW_H diff --git a/client/internal/gui/include/UIManager.h b/client/internal/gui/include/UIManager.h new file mode 100644 index 0000000000000000000000000000000000000000..bc6e071114c739df3519b60134fe6528dfbcfdc5 --- /dev/null +++ b/client/internal/gui/include/UIManager.h @@ -0,0 +1,22 @@ +#ifndef INCLUDE_UIMANAGER_H_ +#define INCLUDE_UIMANAGER_H_ + +#include +#include "EntryWindow.h" +#include "TasksWindow.h" + +class UIManager : public QWidget { +Q_OBJECT +public: + UIManager(QWidget *parent = nullptr); + +public slots: + void showEntryWindow(); + void showTasksWindow(); + +private: + EntryWindow* entryWindow = nullptr; + TasksWindow* userWindow = nullptr; +}; + +#endif // INCLUDE_UIMANAGER_H_ diff --git a/client/internal/gui/main.cpp b/client/internal/gui/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b348568625fca7832c6ed4625f30bc4129db0e10 --- /dev/null +++ b/client/internal/gui/main.cpp @@ -0,0 +1,13 @@ +#include "EntryWindow.h" +#include "TasksWindow.h" + +#include + +#include "UIManager.h" + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + UIManager uiManager; + uiManager.showEntryWindow(); + return a.exec(); +} diff --git a/client/internal/gui/src/AddTaskDialog.cpp b/client/internal/gui/src/AddTaskDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..495500107ebd60e9fe405b2e1864e196f6dc10e4 --- /dev/null +++ b/client/internal/gui/src/AddTaskDialog.cpp @@ -0,0 +1,59 @@ +#include "AddTaskDialog.h" + +#include +#include + +#include "Core.h" + +AddTaskDialog::AddTaskDialog(QWidget *parent) : QDialog(parent) { + setupUi(this); + connect(createButton, &QPushButton::clicked, this, &AddTaskDialog::on_createButton_clicked); + connect(backButton, &QPushButton::clicked, this, &AddTaskDialog::on_backButton_clicked); +} + +void AddTaskDialog::setupUi(QDialog *AddTaskDialog) { + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(AddTaskDialog->sizePolicy().hasHeightForWidth()); + AddTaskDialog->setSizePolicy(sizePolicy); + + verticalLayout = new QVBoxLayout(AddTaskDialog); + + label = new QLabel(AddTaskDialog); + label->setText(QString::fromUtf8("Введите текст задания")); + verticalLayout->addWidget(label); + + editor = new QTextEdit(AddTaskDialog); + verticalLayout->addWidget(editor); + + createButton = new QPushButton(AddTaskDialog); + createButton->setText(QString::fromUtf8("Создать задание")); + verticalLayout->addWidget(createButton); + + backButton = new QPushButton(AddTaskDialog); + backButton->setText(QString::fromUtf8("Назад")); + verticalLayout->addWidget(backButton); +} + +void AddTaskDialog::on_createButton_clicked() { + std::string desc = editor->toPlainText().toUtf8().constData(); + if (desc.empty()) { + QMessageBox::warning(this, "Ошибка отправки", "Описание не может быть пустыми"); + return; + } + unsigned result = Core::createTask(desc); + switch (result) { + case 200: + accept(); + close(); + break; + default: + QMessageBox::critical(this, "Отправка невозможна", "Нет соединения с сервером"); + } +} + +void AddTaskDialog::on_backButton_clicked() { + reject(); + close(); +} diff --git a/client/internal/gui/src/AuthDialog.cpp b/client/internal/gui/src/AuthDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..619f2e62ce2d0b924ad72ea8a52f4f736599a7e8 --- /dev/null +++ b/client/internal/gui/src/AuthDialog.cpp @@ -0,0 +1,84 @@ +#include "AuthDialog.h" + +#include +#include + +#include "Core.h" + +AuthDialog::AuthDialog(QWidget *parent) : QDialog(parent) { setupUi(this); } + +void AuthDialog::setupUi(QDialog *AuthDialog) { + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(AuthDialog->sizePolicy().hasHeightForWidth()); + AuthDialog->setSizePolicy(sizePolicy); + + verticalLayout = new QVBoxLayout(AuthDialog); + + fields = new QWidget(AuthDialog); + fields->setEnabled(true); + + fieldsLayout = new QFormLayout(fields); + + loginLabel = new QLabel(this); + login = new QLineEdit(this); + + fieldsLayout->addRow(loginLabel, login); + + passwordLabel = new QLabel(this); + password = new QLineEdit(this); + password->setEchoMode(QLineEdit::Password); + + fieldsLayout->addRow(passwordLabel, password); + + verticalLayout->addWidget(fields); + + loginButton = new QPushButton(AuthDialog); + loginButton->setDefault(true); + + verticalLayout->addWidget(loginButton); + + backButton = new QPushButton(AuthDialog); + + verticalLayout->addWidget(backButton); + + retranslateUi(AuthDialog); + + connect(loginButton, &QPushButton::clicked, this, &AuthDialog::on_loginButton_clicked); + connect(backButton, &QPushButton::clicked, this, &AuthDialog::on_backButton_clicked); +} + +void AuthDialog::retranslateUi(QDialog *AuthDialog) { + AuthDialog->setWindowTitle("Вход"); + loginLabel->setText("Логин"); + passwordLabel->setText("Пароль"); + loginButton->setText("Войти"); + backButton->setText("Назад"); +} + +void AuthDialog::on_loginButton_clicked() { + std::string log = login->text().toUtf8().constData(); + std::string pass = password->text().toUtf8().constData(); + if (log.empty() || pass.empty()) { + QMessageBox::warning(this, "Ошибка авторизации", "Логин и пароль не могут быть пустыми"); + return; + } + unsigned result = Core::login(std::string(log), std::string(pass)); + switch (result) { + case 200: + accept(); + close(); + break; + case 404: + QMessageBox::warning(this, "Ошибка авторизации", "Неправильный логин или пароль"); + break; + default: + QMessageBox::critical(this, "Авторизация невозможна", "Нет соединения с сервером"); + } +} + +void AuthDialog::on_backButton_clicked() { + reject(); + close(); +} diff --git a/client/internal/gui/src/EntryWindow.cpp b/client/internal/gui/src/EntryWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a0a4dbccdef2d2f2c13fb4880b47c8130d65977 --- /dev/null +++ b/client/internal/gui/src/EntryWindow.cpp @@ -0,0 +1,91 @@ +#include "EntryWindow.h" + +#include +#include +#include +#include + +#include "AuthDialog.h" +#include "SignUpDialog.h" +#include "UIManager.h" + +EntryWindow::EntryWindow(QWidget *parent) : QMainWindow(parent) { setupUi(this); } + +void EntryWindow::setupUi(QMainWindow *EntryWindow) { + EntryWindow->resize(600, 600); + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(EntryWindow->sizePolicy().hasHeightForWidth()); + EntryWindow->setSizePolicy(sizePolicy); + + centralwidget = new QWidget(EntryWindow); + verticalLayout = new QVBoxLayout(centralwidget); + + introLabel = new QLabel(centralwidget); + introLabel->setText("Добро пожаловать в SourcedOut!"); + introLabel->setGeometry(QRect(140, 200, 331, 31)); + + verticalLayout->addWidget(introLabel, 0, Qt::AlignHCenter); + + loginButton = new QPushButton(centralwidget); + QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(loginButton->sizePolicy().hasHeightForWidth()); + loginButton->setSizePolicy(sizePolicy1); + verticalLayout->addWidget(loginButton); + + horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + verticalLayout->addItem(horizontalSpacer); + + signUpButton = new QPushButton(centralwidget); + sizePolicy1.setHeightForWidth(signUpButton->sizePolicy().hasHeightForWidth()); + signUpButton->setSizePolicy(sizePolicy1); + + verticalLayout->addWidget(signUpButton); + + horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + verticalLayout->addItem(horizontalSpacer_2); + + exitButton = new QPushButton(centralwidget); + sizePolicy1.setHeightForWidth(exitButton->sizePolicy().hasHeightForWidth()); + exitButton->setSizePolicy(sizePolicy1); + + verticalLayout->addWidget(exitButton); + + EntryWindow->setCentralWidget(centralwidget); + + connect(signUpButton, &QPushButton::clicked, this, &EntryWindow::on_signUpButton_clicked); + connect(loginButton, &QPushButton::clicked, this, &EntryWindow::on_loginButton_clicked); + connect(exitButton, &QPushButton::clicked, this, &EntryWindow::on_exitButton_clicked); + + retranslateUi(EntryWindow); +} + +void EntryWindow::retranslateUi(QMainWindow *EntryWindow) { + EntryWindow->setWindowTitle(QCoreApplication::translate("EntryWindow", "Главное окно", nullptr)); + loginButton->setText(QCoreApplication::translate("EntryWindow", "Войти", nullptr)); + signUpButton->setText(QCoreApplication::translate("EntryWindow", "Зарегистрироваться", nullptr)); + exitButton->setText(QCoreApplication::translate("EntryWindow", "Выйти", nullptr)); +} + +void EntryWindow::on_exitButton_clicked() { QApplication::quit(); } + +void EntryWindow::on_loginButton_clicked() { + AuthDialog authDialog(this); + if (authDialog.exec() == QDialog::Accepted) { + hide(); + qobject_cast(parent())->showTasksWindow(); + } +} + +void EntryWindow::on_signUpButton_clicked() { + SignUpDialog signupDialog(this); + if (signupDialog.exec() == QDialog::Accepted) { + hide(); + qobject_cast(parent())->showTasksWindow(); + } +} diff --git a/client/internal/gui/src/SignUpDialog.cpp b/client/internal/gui/src/SignUpDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d34cb5ba695004fe21807a2f80d1a8079c8e2f6 --- /dev/null +++ b/client/internal/gui/src/SignUpDialog.cpp @@ -0,0 +1,114 @@ +#include "SignUpDialog.h" + +#include +#include +#include + +#include "Core.h" + +SignUpDialog::SignUpDialog(QWidget *parent) : QDialog(parent) { + setupUi(this); +} + +void SignUpDialog::setupUi(QDialog *SignUpDialog) { + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(SignUpDialog->sizePolicy().hasHeightForWidth()); + SignUpDialog->setSizePolicy(sizePolicy); + + verticalLayout = new QVBoxLayout(SignUpDialog); + + fields = new QWidget(SignUpDialog); + fields->setEnabled(true); + + fieldsLayout = new QFormLayout(fields); + + loginLabel = new QLabel(this); + login = new QLineEdit(this); + + fieldsLayout->addRow(loginLabel, login); + + usernameLabel = new QLabel(this); + username = new QLineEdit(this); + + fieldsLayout->addRow(usernameLabel, username); + + passwordLabel = new QLabel(this); + password = new QLineEdit(this); + password->setEchoMode(QLineEdit::Password); + + fieldsLayout->addRow(passwordLabel, password); + + passwordRepeatLabel = new QLabel(this); + passwordRepeat = new QLineEdit(this); + passwordRepeat->setEchoMode(QLineEdit::Password); + + fieldsLayout->addRow(passwordRepeatLabel, passwordRepeat); + + verticalLayout->addWidget(fields); + + signUpButton = new QPushButton(SignUpDialog); + signUpButton->setDefault(true); + + verticalLayout->addWidget(signUpButton); + + backButton = new QPushButton(SignUpDialog); + + verticalLayout->addWidget(backButton); + + retranslateUi(SignUpDialog); + + connect(signUpButton, &QPushButton::clicked, this, &SignUpDialog::on_signUpButton_clicked); + connect(backButton, &QPushButton::clicked, this, &SignUpDialog::on_backButton_clicked); +} + +void SignUpDialog::retranslateUi(QDialog *SignUpDialog) { + SignUpDialog->setWindowTitle("Регистрация"); + loginLabel->setText("Введите логин"); + usernameLabel->setText("Введите имя"); + passwordLabel->setText("Введите пароль"); + passwordRepeatLabel->setText("Повторите пароль"); + signUpButton->setText("Зарегистрироваться"); + backButton->setText("Назад"); +} + +void SignUpDialog::on_signUpButton_clicked() { + std::string log = this->login->text().toUtf8().constData(); + std::string usr = this->username->text().toUtf8().constData(); + std::string pass = this->password->text().toUtf8().constData(); + std::string passRepeat = this->passwordRepeat->text().toUtf8().constData(); + if (pass == passRepeat && !log.empty() && !usr.empty()) { + unsigned response = Core::signUp(log, usr, pass); + switch (response) { + case 200: + QMessageBox::information(this, "Успех!", "Вы успешно зарегистрированы!"); + accept(); + close(); + break; + case 403: + QMessageBox::warning(this, "Регистрация невозможна", "Произошла ошибка! Попробуйте ещё раз. \n" + "\n" + "Логин должен быть в виде: example@mail.ru \n" + "Имя от 3 до 20 символов \n" + "Пароль от 8 до 30 символов"); + break; + default: + QMessageBox::critical(this, "Регистрация невозможна!", "Нет соединения с сервером!"); + } + + } else if (log.empty()) { + QMessageBox::warning(this, "Ошибка!", "Логин не может быть пустым!"); + } else if (usr.empty()) { + QMessageBox::warning(this, "Ошибка!", "Имя не может быть пустым!"); + } else { + QMessageBox::warning(this, "Ошибка!", "Пароли не совпадают!"); + } +} + +void SignUpDialog::on_backButton_clicked() { + reject(); + close(); +} + +// setupUi diff --git a/client/internal/gui/src/SolutionsWindow.cpp b/client/internal/gui/src/SolutionsWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..af3d1f47efc3bbca4bca002dceb26071d4b8d9ab --- /dev/null +++ b/client/internal/gui/src/SolutionsWindow.cpp @@ -0,0 +1,82 @@ +#include "SolutionsWindow.h" + +#include +#include +#include +#include + +#include "Solution.h" +#include "Core.h" + +SolutionsWindow::SolutionsWindow(Task task, QWidget *parent) : QMainWindow(parent), task(std::move(task)) { + setupUi(this); + connect(backButton, &QPushButton::clicked, this, &SolutionsWindow::on_backButton_clicked); + connect(chooseFileButton, &QPushButton::clicked, this, &SolutionsWindow::on_chooseFileButton_clicked); + connect(sendButton, &QPushButton::clicked, this, &SolutionsWindow::on_sendButton_clicked); +} + +void SolutionsWindow::setupUi(QMainWindow *SolutionsWindow) { + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(SolutionsWindow->sizePolicy().hasHeightForWidth()); + SolutionsWindow->setSizePolicy(sizePolicy); + + centralwidget = new QWidget(SolutionsWindow); + verticalLayout = new QVBoxLayout(centralwidget); + + taskBox = new QGroupBox(centralwidget); + taskLayout = new QVBoxLayout(taskBox); + + taskDescription = new QLabel(taskBox); + std::string description = task.description; + taskDescription->setText(QString(description.c_str())); + + taskLayout->addWidget(taskDescription); + + filename = new QLabel(this); + + chooseFileButton = new QPushButton(this); + chooseFileButton->setText(QString::fromUtf8("Выберите файл")); + + sendButton = new QPushButton(this); + sendButton->setText(QString::fromUtf8("Отправить")); + + result = new QTextEdit(this); + result->setReadOnly(true); + result->setText(QString::fromUtf8("Отправьте для принятия решения")); + + backButton = new QPushButton(this); + backButton->setText(QString::fromUtf8("Назад")); + + verticalLayout->addWidget(taskBox); + verticalLayout->addWidget(filename); + verticalLayout->addWidget(chooseFileButton); + verticalLayout->addWidget(sendButton); + verticalLayout->addWidget(result); + verticalLayout->addWidget(backButton); + + SolutionsWindow->setCentralWidget(centralwidget); +} + +void SolutionsWindow::on_backButton_clicked() { + close(); + qobject_cast(parent())->show(); +} + +void SolutionsWindow::on_chooseFileButton_clicked() { + QString fileName = QFileDialog::getOpenFileName(this, tr("Choose a file to send"), QDir::homePath()); + filename->setText(QFileInfo(fileName).fileName()); + path_to_file = fileName.toUtf8().constData(); +} + +void SolutionsWindow::on_sendButton_clicked() { + if (path_to_file.empty()) { + QMessageBox::warning(this, "Ошибка отправки", "Файл должен быть указан"); + return; + } + Solution sol = Core::submitSolution(task.id, filename->text().toUtf8().constData(), path_to_file); + result->setText(QString::fromStdString(sol.result)); +} + + diff --git a/client/internal/gui/src/TasksWindow.cpp b/client/internal/gui/src/TasksWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f14654b56d4ea7c62af1cedeb87253f443216f5 --- /dev/null +++ b/client/internal/gui/src/TasksWindow.cpp @@ -0,0 +1,127 @@ +#include "TasksWindow.h" + +#include +#include +#include + +#include "SolutionsWindow.h" +#include "AddTaskDialog.h" +#include "UIManager.h" +#include "Core.h" + +TasksWindow::TasksWindow(QWidget *parent) : QMainWindow(parent) { + setupUi(this); + connect(tasks, &QComboBox::currentIndexChanged, this, &TasksWindow::indexChanged); + connect(goToTaskButton, &QPushButton::clicked, this, &TasksWindow::on_goToTaskButton_clicked); + connect(backButton, &QPushButton::clicked, this, &TasksWindow::on_backButton_clicked); + connect(addTaskButton, &QPushButton::clicked, this, &TasksWindow::on_addTaskButton_clicked); +} + + + +void TasksWindow::setupUi(QMainWindow *UserWindow) { + tasks_vector = Core::getAllTasks(); + + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(UserWindow->sizePolicy().hasHeightForWidth()); + UserWindow->setSizePolicy(sizePolicy); + + centralwidget = new QWidget(UserWindow); + verticalLayout = new QVBoxLayout(centralwidget); + + taskChooseGroupBox = new QGroupBox(centralwidget); + sizePolicy.setHeightForWidth(taskChooseGroupBox->sizePolicy().hasHeightForWidth()); + taskChooseGroupBox->setFixedHeight(85); + taskChooseGroupBox->setSizePolicy(sizePolicy); + + taskVerticalLayout = new QVBoxLayout(taskChooseGroupBox); + + label = new QLabel(this); + label->setText(QString::fromUtf8("Выберите номер задания:")); + + tasks = new QComboBox(this); + for (int i = 0; i < tasks_vector.size(); i++) { + tasks->insertItem(i, QString::number(tasks_vector[i].id)); + } + + if (tasks_vector.empty()) { + tasks->insertItem(0, QString::fromUtf8("Пока заданий нет")); + } + tasks->setCurrentIndex(0); + + taskVerticalLayout->addWidget(label); + taskVerticalLayout->addWidget(tasks); + + taskDescription = new QTextEdit(this); + taskDescription->setReadOnly(true); + std::string description = tasks_vector.empty() ? "" : tasks_vector[0].description; + taskDescription->setText(QString(description.c_str())); + + buttonsWidget = new QWidget(centralwidget); + sizePolicy.setHeightForWidth(buttonsWidget->sizePolicy().hasHeightForWidth()); + buttonsWidget->setFixedHeight(45); + + buttonsLayout = new QHBoxLayout(buttonsWidget); + + goToTaskButton = new QPushButton(this); + goToTaskButton->setText(QString::fromUtf8("Перейти к сдаче")); + + if (tasks_vector.empty()) + goToTaskButton->setDisabled(true); + + addTaskButton = new QPushButton(this); + addTaskButton->setText(QString::fromUtf8("Добавить задание")); + + backButton = new QPushButton(this); + backButton->setText(QString::fromUtf8("Выйти")); + + buttonsLayout->addWidget(goToTaskButton); + buttonsLayout->addWidget(addTaskButton); + buttonsLayout->addWidget(backButton); + + verticalLayout->addWidget(taskChooseGroupBox); + verticalLayout->addWidget(taskDescription); + verticalLayout->addWidget(buttonsWidget); + + UserWindow->setCentralWidget(centralwidget); +} + +void TasksWindow::indexChanged() { + std::string description = tasks_vector[tasks->currentIndex()].description; + taskDescription->setText(QString(description.c_str())); +} + +void TasksWindow::on_backButton_clicked() { + Core::logout(); + close(); + qobject_cast(parent())->showEntryWindow(); +} + +void TasksWindow::on_goToTaskButton_clicked() { + hide(); + auto* solutionsWindow = new SolutionsWindow(tasks_vector[tasks->currentIndex()], this); + solutionsWindow->show(); +} + +void TasksWindow::on_addTaskButton_clicked() { + AddTaskDialog addTaskDialog(this); + if (addTaskDialog.exec() == QDialog::Accepted) { + updateTasks(); + } +} + +void TasksWindow::updateTasks() { + tasks_vector = Core::getAllTasks(); + tasks->clear(); + for (int i = 0; i < tasks_vector.size(); i++) { + tasks->insertItem(i, QString::number(tasks_vector[i].id)); + } + tasks->setCurrentIndex(0); + + std::string description = tasks_vector[0].description; + taskDescription->setText(QString(description.c_str())); + + goToTaskButton->setDisabled(false); +} diff --git a/client/internal/gui/src/UIManager.cpp b/client/internal/gui/src/UIManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..101e034b7ae11e86dea4f5e357bf4268ef5ee163 --- /dev/null +++ b/client/internal/gui/src/UIManager.cpp @@ -0,0 +1,19 @@ +#include "UIManager.h" + +#include "EntryWindow.h" +#include "TasksWindow.h" +#include "SignUpDialog.h" +#include "AuthDialog.h" + +void UIManager::showEntryWindow() { + entryWindow->show(); +} + +void UIManager::showTasksWindow() { + userWindow->show(); +} + +UIManager::UIManager(QWidget *parent) : QWidget(parent) { + userWindow = new TasksWindow(this); + entryWindow = new EntryWindow(this); +} diff --git a/client/internal/gui/tests/CMakeLists.txt b/client/internal/gui/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..5877dc8f3540e9e3e1ab3fba04817c5a58af3ecb --- /dev/null +++ b/client/internal/gui/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +project(test_gui) + +set(CMAKE_CXX_STANDARD 20) +add_compile_options(-coverage) + +file(GLOB SOURCES *.cpp) + +enable_testing() +find_package(GTest REQUIRED) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +target_link_libraries(${PROJECT_NAME} ${GUI_lib_LIB} GTest::GTest GTest::gmock) +target_include_directories(${PROJECT_NAME} PUBLIC ${GUI_lib_INCLUDE_DIRS}) + +add_test(test_gui test_gui) \ No newline at end of file diff --git a/client/internal/gui/tests/main.cpp b/client/internal/gui/tests/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2348fac51843add819fb6d6ca628caa47193fa6f --- /dev/null +++ b/client/internal/gui/tests/main.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include "UIManager.h" +#include "EntryWindow.h" +#include "TasksWindow.h" + +#include + +TEST(ConstructorTest, EntryWindow) { + int fake = 0; + QApplication a(fake, nullptr); + EntryWindow mw; + EXPECT_NO_FATAL_FAILURE(mw.show()); + a.exec(); +} + +TEST(ConstructorTest, UserWindow) { + int fake = 0; + QApplication a(fake, nullptr); + TasksWindow uw; + EXPECT_NO_FATAL_FAILURE(uw.show()); + a.exec(); +} + +TEST(ConstructorTest, UIManagerMW) { + int fake_argc = 0; + QApplication a(fake_argc, nullptr); + UIManager um; + EXPECT_NO_FATAL_FAILURE(um.showEntryWindow()); + a.exec(); +} + +TEST(ConstructorTest, UIManagerUW) { + int fake_argc = 0; + QApplication a(fake_argc, nullptr); + UIManager um; + EXPECT_NO_FATAL_FAILURE(um.showTasksWindow()); + a.exec(); +} + +TEST(ConstructorTest, MainWindow_AuthDialog) { + int fake_argc = 0; + QApplication a(fake_argc, nullptr); + EntryWindow mainWindow; + EXPECT_NO_FATAL_FAILURE(mainWindow.getAuthDialog()->show()); +} + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/client/internal/httpClient/CMakeLists.txt b/client/internal/httpClient/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..591e57ae1873d5c1f827a6621cb2dc7b8dce7179 --- /dev/null +++ b/client/internal/httpClient/CMakeLists.txt @@ -0,0 +1,21 @@ +project("HttpClientLib") + +set(CMAKE_CXX_STANDARD 20) + +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/virtual) +file(GLOB INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/virtual/*.h) + +include_directories(${INCLUDE_DIRS} ${Boost_INCLUDE_DIR} ${libClientEntities_INCLUDE_DIRS}) + +add_library(${PROJECT_NAME} ${SOURCES} ${INCLUDES}) +target_link_libraries(${PROJECT_NAME} ${libClientEntities_LIB} ${Boost_LIBRARIES}) + +set(HTTPCLIENT_lib_LIB ${PROJECT_NAME}) +set(HTTPCLIENT_lib_LIB ${HTTPCLIENT_lib_LIB} PARENT_SCOPE) + +set(HTTPCLIENT_lib_INCLUDE_DIRS ${INCLUDE_DIRS}) +set(HTTPCLIENT_lib_INCLUDE_DIRS ${HTTPCLIENT_lib_INCLUDE_DIRS} PARENT_SCOPE) + +enable_testing() +add_subdirectory(tests) \ No newline at end of file diff --git a/client/internal/httpClient/include/HttpClient.h b/client/internal/httpClient/include/HttpClient.h new file mode 100644 index 0000000000000000000000000000000000000000..42c5f6dcaf17be01c356a07040684fae932bb308 --- /dev/null +++ b/client/internal/httpClient/include/HttpClient.h @@ -0,0 +1,36 @@ +#ifndef APP_HTTPCLIENT_HTTPCLIENT_HTTPCLIENT_H_ +#define APP_HTTPCLIENT_HTTPCLIENT_HTTPCLIENT_H_ + +#include +#include +#include +#include +#include + +#include "IHttpClient.h" + +namespace beast = boost::beast; +namespace http = beast::http; +namespace net = boost::asio; +using tcp = net::ip::tcp; + +class HttpClient : public IHttpClient { +public: + HttpClient(std::string_view host_, std::string_view port_); + http::response makeGetRequest(std::string_view target, std::string_view body) override; + http::response makePostRequest(std::string_view target, std::string_view body) override; + +private: + http::response makeRequest(std::string_view target, + http::verb method, + std::string_view body) override; + + net::io_context io_context{}; + tcp::resolver resolver; + beast::tcp_stream stream; + std::string host; + std::string port; +}; + + +#endif // APP_HTTPCLIENT_HTTPCLIENT_HTTPCLIENT_H_ diff --git a/client/internal/httpClient/include/HttpClientManager.h b/client/internal/httpClient/include/HttpClientManager.h new file mode 100644 index 0000000000000000000000000000000000000000..90073512ef5b5ad32418c89f44603dee97bfab05 --- /dev/null +++ b/client/internal/httpClient/include/HttpClientManager.h @@ -0,0 +1,37 @@ +#ifndef APP_HTTPCLIENT_HTTPCLIENTMANAGER_HTTPCLIENTMANAGER_H_ +#define APP_HTTPCLIENT_HTTPCLIENTMANAGER_HTTPCLIENTMANAGER_H_ + +#include +#include +#include + +#include "IHttpClient.h" +#include "User.h" +#include "Solution.h" +#include "Task.h" +#include "Metric.h" +#include "Serializer.h" + +class HttpClientManager { +public: + HttpClientManager(std::string_view host_, std::string_view port_); + + std::pair loginUser(const std::string &login, const std::string &password); + std::pair registerUser(const std::string &login, const std::string &username, + const std::string &password); + Solution submitSolution(const int& user_id, const int &task_id, const std::string& filename, + const std::string &path_to_solution); + unsigned int getAllSolutionsForTask(const int& user_id, const int& task_id); + std::vector getAllTasks(); + unsigned int createTask(const std::string& desc); +// std::vector getMetrics(const int& sol_id); + void setHttpClient(std::shared_ptr client_); +private: + std::string host; + std::string port; + std::shared_ptr client; + std::shared_ptr serializer; +}; + + +#endif // APP_HTTPCLIENT_HTTPCLIENTMANAGER_HTTPCLIENTMANAGER_H_ diff --git a/client/internal/httpClient/include/Serializer.h b/client/internal/httpClient/include/Serializer.h new file mode 100644 index 0000000000000000000000000000000000000000..0e874c1895ba4f9d3f372eb71987e7e3f3896595 --- /dev/null +++ b/client/internal/httpClient/include/Serializer.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include +#include "User.h" +#include "Task.h" +#include "Solution.h" + +class Serializer { +public: + std::string serialLoginData(std::string_view login, std::string_view password); + std::string serialRegisterData(std::string_view login, std::string_view username, std::string_view password); + std::string serialSolutionData(const int& user_id, const int& task_id, const std::string& filename, + const std::string& path_to_file); + std::string serialNewTaskData(std::string_view desc); + + User deserialUserData(std::string_view body); + Solution deserialSolutionData(std::string_view body); + std::vector deserialAllTasks(std::string_view body); +}; + diff --git a/client/internal/httpClient/src/HttpClient.cpp b/client/internal/httpClient/src/HttpClient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2dd02453ad3ba76c2e309a8e637f7eadee6339db --- /dev/null +++ b/client/internal/httpClient/src/HttpClient.cpp @@ -0,0 +1,41 @@ +#include "httpClient.h" + +#include +#include + +HttpClient::HttpClient(std::string_view host_, std::string_view port_) : + host(host_), + port(port_), + resolver(io_context), + stream(io_context) {} + +http::response HttpClient::makeRequest(std::string_view target, + http::verb method, + std::string_view body) { + auto const results = resolver.resolve(host, port); + stream.connect(results); + http::request req{http::verb::get, target, 11}; + req.set(http::field::host, host); + req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + req.set(http::field::content_type, "text/plain"); + req.content_length(body.size()); + req.body() = body; + + http::write(stream, req); + + beast::flat_buffer buffer; + http::response res; + http::read(stream, buffer, res); + + return res; +} + +http::response HttpClient::makeGetRequest(std::string_view target, + std::string_view body) { + return makeRequest(target, http::verb::get, body); +} + +http::response HttpClient::makePostRequest(std::string_view target, + std::string_view body) { + return makeRequest(target, http::verb::post, body); +} diff --git a/client/internal/httpClient/src/HttpClientManager.cpp b/client/internal/httpClient/src/HttpClientManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f9f7cdf5a4c6e539ad8cfcd285031499a34c130 --- /dev/null +++ b/client/internal/httpClient/src/HttpClientManager.cpp @@ -0,0 +1,85 @@ +#include "HttpClientManager.h" + +#include +#include +#include + +#include "HttpClient.h" + +HttpClientManager::HttpClientManager(std::string_view host_, std::string_view port_) : + host(host_), port(port_), + client(std::make_shared(host_, port_)), + serializer(std::make_shared()) {} + +std::pair HttpClientManager::loginUser(const std::string &login, const std::string &password) { + std::string body = serializer->serialLoginData(login, password); + http::response res = client->makeGetRequest("/user/login", body); + unsigned status = res.result_int(); + std::string res_body; + for (auto seq : res.body().data()) { + auto* cbuf = boost::asio::buffer_cast(seq); + res_body.append(cbuf, boost::asio::buffer_size(seq)); + } + User user{0, "", "", ""}; + if (status == 200) + user = serializer->deserialUserData(res_body); + return {status, user}; +} + +std::pair HttpClientManager::registerUser(const std::string &login, const std::string &username, + const std::string &password) { + std::string body = serializer->serialRegisterData(login, username, password); + http::response res = client->makeGetRequest("/user/register", body); + unsigned status = res.result_int(); + std::string res_body; + for (auto seq : res.body().data()) { + auto* cbuf = boost::asio::buffer_cast(seq); + res_body.append(cbuf, boost::asio::buffer_size(seq)); + } + User user; + if (status == 200) { + user = serializer->deserialUserData(res_body); + } + return {status, user}; +} + +Solution HttpClientManager::submitSolution(const int &user_id, const int &task_id, const std::string& filename, + const std::string &path_to_sound) { + std::string body = serializer->serialSolutionData(user_id, task_id, filename, path_to_sound); + http::response res = client->makeGetRequest("/solution/submit", body); + std::cout << res << std::endl; + // unsigned status = res.result_int(); + std::string res_body; + for (auto seq : res.body().data()) { + auto* cbuf = boost::asio::buffer_cast(seq); + res_body.append(cbuf, boost::asio::buffer_size(seq)); + } + Solution sol = serializer->deserialSolutionData(res_body); + return sol; +} + +unsigned int HttpClientManager::getAllSolutionsForTask(const int &user_id, const int &task_id) { + return 0; +} + +void HttpClientManager::setHttpClient(std::shared_ptr client_) { + client = client_; +} + +std::vector HttpClientManager::getAllTasks() { + http::response res = client->makeGetRequest("/task/all", ""); + std::string res_body; + for (auto seq : res.body().data()) { + auto* cbuf = boost::asio::buffer_cast(seq); + res_body.append(cbuf, boost::asio::buffer_size(seq)); + } + std::vector tasks = serializer->deserialAllTasks(res_body); + return tasks; +} + +unsigned int HttpClientManager::createTask(const std::string &desc) { + std::string body = serializer->serialNewTaskData(desc); + http::response res = client->makeGetRequest("/task/create", body); + unsigned int result = res.result_int(); + return result; +} diff --git a/client/internal/httpClient/src/Serializer.cpp b/client/internal/httpClient/src/Serializer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..859db2c2076a27028d725cfcb8d4fc9c7fac6eb0 --- /dev/null +++ b/client/internal/httpClient/src/Serializer.cpp @@ -0,0 +1,90 @@ +#include "Serializer.h" + +#include +#include +#include +#include + +std::string Serializer::serialLoginData(std::string_view login, std::string_view password) { + boost::property_tree::ptree json; + json.put("login", login); + json.put("password", password); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +User Serializer::deserialUserData(std::string_view body) { + std::stringstream ss; + ss << body; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + User res = { + json.get("user_id"), + json.get("login"), + json.get("password"), + json.get("username") + }; + return res; +} + +std::string +Serializer::serialRegisterData(std::string_view login, std::string_view username, std::string_view password) { + boost::property_tree::ptree json; + json.put("login", login); + json.put("username", username); + json.put("password", password); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +std::vector Serializer::deserialAllTasks(std::string_view body) { + std::stringstream ss; + ss << body; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + std::vector tasks; + for (auto &sound : json.get_child("tasks")) { + Task new_task(sound.second.get("task_id"), sound.second.get("description")); + tasks.push_back(new_task); + } + return tasks; +} + +std::string Serializer::serialSolutionData(const int &user_id, const int &task_id, const std::string& filename, + const std::string& path_to_file) { + boost::property_tree::ptree json; + json.put("user_id", user_id); + json.put("task_id", task_id); + json.put("filename", filename); + std::ifstream file(path_to_file); + std::ostringstream sstr; + sstr << file.rdbuf(); + std::string source = sstr.str(); + json.put("filedata", source); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +Solution Serializer::deserialSolutionData(std::string_view body) { + std::stringstream ss; + ss << body; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + Solution res = { + json.get("sol_id"), + json.get("source"), + json.get("result"), + }; + return res; +} + +std::string Serializer::serialNewTaskData(std::string_view desc) { + boost::property_tree::ptree json; + json.put("description", desc); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} diff --git a/client/internal/httpClient/tests/CMakeLists.txt b/client/internal/httpClient/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7b0e0c73a0dbcc7dabb6b70d9db49dcf860836fc --- /dev/null +++ b/client/internal/httpClient/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +project(test_httpclient) + +set(CMAKE_CXX_STANDARD 20) +add_compile_options(-coverage) + +file(GLOB SOURCES *.cpp) + +enable_testing() +find_package(GTest REQUIRED) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +target_link_libraries(${PROJECT_NAME} ${HTTPCLIENT_lib_LIB} GTest::GTest GTest::gmock) +target_include_directories(${PROJECT_NAME} PUBLIC ${HTTPCLIENT_lib_INCLUDE_DIRS}) + +add_test(test_httpclient test_httpclient) \ No newline at end of file diff --git a/client/internal/httpClient/tests/HttpClientManagerSuite.cpp b/client/internal/httpClient/tests/HttpClientManagerSuite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9959566af4ef8b2e6a4e059454a31714fb1d8722 --- /dev/null +++ b/client/internal/httpClient/tests/HttpClientManagerSuite.cpp @@ -0,0 +1,63 @@ +#include +#include +#include "HttpClientManager.h" +#include "IHttpClient.h" + +class HttpClientMock : public IHttpClient { + public: + HttpClientMock() = default; + MOCK_METHOD(ResponseStruct, makeGetRequest, + (const Host& host, const std::string& target, const std::shared_ptr& params, + const std::shared_ptr& headers), + (override)); + MOCK_METHOD(ResponseStruct, makePostRequest, + (const Host& host, const std::string& target, const std::shared_ptr& body, + const std::shared_ptr& headers), + (override)); + MOCK_METHOD(ResponseStruct, parseResponse, (Response response), (override)); + MOCK_METHOD(std::string, createURL, (const std::string& target, const std::shared_ptr& params), (override)); + MOCK_METHOD(bool, connect, (unsigned short port), (override)); + MOCK_METHOD(ResponseStruct, makeRequest, + (const Host& host, const std::string& target, boost::beast::http::verb method, + const std::shared_ptr& params, const std::shared_ptr& body, + const std::shared_ptr& headers), + (override)); +}; + +class HttpClientManagerSuite : public ::testing::Test { + protected: + void SetUp() override { manager.setHttpClient(client); } + + std::shared_ptr client = std::make_shared(); + HttpClientManager manager = HttpClientManager("1", "1.1.1.1", 1, "."); +}; + +TEST_F(HttpClientManagerSuite, LoginUserTest) { + EXPECT_CALL(*client, makeGetRequest); + manager.loginUser("1", "2"); +} + +TEST_F(HttpClientManagerSuite, RegisterUserTest) { + EXPECT_CALL(*client, makePostRequest); + manager.registerUser("1", "2", "3"); +} + +TEST_F(HttpClientManagerSuite, SubmitSolutionTest) { + EXPECT_CALL(*client, makePostRequest); + manager.submitSolution(1, "2234"); +} + +TEST_F(HttpClientManagerSuite, GetAllSolutionsForTaskTest) { + EXPECT_CALL(*client, makeGetRequest); + manager.getAllSolutionsForTask(1, 2); +} + +TEST_F(HttpClientManagerSuite, GetAllTasksTest) { + EXPECT_CALL(*client, makeGetRequest); + manager.getAllTasks(); +} + +TEST_F(HttpClientManagerSuite, GetMetricsTest) { + EXPECT_CALL(*client, makeGetRequest); + manager.getMetrics(1); +} diff --git a/client/internal/httpClient/tests/HttpClientSuite.cpp b/client/internal/httpClient/tests/HttpClientSuite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fc3efbf59671e6f5adb447213969d39dc4842992 --- /dev/null +++ b/client/internal/httpClient/tests/HttpClientSuite.cpp @@ -0,0 +1,11 @@ +#include +#include + +#include "HttpClient.h" + +TEST(HttpClientSuite, WrongGetTest) { + HttpClient client; + Host h("1", "1.1.1.1", 80); + ResponseStruct res = client.makeGetRequest(h, "/ser/login"); + EXPECT_EQ(res.status, 500); +} diff --git a/client/internal/httpClient/tests/main.cpp b/client/internal/httpClient/tests/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9c20e8f9cc438548f3145e852cdb7074b9a5dfd --- /dev/null +++ b/client/internal/httpClient/tests/main.cpp @@ -0,0 +1,8 @@ +#include +#include + +int main(int argc, char** argv) { + ::testing::InitGoogleMock(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/client/internal/httpClient/virtual/IHttpClient.h b/client/internal/httpClient/virtual/IHttpClient.h new file mode 100644 index 0000000000000000000000000000000000000000..9de99fc020cdab8014deeedf8b44b79b7fc6fa1e --- /dev/null +++ b/client/internal/httpClient/virtual/IHttpClient.h @@ -0,0 +1,27 @@ +#ifndef APP_HTTPCLIENT_HTTPCLIENT_IHTTPCLIENT_H_ +#define APP_HTTPCLIENT_HTTPCLIENT_IHTTPCLIENT_H_ + +#include +#include +#include +#include +#include + +namespace beast = boost::beast; +namespace http = beast::http; +namespace net = boost::asio; +using tcp = net::ip::tcp; + +class IHttpClient { +public: + virtual http::response makeGetRequest(std::string_view target, std::string_view body) = 0; + virtual http::response makePostRequest(std::string_view target, std::string_view body) = 0; + +protected: + virtual http::response makeRequest(std::string_view target, + http::verb method, + std::string_view body) = 0; +}; + + +#endif // APP_HTTPCLIENT_HTTPCLIENT_HTTPCLIENT_H_ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e101d36528c09d9dc566b316291869e4ab112fcc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,43 @@ +version: "3.9" + +volumes: + database-volume: + +services: + db: + image: postgres + container_name: db + restart: always + build: + context: ./Docker + dockerfile: db.Dockerfile + environment: + POSTGRES_DB: "temp" + POSTGRES_USER: "temp" + POSTGRES_PASSWORD: "temp" + volumes: + - database-volume:/var/lib/postgresql/data + ports: + - "5432:5432" + networks: + - db-network + server: + image: raiden454/cpp-app:latest + networks: + - db-network + env_file: + - .env + volumes: + - .:/project + command: bash -c "make build-project && make server-run" + ports: + - "8080:8080" + depends_on: + - db + links: + - db:db + +networks: + db-network: + driver: bridge + name: db_network diff --git a/log.txt b/log.txt new file mode 100644 index 0000000000000000000000000000000000000000..d72fd2988823140d02a82d7cd5b1b30c53e0fbf3 --- /dev/null +++ b/log.txt @@ -0,0 +1,21 @@ +Opened database successfully: mydb +Opened database successfully: mydb +Opened database successfully: mydb +Opened database successfully: mydb +OK +Opened database successfully: mydb +OK +Opened database successfully: mydb +Opened database successfully: mydb +OK +Opened database successfully: mydb +OK +Opened database successfully: mydb +Opened database successfully: mydb +OK +Opened database successfully: mydb +OK +Opened database successfully: mydb +OK +Opened database successfully: mydb +OK diff --git a/run_format.sh b/run_format.sh new file mode 100755 index 0000000000000000000000000000000000000000..1eef1550568f785ee1a00429f4fb72ab0b71752e --- /dev/null +++ b/run_format.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +THIS_PATH="$(realpath "$0")" +THIS_DIR="$(dirname "$THIS_PATH")" + +FILE_LIST="$(find "$THIS_DIR/server" | grep -E ".*(\.cpp|\.h|\.hpp|\.hh)$")" + +echo -e "Files found to format = \n\"\"\"\n$FILE_LIST\n\"\"\"" + +clang-format --verbose -i --style=file $FILE_LIST \ No newline at end of file diff --git a/run_linters.sh b/run_linters.sh new file mode 100755 index 0000000000000000000000000000000000000000..058bda2f06957dd82e5bb9bedf25092136d3adf8 --- /dev/null +++ b/run_linters.sh @@ -0,0 +1,30 @@ +#!/bin/bash + + +#!/bin/bash +set -o pipefail + +function check_log() { + LOG=$( { ${1}; } 2>&1 ) + STATUS=$? + echo "$LOG" + if echo "$LOG" | grep -q -E "${2}" + then + exit 1 + fi + + if [ $STATUS -ne 0 ] + then + exit $STATUS + fi +} + +echo -e "\nRun linters" + +echo -e "\nRUN cpplint.py" +check_log "cpplint --extensions=cpp,hpp --recursive ./server/* ./client/*" "Can't open for reading" + +echo - e "\nRUN cppcheck" +check_log "cppcheck server --std=c++17 --enable=all" "\(information\)" + +echo -e "SUCCESS" \ No newline at end of file diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a87d5ea61fd8504ff60fac72672d6f00da9fb499 --- /dev/null +++ b/server/CMakeLists.txt @@ -0,0 +1,29 @@ +set(CMAKE_PREFIX_PATH build) +project(SourcedOut CXX) +find_package(Boost 1.81.0 REQUIRED) +IF(APPLE) + set(CMAKE_THREAD_LIBS_INIT "-lpthread") + set(CMAKE_HAVE_THREADS_LIBRARY 1) + set(CMAKE_USE_WIN32_THREADS_INIT 0) + set(CMAKE_USE_PTHREADS_INIT 1) + set(THREADS_PREFER_PTHREAD_FLAG ON) +ELSE() + find_package(Threads REQUIRED) +ENDIF() + +find_library(PQXX_LIB pqxx) +#find_package(libpqxx REQUIRED) +find_package(GTest REQUIRED) +#find_package(nlohmann_json REQUIRED) +message(STATUS ${nlohmann_json_LIBRARIES}) + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + +add_subdirectory(pkg) +add_subdirectory(internal) +add_subdirectory(cmd) + +message(STATUS ${Boost_LIBRARIES}) +set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-lpthread -pthread") + diff --git a/server/cmd/.env b/server/cmd/.env new file mode 100644 index 0000000000000000000000000000000000000000..7cac90d91390a5f095999d7b06b7b0e61d0ffc0a --- /dev/null +++ b/server/cmd/.env @@ -0,0 +1,6 @@ +PGHOSTADDR=0.0.0.0 +PGPORT=5432 +PGDATABASE=mydb +PGUSER=postgres +PGPASSWORD=root +POOL_SIZE=10 \ No newline at end of file diff --git a/server/cmd/CMakeLists.txt b/server/cmd/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fa268c279b04fc774eb72485f732f9be57013663 --- /dev/null +++ b/server/cmd/CMakeLists.txt @@ -0,0 +1,13 @@ +set(PROJECT_NAME "Server") + +project(${PROJECT_NAME}) + +add_executable(Server main.cpp) +target_link_libraries(${PROJECT_NAME} ${HTTPSERVER_LIB} ${SERVICE_lib_LIBRARY} ${libRepository_LIB} + ${METRICS_LIBRARY} ${libEntities_LIB} ${ANTLR4_LIB} ${libDbManager_LIB} ${PQXX_LIB}) +message("a = ${SERVICE_lib_INCLUDE_DIRS}") +target_include_directories(Server PUBLIC ${Boost_INCLUDE_DIR} ${HTTPSERVER_INCLUDE_DIRS} ${SERVICE_lib_INCLUDE_DIRS} ${libRepository_INCLUDE_DIRS} + ${libDbManager_INCLUDE_DIRS} ${METRICS_lib_INCLUDE_DIRS} ${libEntities_INCLUDE_DIRS} ${ANTLR4_LIB_INCLUDE_DIRS}) + +message("Built server") + diff --git a/server/cmd/main.cpp b/server/cmd/main.cpp new file mode 100755 index 0000000000000000000000000000000000000000..5a7fa95fe44b6e902b324d4e952148cc2ffc8b37 --- /dev/null +++ b/server/cmd/main.cpp @@ -0,0 +1,38 @@ +#include +#include + +#include "HttpServer.h" + +namespace net = boost::asio; +using tcp = boost::asio::ip::tcp; + +int main(int argc, char* argv[]) { + // Check command line arguments. + // if (argc != 5) + // { + // std::cerr << + // "Usage: http-server-async
\n" << + // "Example:\n" << + // " http-server-async 0.0.0.0 8080 . 1\n"; + // return EXIT_FAILURE; + // } + std::cout << "SERVER RUN12" << std::endl; + auto const address = net::ip::make_address("0.0.0.0"); + auto const port = static_cast(std::atoi("8080")); + auto const doc_root = std::make_shared("."); + auto const threads = std::max(1, std::atoi("1")); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // Create and launch a listening port + std::make_shared(ioc, tcp::endpoint{address, port}, doc_root)->run(); + + // Run the I/O service on the requested number of threads + std::vector v; + v.reserve(threads - 1); + for (auto i = threads - 1; i > 0; --i) v.emplace_back([&ioc] { ioc.run(); }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/conanfile.txt b/server/conanfile.txt similarity index 75% rename from conanfile.txt rename to server/conanfile.txt index 18ee8ce4289f4e6832e3384009f500ec0562d3b4..e4b350b05b6838cf5c8ae735fc792b5cb408c9a2 100644 --- a/conanfile.txt +++ b/server/conanfile.txt @@ -3,7 +3,7 @@ boost/1.81.0 antlr4-cppruntime/4.12.0 libpqxx/7.7.5 gtest/cci.20210126 - +nlohmann_json/3.11.2 [generators] CMakeDeps diff --git a/config.sh b/server/config.sh similarity index 100% rename from config.sh rename to server/config.sh diff --git a/server/internal/CMakeLists.txt b/server/internal/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..cf13b8aa4917b54da7dc41308712eb6662137267 --- /dev/null +++ b/server/internal/CMakeLists.txt @@ -0,0 +1,30 @@ +add_subdirectory(entities) +add_subdirectory(dbManager) +add_subdirectory(repository) +add_subdirectory(metrics) +add_subdirectory(service) +add_subdirectory(httpServer) + + +set(libEntities_LIB ${libEntities_LIB} PARENT_SCOPE) +set(libEntities_INCLUDE_DIRS ${libEntities_INCLUDE_DIRS} PARENT_SCOPE) + +set(METRICS_LIBRARY ${METRICS_LIBRARY} PARENT_SCOPE) +set(METRICS_lib_INCLUDE_DIRS ${METRICS_lib_INCLUDE_DIRS} PARENT_SCOPE) + +set(libDbManager_LIB ${libDbManager_LIB} PARENT_SCOPE) +set(libDbManager_INCLUDE_DIRS ${libDbManager_INCLUDE_DIRS} PARENT_SCOPE) + +set(libRepository_LIB ${libRepository_LIB} PARENT_SCOPE) +set(libRepository_INCLUDE_DIRS ${libRepository_INCLUDE_DIRS} PARENT_SCOPE) + +set(SERVICE_lib_LIBRARY ${SERVICE_lib_LIBRARY} PARENT_SCOPE) +set(SERVICE_lib_INCLUDE_DIRS ${SERVICE_lib_INCLUDE_DIRS} PARENT_SCOPE) + +set(HTTPSERVER_LIB ${HTTPSERVER_LIB} PARENT_SCOPE) +set(HTTPSERVER_INCLUDE_DIRS ${HTTPSERVER_INCLUDE_DIRS} PARENT_SCOPE) + +set(ALL_INCLUDE_DIRS ${HTTPSERVER_INCLUDE_DIRS}; ${SERVICE_lib_INCLUDE_DIRS}; ${libRepository_INCLUDE_DIRS}; + ${libDbManager_INCLUDE_DIRS}; ${METRICS_lib_INCLUDE_DIRS}; ${libEntities_INCLUDE_DIRS} PARENT_SCOPE) + +message("b = ${SERVICE_lib_INCLUDE_DIRS}") \ No newline at end of file diff --git a/server/internal/dbManager/CMakeLists.txt b/server/internal/dbManager/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9bb5cfa9b4ab3b5eacffe0598b4e4cfc4550fa41 --- /dev/null +++ b/server/internal/dbManager/CMakeLists.txt @@ -0,0 +1,30 @@ +project("DbManagerLib") + +set(LIB_NAME libDbManager) + +file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) + +message("SOURCES = ${SOURCES}") +message("HEADERS = ${HEADERS}") + + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -O2 -pedantic -Wformat=2 -Wfloat-equal -Wconversion \ +-Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align") + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lboost_filesystem") + + +add_library(${LIB_NAME} ${SOURCES} ${HEADERS}) +target_include_directories(${LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(${LIB_NAME} ${Boost_LIBRARIES} ${PQXX_LIB}) + +set(libDbManager_LIB ${LIB_NAME}) +set(libDbManager_LIB ${libDbManager_LIB} PARENT_SCOPE) + +set(libDbManager_INCLUDE_DIRS ${LIB_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(libDbManager_INCLUDE_DIRS ${libDbManager_INCLUDE_DIRS} PARENT_SCOPE) + +message("libDbManager_LIB = ${libDbManager_LIB}") +message("libDbManager_INCLUDE_DIRS = ${libDbManager_INCLUDE_DIRS}") + diff --git a/server/internal/dbManager/include/dbConnection.hpp b/server/internal/dbManager/include/dbConnection.hpp new file mode 100644 index 0000000000000000000000000000000000000000..375aecbb19e2c1f8920220018a3583d8ac884a9f --- /dev/null +++ b/server/internal/dbManager/include/dbConnection.hpp @@ -0,0 +1,25 @@ + +#ifndef SOURCEDOUT_DBCONNECTION_HPP +#define SOURCEDOUT_DBCONNECTION_HPP + +#include +// #include "dotenv.h" +// using namespace dotenv; +class dbConnection { + public: + dbConnection(); + [[nodiscard]] std::shared_ptr connection() const; + + private: + void establish_connection(); + + std::string m_dbhost = "localhost"; + int m_dbport = 5432; + std::string m_dbname = "demo"; + std::string m_dbuser = "postgres"; + std::string m_dbpass = "postgres"; + + std::shared_ptr m_connection; +}; + +#endif // SOURCEDOUT_DBCONNECTION_HPP diff --git a/server/internal/dbManager/include/dbManager.hpp b/server/internal/dbManager/include/dbManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7c0202adf0464d48e81c59099a07e48a66264ebb --- /dev/null +++ b/server/internal/dbManager/include/dbManager.hpp @@ -0,0 +1,33 @@ +#ifndef SOURCEDOUT_DBMANAGER_HPP +#define SOURCEDOUT_DBMANAGER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +// #include "dotenv.h" +// using namespace dotenv; +class dbManager { + public: + dbManager(); + + std::shared_ptr connection(); + + void freeConnection(const std::shared_ptr &); + + private: + const size_t POOL_SIZE = 10; + std::condition_variable m_condition; + + void createPool(); + + std::queue> connection_pool; + std::mutex m_mutex; +}; + +#endif // SOURCEDOUT_DBMANAGER_HPP diff --git a/server/internal/dbManager/src/dbConnection.cpp b/server/internal/dbManager/src/dbConnection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3a6afad7222177dd83d86ceb2ac739d22638707e --- /dev/null +++ b/server/internal/dbManager/src/dbConnection.cpp @@ -0,0 +1,17 @@ +// +// Created by qwert on 01.05.2023. +// + +// #include "../include/dbConnection.hpp" + +// #include + +// dbConnection::dbConnection() { establish_connection(); } + +// std::shared_ptr dbConnection::connection() const { return m_connection; } + +// // void dbConnection::establish_connection() { +// // pqxx::connection c("dbname =mydb" "user = temp password =temp hostaddr =db port = 5432"); +// // m_connection.reset(&c); + +// // } diff --git a/server/internal/dbManager/src/dbManager.cpp b/server/internal/dbManager/src/dbManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7a1f01746d58ead91f9b1bdd8a2d5ebe5128a386 --- /dev/null +++ b/server/internal/dbManager/src/dbManager.cpp @@ -0,0 +1,35 @@ + +#include "dbManager.hpp" + +dbManager::dbManager() { createPool(); } + +void dbManager::createPool() { + std::lock_guard locker_(m_mutex); + + for (auto i = 0; i < POOL_SIZE; i++) { + connection_pool.emplace(std::make_shared()); + } +} + +std::shared_ptr dbManager::connection() { + std::unique_lock lock_(m_mutex); + + while (connection_pool.empty()) { + m_condition.wait(lock_); + } + + auto conn_ = connection_pool.front(); + connection_pool.pop(); + + return conn_; +} + +void dbManager::freeConnection(const std::shared_ptr &conn_) { + std::unique_lock lock_(m_mutex); + + connection_pool.push(conn_); + + lock_.unlock(); + + m_condition.notify_one(); +} diff --git a/server/internal/entities/CMakeLists.txt b/server/internal/entities/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7b6ad66178ab491134855e3b0327e11e56c73072 --- /dev/null +++ b/server/internal/entities/CMakeLists.txt @@ -0,0 +1,31 @@ +project("EntitiesLib") + +set(LIB_NAME libEntities) + +file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) + +message("SOURCES = ${SOURCES}") +message("HEADERS = ${HEADERS}") + + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -O2 -pedantic -Wformat=2 -Wfloat-equal -Wconversion \ +-Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align") + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lboost_filesystem") + + +add_library(${LIB_NAME} ${SOURCES} ${HEADERS}) +target_include_directories(${LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +#target_link_libraries(${LIB_NAME} ${Boost_LIBRARIES} nlohmann_json::nlohmann_json) + +set(libEntities_LIB ${LIB_NAME}) +set(libEntities_LIB ${libEntities_LIB} PARENT_SCOPE) +set(libEntities_INCLUDE_DIRS ${LIB_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(libEntities_INCLUDE_DIRS ${libEntities_INCLUDE_DIRS} PARENT_SCOPE) + +message("libEntities_LIB = ${libEntities_LIB}") +message("libEntities_INCLUDE_DIRS = ${libEntities_INCLUDE_DIRS}") + +enable_testing() +add_subdirectory(tests) \ No newline at end of file diff --git a/server/internal/entities/include/MetricStat.hpp b/server/internal/entities/include/MetricStat.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b04001ed5d5921d0fe630f6a8e2434c4591b632d --- /dev/null +++ b/server/internal/entities/include/MetricStat.hpp @@ -0,0 +1,58 @@ +#ifndef SOURCEDOUT_METRICSTAT_HPP +#define SOURCEDOUT_METRICSTAT_HPP + +#include + +class MetricStat { + public: + MetricStat() noexcept; + + MetricStat(size_t solutionId, float textBasedRes, float tokenBasedRes, float treeBasedRes, bool verdict, + float meanRes) noexcept; + + MetricStat(size_t id, size_t solutionId, float textBasedRes, float tokenBasedRes, float treeBasedRes, bool verdict, + float meanRes) noexcept; + + [[nodiscard]] size_t getId() const noexcept; + + void setId(size_t id); + + [[nodiscard]] size_t getSolutionId() const noexcept; + + void setSolutionId(size_t solutionId) noexcept; + + [[nodiscard]] float getTextBasedRes() const noexcept; + + void setTextBasedRes(float textBasedRes) noexcept; + + [[nodiscard]] float getTokenBasedRes() const noexcept; + + void setTokenBasedRes(float tokenBasedRes) noexcept; + + [[nodiscard]] float getTreeBasedRes() const noexcept; + + void setTreeBasedRes(float treeBasedRes) noexcept; + + [[nodiscard]] bool isVerdict() const noexcept; + + void setVerdict(bool verdict) noexcept; + + [[nodiscard]] float getMeanRes() const noexcept; + + void setMeanRes(float meanRes) noexcept; + + bool operator==(const MetricStat &rhs) const noexcept; + + bool operator!=(const MetricStat &rhs) const noexcept; + + private: + size_t id; + size_t solution_id; + float text_based_res; + float token_based_res; + float tree_based_res; + bool verdict; + float mean_res; +}; + +#endif // SOURCEDOUT_METRICSTAT_HPP diff --git a/server/internal/entities/include/Solution.hpp b/server/internal/entities/include/Solution.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9a0b49ee3d41f61594ae986b28323eeacb2f92be --- /dev/null +++ b/server/internal/entities/include/Solution.hpp @@ -0,0 +1,75 @@ +#ifndef SOURCEDOUT_SOLUTION_HPP +#define SOURCEDOUT_SOLUTION_HPP + +#include +#include +#include + +class Solution { + public: + Solution(size_t id, std::string sendDate, size_t senderId, std::string source, size_t taskId, std::string result, + std::string tokens, std::string astTree, size_t orig_solution, std::string language) noexcept; + + Solution(std::string sendDate, size_t senderId, std::string source, size_t taskId, std::string result, + std::string tokens, std::string astTree, size_t orig_solution, std::string language) noexcept; + + Solution() noexcept; + + [[nodiscard]] size_t getId() const noexcept; + + [[nodiscard]] const std::string &getSendDate() const noexcept; + + void setSendDate(const std::string &sendDate) noexcept; + + [[nodiscard]] size_t getSenderId() const noexcept; + + void setSenderId(size_t senderId) noexcept; + + [[nodiscard]] const std::string &getSource() const noexcept; + + void setSource(const std::string &source) noexcept; + + [[nodiscard]] const std::string &getTokens() const noexcept; + + void setTokens(const std::string &tokens) noexcept; + + [[nodiscard]] const std::string &getAstTree() const noexcept; + + void setAstTree(const std::string &astTree) noexcept; + + [[nodiscard]] size_t getTaskId() const noexcept; + + void setTaskId(size_t taskId) noexcept; + + [[nodiscard]] const std::string &getResult() const noexcept; + + void setResult(const std::string &result) noexcept; + + [[nodiscard]] size_t getOrigSolution() const; + + void setOrigSolution(size_t origSolution); + + [[nodiscard]] const std::string &getLanguage() const; + + void setLanguage(const std::string &language); + + void setId(size_t id) noexcept; + + bool operator==(const Solution &rhs) const noexcept; + + bool operator!=(const Solution &rhs) const noexcept; + + private: + size_t id; + std::string send_date; + size_t sender_id; + std::string source; + std::string tokens; + std::string astTree; + size_t task_id; + std::string result; + size_t orig_solution = 0; + std::string language; +}; + +#endif // SOURCEDOUT_SOLUTION_HPP diff --git a/server/internal/entities/include/Task.hpp b/server/internal/entities/include/Task.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d34ffe19296089edf7e6964db4e6a0ad4c43bcd6 --- /dev/null +++ b/server/internal/entities/include/Task.hpp @@ -0,0 +1,39 @@ +#ifndef SOURCEDOUT_TASK_HPP +#define SOURCEDOUT_TASK_HPP +#include +#include +class Task { + private: + size_t id; + std::string description; + float treshhold; + std::string name; + + public: + Task(size_t id, std::string description_, float treshold_, std::string name) noexcept; + + Task(std::string description_, float treshold_, std::string name) noexcept; + + Task() noexcept; + + [[nodiscard]] size_t getId() const noexcept; + + [[nodiscard]] const std::string &getDescription() const noexcept; + + float getTreshhold() const noexcept; + + void setTreshhold(float treshhold) noexcept; + + void setDescription(const std::string &description) noexcept; + + void setId(size_t id) noexcept; + + [[nodiscard]] const std::string &getName() const; + + void setName(const std::string &name); + + bool operator==(const Task &rhs) const noexcept; + + bool operator!=(const Task &rhs) const noexcept; +}; +#endif // SOURCEDOUT_TASK_HPP diff --git a/server/internal/entities/include/User.hpp b/server/internal/entities/include/User.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1c4f6a3f8a92662d8156729ebc47ee5a3ec6c4f7 --- /dev/null +++ b/server/internal/entities/include/User.hpp @@ -0,0 +1,45 @@ +#ifndef SOURCEDOUT_USER_HPP + +#include +#include + +#define SOURCEDOUT_USER_HPP + +class User { + private: + size_t id; + std::string login; + std::string password; + std::string username; + + public: + User(size_t id_, std::string login_, std::string password_, std::string username_) noexcept; + + User(std::string login_, std::string password_, std::string username_) noexcept; + + User() noexcept; + + [[nodiscard]] const std::string &getLogin() const noexcept; + + void setLogin(const std::string &login) noexcept; + + [[nodiscard]] const std::string &getPassword() const noexcept; + + void setPassword(const std::string &password) noexcept; + + [[nodiscard]] const std::string &getUsername() const noexcept; + + void setUsername(const std::string &username) noexcept; + + [[nodiscard]] size_t getId() const noexcept; + + friend std::ostream &operator<<(std::ostream &os, const User &user) noexcept; + + void setId(size_t id) noexcept; + + bool operator==(const User &rhs) const noexcept; + + bool operator!=(const User &rhs) const noexcept; +}; + +#endif // SOURCEDOUT_USER_HPP diff --git a/server/internal/entities/src/MetricStat.cpp b/server/internal/entities/src/MetricStat.cpp new file mode 100644 index 0000000000000000000000000000000000000000..86fed05b69ce89c64aca8131be2d94f18e742eec --- /dev/null +++ b/server/internal/entities/src/MetricStat.cpp @@ -0,0 +1,57 @@ + +#include "MetricStat.hpp" + +MetricStat::MetricStat(unsigned long solutionId, float textBasedRes, float tokenBasedRes, float treeBasedRes, + bool verdict, float meanRes) noexcept + : id(0), + solution_id(solutionId), + text_based_res(textBasedRes), + token_based_res(tokenBasedRes), + tree_based_res(treeBasedRes), + verdict(verdict), + mean_res(meanRes) {} + +MetricStat::MetricStat(size_t id, size_t solutionId, float textBasedRes, float tokenBasedRes, float treeBasedRes, + bool verdict, float meanRes) noexcept + : id(id), + solution_id(solutionId), + text_based_res(textBasedRes), + token_based_res(tokenBasedRes), + tree_based_res(treeBasedRes), + verdict(verdict), + mean_res(meanRes) {} + +MetricStat::MetricStat() noexcept + : id(0), solution_id(0), text_based_res(0), token_based_res(0), tree_based_res(), verdict(true), mean_res(0) {} + +size_t MetricStat::getId() const noexcept { return id; } + +void MetricStat::setId(size_t id_) { id = id_; } + +size_t MetricStat::getSolutionId() const noexcept { return solution_id; } + +void MetricStat::setSolutionId(size_t solutionId) noexcept { solution_id = solutionId; } + +float MetricStat::getTextBasedRes() const noexcept { return text_based_res; } + +void MetricStat::setTextBasedRes(float textBasedRes) noexcept { text_based_res = textBasedRes; } + +float MetricStat::getTokenBasedRes() const noexcept { return token_based_res; } + +void MetricStat::setTokenBasedRes(float tokenBasedRes) noexcept { token_based_res = tokenBasedRes; } + +float MetricStat::getTreeBasedRes() const noexcept { return tree_based_res; } + +void MetricStat::setTreeBasedRes(float treeBasedRes) noexcept { tree_based_res = treeBasedRes; } + +bool MetricStat::isVerdict() const noexcept { return verdict; } + +void MetricStat::setVerdict(bool verdict_) noexcept { verdict = verdict_; } + +float MetricStat::getMeanRes() const noexcept { return mean_res; } + +void MetricStat::setMeanRes(float meanRes) noexcept { mean_res = meanRes; } + +bool MetricStat::operator==(const MetricStat &rhs) const noexcept { return id == rhs.id; } + +bool MetricStat::operator!=(const MetricStat &rhs) const noexcept { return !(rhs == *this); } diff --git a/server/internal/entities/src/Solution.cpp b/server/internal/entities/src/Solution.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0dfebf14677be85b002a6f4b7eaf2b447d270abe --- /dev/null +++ b/server/internal/entities/src/Solution.cpp @@ -0,0 +1,78 @@ + +#include "Solution.hpp" + +#include +#include + +Solution::Solution(size_t id, std::string sendDate, size_t senderId, std::string source, size_t taskId, + std::string result, std::string tokens, std::string astTree, size_t orig_solution, + std::string language_) noexcept + : id(id), + send_date(std::move(sendDate)), + sender_id(senderId), + source(std::move(source)), + tokens(std::move(tokens)), + astTree(std::move(astTree)), + task_id(taskId), + result(std::move(result)), + orig_solution(orig_solution), + language(std::move(language_)) {} + +Solution::Solution(std::string sendDate, size_t senderId, std::string source, size_t taskId, std::string result, + std::string tokens, std::string astTree, size_t orig_solution, std::string language_) noexcept + : id(0), + send_date(std::move(sendDate)), + sender_id(senderId), + source(std::move(source)), + tokens(std::move(tokens)), + astTree(std::move(astTree)), + task_id(taskId), + result(std::move(result)), + orig_solution(orig_solution), + language(std::move(language_)) {} + +size_t Solution::getId() const noexcept { return id; } + +void Solution::setId(size_t id_) noexcept { id = id_; } + +const std::string &Solution::getSendDate() const noexcept { return send_date; } + +void Solution::setSendDate(const std::string &sendDate) noexcept { send_date = sendDate; } + +size_t Solution::getSenderId() const noexcept { return sender_id; } + +void Solution::setSenderId(size_t senderId) noexcept { sender_id = senderId; } + +const std::string &Solution::getSource() const noexcept { return source; } + +void Solution::setSource(const std::string &source_) noexcept { Solution::source = source_; } + +const std::string &Solution::getTokens() const noexcept { return tokens; } + +void Solution::setTokens(const std::string &tokens_) noexcept { Solution::tokens = tokens_; } + +const std::string &Solution::getAstTree() const noexcept { return astTree; } + +void Solution::setAstTree(const std::string &astTree_) noexcept { Solution::astTree = astTree_; } + +size_t Solution::getTaskId() const noexcept { return task_id; } + +void Solution::setTaskId(size_t taskId) noexcept { task_id = taskId; } + +const std::string &Solution::getResult() const noexcept { return result; } + +void Solution::setResult(const std::string &result_) noexcept { Solution::result = result_; } + +bool Solution::operator==(const Solution &rhs) const noexcept { return id == rhs.id; } + +bool Solution::operator!=(const Solution &rhs) const noexcept { return !(rhs == *this); } + +size_t Solution::getOrigSolution() const { return orig_solution; } + +void Solution::setOrigSolution(size_t origSolution) { orig_solution = origSolution; } + +Solution::Solution() noexcept : id(0), sender_id(0), task_id(0) {} + +const std::string &Solution::getLanguage() const { return language; } + +void Solution::setLanguage(const std::string &language_) { Solution::language = language_; } diff --git a/server/internal/entities/src/Task.cpp b/server/internal/entities/src/Task.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c0c3586da38e09ccc1c45aa0aaf1971d741cc980 --- /dev/null +++ b/server/internal/entities/src/Task.cpp @@ -0,0 +1,32 @@ +#include "Task.hpp" + +#include +#include + +Task::Task(std::string description_, float treshold_, std::string name_) noexcept + : id(0), description(std::move(description_)), treshhold(treshold_), name(std::move(name_)) {} + +Task::Task(size_t id, std::string description_, float treshold_, std::string name_) noexcept + : id(id), description(std::move(description_)), treshhold(treshold_), name(std::move(name_)) {} + +Task::Task() noexcept : id(0), treshhold(0) {} + +unsigned long Task::getId() const noexcept { return id; } + +const std::string &Task::getDescription() const noexcept { return description; } + +void Task::setId(size_t id_) noexcept { id = id_; } + +void Task::setDescription(const std::string &description_) noexcept { Task::description = description_; } + +bool Task::operator==(const Task &rhs) const noexcept { return id == rhs.id; } + +bool Task::operator!=(const Task &rhs) const noexcept { return !(rhs == *this); } + +float Task::getTreshhold() const noexcept { return treshhold; } + +void Task::setTreshhold(float treshhold_) noexcept { Task::treshhold = treshhold_; } + +const std::string &Task::getName() const { return name; } + +void Task::setName(const std::string &name_) { Task::name = name_; } diff --git a/server/internal/entities/src/User.cpp b/server/internal/entities/src/User.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f4795d7eddeba2f688d5fc90b4f90abdae3fb1d4 --- /dev/null +++ b/server/internal/entities/src/User.cpp @@ -0,0 +1,37 @@ +#include "../include/User.hpp" + +#include + +User::User(size_t id_, std::string login_, std::string password_, std::string username_) noexcept + : id(id_), login(std::move(login_)), password(std::move(password_)), username(std::move(username_)) {} + +User::User(std::string login_, std::string password_, std::string username_) noexcept + : id(0), login(std::move(login_)), password(std::move(password_)), username(std::move(username_)) {} + +User::User() noexcept : id(0) {} + +const std::string &User::getLogin() const noexcept { return login; } + +void User::setId(size_t id_) noexcept { id = id_; } + +void User::setLogin(const std::string &login_) noexcept { User::login = login_; } + +const std::string &User::getPassword() const noexcept { return password; } + +void User::setPassword(const std::string &password_) noexcept { User::password = password_; } + +const std::string &User::getUsername() const noexcept { return username; } + +void User::setUsername(const std::string &username_) noexcept { User::username = username_; } + +size_t User::getId() const noexcept { return id; } + +std::ostream &operator<<(std::ostream &os, const User &user) noexcept { + os << "id: " << user.id << " login: " << user.login << " password: " << user.password + << " username: " << user.username; + return os; +} + +bool User::operator==(const User &rhs) const noexcept { return id == rhs.id; } + +bool User::operator!=(const User &rhs) const noexcept { return !(rhs == *this); } diff --git a/server/internal/entities/tests/CMakeLists.txt b/server/internal/entities/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/server/internal/httpServer/CMakeLists.txt b/server/internal/httpServer/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b49f36abdbec99a6008a7a156f97644a828ecd43 --- /dev/null +++ b/server/internal/httpServer/CMakeLists.txt @@ -0,0 +1,21 @@ +project("HttpServerLib") + +set(CMAKE_CXX_STANDARD 20) + +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/virtual) + +include_directories(${INCLUDE_DIRS}) +add_library(${PROJECT_NAME} ${SOURCES}) + +target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDE_DIRS} ${Boost_INCLUDE_DIR} ${libEntities_INCLUDE_DIRS} ${SERVICE_lib_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${SERVICE_lib_LIBRARY} ${libEntities_LIB} ${PQXX_LIB}) + +set(HTTPSERVER_LIB ${PROJECT_NAME}) +set(HTTPSERVER_LIB ${HTTPSERVER_LIB} PARENT_SCOPE) + +set(HTTPSERVER_INCLUDE_DIRS ${INCLUDE_DIRS}) +set(HTTPSERVER_INCLUDE_DIRS ${HTTPSERVER_INCLUDE_DIRS} PARENT_SCOPE) + +# enable_testing() +# add_subdirectory(tests) \ No newline at end of file diff --git a/server/internal/httpServer/include/HttpConnection.h b/server/internal/httpServer/include/HttpConnection.h new file mode 100644 index 0000000000000000000000000000000000000000..e2b5f73368de097fd79601029b9aaee759125571 --- /dev/null +++ b/server/internal/httpServer/include/HttpConnection.h @@ -0,0 +1,33 @@ +#ifndef SOURCEDOUT_HTTPSERVER_HTTPCONNECTION_H +#define SOURCEDOUT_HTTPSERVER_HTTPCONNECTION_H + +#include +#include +#include + +#include "Router.h" + +namespace beast = boost::beast; +namespace http = beast::http; +namespace net = boost::asio; +using tcp = boost::asio::ip::tcp; + +class HttpConnection : public std::enable_shared_from_this { + public: + explicit HttpConnection(tcp::socket&& socket_, std::shared_ptr const& doc_root_); + void run(); + void read(); + void handleRead(beast::error_code e, std::size_t bytes_transferred); + void sendResponse(http::message_generator&& msg); + void handleWrite(bool keep_alive, beast::error_code e, std::size_t bytes_transferred); + void close(); + + private: + beast::tcp_stream stream; + beast::flat_buffer buffer; + std::shared_ptr doc_root; + http::request request; + std::shared_ptr router; +}; + +#endif // SOURCEDOUT_HTTPSERVER_HTTPCONNECTION_H diff --git a/server/internal/httpServer/include/HttpServer.h b/server/internal/httpServer/include/HttpServer.h new file mode 100644 index 0000000000000000000000000000000000000000..6e8536f91119b3021fd3672dbf3ccf2bb19d1b89 --- /dev/null +++ b/server/internal/httpServer/include/HttpServer.h @@ -0,0 +1,29 @@ +#ifndef SOURCEDOUT_HTTPSERVER_HTTPSERVER_H +#define SOURCEDOUT_HTTPSERVER_HTTPSERVER_H + +#include +#include +#include + +#include "HttpConnection.h" +#include "Router.h" + +namespace net = boost::asio; +using tcp = boost::asio::ip::tcp; + +class HttpServer : public std::enable_shared_from_this { + public: + HttpServer(net::io_context& io_context_, tcp::endpoint endpoint_, + std::shared_ptr const& doc_root_); + void run(); + + private: + void startAccept(); + void handleAccept(beast::error_code ec, tcp::socket socket); + + net::io_context& io_context; + tcp::acceptor acceptor; + std::shared_ptr doc_root; +}; + +#endif // SOURCEDOUT_HTTPSERVER_HTTPSERVER_H diff --git a/server/internal/httpServer/include/Router.h b/server/internal/httpServer/include/Router.h new file mode 100644 index 0000000000000000000000000000000000000000..4f9c4faf3213113bf1a764b40a880c94f2cdf8e8 --- /dev/null +++ b/server/internal/httpServer/include/Router.h @@ -0,0 +1,34 @@ +#ifndef SOURCEDOUT_HTTPSERVER_ROUTER_UTILS_H +#define SOURCEDOUT_HTTPSERVER_ROUTER_UTILS_H + +#include +#include +#include +#include + +#include "SolutionManager.h" +#include "TaskManager.h" +#include "UserManager.h" + +namespace beast = boost::beast; +namespace http = beast::http; +namespace net = boost::asio; + +class Router : public std::enable_shared_from_this { + public: + explicit Router(std::string_view doc_root_); + + http::message_generator handleRequest(http::request&& req); + + void setSolutionManager(std::shared_ptr mng) { solutionManager = mng; } + void setUserManager(std::shared_ptr mng) { userManager = mng; } + void setTaskManager(std::shared_ptr mng) { taskManager = mng; } + + private: + std::shared_ptr solutionManager; + std::shared_ptr userManager; + std::shared_ptr taskManager; + std::string doc_root; +}; + +#endif // SOURCEDOUT_HTTPSERVER_ROUTER_UTILS_H diff --git a/server/internal/httpServer/include/Serializer.h b/server/internal/httpServer/include/Serializer.h new file mode 100644 index 0000000000000000000000000000000000000000..1398b0585932db245598e4fb437f59bc10a1a8e4 --- /dev/null +++ b/server/internal/httpServer/include/Serializer.h @@ -0,0 +1,27 @@ +#ifndef APP_HTTPSERVER_HTTPSERVER_MANAGERS_SERIALIZER_H_ +#define APP_HTTPSERVER_HTTPSERVER_MANAGERS_SERIALIZER_H_ + +#include +#include +#include + +#include "Solution.hpp" +#include "Task.hpp" +#include "User.hpp" + +class Serializer { + public: + std::tuple deserialNewSolutionData(const std::string& val); + std::tuple deserialTaskData(const std::string& val); + std::string deserialNewTaskData(const std::string& val); + size_t deserialSolutionData(const std::string& val); + std::tuple deserialUserData(const std::string& val); + std::tuple deserialNewUserData(const std::string& val); + + std::string serialSolutions(const std::vector& solutions); + std::string serialAllTasks(const std::vector& tasks); + std::string serialUserData(const User& user); + std::string serialSolution(const Solution& sol); +}; + +#endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_SERIALIZER_H_ diff --git a/server/internal/httpServer/include/SolutionManager.h b/server/internal/httpServer/include/SolutionManager.h new file mode 100644 index 0000000000000000000000000000000000000000..a6b9c3cbadde7a68733aa9e2840424609934976b --- /dev/null +++ b/server/internal/httpServer/include/SolutionManager.h @@ -0,0 +1,27 @@ +#ifndef APP_HTTPSERVER_HTTPSERVER_MANAGERS_SolutionMANAGER_H_ +#define APP_HTTPSERVER_HTTPSERVER_MANAGERS_SolutionMANAGER_H_ + +#include +#include +#include + +#include "ISolutionManager.h" +#include "Serializer.h" +#include "SolutionService.h" + +namespace beast = boost::beast; +namespace http = boost::beast::http; + +class SolutionManager : public ISolutionManager { + public: + SolutionManager(); + http::message_generator getAllSolutions(http::request&& req) override; + http::message_generator createSolution(http::request&& req) override; + void setService(std::shared_ptr service); + + private: + std::shared_ptr solutionService; + std::shared_ptr serializer; +}; + +#endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_SolutionMANAGER_H_ diff --git a/server/internal/httpServer/include/TaskManager.h b/server/internal/httpServer/include/TaskManager.h new file mode 100644 index 0000000000000000000000000000000000000000..1f5f5fba110d738746570588e4daae1e40c1dd3e --- /dev/null +++ b/server/internal/httpServer/include/TaskManager.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +#include "ITaskManager.h" +#include "Serializer.h" +#include "TaskService.h" + +class TaskManager : public ITaskManager { + public: + TaskManager(); + http::message_generator createTask(http::request&& req) override; + http::message_generator getAllTasks(http::request&& req) override; + void setService(std::shared_ptr service); + + private: + std::shared_ptr taskService; + std::shared_ptr serializer; +}; diff --git a/server/internal/httpServer/include/UserManager.h b/server/internal/httpServer/include/UserManager.h new file mode 100644 index 0000000000000000000000000000000000000000..66c231e338075278348e418a54994900ff60b7b7 --- /dev/null +++ b/server/internal/httpServer/include/UserManager.h @@ -0,0 +1,23 @@ +#ifndef APP_HTTPSERVER_HTTPSERVER_MANAGERS_USERMANAGER_H_ +#define APP_HTTPSERVER_HTTPSERVER_MANAGERS_USERMANAGER_H_ + +#include +#include + +#include "IUserManager.h" +#include "Serializer.h" +#include "UserService.h" + +class UserManager : public IUserManager { + public: + UserManager(); + http::message_generator loginUser(http::request&& req) override; + http::message_generator registerUser(http::request&& req) override; + void setService(std::shared_ptr service); + + private: + std::shared_ptr userService; + std::shared_ptr serializer; +}; + +#endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_USERMANAGER_H_ diff --git a/server/internal/httpServer/include/Utils.h b/server/internal/httpServer/include/Utils.h new file mode 100644 index 0000000000000000000000000000000000000000..a817790c02e578a1bff93a265b58cd91715ce794 --- /dev/null +++ b/server/internal/httpServer/include/Utils.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace beast = boost::beast; +namespace http = beast::http; + +void fail(beast::error_code ec, char const* what); + +http::response getBadRequest(const http::request& request, + beast::string_view why); diff --git a/server/internal/httpServer/src/HttpConnection.cpp b/server/internal/httpServer/src/HttpConnection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b6b408e61053a64429fa3ac0926702bce4ba50a --- /dev/null +++ b/server/internal/httpServer/src/HttpConnection.cpp @@ -0,0 +1,55 @@ +#include "HttpConnection.h" + +#include + +#include "Utils.h" + +HttpConnection::HttpConnection(tcp::socket&& socket_, std::shared_ptr const& doc_root_) + : stream(std::move(socket_)), doc_root(doc_root_) { + router = std::make_shared(*doc_root_); +} + +void HttpConnection::run() { + net::dispatch(stream.get_executor(), beast::bind_front_handler(&HttpConnection::read, shared_from_this())); +} + +void HttpConnection::read() { + request = {}; + stream.expires_after(std::chrono::seconds(30)); + http::async_read(stream, buffer, request, + beast::bind_front_handler(&HttpConnection::handleRead, shared_from_this())); +} + +void HttpConnection::handleRead(beast::error_code e, std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + + if (e == http::error::end_of_stream) return close(); + + if (e) return fail(e, "read"); + + sendResponse(router->handleRequest(std::move(request))); +} + +void HttpConnection::sendResponse(http::message_generator&& msg) { + bool keep_alive = msg.keep_alive(); + + beast::async_write(stream, std::move(msg), + beast::bind_front_handler(&HttpConnection::handleWrite, shared_from_this(), keep_alive)); +} + +void HttpConnection::handleWrite(bool keep_alive, beast::error_code e, std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + + if (e) return fail(e, "write"); + + if (!keep_alive) { + return close(); + } + + read(); +} + +void HttpConnection::close() { + beast::error_code e; + stream.socket().shutdown(tcp::socket::shutdown_send, e); +} diff --git a/server/internal/httpServer/src/HttpServer.cpp b/server/internal/httpServer/src/HttpServer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8256d6be7bad1a694e72f20b2ea7913e888206d --- /dev/null +++ b/server/internal/httpServer/src/HttpServer.cpp @@ -0,0 +1,53 @@ +#include "HttpServer.h" + +#include +#include + +#include "Utils.h" + +HttpServer::HttpServer(net::io_context& io_context_, tcp::endpoint endpoint_, + std::shared_ptr const& doc_root_) + : io_context(io_context_), acceptor(net::make_strand(io_context_)), doc_root(doc_root_) { + beast::error_code ec; + acceptor.open(endpoint_.protocol(), ec); + if (ec) { + fail(ec, "open"); + return; + } + + acceptor.set_option(net::socket_base::reuse_address(true), ec); + if (ec) { + fail(ec, "set_option"); + return; + } + + acceptor.bind(endpoint_, ec); + if (ec) { + fail(ec, "bind"); + return; + } + + acceptor.listen(net::socket_base::max_listen_connections, ec); + if (ec) { + fail(ec, "listen"); + return; + } +} + +void HttpServer::run() { startAccept(); } + +void HttpServer::startAccept() { + acceptor.async_accept(net::make_strand(io_context), + beast::bind_front_handler(&HttpServer::handleAccept, shared_from_this())); +} + +void HttpServer::handleAccept(beast::error_code ec, tcp::socket socket) { + if (ec) { + fail(ec, "accept"); + return; + } else { + std::make_shared(std::move(socket), doc_root)->run(); + } + + startAccept(); +} diff --git a/server/internal/httpServer/src/Router.cpp b/server/internal/httpServer/src/Router.cpp new file mode 100644 index 0000000000000000000000000000000000000000..68aba41d19c144ba1539718eacb979d12c9ffc18 --- /dev/null +++ b/server/internal/httpServer/src/Router.cpp @@ -0,0 +1,37 @@ +#include "Router.h" + +#include +#include + +#include "Utils.h" + +Router::Router(std::string_view doc_root_) + : solutionManager(std::make_shared()), + userManager(std::make_shared()), + taskManager(std::make_shared()), + doc_root(doc_root_) {} + +http::message_generator Router::handleRequest(http::request &&req) { + if (req.method() != http::verb::get && req.method() != http::verb::post) + return getBadRequest(req, "Unknown HTTP-method"); + + if (req.target().empty() || req.target()[0] != '/' || req.target().find("..") != beast::string_view::npos) { + return getBadRequest(req, "Illegal request-target"); + } + + if (req.target() == "/solution/submit") { + return solutionManager->createSolution(std::move(req)); + } else if (req.target() == "/solution/all") { + return solutionManager->getAllSolutions(std::move(req)); + } else if (req.target() == "/user/register") { + return userManager->registerUser(std::move(req)); + } else if (req.target() == "/user/login") { + return userManager->loginUser(std::move(req)); + } else if (req.target() == "/task/create") { + return taskManager->createTask(std::move(req)); + } else if (req.target() == "/task/all") { + return taskManager->getAllTasks(std::move(req)); + } + + return getBadRequest(req, "Unknown request-target"); +} diff --git a/server/internal/httpServer/src/Serializer.cpp b/server/internal/httpServer/src/Serializer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..de400b76352a651277a92030a7932a844b4564b5 --- /dev/null +++ b/server/internal/httpServer/src/Serializer.cpp @@ -0,0 +1,114 @@ +#include "Serializer.h" + +#include +#include +#include + +std::tuple Serializer::deserialNewSolutionData( + const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + std::tuple res = { + json.get("user_id"), json.get("task_id"), json.get("filename"), + json.get("filedata")}; + return res; +} + +size_t Serializer::deserialSolutionData(const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + return json.get("sol_id"); +} + +std::tuple Serializer::deserialTaskData(const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + std::tuple res = {json.get("user_id"), json.get("task_id")}; + return res; +} + +std::string Serializer::deserialNewTaskData(const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + return json.get("description"); +} + +std::tuple Serializer::deserialUserData(const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + std::tuple res = {json.get("login"), json.get("password")}; + return res; +} + +std::tuple Serializer::deserialNewUserData(const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + std::tuple res = { + json.get("login"), json.get("password"), json.get("username")}; + return res; +} + +std::string Serializer::serialSolutions(const std::vector &solutions) { + boost::property_tree::ptree json; + json.put("count", solutions.size()); + boost::property_tree::ptree solutions_nodes; + for (auto &i : solutions) { + boost::property_tree::ptree node; + node.put("sol_id", i.getId()); + node.put("task_id", i.getTaskId()); + solutions_nodes.push_back(std::make_pair("", node)); + } + json.add_child("solutions", solutions_nodes); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +std::string Serializer::serialAllTasks(const std::vector &tasks) { + boost::property_tree::ptree json; + json.put("count", tasks.size()); + boost::property_tree::ptree tasks_nodes; + for (auto &i : tasks) { + boost::property_tree::ptree node; + node.put("task_id", i.getId()); + node.put("description", i.getDescription()); + tasks_nodes.push_back(std::make_pair("", node)); + } + json.add_child("tasks", tasks_nodes); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +std::string Serializer::serialUserData(const User &user) { + boost::property_tree::ptree json; + json.put("user_id", user.getId()); + json.put("login", user.getLogin()); + json.put("username", user.getUsername()); + json.put("password", user.getPassword()); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +std::string Serializer::serialSolution(const Solution &sol) { + boost::property_tree::ptree json; + json.put("sol_id", sol.getId()); + json.put("source", sol.getSource()); + json.put("result", sol.getResult()); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} diff --git a/server/internal/httpServer/src/SolutionManager.cpp b/server/internal/httpServer/src/SolutionManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6174c7edd91d7b55a35a222e658b753ab17aa04d --- /dev/null +++ b/server/internal/httpServer/src/SolutionManager.cpp @@ -0,0 +1,51 @@ +// +// Created by Николай Степанов on 03.05.2023. +// +#include "SolutionManager.h" + +#include +#include + +#include "SolutionService.h" +#include "Utils.h" + +SolutionManager::SolutionManager() + : serializer(std::make_shared()), solutionService(std::make_shared()) {} + +void SolutionManager::setService(std::shared_ptr service) { solutionService = std::move(service); } + +http::message_generator SolutionManager::createSolution(http::request&& req) { + try { + size_t user_id, task_id; + std::string filename, filedata; + std::tie(user_id, task_id, filename, filedata) = serializer->deserialNewSolutionData(req.body()); + Solution sol = solutionService->createSolution(user_id, task_id, filename, filedata); + + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialSolution(sol); + res.prepare_payload(); + return res; + } catch (const std::exception& e) { + return getBadRequest(req, e.what()); + } +} + +http::message_generator SolutionManager::getAllSolutions(http::request&& req) { + try { + size_t user_id, task_id; + std::tie(user_id, task_id) = serializer->deserialTaskData(req.body()); + std::vector solutions; + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialSolutions(solutions); + res.prepare_payload(); + return res; + } catch (const std::exception& e) { + return getBadRequest(req, e.what()); + } +} diff --git a/server/internal/httpServer/src/TaskManager.cpp b/server/internal/httpServer/src/TaskManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f8f579f316a5ab79b330ba6bc048faf54f117ca --- /dev/null +++ b/server/internal/httpServer/src/TaskManager.cpp @@ -0,0 +1,42 @@ +// +// Created by Николай Степанов on 03.05.2023. +// +#include "TaskManager.h" + +#include "TaskService.h" +#include "Utils.h" + +TaskManager::TaskManager() : serializer(std::make_shared()), taskService(std::make_shared()) {} + +void TaskManager::setService(std::shared_ptr service) { taskService = service; } + +http::message_generator TaskManager::createTask(http::request &&req) { + try { + std::string description = serializer->deserialNewTaskData(req.body()); + taskService->createTask(description); + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.prepare_payload(); + return res; + } catch (...) { + return getBadRequest(req, "Something went wrong!"); + } +} + +http::message_generator TaskManager::getAllTasks(http::request &&req) { + try { + std::vector tasks = taskService->getAllTasks(); + // std::vector tasks = TmpTaskService::getAllTasks(); + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialAllTasks(tasks); + res.prepare_payload(); + return res; + } catch (...) { + return getBadRequest(req, "Something went wrong!"); + } +} diff --git a/server/internal/httpServer/src/UserManager.cpp b/server/internal/httpServer/src/UserManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ed55c414461e56c4a7f982962c142ef4d7e90058 --- /dev/null +++ b/server/internal/httpServer/src/UserManager.cpp @@ -0,0 +1,77 @@ +#include "UserManager.h" + +#include "ServiceExceptions.h" +#include "Utils.h" + +UserManager::UserManager() : serializer(std::make_shared()), userService(std::make_shared()) {} + +void UserManager::setService(std::shared_ptr service) { userService = service; } + +http::message_generator UserManager::loginUser(http::request &&req) { + std::string login, password; + + try { + std::tie(login, password) = serializer->deserialUserData(req.body()); + } catch (...) { + return getBadRequest(req, "Неправильные параметры!"); + } + + User user; + bool flag = true; + + try { + user = userService->login(login, password); + } catch (...) { + flag = false; + } + + if (flag) { + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialUserData(user); + std::cout << serializer->serialUserData(user) << std::endl; + res.prepare_payload(); + return res; + } else { + http::response res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialUserData(user); + res.prepare_payload(); + return res; + } +} + +http::message_generator UserManager::registerUser(http::request &&req) { + std::string login, password, username; + + try { + std::tie(login, password, username) = serializer->deserialNewUserData(req.body()); + } catch (...) { + return getBadRequest(req, "Неправильные параметры!"); + } + + try { + User user = userService->createUser(login, username, password); + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialUserData(user); + res.prepare_payload(); + return res; + } catch (const ValidateException &e) { + http::response res{http::status::forbidden, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = "Не валидные данные"; + res.prepare_payload(); + return res; + } catch (...) { + return getBadRequest(req, "Something went wrong!"); + } +} diff --git a/server/internal/httpServer/src/Utils.cpp b/server/internal/httpServer/src/Utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a7f999fa717f26a29f900906f8819e67ffcafa2f --- /dev/null +++ b/server/internal/httpServer/src/Utils.cpp @@ -0,0 +1,14 @@ +#include "Utils.h" + +void fail(beast::error_code ec, const char* what) { std::cerr << what << ": " << ec.message() << "\n"; } + +http::response getBadRequest(const http::request& request, + beast::string_view why) { + http::response res{http::status::bad_request, request.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(request.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; +} diff --git a/server/internal/httpServer/tests/CMakeLists.txt b/server/internal/httpServer/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c20edbc62344579fe90f83e48c902becdb28987f --- /dev/null +++ b/server/internal/httpServer/tests/CMakeLists.txt @@ -0,0 +1,18 @@ +project(test_httpserver) + +# set(CMAKE_CXX_STANDARD 20) +# add_compile_options(-coverage) + +file(GLOB SOURCES *.cpp) + +enable_testing() +find_package(GTest REQUIRED) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +message("sdcscdscsdcsdIB=${HTTPSERVER_INCLUDE_DIRS}") + +target_link_libraries(${PROJECT_NAME} ${HTTPSERVER_LIB} GTest::GTest gmock) +target_include_directories(${PROJECT_NAME} PUBLIC ${HTTPSERVER_INCLUDE_DIRS}) + +add_test(test_httpserver test_httpserver) \ No newline at end of file diff --git a/server/internal/httpServer/tests/RouterSuite.cpp b/server/internal/httpServer/tests/RouterSuite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..88f4f1171902ce52234f83906ffdad8682e01daa --- /dev/null +++ b/server/internal/httpServer/tests/RouterSuite.cpp @@ -0,0 +1,124 @@ +#include +#include + +#include "ISolutionManager.h" +#include "ITaskManager.h" +#include "IUserManager.h" +#include "Router.h" + +class SolutionManagerMock : public ISolutionManager { + public: + SolutionManagerMock() = default; + MOCK_METHOD(Response, getAllSolutions, (const Request& req), (override)); + MOCK_METHOD(Response, createSolution, (const Request& req), (override)); + MOCK_METHOD(Response, getMetrics, (const Request& req), (override)); +}; + +class TaskManagerMock : public ITaskManager { + public: + TaskManagerMock() = default; + MOCK_METHOD(Response, createTask, (const Request& req), (override)); + MOCK_METHOD(Response, getAllTasks, (const Request& req), (override)); +}; + +class UserManagerMock : public IUserManager { + public: + UserManagerMock() = default; + MOCK_METHOD(Response, loginUser, (const Request& req), (override)); + MOCK_METHOD(Response, registerUser, (const Request& req), (override)); +}; + +class RouterSuite : public ::testing::Test { + protected: + void SetUp() override { + router.setSolutionManager(solutionManager); + router.setUserManager(userManager); + router.setTaskManager(taskManager); + } + + std::shared_ptr userManager = std::make_shared(); + std::shared_ptr taskManager = std::make_shared(); + std::shared_ptr solutionManager = std::make_shared(); + Router router; +}; + +TEST_F(RouterSuite, RegisterUserTest) { + Request req; + req.method = "POST"; + req.uri = "/user/register"; + Response res; + EXPECT_CALL(*userManager, registerUser); + router.handleRequest(req, res); +} + +TEST_F(RouterSuite, LoginUserTest) { + Request req; + req.method = "GET"; + req.uri = "/user/login"; + Response res; + EXPECT_CALL(*userManager, loginUser); + router.handleRequest(req, res); +} + +TEST_F(RouterSuite, GetAllTasksTest) { + Request req; + req.method = "GET"; + req.uri = "/task/all"; + Response res; + EXPECT_CALL(*taskManager, getAllTasks); + router.handleRequest(req, res); +} + +TEST_F(RouterSuite, CreateTaskTest) { + Request req; + req.method = "POST"; + req.uri = "/task/create"; + Response res; + EXPECT_CALL(*taskManager, createTask); + router.handleRequest(req, res); +} + +TEST_F(RouterSuite, GetSolutionMetricsTest) { + Request req; + req.method = "GET"; + req.uri = "/solution/metrics"; + Response res; + EXPECT_CALL(*solutionManager, getMetrics); + router.handleRequest(req, res); +} + +TEST_F(RouterSuite, CreateSolutionTest) { + Request req; + req.method = "POST"; + req.uri = "/solution/create"; + Response res; + EXPECT_CALL(*solutionManager, createSolution); + router.handleRequest(req, res); +} + +TEST_F(RouterSuite, GetAllSolutionsTest) { + Request req; + req.method = "GET"; + req.uri = "/solution/all"; + Response res; + EXPECT_CALL(*solutionManager, getAllSolutions); + router.handleRequest(req, res); +} + +TEST_F(RouterSuite, BadMethodTest) { + Request req; + req.method = ""; + req.uri = "/solution/all"; + Response res; + router.handleRequest(req, res); + EXPECT_EQ(res.status, 500); +} + +TEST_F(RouterSuite, BadUriTest) { + Request req; + req.method = "GET"; + req.uri = ""; + Response res; + router.handleRequest(req, res); + EXPECT_EQ(res.status, 500); +} diff --git a/server/internal/httpServer/tests/SolutionManagerSuite.cpp b/server/internal/httpServer/tests/SolutionManagerSuite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a478f3f41613a4fa2c1fc3196f10a51c2ba4c84 --- /dev/null +++ b/server/internal/httpServer/tests/SolutionManagerSuite.cpp @@ -0,0 +1,58 @@ +#include +#include + +#include "ISolutionService.h" +#include "SolutionManager.h" + +class SolutionServiceMock : public ISolutionService { + public: + SolutionServiceMock() = default; + MOCK_METHOD(Solution, createSolution, (size_t userId, size_t taskId, std::string source), (override)); + MOCK_METHOD(std::vector, getSolutionsByUserAndTaskId, (size_t userId, size_t taskId), (override)); + MOCK_METHOD((std::pair), getMetrics, (size_t solId), (override)); + MOCK_METHOD(void, deleteSolutionById, (size_t solId), (override)); +}; + +class SolutionManagerSuite : public ::testing::Test { + protected: + void SetUp() override { manager.setService(solutionService); } + + std::shared_ptr solutionService = std::make_shared(); + SolutionManager manager; +}; + +TEST_F(SolutionManagerSuite, GetSolutionByUserAndTaskTest) { + Request req; + Header h1, h2; + h1.name = "user_id"; + h2.name = "task_id"; + h1.value = '1'; + h2.value = '2'; + req.headers = {h1, h2}; + EXPECT_CALL(*solutionService, getSolutionsByUserAndTaskId(1, 2)); + manager.getAllSolutions(req); +} + +TEST_F(SolutionManagerSuite, GetMetricsTest) { + Request req; + Header h1; + h1.name = "sol_id"; + h1.value = "1"; + req.headers = {h1}; + EXPECT_CALL(*solutionService, getMetrics(1)); + manager.getMetrics(req); +} + +TEST_F(SolutionManagerSuite, CreateSolutionTest) { + Request req; + Header h1, h2, h3; + h1.name = "user_id"; + h1.value = "1"; + h2.name = "task_id"; + h2.value = "2"; + h3.name = "source"; + h3.value = "int main() { return 0; }"; + req.headers = {h1, h2, h3}; + EXPECT_CALL(*solutionService, createSolution(1, 2, "int main() { return 0; }")); + manager.createSolution(req); +} diff --git a/server/internal/httpServer/tests/TaskManagerSuite.cpp b/server/internal/httpServer/tests/TaskManagerSuite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..94426190e3e375e245224bed72da91edc6618904 --- /dev/null +++ b/server/internal/httpServer/tests/TaskManagerSuite.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include "ITaskService.h" +#include "TaskManager.h" + +class TaskServiceMock : public ITaskService { + public: + TaskServiceMock() = default; + ~TaskServiceMock() = default; + + MOCK_METHOD(Task, createTask, (std::string desc), (override)); + MOCK_METHOD(Task, getTask, (size_t id), (override)); + MOCK_METHOD(std::vector, getAllTasks, (), (override)); + MOCK_METHOD(void, deleteTask, (size_t id), (override)); +}; + +class TaskManagerSuite : public ::testing::Test { + protected: + void SetUp() override { manager.setService(taskService); } + + std::shared_ptr taskService = std::make_shared(); + TaskManager manager; +}; + +TEST_F(TaskManagerSuite, CreateTaskTest) { + Request req; + Header h1, h2; + h1.name = "description"; + h1.value = "a"; + req.headers = {h1}; + EXPECT_CALL(*taskService, createTask("a")); + manager.createTask(req); +} + +TEST_F(TaskManagerSuite, GetAllTaskTest) { + Request req; + EXPECT_CALL(*taskService, getAllTasks); + manager.getAllTasks(req); +} diff --git a/server/internal/httpServer/tests/UserManagerSuite.cpp b/server/internal/httpServer/tests/UserManagerSuite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26f5095a28341d2b240a147df29480d837f6f20e --- /dev/null +++ b/server/internal/httpServer/tests/UserManagerSuite.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include "IUserService.h" +#include "UserManager.h" + +class UserServiceMock : public IUserService { + public: + UserServiceMock() = default; + ~UserServiceMock() = default; + MOCK_METHOD(User, createUser, (std::string login, std::string username, std::string password), (override)); + MOCK_METHOD(User, getUserById, (size_t id), (override)); + MOCK_METHOD(void, deleteUser, (size_t id), (override)); +}; + +class UserManagerSuite : public ::testing::Test { + protected: + void SetUp() override { manager.setService(userService); } + + std::shared_ptr userService = std::make_shared(); + UserManager manager; +}; + +TEST_F(UserManagerSuite, LoginUserTest) { + Request req; + Header h1, h2; + h1.name = "login"; + h1.value = 1; + req.headers = {h1}; + EXPECT_CALL(*userService, getUserById(1)); + manager.loginUser(req); +} + +TEST_F(UserManagerSuite, RgisterUserTest) { + Request req; + Header h1, h2, h3; + h1.name = "login"; + h1.value = "1"; + h2.name = "username"; + h2.value = "2"; + h3.name = "password"; + h3.value = "3"; + req.headers = {h1, h2, h3}; + EXPECT_CALL(*userService, createUser("1", "2", "3")); + manager.registerUser(req); +} diff --git a/server/internal/httpServer/tests/main.cpp b/server/internal/httpServer/tests/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d29e70ca79814cd5c451fbc727fccc128699612 --- /dev/null +++ b/server/internal/httpServer/tests/main.cpp @@ -0,0 +1,8 @@ +#include +#include + +int main(int argc, char** argv) { + ::testing::InitGoogleMock(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/server/internal/httpServer/virtual/ISolutionManager.h b/server/internal/httpServer/virtual/ISolutionManager.h new file mode 100644 index 0000000000000000000000000000000000000000..6ff52fe823b142a67cc1fc232f76ca675cd0e013 --- /dev/null +++ b/server/internal/httpServer/virtual/ISolutionManager.h @@ -0,0 +1,15 @@ +#ifndef APP_HTTPSERVER_HTTPSERVER_MANAGERS_ISolutionMANAGER_H_ +#define APP_HTTPSERVER_HTTPSERVER_MANAGERS_ISolutionMANAGER_H_ + +#include + +namespace beast = boost::beast; +namespace http = boost::beast::http; + +class ISolutionManager { + public: + virtual http::message_generator getAllSolutions(http::request&& req) = 0; + virtual http::message_generator createSolution(http::request&& req) = 0; +}; + +#endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_ISolutionMANAGER_H_ diff --git a/server/internal/httpServer/virtual/ITaskManager.h b/server/internal/httpServer/virtual/ITaskManager.h new file mode 100644 index 0000000000000000000000000000000000000000..0b01c8aa3792ead3dd628dd2b8e26d313529dd6b --- /dev/null +++ b/server/internal/httpServer/virtual/ITaskManager.h @@ -0,0 +1,17 @@ +#ifndef APP_HTTPSERVER_HTTPSERVER_MANAGERS_ITaskMANAGER_H_ +#define APP_HTTPSERVER_HTTPSERVER_MANAGERS_ITaskMANAGER_H_ + +#include +#include +#include + +namespace beast = boost::beast; +namespace http = boost::beast::http; + +class ITaskManager { + public: + virtual http::message_generator createTask(http::request&& req) = 0; + virtual http::message_generator getAllTasks(http::request&& req) = 0; +}; + +#endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_ITaskMANAGER_H_ diff --git a/server/internal/httpServer/virtual/IUserManager.h b/server/internal/httpServer/virtual/IUserManager.h new file mode 100644 index 0000000000000000000000000000000000000000..5a467d4797794969d40f54d6e4f54dd2d251d3fc --- /dev/null +++ b/server/internal/httpServer/virtual/IUserManager.h @@ -0,0 +1,17 @@ +#ifndef APP_HTTPSERVER_HTTPSERVER_MANAGERS_IUserMANAGER_H_ +#define APP_HTTPSERVER_HTTPSERVER_MANAGERS_IUserMANAGER_H_ + +#include +#include +#include + +namespace beast = boost::beast; +namespace http = boost::beast::http; + +class IUserManager { + public: + virtual http::message_generator loginUser(http::request&& req) = 0; + virtual http::message_generator registerUser(http::request&& req) = 0; +}; + +#endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_IUserMANAGER_H_ diff --git a/server/internal/metrics/CMakeLists.txt b/server/internal/metrics/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..531f6f6bfdc5a9bc7564cc06c844806cc402cc43 --- /dev/null +++ b/server/internal/metrics/CMakeLists.txt @@ -0,0 +1,28 @@ +project("MetricsLib") + +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) + + +include_directories(${INCLUDE_DIRS}) +add_library(${PROJECT_NAME} ${SOURCES}) + +message("ANTLR4_LIB = ${ANTLR4_LIB}") +message("ANTLR4_LIB_INCLUDE_DIRS = ${ANTLR4_LIB_INCLUDE_DIRS}") + +target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES}) + + +set(METRICS_LIBRARY ${PROJECT_NAME}) +set(METRICS_LIBRARY ${METRICS_LIBRARY} PARENT_SCOPE) + + +set(METRICS_lib_INCLUDE_DIRS ${INCLUDE_DIRS}) +set(METRICS_lib_INCLUDE_DIRS ${METRICS_lib_INCLUDE_DIRS} PARENT_SCOPE) + + +enable_testing() +#if(BUILD_TESTS) +add_subdirectory(tests) +#endif() \ No newline at end of file diff --git a/server/internal/metrics/include/TextMetricsLib.h b/server/internal/metrics/include/TextMetricsLib.h new file mode 100644 index 0000000000000000000000000000000000000000..b55f18a27949bff456f79a460c7b518cb2fcb510 --- /dev/null +++ b/server/internal/metrics/include/TextMetricsLib.h @@ -0,0 +1,45 @@ +// +// Created by march on 02.05.2023. +// + +#ifndef SOURCEDOUT_DECLARATION_H +#define SOURCEDOUT_DECLARATION_H + +#include +#include +#include +#include +#include +#include + +class ITextMetric { + public: + virtual ~ITextMetric() = default; + virtual void setData(std::string text1, std::string text2) = 0; + virtual double getMetric() = 0; +}; + +class PrepareDataTextMetric : public ITextMetric { + public: + void setData(std::string text1, std::string text2) override; + + protected: + std::vector tokens1; + std::vector tokens2; + + private: + static std::string deleteComments(const std::string& text); + static std::vector tbmTokenizer(const std::string& text); +}; + +class LevDistTextMetric : public PrepareDataTextMetric { + public: + double getMetric() override; +}; + +class JaccardTextMetric : public PrepareDataTextMetric { + public: + double getMetric() override; +}; + +#endif // SOURCEDOUT_DECLARATION_H diff --git a/server/internal/metrics/include/TokenMetricLib.h b/server/internal/metrics/include/TokenMetricLib.h new file mode 100644 index 0000000000000000000000000000000000000000..1edd6769199b154f1ebc6e19af084d1db5f8b2bd --- /dev/null +++ b/server/internal/metrics/include/TokenMetricLib.h @@ -0,0 +1,41 @@ +// +// Created by march on 04.05.2023. +// + +#ifndef SOURCEDOUT_TOKENMETRICLIB_H +#define SOURCEDOUT_TOKENMETRICLIB_H + +#include +#include +#include +#include +#include +#include + +class ITokenMetric { + public: + virtual ~ITokenMetric() = default; + virtual void setData(std::vector tokens1, std::vector tokens2) = 0; + virtual double getMetric() = 0; +}; + +class PrepareDataTokenMetric : public ITokenMetric { + public: + void setData(std::vector _tokens1, std::vector _tokens2) override; + + protected: + std::vector tokens1; + std::vector tokens2; +}; + +class LevDistTokenMetric : public PrepareDataTokenMetric { + public: + double getMetric() override; +}; + +class WShinglingTokenMetric : public PrepareDataTokenMetric { + public: + double getMetric() override; +}; + +#endif // SOURCEDOUT_TOKENMETRICLIB_H diff --git a/server/internal/metrics/src/TextMetricImpl.cpp b/server/internal/metrics/src/TextMetricImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f886e3bf65bfa0b7f339342d574fa0fbe7fe4d97 --- /dev/null +++ b/server/internal/metrics/src/TextMetricImpl.cpp @@ -0,0 +1,110 @@ +// +// Created by march on 02.05.2023. +// + +#include "TextMetricsLib.h" + +void PrepareDataTextMetric::setData(std::string text1, std::string text2) { + std::string non_comm_text1 = deleteComments(text1); + std::string non_comm_text2 = deleteComments(text2); + + tokens1 = tbmTokenizer(non_comm_text1); + tokens2 = tbmTokenizer(non_comm_text2); +} + +std::string PrepareDataTextMetric::deleteComments(const std::string &text) { + std::string modif; + std::string res; + + std::stringstream ss; + std::string line; + + ss << text; + + while (getline(ss, line)) { + line.push_back('\0'); + modif += line; + } + + bool s_comm = false; + bool m_comm = false; + + for (size_t i = 0; i < modif.size(); i++) { + if (s_comm && modif[i] == '\0') + s_comm = false; + else if (m_comm && modif[i] == '*' && modif[i + 1] == '/') + m_comm = false, i++; + else if (s_comm || m_comm) + continue; + else if (modif[i] == '/' && modif[i + 1] == '/') + s_comm = true, i++; + else if (modif[i] == '/' && modif[i + 1] == '*') + m_comm = true, i++; + + else if (modif[i] != '\0') + res += modif[i]; + else if (!res.empty() && res[res.size() - 1] != '\n') + res += '\n'; + } + return res; +} + +std::vector PrepareDataTextMetric::tbmTokenizer(const std::string &text) { + boost::char_separator sep(" {}();,\"\0\'"); + std::vector res; + boost::tokenizer > tokens(text, sep); + + for (const std::string &s : tokens) { + if (!s.empty() && s[0] != '\n' && s[0] != '\0') { + res.push_back(s); + } + } + return res; +} + +double LevDistTextMetric::getMetric() { + unsigned long n = tokens1.size(); + unsigned long m = tokens2.size(); + int x, y, z; + + std::vector > lev(n, std::vector(m, 0)); + + for (size_t i = 0; i < n; i++) { + for (size_t j = 0; j < m; j++) { + if (std::min(i, j) == 0) { + lev[i][j] = static_cast(std::max(i, j)); + } else { + x = lev[i - 1][j]; + y = lev[i][j - 1]; + z = lev[i - 1][j - 1]; + lev[i][j] = std::min(x, std::min(y, z)); + if (tokens1[i] != tokens2[j]) { + lev[i][j]++; + } + } + } + } + + if (n == 0 || m == 0) return 0; + double res = 1.0 - static_cast(lev[n - 1][m - 1]) / static_cast(std::max(n, m)); + return res; +} + +double JaccardTextMetric::getMetric() { + std::set s1; + std::set s2; + + for (auto &i : tokens1) s1.insert(i); + for (auto &i : tokens2) s2.insert(i); + + std::set intersect_sets; + set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), std::inserter(intersect_sets, intersect_sets.begin())); + + std::set union_sets; + set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), std::inserter(union_sets, union_sets.begin())); + + if (union_sets.empty()) + return 0; + else + return static_cast(intersect_sets.size()) / static_cast(union_sets.size()); +} diff --git a/server/internal/metrics/src/TokenMetricImpl.cpp b/server/internal/metrics/src/TokenMetricImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..62773879143d1ff4da7552f4f041ca90a9100683 --- /dev/null +++ b/server/internal/metrics/src/TokenMetricImpl.cpp @@ -0,0 +1,72 @@ +// +// Created by march on 04.05.2023. +// + +#include "TokenMetricLib.h" + +double LevDistTokenMetric::getMetric() { + unsigned long n = tokens1.size(); + unsigned long m = tokens2.size(); + int x, y, z; + + std::vector> lev(n, std::vector(m, 0)); + + for (size_t i = 0; i < n; i++) { + for (size_t j = 0; j < m; j++) { + if (std::min(i, j) == 0) { + lev[i][j] = static_cast(std::max(i, j)); + } else { + x = lev[i - 1][j]; + y = lev[i][j - 1]; + z = lev[i - 1][j - 1]; + lev[i][j] = std::min(x, std::min(y, z)); + if (tokens1[i] != tokens2[j]) { + lev[i][j]++; + } + } + } + } + + if (n == 0 || m == 0) return 0; + double res = 1.0 - static_cast(lev[n - 1][m - 1]) / static_cast(std::max(n, m)); + return res; +} + +double WShinglingTokenMetric::getMetric() { + unsigned long n = tokens1.size(); + unsigned long m = tokens2.size(); + + if (n < 3 || m < 3) return 0; + + std::vector> sh1; + std::vector> sh2; + + for (size_t i = 0; i < n - 2; i++) { + sh1.emplace_back(tokens1[i], tokens1[i + 1], tokens1[i + 2]); + } + for (size_t i = 0; i < m - 2; i++) { + sh2.emplace_back(tokens2[i], tokens2[i + 1], tokens2[i + 2]); + } + + std::set> s1; + std::set> s2; + + for (auto &i : sh1) s1.insert(i); + for (auto &i : sh2) s2.insert(i); + + std::set> intersect_sets; + set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), std::inserter(intersect_sets, intersect_sets.begin())); + + std::set> union_sets; + set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), std::inserter(union_sets, union_sets.begin())); + + if (union_sets.empty()) + return 0; + else + return static_cast(intersect_sets.size()) / static_cast(union_sets.size()); +} + +void PrepareDataTokenMetric::setData(std::vector _tokens1, std::vector _tokens2) { + tokens1 = std::move(_tokens1); + tokens2 = std::move(_tokens2); +} diff --git a/text-basic-metrics/code1.txt b/server/internal/metrics/testProgs/cpp/code1.txt similarity index 100% rename from text-basic-metrics/code1.txt rename to server/internal/metrics/testProgs/cpp/code1.txt diff --git a/text-basic-metrics/code2.txt b/server/internal/metrics/testProgs/cpp/code2.txt similarity index 93% rename from text-basic-metrics/code2.txt rename to server/internal/metrics/testProgs/cpp/code2.txt index 2115b76e6866f8af7e14dbd7de1ed6fe3a2c9ec2..bd5977aef79849c0850503cc11189ab9ba97f1d1 100644 --- a/text-basic-metrics/code2.txt +++ b/server/internal/metrics/testProgs/cpp/code2.txt @@ -1,5 +1,6 @@ // однострочный комментарий // еще один + // вау еще один #include @@ -19,11 +20,13 @@ int main() { string res1; // ещё в код напихаю комментов ss1 << "a bwfw ce "; + cout << "mem"; while(getline(ss, res1, ' ')){ //комментарий после строки с кодом /* * летс гоу * худшее место для многострочного коммента */ cout << res1 << endl; /* многострочный однострочно */ + cout << "mem2" << endl; } } \ No newline at end of file diff --git a/server/internal/metrics/testProgs/python/pycode1.txt b/server/internal/metrics/testProgs/python/pycode1.txt new file mode 100644 index 0000000000000000000000000000000000000000..987fe46ae1fa9b045a7b26d0182b6f41493f3529 --- /dev/null +++ b/server/internal/metrics/testProgs/python/pycode1.txt @@ -0,0 +1,18 @@ +n, m = map(int, input()).split() +arr = [[0 for _ in range(m)] for _ in range(n)] +current_element = 1 + +for diag_id in range(n + m): + if diag_id % 2 == 0: + for x in range(n): + if 0 <= diag_id - x < m: + arr[x][diag_id - x] = current_element + current_element += 1 + else: + for x in reversed(range(n)): + if 0 <= diag_id - x < m: + arr[x][diag_id - x] = current_element + current_element += 1 + +for line in arr: + print(*line) \ No newline at end of file diff --git a/server/internal/metrics/testProgs/python/pycode2.txt b/server/internal/metrics/testProgs/python/pycode2.txt new file mode 100644 index 0000000000000000000000000000000000000000..3ddbf2898c2632799f8a351464ae091cf8b8cb5e --- /dev/null +++ b/server/internal/metrics/testProgs/python/pycode2.txt @@ -0,0 +1,21 @@ +n, m = map(int, input()).split() +arr = [[0 for _ in range(m)] for _ in range(n)] +current_element1 = 1 + +for i in range(n): + print(i) + +for diag_id in range(n + m): + if diag_id % 2 == 0: + for x in range(n): + if 0 <= diag_id - x < m: + arr[x][diag_id - x] = current_element + current_element += 1 + else: + for x in reversed(range(n)): + if 0 <= diag_id - x < m: + arr[x][diag_id - x] = current_element + current_element += 1 + +for line in arr: + print(*line) \ No newline at end of file diff --git a/server/internal/metrics/tests/CMakeLists.txt b/server/internal/metrics/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0b5a55d4c1cf6aeaa1d772a77018a75d11bd41c3 --- /dev/null +++ b/server/internal/metrics/tests/CMakeLists.txt @@ -0,0 +1,7 @@ +file(GLOB SOURCES src/*.cpp) + +add_executable(metrics_test ${SOURCES}) + +target_link_libraries(metrics_test MetricsLib GTest::gtest_main GTest::gmock) + +add_test(metrics_test metrics_test) \ No newline at end of file diff --git a/server/internal/metrics/tests/src/main.cpp b/server/internal/metrics/tests/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..50ba1ce9baac47339bd9f92b0ac42b9575953fc1 --- /dev/null +++ b/server/internal/metrics/tests/src/main.cpp @@ -0,0 +1,12 @@ +// +// Created by march on 16.05.2023. +// + +#include +#include + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/server/internal/metrics/tests/src/test-codes/code1.txt b/server/internal/metrics/tests/src/test-codes/code1.txt new file mode 100644 index 0000000000000000000000000000000000000000..5fa95b8f8462b43d7b70943fc4b1f5d124dec881 --- /dev/null +++ b/server/internal/metrics/tests/src/test-codes/code1.txt @@ -0,0 +1,29 @@ +// однострочный комментарий +// еще один +// вау еще один + +#include +#include +#include + +using namespace std; + +/* многострочный комм + * // внутри него однострочный + * + */ + + +int main() { + stringstream ss; + string res; + // ещё в код напихаю комментов + ss << "a bwfw ce "; + while(getline(ss, res, ' ')){ //комментарий после строки с кодом + /* + * летс гоу + * худшее место для многострочного коммента + */ + cout << res << endl; /* многострочный однострочно */ + } +} diff --git a/server/internal/metrics/tests/src/text_metrics_tests.cpp b/server/internal/metrics/tests/src/text_metrics_tests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..35f5dbfdde6f1b80696f69b4ee4d1d8dd81d004e --- /dev/null +++ b/server/internal/metrics/tests/src/text_metrics_tests.cpp @@ -0,0 +1,58 @@ +// +// Created by march on 04.05.2023. +// + +#include +#include + +#include "TextMetricsLib.h" + +class LevDistTextMetricTest : public ::testing::Test { + protected: + std::unique_ptr levDistTextMetric; + void SetUp() { levDistTextMetric = std::make_unique(); } + void TearDown() {} +}; + +class JaccardTextMetricTest : public ::testing::Test { + protected: + std::unique_ptr jaccardTextMetric; + void SetUp() { jaccardTextMetric = std::make_unique(); } + void TearDown() {} +}; + +TEST_F(LevDistTextMetricTest, check_eq_progs) { + levDistTextMetric->setData("a b c d e f", "a b c d e f"); + + EXPECT_EQ(levDistTextMetric->getMetric(), 1); +} + +TEST_F(LevDistTextMetricTest, check_absolutely_not_eq_progs) { + levDistTextMetric->setData("a b c", "d e f g"); + + EXPECT_EQ(levDistTextMetric->getMetric() < 0.5, true); +} + +TEST_F(LevDistTextMetricTest, test_with_empty_prog) { + levDistTextMetric->setData("a b c", ""); + + EXPECT_EQ(levDistTextMetric->getMetric(), 0); +} + +TEST_F(JaccardTextMetricTest, check_eq_progs) { + jaccardTextMetric->setData("a b c d e f", "d e a b c f"); + + EXPECT_EQ(jaccardTextMetric->getMetric(), 1); +} + +TEST_F(JaccardTextMetricTest, check_absolutely_not_eq_progs) { + jaccardTextMetric->setData("a b c", "d e f g"); + + EXPECT_EQ(jaccardTextMetric->getMetric(), 0); +} + +TEST_F(JaccardTextMetricTest, test_with_empty_prog) { + jaccardTextMetric->setData("a b c", ""); + + EXPECT_EQ(jaccardTextMetric->getMetric(), 0); +} diff --git a/server/internal/metrics/tests/src/token_metrics_tests.cpp b/server/internal/metrics/tests/src/token_metrics_tests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8125295da8c456d82741bddedbfa5ea58304c2cf --- /dev/null +++ b/server/internal/metrics/tests/src/token_metrics_tests.cpp @@ -0,0 +1,99 @@ +// +// Created by march on 16.05.2023. +// + +#include +#include + +#include +#include + +#include "TokenMetricLib.h" + +class LevDistTokenMetricTest : public ::testing::Test { + protected: + std::unique_ptr levDistTokenMetric; + void SetUp() { levDistTokenMetric = std::make_unique(); } + void TearDown() {} +}; + +class WShinglingTokenMetricTest : public ::testing::Test { + protected: + std::unique_ptr wShinglingTokenMetric; + void SetUp() { wShinglingTokenMetric = std::make_unique(); } + void TearDown() {} +}; + +TEST_F(LevDistTokenMetricTest, check_eq_progs) { + std::vector tokens1 = {1, 2, 3}; + + levDistTokenMetric->setData(tokens1, tokens1); + + EXPECT_EQ(levDistTokenMetric->getMetric(), 1); +} + +TEST_F(LevDistTokenMetricTest, check_absolutely_not_eq_progs) { + std::vector tokens1 = {1, 2, 3}; + std::vector tokens2 = {3, 4, 5, 6}; + + levDistTokenMetric->setData(tokens1, tokens2); + + EXPECT_EQ(levDistTokenMetric->getMetric() < 0.5, true); +} + +TEST_F(LevDistTokenMetricTest, test_with_empty_prog) { + std::vector tokens1 = {1, 2, 3}; + std::vector tokens2 = {}; + + levDistTokenMetric->setData(tokens1, tokens2); + + EXPECT_EQ(levDistTokenMetric->getMetric(), 0); +} + +TEST_F(WShinglingTokenMetricTest, check_eq_progs) { + std::vector tokens1 = {1, 2, 3, 4, 5, 6}; + std::vector tokens2 = {1, 2, 3, 4, 5, 6}; + + wShinglingTokenMetric->setData(tokens1, tokens1); + + EXPECT_EQ(wShinglingTokenMetric->getMetric(), 1); +} + +TEST_F(WShinglingTokenMetricTest, check_absolutely_not_eq_progs) { + std::vector tokens1 = {1, 2, 3}; + std::vector tokens2 = {4, 5, 6, 9}; + + wShinglingTokenMetric->setData(tokens1, tokens2); + + EXPECT_EQ(wShinglingTokenMetric->getMetric(), 0); +} + +TEST_F(WShinglingTokenMetricTest, test_with_empty_prog) { + std::vector tokens1 = {1, 2, 3}; + std::vector tokens2 = {}; + + wShinglingTokenMetric->setData(tokens1, tokens2); + + EXPECT_EQ(wShinglingTokenMetric->getMetric(), 0); +} + +TEST_F(WShinglingTokenMetricTest, test_with_small_prog) { + std::vector tokens1 = {1, 2, 3, 4}; + std::vector tokens2 = {9}; + + wShinglingTokenMetric->setData(tokens1, tokens2); + + EXPECT_EQ(wShinglingTokenMetric->getMetric(), 0); +} + +TEST_F(WShinglingTokenMetricTest, test_with_big_size) { + std::vector tokens = {9, 45, 132, 85, 86, 89, 78, 45, 132, 128, 45, 132, 101, 1, + 128, 132, 127, 132, 103, 103, 132, 128, 84, 85, 132, 115, 1, 86, + 89, 43, 85, 132, 97, 1, 86, 132, 120, 128, 132, 113, 1, 128, + 90, 132, 127, 132, 102, 102, 132, 128, 59, 1, 128, 90, -1}; + + wShinglingTokenMetric->setData(tokens, tokens); + + double res = wShinglingTokenMetric->getMetric(); + EXPECT_EQ(res, 1); +} diff --git a/server/internal/repository/CMakeLists.txt b/server/internal/repository/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..353d17ff159157489eab050b5c547515b637b80f --- /dev/null +++ b/server/internal/repository/CMakeLists.txt @@ -0,0 +1,46 @@ +project("RepositoryLib") + +set(LIB_NAME libRepository) + +file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/virtual/*.hpp) + + +message("SOURCES = ${SOURCES}") +message("HEADERS = ${HEADERS}") + + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -O2 -pedantic -Wformat=2 -Wfloat-equal -Wconversion \ +-Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -ftest-coverage") + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lboost_filesystem") + +add_custom_target( + ${PROJECT_NAME}_COVERAGE + COMMAND gcovr ${CMAKE_CURRENT_BINARY_DIR} -r ${CMAKE_CURRENT_SOURCE_DIR} +) +add_custom_target( + ${PROJECT_NAME}_COVERAGE_FILE + COMMAND rm -r ${CMAKE_CURRENT_BINARY_DIR}/report || echo "There are no reports" + COMMAND mkdir ${CMAKE_CURRENT_BINARY_DIR}/report + COMMAND gcovr ${CMAKE_CURRENT_BINARY_DIR} -r ${CMAKE_CURRENT_SOURCE_DIR} --html-details report/report.html +) + +add_library(${LIB_NAME} ${SOURCES} ${HEADERS}) +target_include_directories(${LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/virtual) +target_link_libraries(${LIB_NAME} ${Boost_LIBRARIES} ${PQXX_LIB} ${libEntities_LIB} ${libDbManager_LIB}) + +set(libRepository_LIB ${LIB_NAME}) +set(libRepository_LIB ${libRepository_LIB} PARENT_SCOPE) + +set(libRepository_INCLUDE_DIRS ${LIB_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/virtual) +set(libRepository_INCLUDE_DIRS ${libRepository_INCLUDE_DIRS} PARENT_SCOPE) + + +message(Entities_LIB = "${libEntities_LIB}") +message(dbManager = "${libDbManager_LIB}") +message("libRepository_LIB = ${libRepository_LIB}") +message("libRepository_INCLUDE_DIRS = ${libRepository_INCLUDE_DIRS}") + +# enable_testing() +# add_subdirectory(tests) \ No newline at end of file diff --git a/server/internal/repository/include/MetricRepository.hpp b/server/internal/repository/include/MetricRepository.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ca0fa5fe84e2ea374c5dce596baa01acb1a3e8ac --- /dev/null +++ b/server/internal/repository/include/MetricRepository.hpp @@ -0,0 +1,32 @@ +#ifndef SOURCEDOUT_METRICREPOSITORY_HPP +#define SOURCEDOUT_METRICREPOSITORY_HPP + +#include +#include + +#include "IMetricRepository.hpp" +#include "dbManager.hpp" + +using namespace pqxx; + +class MetricRepository : public IMetricRepository { + public: + MetricRepository(); + + std::optional getById(size_t id) override; + + size_t storeMetric(MetricStat metric) override; + + void updateMetric(MetricStat metric) override; + + void deleteMetric(MetricStat metric) override; + + void deleteMetricById(size_t id) override; + + private: + std::shared_ptr manager; + + static MetricStat makeMetric(const result::const_iterator &c); +}; + +#endif // SOURCEDOUT_METRICREPOSITORY_HPP diff --git a/server/internal/repository/include/SolutionRepository.hpp b/server/internal/repository/include/SolutionRepository.hpp new file mode 100644 index 0000000000000000000000000000000000000000..04f5ae8ebf0833a763815d409b8bf32dd8da5a7d --- /dev/null +++ b/server/internal/repository/include/SolutionRepository.hpp @@ -0,0 +1,45 @@ +#ifndef SOURCEDOUT_SOLUTIONREPOSITORY_HPP +#define SOURCEDOUT_SOLUTIONREPOSITORY_HPP + +#include +#include +#include +#include +#include + +#include "ISolutionRepository.hpp" +#include "dbManager.hpp" + +using namespace pqxx; + +class SolutionRepository : public ISolutionRepository { + public: + SolutionRepository(); + + std::optional getSolutionById(size_t id) override; + + std::vector getSolutionsBySenderId(size_t sender_id) override; + + std::vector getSolutionsByTaskId(size_t task_id) override; + + std::vector getSolutionsByTaskIdAndSenderId(size_t, size_t) override; + + std::vector getSolutionsByTaskIdAndLanguage(size_t task_id, std::string lang) override; + + size_t storeSolution(Solution solution) override; + + void updateSolution(Solution solution) override; + + void deleteSolutionById(size_t id) override; + + void deleteSolution(Solution solution) override; + + std::optional getOriginalSolution(size_t id) override; + + private: + static Solution makeSolution(const result::const_iterator &c); + + std::shared_ptr manager; +}; + +#endif diff --git a/server/internal/repository/include/TaskRepository.hpp b/server/internal/repository/include/TaskRepository.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3bc409b3d7f2d1224775ed9c857796ccec1fa198 --- /dev/null +++ b/server/internal/repository/include/TaskRepository.hpp @@ -0,0 +1,35 @@ +#ifndef SOURCEDOUT_TASKREPOSITORY_HPP +#define SOURCEDOUT_TASKREPOSITORY_HPP + +#include +#include +#include + +#include "ITaskRepository.hpp" +#include "dbManager.hpp" +#include "pqxx/pqxx" + +using namespace pqxx; + +class TaskRepository : public ITaskRepository { + public: + TaskRepository(); + std::optional getTaskById(size_t id) override; + + std::vector getAllTasks() override; + + void updateTask(const Task& task) override; + + size_t storeTask(Task task) override; + + void deleteTask(Task task) override; + + void deleteTaskById(size_t task_id) override; + + private: + static Task makeTask(const result::const_iterator& c); + + std::shared_ptr manager; +}; + +#endif // SOURCEDOUT_TASKREPOSITORY_HPP diff --git a/server/internal/repository/include/UserRepository.hpp b/server/internal/repository/include/UserRepository.hpp new file mode 100644 index 0000000000000000000000000000000000000000..136e5cdeff011593e6b23cd19ac0b6c16241d06b --- /dev/null +++ b/server/internal/repository/include/UserRepository.hpp @@ -0,0 +1,39 @@ +#ifndef SOURCEDOUT_USERREPOSITORY_HPP +#define SOURCEDOUT_USERREPOSITORY_HPP + +#include +#include +#include +#include +#include + +#include "IUserRepository.hpp" +#include "dbManager.hpp" + +using namespace pqxx; + +class UserRepository : public IUserRepository { + public: + UserRepository(); + + std::optional getUserById(size_t id) override; + + std::optional getUserByLogin(std::string login) override; + + size_t makeUser(User user) override; + + void deleteUser(User user) override; + + void deleteByUserId(size_t user_id) override; + + std::vector getAllUsers() override; + + void update(User user) override; + + private: + static User makeUser(const result::const_iterator &c); + + std::shared_ptr manager; +}; + +#endif // SOURCEDOUT_USERREPOSITORY_HPP diff --git a/server/internal/repository/src/MetricRepository.cpp b/server/internal/repository/src/MetricRepository.cpp new file mode 100644 index 0000000000000000000000000000000000000000..33189de679cffaa322cbfc2a0105e58baf587818 --- /dev/null +++ b/server/internal/repository/src/MetricRepository.cpp @@ -0,0 +1,84 @@ +#include "MetricRepository.hpp" + +#include + +#include "MetricStat.hpp" + +std::optional MetricRepository::getById(size_t id) { + try { + auto c = manager->connection(); + std::string sql = "SELECT * FROM metricStat WHERE id=" + std::to_string(id); + nontransaction n(*c); + result r(n.exec(sql)); + manager->freeConnection(c); + if (r.empty()) return std::nullopt; + return makeMetric(r.begin()); + } catch (...) { + throw; + } +} + +size_t MetricRepository::storeMetric(MetricStat metric) { + try { + auto c = manager->connection(); + + std::string sql = (boost::format("INSERT INTO metricStat (solution_id, text_based_res, token_based_res, " + "tree_based_res, verdict, mean_res) " + "VALUES ('%s', '%s', '%s', '%s', '%s', '%s') RETURNING id; ") % + metric.getSolutionId() % metric.getTextBasedRes() % metric.getTokenBasedRes() % + metric.getTreeBasedRes() % metric.isVerdict() % metric.getMeanRes()) + .str(); + work w(*c); + row r = (w.exec1(sql)); + w.commit(); + manager->freeConnection(c); + return r["id"].as(); + } catch (...) { + throw; + } +} + +void MetricRepository::updateMetric(MetricStat metric) { + try { + auto c = manager->connection(); + + std::string sql = + (boost::format("UPDATE metricStat SET solution_id = '%s', text_based_res = '%s', token_based_res " + "= '%s', tree_based_res = '%s', verdict = '%s', mean_res = '%s';") % + metric.getSolutionId() % metric.getTextBasedRes() % metric.getTokenBasedRes() % metric.getTreeBasedRes() % + metric.isVerdict() % metric.getMeanRes()) + .str(); + work w(*c); + w.exec(sql); + manager->freeConnection(c); + } catch (...) { + throw; + } +} + +void MetricRepository::deleteMetric(MetricStat metric) { deleteMetricById(metric.getId()); } + +void MetricRepository::deleteMetricById(size_t id) { + try { + auto c = manager->connection(); + std::string sql = "DELETE FROM metricStat WHERE id=" + std::to_string(id); + work w(*c); + w.exec(sql); + w.commit(); + manager->freeConnection(c); + } catch (...) { + throw; + } +} + +MetricStat MetricRepository::makeMetric(const result::const_iterator &c) { + return {c.at(c.column_number("id")).as(), + c.at(c.column_number("solution_id")).as(), + c.at(c.column_number("text_based_res")).as(), + c.at(c.column_number("token_based_res")).as(), + c.at(c.column_number("tree_based_res")).as(), + c.at(c.column_number("verdict")).as(), + c.at(c.column_number("mean_res")).as()}; +} + +MetricRepository::MetricRepository() { manager = std::make_shared(); } diff --git a/server/internal/repository/src/SolutionRepository.cpp b/server/internal/repository/src/SolutionRepository.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44b3b0bbd9148e8be24beea5d06bab5025d46511 --- /dev/null +++ b/server/internal/repository/src/SolutionRepository.cpp @@ -0,0 +1,199 @@ +#include "SolutionRepository.hpp" + +#include +#include +#include + +#include "Solution.hpp" + +using namespace pqxx; + +std::optional SolutionRepository::getSolutionById(size_t id) { + try { + auto c = manager->connection(); + std::string sql = "SELECT * FROM solutions WHERE id=" + std::to_string(id); + nontransaction n(*c); + result r(n.exec(sql)); + manager->freeConnection(c); + if (r.empty()) return std::nullopt; + return makeSolution(r.begin()); + } catch (...) { + throw; + } +} + +std::vector SolutionRepository::getSolutionsBySenderId(size_t sender_id) { + try { + auto c = manager->connection(); + std::string sql = "SELECT * FROM solutions WHERE sender_id=" + std::to_string(sender_id); + nontransaction n(*c); + auto stream = stream_from::query(n, sql); + std::vector solutions; + std::tuple + row; + while (stream >> row) { + solutions.emplace_back(get<0>(row), get<1>(row), get<2>(row), get<3>(row), get<4>(row), get<5>(row), + get<6>(row), get<7>(row), get<8>(row), get<9>(row)); + } + stream.complete(); + manager->freeConnection(c); + return solutions; + } catch (...) { + throw; + } +} + +std::vector SolutionRepository::getSolutionsByTaskId(size_t task_id) { + try { + auto c = manager->connection(); + std::string sql = "SELECT * FROM solutions WHERE task_id=" + std::to_string(task_id); + nontransaction n(*c); + auto stream = stream_from::query(n, sql); + std::vector solutions; + std::tuple + row; + while (stream >> row) { + solutions.emplace_back(get<0>(row), get<1>(row), get<2>(row), get<3>(row), get<4>(row), get<5>(row), + get<6>(row), get<7>(row), get<8>(row), get<9>(row)); + } + stream.complete(); + manager->freeConnection(c); + return solutions; + } catch (...) { + throw; + } +} + +std::vector SolutionRepository::getSolutionsByTaskIdAndSenderId(size_t task_id, size_t sender_id) { + try { + auto c = manager->connection(); + std::string sql = + (boost::format("SELECT * FROM solutions WHERE task_id='%s' AND sender_id='%s'") % task_id % sender_id) + .str(); + nontransaction n(*c); + auto stream = stream_from::query(n, sql); + std::vector solutions; + std::tuple + row; + while (stream >> row) { + solutions.emplace_back(get<0>(row), get<1>(row), get<2>(row), get<3>(row), get<4>(row), get<5>(row), + get<6>(row), get<7>(row), get<8>(row), get<9>(row)); + } + stream.complete(); + manager->freeConnection(c); + return solutions; + } catch (...) { + throw; + } +} + +std::vector SolutionRepository::getSolutionsByTaskIdAndLanguage(size_t task_id, std::string lang) { + try { + auto c = manager->connection(); + std::string sql = + (boost::format("SELECT * FROM solutions WHERE task_id='%s' AND language='%s'") % task_id % lang).str(); + nontransaction n(*c); + auto stream = stream_from::query(n, sql); + std::vector solutions; + std::tuple + row; + while (stream >> row) { + solutions.emplace_back(get<0>(row), get<1>(row), get<2>(row), get<3>(row), get<4>(row), get<5>(row), + get<6>(row), get<7>(row), get<8>(row), get<9>(row)); + } + stream.complete(); + manager->freeConnection(c); + return solutions; + } catch (...) { + throw; + } +} + +std::optional SolutionRepository::getOriginalSolution(size_t id) { + try { + auto c = manager->connection(); + std::string sql = "SELECT * FROM solutions WHERE original_solution_id=" + std::to_string(id); + nontransaction n(*c); + result r(n.exec(sql)); + manager->freeConnection(c); + if (r.empty()) return std::nullopt; + return makeSolution(r.begin()); + } catch (...) { + throw; + } +} + +size_t SolutionRepository::storeSolution(Solution solution) { + try { + auto c = manager->connection(); + + std::string sql = + (boost::format("INSERT INTO solutions (send_date,sender_id, source, task_id, result, tokens, " + "astTree, original_solution_id, language) " + "VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') RETURNING id; ") % + solution.getSendDate() % solution.getSenderId() % solution.getSource() % solution.getTaskId() % + solution.getResult() % solution.getTokens() % solution.getAstTree() % solution.getOrigSolution() % + solution.getLanguage()) + .str(); + work w(*c); + row r = (w.exec1(sql)); + w.commit(); + manager->freeConnection(c); + return r["id"].as(); + } catch (...) { + throw; + } +} + +void SolutionRepository::updateSolution(Solution solution) { + try { + auto c = manager->connection(); + + std::string sql = (boost::format("UPDATE solutions SET send_date = '%s', sender_id = '%s', source = '%s'," + " task_id = '%s', result = '%s', tokens = '%s', astTree = '%s', " + "original_solution_id = '%s', language = '%s';") % + solution.getSendDate() % solution.getSenderId() % solution.getSource() % + solution.getTaskId() % solution.getResult() % solution.getTokens() % solution.getAstTree() % + solution.getOrigSolution() % solution.getLanguage()) + .str(); + work w(*c); + w.exec(sql); + manager->freeConnection(c); + } catch (...) { + throw; + } +} + +void SolutionRepository::deleteSolutionById(size_t id) { + try { + auto c = manager->connection(); + std::string sql = "DELETE FROM solutions WHERE id=" + std::to_string(id); + work w(*c); + w.exec(sql); + w.commit(); + manager->freeConnection(c); + } catch (...) { + throw; + } +} + +void SolutionRepository::deleteSolution(Solution solution) { deleteSolutionById(solution.getId()); } + +Solution SolutionRepository::makeSolution(const result::const_iterator &c) { + return {c.at(c.column_number("id")).as(), + c.at(c.column_number("send_date")).as(), + c.at(c.column_number("sender_id")).as(), + c.at(c.column_number("source")).as(), + c.at(c.column_number("task_id")).as(), + c.at(c.column_number("result")).as(), + c.at(c.column_number("tokens")).as(), + c.at(c.column_number("astTree")).as(), + c.at(c.column_number("original_solution_id")).as(), + c.at(c.column_number("language")).as()}; +} + +SolutionRepository::SolutionRepository() { manager = std::make_shared(); } diff --git a/server/internal/repository/src/TaskRepository.cpp b/server/internal/repository/src/TaskRepository.cpp new file mode 100644 index 0000000000000000000000000000000000000000..813f009daac8e07b4ce588805f9a65fb16666bed --- /dev/null +++ b/server/internal/repository/src/TaskRepository.cpp @@ -0,0 +1,95 @@ +#include "TaskRepository.hpp" + +#include +#include +#include + +#include "Task.hpp" + +std::optional TaskRepository::getTaskById(size_t id) { + try { + auto c = manager->connection(); + std::string sql = "SELECT * FROM tasks WHERE id=" + std::to_string(id); + nontransaction n(*c); + result r(n.exec(sql)); + manager->freeConnection(c); + if (r.empty()) { + return std::nullopt; + } + return makeTask(r.begin()); + } catch (...) { + throw; + } +} + +std::vector TaskRepository::getAllTasks() { + try { + auto c = manager->connection(); + std::string sql = "SELECT * FROM tasks"; + nontransaction n(*c); + auto stream = stream_from::query(n, sql); + std::vector tasks; + std::tuple row; + while (stream >> row) { + tasks.emplace_back(get<0>(row), get<1>(row), get<2>(row), get<3>(row)); + } + stream.complete(); + manager->freeConnection(c); + return tasks; + } catch (...) { + throw; + } +} + +void TaskRepository::updateTask(const Task &task) { + try { + auto c = manager->connection(); + std::string sql = (boost::format("UPDATE tasks SET description = '%s', treshold = '%s', name = '%s';") % + task.getDescription() % task.getTreshhold() % task.getName()) + .str(); + work w(*c); + w.exec(sql); + manager->freeConnection(c); + } catch (...) { + throw; + } +} + +size_t TaskRepository::storeTask(Task task) { + try { + auto c = manager->connection(); + std::string sql = (boost::format("INSERT INTO tasks (description, treshold, name) " + "VALUES ('%s', '%s', '%s') RETURNING id; ") % + task.getDescription() % task.getTreshhold() % task.getName()) + .str(); + work w(*c); + row r = w.exec1(sql); + w.commit(); + manager->freeConnection(c); + return r["id"].as(); + } catch (...) { + throw; + } +} + +void TaskRepository::deleteTask(Task task) { deleteTaskById(task.getId()); } + +void TaskRepository::deleteTaskById(size_t task_id) { + try { + auto c = manager->connection(); + std::string sql = "DELETE FROM tasks WHERE id=" + std::to_string(task_id); + work w(*c); + w.exec(sql); + w.commit(); + manager->freeConnection(c); + } catch (...) { + throw; + } +} + +Task TaskRepository::makeTask(const result::const_iterator &c) { + return {c.at(c.column_number("id")).as(), c.at(c.column_number("description")).as(), + c.at(c.column_number("treshold")).as(), c.at(c.column_number("name")).as()}; +} + +TaskRepository::TaskRepository() { manager = std::make_shared(); } diff --git a/server/internal/repository/src/UserRepository.cpp b/server/internal/repository/src/UserRepository.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4d647b97e7474e181c1be8c415c7691b73a60489 --- /dev/null +++ b/server/internal/repository/src/UserRepository.cpp @@ -0,0 +1,110 @@ + +#include "UserRepository.hpp" + +#include +#include + +#include "User.hpp" +#include "dbManager.hpp" + +std::optional UserRepository::getUserById(size_t id) { + try { + auto c = manager->connection(); + std::string sql = "SELECT * FROM Users WHERE id=" + std::to_string(id); + nontransaction n(*c); + result r(n.exec(sql)); + manager->freeConnection(c); + if (r.empty()) return std::nullopt; + return makeUser(r.begin()); + } catch (...) { + throw; + } +} + +std::optional UserRepository::getUserByLogin(std::string login) { + try { + auto c = manager->connection(); + std::string sql = (boost::format("SELECT * FROM Users WHERE login= '%s'") % login).str(); + nontransaction n(*c); + result r(n.exec(sql)); + manager->freeConnection(c); + if (r.empty()) return std::nullopt; + return makeUser(r.begin()); + } catch (...) { + throw; + } +} + +size_t UserRepository::makeUser(User user) { + try { + auto c = manager->connection(); + + std::string sql = (boost::format("INSERT INTO users (login,password,username) " + "VALUES ('%s', '%s', '%s') RETURNING id; ") % + user.getLogin() % user.getPassword() % user.getUsername()) + .str(); + work w(*c); + row r = w.exec1(sql); + w.commit(); + manager->freeConnection(c); + return r["id"].as(); + } catch (...) { + throw; + } +} + +void UserRepository::deleteByUserId(size_t user_id) { + try { + auto c = manager->connection(); + std::string sql = "DELETE FROM Users WHERE id=" + std::to_string(user_id); + work w(*c); + w.exec(sql); + w.commit(); + manager->freeConnection(c); + } catch (...) { + throw; + } +} + +void UserRepository::deleteUser(User user) { deleteByUserId(user.getId()); } + +std::vector UserRepository::getAllUsers() { + try { + auto c = manager->connection(); + std::string sql = "SELECT * FROM Users"; + nontransaction n(*c); + auto stream = stream_from::query(n, sql); + std::vector users; + std::tuple row; + while (stream >> row) { + users.emplace_back(get<0>(row), get<1>(row), get<2>(row), get<3>(row)); + } + stream.complete(); + manager->freeConnection(c); + return users; + } catch (...) { + throw; + } +} + +User UserRepository::makeUser(const result::const_iterator &c) { + return {c.at(c.column_number("id")).as(), c.at(c.column_number("login")).as(), + c.at(c.column_number("password")).as(), c.at(c.column_number("username")).as()}; +} + +UserRepository::UserRepository() { manager = std::make_shared(); } + +void UserRepository::update(User user) { + try { + auto c = manager->connection(); + + std::string sql = (boost::format("UPDATE Users SET login = '%s', password = '%s', username = '%s';") % + user.getLogin() % user.getPassword() % user.getUsername()) + .str(); + work w(*c); + w.exec(sql); + manager->freeConnection(c); + } catch (...) { + throw; + } +} diff --git a/server/internal/repository/tests/CMakeLists.txt b/server/internal/repository/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..23379e51ffad6c7ea1c1a2e9f15782c50f9c16b8 --- /dev/null +++ b/server/internal/repository/tests/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.19) + +project("RepositoryTests") + +set(CMAKE_CXX_STANDARD 20) + +file(GLOB SOURCES *.cpp) + +if(BUILD_TYPE) + set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-fprofile-arcs -ftest-coverage -O0 -g") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -coverage -lgcov" ) +endif(BUILD_TYPE) + + +file(GLOB TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +message("${TEST_SOURCES}") +add_executable(${PROJECT_NAME} ${TEST_SOURCES}) + +target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${libpqxx_LIBRARIES} ${libEntities_LIB} ${libDbManager_LIB} ${GTest_LIBRARIES} ${libRepository_LIB}) +target_include_directories(${PROJECT_NAME} PUBLIC ${libRepository_INCLUDE_DIRS}) + +target_compile_options(${PROJECT_NAME} PUBLIC --coverage) +target_link_options(${PROJECT_NAME} PUBLIC --coverage) + +gtest_discover_tests(${PROJECT_NAME}) + +add_custom_target( + ${PROJECT_NAME}_TEST + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} +) + + +add_custom_target( + ${PROJECT_NAME}_VALGRIND + COMMAND valgrind -s --leak-check=full --show-leak-kinds=all ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} +) + + + diff --git a/server/internal/repository/tests/RepositoryTests.cpp b/server/internal/repository/tests/RepositoryTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c14d237d4f995d6b9ae5c983c9ef7a971f28ba2b --- /dev/null +++ b/server/internal/repository/tests/RepositoryTests.cpp @@ -0,0 +1,169 @@ +#include +#include + +#include + +#include "MetricRepository.hpp" +#include "SolutionRepository.hpp" +#include "TaskRepository.hpp" +#include "UserRepository.hpp" + +TEST(UserRepository_CRUD_Test, CRUD) { + UserRepository rep; + + EXPECT_NO_FATAL_FAILURE(rep.getUserById(1)); + + User user("test@test.com", "test", "testuser"); + size_t id = rep.makeUser(user); + user.setId(id); + std::optional new_user_opt = rep.getUserById(id); + User new_user; + if (new_user_opt) { + new_user = new_user_opt.value(); + } + EXPECT_EQ(user, new_user); + new_user.setUsername("new_test_user"); + EXPECT_NO_FATAL_FAILURE(rep.update(new_user)); + + EXPECT_NO_FATAL_FAILURE(rep.deleteUser(new_user)); +} + +TEST(TaskRepository_CRUD_Test, CRUD) { + TaskRepository rep; + + Task task("test task", 0.5); + size_t id = rep.storeTask(task); + EXPECT_NO_FATAL_FAILURE(rep.getTaskById(1)); + task.setId(id); + std::optional new_task_opt = rep.getTaskById(id); + Task new_task; + if (new_task_opt) new_task = new_task_opt.value(); + EXPECT_EQ(task, new_task); + new_task.setDescription("new_test_description"); + EXPECT_NO_FATAL_FAILURE(rep.updateTask(new_task)); + EXPECT_NO_FATAL_FAILURE(rep.deleteTask(new_task)); +} + +TEST(SolutionRepository_CRUD_Test, CRUD) { + SolutionRepository rep; + + Solution solution("01.01.1970", 1, ":/C/Users", 1, "result", "tokens.txt", "tree.txt", 1); + size_t id = rep.storeSolution(solution); + EXPECT_NO_FATAL_FAILURE(rep.getSolutionById(163)); + solution.setId(id); + std::optional new_solution_opt = rep.getSolutionById(id); + Solution new_solution; + if (new_solution_opt) new_solution = new_solution_opt.value(); + EXPECT_EQ(solution, new_solution); + new_solution.setSource(":/D"); + EXPECT_NO_FATAL_FAILURE(rep.updateSolution(new_solution)); + EXPECT_NO_FATAL_FAILURE(rep.deleteSolution(new_solution)); +} + +TEST(MetricRepository_CRUD_Test, CRUD) { + MetricRepository rep; + MetricStat metricStat(1, 0.8f, 0.9f, 0.89f, true, 0.85f); + size_t id = rep.storeMetric(metricStat); + EXPECT_NO_FATAL_FAILURE(rep.getById(1)); + metricStat.setId(id); + std::optional new_stat_opt = rep.getById(id); + MetricStat new_stat; + if (new_stat_opt) new_stat = new_stat_opt.value(); + EXPECT_EQ(metricStat, new_stat); + new_stat.setMeanRes(1); + EXPECT_NO_FATAL_FAILURE(rep.updateMetric(new_stat)); + EXPECT_NO_FATAL_FAILURE(rep.deleteMetric(new_stat)); +} + +TEST(UserRepository_CRUD_Test, getAllUsers) { + UserRepository rep; + std::vector v = {{"test@test.com", "test", "testuser"}, {"test2@test.com", "test2", "testuser2"}}; + EXPECT_NO_FATAL_FAILURE(rep.getAllUsers()); + std::vector new_v = rep.getAllUsers(); + EXPECT_EQ(v, new_v); +} + +TEST(UserRepository_CRUD_Test, loginLikeId) { + UserRepository rep; + User user("test@test.com", "test", "testuser"); + size_t id = rep.makeUser(user); + user.setId(id); + std::optional new_user_id = rep.getUserById(id); + std::optional new_user_login = rep.getUserByLogin(user.getLogin()); + User new_user; + EXPECT_EQ(new_user_id, new_user_login); + EXPECT_NO_FATAL_FAILURE(rep.deleteUser(user)); +} + +TEST(SolutionRepository_CRUD_Test, CRUD_getSolutionsBySenderId) { + SolutionRepository rep; + + Solution solution1("01.01.1970", 1, ":/C/Users", 1, "result", "tokens.txt", "tree.txt", 1); + Solution solution2("01.01.1970", 1, "home/usr", 1, "result", "tokens.txt", "tree.txt", 1); + + size_t id1 = rep.storeSolution(solution1); + solution1.setId(id1); + EXPECT_EQ(solution1, rep.getSolutionById(id1)); + size_t id2 = rep.storeSolution(solution2); + solution2.setId(id2); + EXPECT_EQ(solution2, rep.getSolutionById(id2)); + std::vector v = {{id1, "01.01.1970", 1, ":/C/Users", 1, "result", "tokens.txt", "tree.txt", 1}, + {id2, "01.01.1970", 1, "home/usr", 1, "result", "tokens.txt", "tree.txt", 1}}; + std::vector new_v = rep.getSolutionsBySenderId(solution1.getSenderId()); + EXPECT_EQ(v, new_v); + EXPECT_NO_FATAL_FAILURE(rep.deleteSolution(solution1)); + EXPECT_NO_FATAL_FAILURE(rep.deleteSolution(solution2)); +} + +TEST(SolutionRepository_CRUD_Test, CRUD_getSolutionsByTaskId) { + SolutionRepository rep; + + Solution solution1("01.01.1970", 1, ":/C/Users", 1, "result", "tokens.txt", "tree.txt", 1); + Solution solution2("01.01.1970", 1, "home/usr", 1, "result", "tokens.txt", "tree.txt", 1); + + size_t id1 = rep.storeSolution(solution1); + solution1.setId(id1); + size_t id2 = rep.storeSolution(solution2); + solution2.setId(id2); + std::vector v = {solution1, solution2}; + std::vector new_v = rep.getSolutionsByTaskId(solution1.getTaskId()); + EXPECT_EQ(v, new_v); + EXPECT_NO_FATAL_FAILURE(rep.deleteSolution(solution1)); + EXPECT_NO_FATAL_FAILURE(rep.deleteSolution(solution2)); +} + +TEST(SolutionRepository_CRUD_Test, tryToAddWithNotExistingTask) { + SolutionRepository rep; + Solution solution("01.01.1970", 1, ":/C/Users", 100500, "result", "tokens.txt", "tree.txt", 1); + try { + rep.storeSolution(solution); + } catch (pqxx::foreign_key_violation &e) { + std::cout << e.what(); + } +} + +TEST(SolutionRepository_CRUD_Test, tryToStoreWithNotExistingSender) { + SolutionRepository rep; + + Solution solution("01.01.1970", 100500, ":/C/Users", 100500, "result", "tokens.txt", "tree.txt", 1); + try { + rep.storeSolution(solution); + } catch (pqxx::foreign_key_violation &keyViolation) { + std::cout << keyViolation.what(); + } +} + +TEST(MetricRepository_CRUD_Test, tryToStoreWithNotExistingSolution) { + MetricRepository rep; + MetricStat metricStat(100500, 0.8f, 0.9f, 0.89f, true, 0.85f); + try { + rep.storeMetric(metricStat); + } catch (pqxx::foreign_key_violation &keyViolation) { + std::cout << keyViolation.what(); + } +} + +int main(int argc, char **argv) { + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/server/internal/repository/virtual/IMetricRepository.hpp b/server/internal/repository/virtual/IMetricRepository.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0034b50ccc642e0717e741ea675245176fcf9a6c --- /dev/null +++ b/server/internal/repository/virtual/IMetricRepository.hpp @@ -0,0 +1,22 @@ +#ifndef SOURCEDOUT_IMETRICREPOSITORY_HPP +#define SOURCEDOUT_IMETRICREPOSITORY_HPP + +#include +#include + +#include "MetricStat.hpp" + +class IMetricRepository { + public: + virtual std::optional getById(size_t id) = 0; + + virtual size_t storeMetric(MetricStat metric) = 0; + + virtual void updateMetric(MetricStat metric) = 0; + + virtual void deleteMetric(MetricStat metric) = 0; + + virtual void deleteMetricById(size_t id) = 0; +}; + +#endif // SOURCEDOUT_IMETRICREPOSITORY_HPP diff --git a/server/internal/repository/virtual/ISolutionRepository.hpp b/server/internal/repository/virtual/ISolutionRepository.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3122a2e8c5967f6e08ef41d4a54ebca9dd92fe09 --- /dev/null +++ b/server/internal/repository/virtual/ISolutionRepository.hpp @@ -0,0 +1,35 @@ +#ifndef SOURCEDOUT_ISOLUTIONREPOSITORY_HPP +#define SOURCEDOUT_ISOLUTIONREPOSITORY_HPP + +#include +#include +#include + +#include "Solution.hpp" + +class ISolutionRepository { + public: + virtual ~ISolutionRepository() = default; + + virtual std::optional getSolutionById(size_t id) = 0; + + virtual std::vector getSolutionsBySenderId(size_t sender_id) = 0; + + virtual std::vector getSolutionsByTaskId(size_t task_id) = 0; + + virtual std::vector getSolutionsByTaskIdAndSenderId(size_t, size_t) = 0; + + virtual std::vector getSolutionsByTaskIdAndLanguage(size_t, std::string) = 0; + + virtual size_t storeSolution(Solution solution) = 0; + + virtual void updateSolution(Solution solution) = 0; + + virtual void deleteSolutionById(size_t id) = 0; + + virtual void deleteSolution(Solution solution) = 0; + + virtual std::optional getOriginalSolution(size_t id) = 0; +}; + +#endif // SOURCEDOUT_ISOLUTIONREPOSITORY_HPP diff --git a/server/internal/repository/virtual/ITaskRepository.hpp b/server/internal/repository/virtual/ITaskRepository.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f1fa1c6f1f7b7f0e5b0225904e044127c38aa228 --- /dev/null +++ b/server/internal/repository/virtual/ITaskRepository.hpp @@ -0,0 +1,27 @@ +#ifndef SOURCEDOUT_ITASKREPOSITORY_HPP +#define SOURCEDOUT_ITASKREPOSITORY_HPP + +#include +#include +#include + +#include "Task.hpp" + +class ITaskRepository { + public: + virtual ~ITaskRepository() = default; + + virtual std::optional getTaskById(size_t id) = 0; + + virtual std::vector getAllTasks() = 0; + + virtual void updateTask(const Task& task) = 0; + + virtual size_t storeTask(Task task) = 0; + + virtual void deleteTask(Task task) = 0; + + virtual void deleteTaskById(size_t task_id) = 0; +}; + +#endif // SOURCEDOUT_ITASKREPOSITORY_HPP diff --git a/server/internal/repository/virtual/IUserRepository.hpp b/server/internal/repository/virtual/IUserRepository.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f723358b37165eb8a8359adbd11cfb173d038f0b --- /dev/null +++ b/server/internal/repository/virtual/IUserRepository.hpp @@ -0,0 +1,28 @@ +#ifndef SOURCEDOUT_IUSERREPOSITORY_HPP +#define SOURCEDOUT_IUSERREPOSITORY_HPP + +#include +#include + +#include "User.hpp" + +class IUserRepository { + public: + virtual ~IUserRepository() = default; + + virtual std::optional getUserById(size_t id) = 0; + + virtual std::optional getUserByLogin(std::string login) = 0; + + virtual size_t makeUser(User user) = 0; + + virtual void deleteUser(User user) = 0; + + virtual void update(User user) = 0; + + virtual void deleteByUserId(size_t user_id) = 0; + + virtual std::vector getAllUsers() = 0; +}; + +#endif // SOURCEDOUT_IUSERREPOSITORY_HPP diff --git a/server/internal/service/CMakeLists.txt b/server/internal/service/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ab9c04c365a1abf461a7fac0869621cea01023ea --- /dev/null +++ b/server/internal/service/CMakeLists.txt @@ -0,0 +1,28 @@ +project("ServiceLib") + +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/virtual) + + +include_directories(${INCLUDE_DIRS}) +add_library(${PROJECT_NAME} ${SOURCES}) + +message("ANTLR4_LIB = ${ANTLR4_LIB}") +message("ANTLR4_LIB_INCLUDE_DIRS = ${ANTLR4_LIB_INCLUDE_DIRS}") + +message("METRICS_lib_INCLUDE_DIRS=${METRICS_lib_INCLUDE_DIRS}") +message("METRICS_LIBRARY=${METRICS_LIBRARY}") + +target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDE_DIRS} ${libEntities_INCLUDE_DIRS} ${libRepository_INCLUDE_DIRS} ${ANTLR4_LIB_INCLUDE_DIRS} ${METRICS_lib_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} ${libRepository_LIB} ${libEntities_LIB} ${ANTLR4_LIB} ${METRICS_LIBRARY} ${PQXX_LIB}) + +set(SERVICE_lib_LIBRARY ${PROJECT_NAME}) +set(SERVICE_lib_LIBRARY ${SERVICE_lib_LIBRARY} PARENT_SCOPE) + +set(SERVICE_lib_INCLUDE_DIRS ${INCLUDE_DIRS}) +set(SERVICE_lib_INCLUDE_DIRS ${SERVICE_lib_INCLUDE_DIRS} PARENT_SCOPE) + + +enable_testing() +add_subdirectory(tests) + diff --git a/server/internal/service/include/FileMethods.h b/server/internal/service/include/FileMethods.h new file mode 100644 index 0000000000000000000000000000000000000000..94e6f41d178f095cb37e01d7f216a3d17207b086 --- /dev/null +++ b/server/internal/service/include/FileMethods.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +inline const std::string CPP_EXTENSION = "cpp"; +inline const std::string PYTHON_EXTENSION = "py"; +inline const std::string UNKNOWN_EXTENSION = ""; + +class FileMethods { + public: + static std::pair checkFileExtension(const std::string& filename); +}; \ No newline at end of file diff --git a/server/internal/service/include/ServiceExceptions.h b/server/internal/service/include/ServiceExceptions.h new file mode 100644 index 0000000000000000000000000000000000000000..aabf3c14d6f418b15a706a4e7b7834fe90bd26b2 --- /dev/null +++ b/server/internal/service/include/ServiceExceptions.h @@ -0,0 +1,25 @@ +#pragma once + +class ValidateException : public std::exception { + std::string _msg; + + public: + ValidateException(const std::string& msg) : _msg(msg) {} + virtual const char* what() const noexcept override { return _msg.c_str(); } +}; + +class LoginException : public std::exception { + std::string _msg; + + public: + LoginException(const std::string& msg) : _msg(msg) {} + virtual const char* what() const noexcept override { return _msg.c_str(); } +}; + +class FileExtensionException : public std::exception { + std::string _msg; + + public: + FileExtensionException(const std::string& msg) : _msg(msg) {} + virtual const char* what() const noexcept override { return _msg.c_str(); } +}; \ No newline at end of file diff --git a/server/internal/service/include/ServiceUtils.h b/server/internal/service/include/ServiceUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..436107014675764d8cc6a48ad7f66bf508ff4b9f --- /dev/null +++ b/server/internal/service/include/ServiceUtils.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +class Utils { + public: + static std::string convertIntArrayIntoString(std::vector& arr); + static std::vector convertStringIntoIntArray(const std::string& str); +}; diff --git a/server/internal/service/include/SolutionService.h b/server/internal/service/include/SolutionService.h new file mode 100644 index 0000000000000000000000000000000000000000..d89a6514b30aa8558508dc5b30355572c4569c5c --- /dev/null +++ b/server/internal/service/include/SolutionService.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include "IAntlrWrapper.h" +#include "IMockMetrics.h" +#include "ISolutionRepository.hpp" +#include "ISolutionService.h" +#include "ITaskRepository.hpp" +#include "TextMetricsLib.h" +#include "TokenMetricLib.h" + +class SolutionService : public ISolutionService { + private: + std::unique_ptr solutionRepo; + std::unique_ptr taskRepo; + std::unique_ptr antlr; + std::unique_ptr textMetric; + std::unique_ptr tokenMetric; + void setAntlrWrapper(const std::string& fileExtension, const std::string& filedata); + std::string setResultVerdict(float textBasedRes, float tokenBasedRes, size_t plagiatSolId, float treshold); + std::pair getMaxTextResMetric(std::vector& solutions, const std::string& filedata, + size_t userId, float treshold); + + std::pair getMaxTokenResMetric(std::vector& solutions, std::vector& tokens, + size_t userId, float treshold); + + public: + explicit SolutionService(std::unique_ptr solutionRepo, + std::unique_ptr taskRepo); + SolutionService(); + Solution createSolution(size_t userId, size_t taskId, const std::string& filename, + const std::string& filedata) override; + void deleteSolutionById(size_t solId) override; + std::vector getSolutionsByUserAndTaskId(size_t user_id, size_t task_id) override; + std::pair getMetrics(size_t solId) override; +}; diff --git a/server/internal/service/include/TaskService.h b/server/internal/service/include/TaskService.h new file mode 100644 index 0000000000000000000000000000000000000000..c8a888169c1767c422dfb2e2ea13bfa391abf338 --- /dev/null +++ b/server/internal/service/include/TaskService.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include "ITaskRepository.hpp" +#include "ITaskService.h" + +class TaskService : public ITaskService { + private: + std::unique_ptr taskRepo; + + public: + explicit TaskService(std::unique_ptr taskRepo); + TaskService(); + Task createTask(const std::string& desc, const std::string& name = "Default name", float treshold = 0.5f) override; + Task getTask(size_t id) override; + std::vector getAllTasks() override; + void deleteTask(size_t id) override; +}; diff --git a/server/internal/service/include/UserService.h b/server/internal/service/include/UserService.h new file mode 100644 index 0000000000000000000000000000000000000000..9223b77285d0e559c761a6720436cb573866823c --- /dev/null +++ b/server/internal/service/include/UserService.h @@ -0,0 +1,18 @@ +#pragma once +#include + +#include "IUserRepository.hpp" +#include "IUserService.h" +#include "UserValidator.h" + +class UserService : public IUserService { + private: + std::unique_ptr userRepo; + + public: + explicit UserService(std::unique_ptr userRepo); + UserService(); + User createUser(const std::string& login, const std::string& username, const std::string& password) override; + User login(const std::string& login, const std::string& password) override; + void deleteUser(size_t id) override; +}; diff --git a/server/internal/service/include/UserValidator.h b/server/internal/service/include/UserValidator.h new file mode 100644 index 0000000000000000000000000000000000000000..f506abb3dbfd20259c6cd479ec8a20eb016ead8b --- /dev/null +++ b/server/internal/service/include/UserValidator.h @@ -0,0 +1,13 @@ +#pragma once + +#include "User.hpp" + +class UserValidator { + private: + static bool validateLogin(const std::string& login); + static bool validatePassword(const std::string& password); + static bool validateUsername(const std::string& username); + + public: + static bool validate(const User& user); +}; diff --git a/server/internal/service/src/FileMethods.cpp b/server/internal/service/src/FileMethods.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a5b695b88c1ad488edc74f7c9a0b3c4c9f2536f --- /dev/null +++ b/server/internal/service/src/FileMethods.cpp @@ -0,0 +1,14 @@ +#include "FileMethods.h" + +#include +#include + +std::pair FileMethods::checkFileExtension(const std::string& filename) { + if (filename.substr(filename.find_last_of(".") + 1) == CPP_EXTENSION) { + return std::make_pair(CPP_EXTENSION, true); + } + if (filename.substr(filename.find_last_of(".") + 1) == PYTHON_EXTENSION) { + return std::make_pair(PYTHON_EXTENSION, true); + } + return std::make_pair(UNKNOWN_EXTENSION, false); +} diff --git a/server/internal/service/src/ServiceUtils.cpp b/server/internal/service/src/ServiceUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..02671b17ee13dbdf5a379588a653f954cd6e1095 --- /dev/null +++ b/server/internal/service/src/ServiceUtils.cpp @@ -0,0 +1,23 @@ +#include "ServiceUtils.h" + +#include +#include +#include +#include +#include + +std::string Utils::convertIntArrayIntoString(std::vector& arr) { + std::string str; + for (size_t i = 0; i < arr.size(); i++) { + str += std::to_string(arr[i]) + " "; + } + return str; +} + +std::vector Utils::convertStringIntoIntArray(const std::string& str) { + std::stringstream ss(str); + std::vector v; + + std::copy(std::istream_iterator(ss), {}, back_inserter(v)); + return v; +} diff --git a/server/internal/service/src/SolutionService.cpp b/server/internal/service/src/SolutionService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bf63eacdd78fca61a7046955dcba8acdbcf5a11a --- /dev/null +++ b/server/internal/service/src/SolutionService.cpp @@ -0,0 +1,196 @@ +#include "SolutionService.h" + +#include +#include +#include +#include +#include + +#include "FileMethods.h" +#include "MyCppAntlr.h" +#include "PythonAntlr.h" +#include "ServiceExceptions.h" +#include "ServiceUtils.h" +#include "SolutionRepository.hpp" +#include "TaskRepository.hpp" + +const std::string PLAGIAT_VERDICT = "Не, ну вы не палитесь. Плагиат."; +const std::string NOT_PLAGIAT_VERDICT = "Красивое решение. А главное уникальное !"; + +SolutionService::SolutionService(std::unique_ptr solutionRepo, + std::unique_ptr taskRepo) + : solutionRepo(std::move(solutionRepo)), taskRepo(std::move(taskRepo)) {} + +SolutionService::SolutionService() { + solutionRepo = std::make_unique(); + taskRepo = std::make_unique(); +} + +void SolutionService::setAntlrWrapper(const std::string& fileExtension, const std::string& filedata) { + std::istringstream in(filedata); + if (fileExtension == CPP_EXTENSION) { + antlr = std::make_unique(in); + } else if (fileExtension == PYTHON_EXTENSION) { + antlr = std::make_unique(in); + } +} + +std::string SolutionService::setResultVerdict(float textBasedRes, float tokenBasedRes, size_t plagiatSolId, + float treshold = 0.5f) { + float meanRes = (tokenBasedRes + textBasedRes) / 2; + if (meanRes < treshold) { + return (boost::format("\n%s\nРезультаты метрик: %.2f\n\tАнализ текста: %.2f\n\tАнализ токенов: %.2f") % + NOT_PLAGIAT_VERDICT % meanRes % textBasedRes % tokenBasedRes) + .str(); + } + try { + std::string closestCode = solutionRepo->getSolutionById(plagiatSolId).value().getSource(); + return (boost::format( + "\n%s\nРезультаты метрик: %.2f\n\tАнализ текста: %.2f\n\tАнализ токенов: %.2f\nОчень похоже на " + "решение, отправленное до вас:\n%s") % + PLAGIAT_VERDICT % meanRes % textBasedRes % tokenBasedRes % closestCode) + .str(); + } catch (...) { + throw; + } +} + +std::pair SolutionService::getMaxTextResMetric(std::vector& solutions, + const std::string& filedata, size_t userId, + float treshold) { + // std::cout << "getMaxTextResMetric start" << std::endl; + std::pair maxMatch = std::make_pair(0.0, 0); + for (auto sol : solutions) { + if (sol.getSenderId() == userId) { + continue; + } + textMetric = std::make_unique(); + textMetric->setData(filedata, sol.getSource()); + float textBasedRes = float(textMetric->getMetric()); + + textMetric = std::make_unique(); + textMetric->setData(filedata, sol.getSource()); + textBasedRes = (textBasedRes + float(textMetric->getMetric())) / 2; + + if (maxMatch.first < textBasedRes) { + maxMatch.first = textBasedRes; + maxMatch.second = sol.getId(); + } + + if (textBasedRes > treshold) { + break; + } + } + // std::cout << "getMaxTextResMetric done" << std::endl; + return maxMatch; +} + +std::pair SolutionService::getMaxTokenResMetric(std::vector& solutions, + std::vector& tokens, size_t userId, + float treshold) { + std::pair maxMatch = std::make_pair(0.0, 0); + // std::cout << "getMaxTokenResMetric start" << std::endl; + for (auto sol : solutions) { + if (sol.getSenderId() == userId) { + continue; + } + tokenMetric = std::make_unique(); + tokenMetric->setData(tokens, Utils::convertStringIntoIntArray(sol.getTokens())); + float tokenBasedRes = float(tokenMetric->getMetric()); + + auto sol_tokens = Utils::convertStringIntoIntArray(sol.getTokens()); + + tokenMetric = std::make_unique(); + tokenMetric->setData(tokens, Utils::convertStringIntoIntArray(sol.getTokens())); + tokenBasedRes = (tokenBasedRes + float(tokenMetric->getMetric())) / 2; + + if (maxMatch.first < tokenBasedRes) { + maxMatch.first = tokenBasedRes; + maxMatch.second = sol.getId(); + } + + if (tokenBasedRes > treshold) { + break; + } + } + // std::cout << "getMaxTokenResMetric done" << std::endl; + return maxMatch; +} + +Solution SolutionService::createSolution(size_t userId, size_t taskId, const std::string& filename, + const std::string& filedata) { + try { + std::pair fileExtension = FileMethods::checkFileExtension(filename); + if (!fileExtension.second) { + throw FileExtensionException("unknown file extension"); + } + + setAntlrWrapper(fileExtension.first, filedata); + std::vector tokensTypes = antlr->getTokensTypes(); + std::string astTree = antlr->getTreeString(); + + std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + + float treshold = taskRepo->getTaskById(taskId).value().getTreshhold(); + + std::vector solutions = solutionRepo->getSolutionsByTaskIdAndLanguage(taskId, fileExtension.first); + + std::pair textBasedRes; + std::pair tokenBasedRes; + + std::thread t1([&textBasedRes, this, &solutions, &filedata, &userId, &treshold]() { + textBasedRes = getMaxTextResMetric(solutions, filedata, userId, treshold); + }); + std::thread t2([&tokenBasedRes, this, &solutions, &tokensTypes, &userId, &treshold]() { + tokenBasedRes = getMaxTokenResMetric(solutions, tokensTypes, userId, treshold); + }); + t1.join(); + t2.join(); + + size_t plagiatSolId = 1; + if (textBasedRes.first > tokenBasedRes.first) { + plagiatSolId = textBasedRes.second; + } else { + plagiatSolId = tokenBasedRes.second; + } + + std::string result = setResultVerdict(textBasedRes.first, tokenBasedRes.first, plagiatSolId, treshold); + + Solution sol = + Solution(std::ctime(&now), userId, filedata, taskId, result, Utils::convertIntArrayIntoString(tokensTypes), + astTree, plagiatSolId, fileExtension.first); + + size_t id = solutionRepo->storeSolution(sol); + sol.setId(id); + return sol; + } catch (...) { + throw; + } +} + +void SolutionService::deleteSolutionById(size_t solId) { + try { + solutionRepo->deleteSolutionById(solId); + } catch (...) { + throw; + } +} + +std::pair SolutionService::getMetrics(size_t solId) { + try { + Solution sol = solutionRepo->getSolutionById(solId).value(); + std::string tokens = sol.getTokens(); + std::string astTree = sol.getAstTree(); + return std::make_pair(tokens, astTree); + } catch (...) { + throw; + } +} + +std::vector SolutionService::getSolutionsByUserAndTaskId(size_t user_id, size_t task_id) { + try { + return solutionRepo->getSolutionsByTaskIdAndSenderId(task_id, user_id); + } catch (...) { + throw; + } +} diff --git a/server/internal/service/src/TaskService.cpp b/server/internal/service/src/TaskService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..873b66873d49fa157a81fefad363f0d34cd74756 --- /dev/null +++ b/server/internal/service/src/TaskService.cpp @@ -0,0 +1,42 @@ +#include "TaskService.h" + +#include "TaskRepository.hpp" + +TaskService::TaskService(std::unique_ptr taskRepo) : taskRepo(std::move(taskRepo)) {} + +TaskService::TaskService() { taskRepo = std::make_unique(); } + +Task TaskService::createTask(const std::string& desc, const std::string& name, float treshold) { + try { + Task task = Task(desc, treshold, name); + size_t id = taskRepo->storeTask(task); + task.setId(id); + return task; + } catch (...) { + throw; + } +} + +std::vector TaskService::getAllTasks() { + try { + return taskRepo->getAllTasks(); + } catch (...) { + throw; + } +} + +Task TaskService::getTask(size_t id) { + try { + return taskRepo->getTaskById(id).value(); + } catch (...) { + throw; + } +} + +void TaskService::deleteTask(size_t id) { + try { + taskRepo->deleteTaskById(id); + } catch (...) { + throw; + } +} diff --git a/server/internal/service/src/UserService.cpp b/server/internal/service/src/UserService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11ceadd905ccdcb7725670a3c8d16a4fa223e4f2 --- /dev/null +++ b/server/internal/service/src/UserService.cpp @@ -0,0 +1,43 @@ +#include "UserService.h" + +#include "ServiceExceptions.h" +#include "UserRepository.hpp" +#include "UserValidator.h" + +UserService::UserService(std::unique_ptr userRepo) : userRepo(std::move(userRepo)) {} + +UserService::UserService() { userRepo = std::make_unique(); } + +User UserService::createUser(const std::string& login, const std::string& username, const std::string& password) { + User user = User(login, password, username); + if (!UserValidator::validate(user)) { + throw ValidateException("invalid user params"); + } + try { + size_t id = userRepo->makeUser(user); + user.setId(id); + return user; + } catch (...) { + throw; + } +} + +User UserService::login(const std::string& login, const std::string& password) { + try { + User u = userRepo->getUserByLogin(login).value(); + if (u.getPassword() != password) { + throw LoginException("incorrect password"); + } + return u; + } catch (...) { + throw; + } +} + +void UserService::deleteUser(size_t id) { + try { + userRepo->deleteByUserId(id); + } catch (...) { + throw; + } +} diff --git a/server/internal/service/src/UserValidator.cpp b/server/internal/service/src/UserValidator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0530fc060a66ee62d7627431eceea7090bf80d66 --- /dev/null +++ b/server/internal/service/src/UserValidator.cpp @@ -0,0 +1,34 @@ +#include "UserValidator.h" + +#include +#include + +bool UserValidator::validate(const User& user) { + if (validateLogin(user.getLogin()) && validatePassword(user.getPassword()) && + validateUsername(user.getUsername())) { + return true; + } + return false; +} + +bool UserValidator::validateLogin(const std::string& login) { + if (login.length() < 3 || login.length() > 30) { + return false; + } + const std::regex pattern("(\\w+)@(\\w+)(\\.(\\w+))+"); + return std::regex_match(login, pattern); +} + +bool UserValidator::validatePassword(const std::string& password) { + if (password.length() < 8 || password.length() > 30) { + return false; + } + return true; +} + +bool UserValidator::validateUsername(const std::string& username) { + if (username.length() < 3 || username.length() > 20) { + return false; + } + return true; +} diff --git a/server/internal/service/tests/CMakeLists.txt b/server/internal/service/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2b5223fbcc24a51008a7a51a61816f13ab185ace --- /dev/null +++ b/server/internal/service/tests/CMakeLists.txt @@ -0,0 +1,14 @@ +project(test_service) + +file(GLOB SOURCES *.cpp) + +enable_testing() +find_package(GTest REQUIRED) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +target_link_libraries(${PROJECT_NAME} ${SERVICE_lib_LIBRARY} GTest::GTest gmock ${PQXX_LIB}) +target_include_directories(${PROJECT_NAME} PUBLIC ${SERVICE_lib_INCLUDE_DIRS}) + + +add_test(test_service test_service) \ No newline at end of file diff --git a/server/internal/service/tests/SolutionServiceTest.cpp b/server/internal/service/tests/SolutionServiceTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8f9130a1be5cae4e6294a9bfbe4f8c253e84199 --- /dev/null +++ b/server/internal/service/tests/SolutionServiceTest.cpp @@ -0,0 +1,130 @@ +#include +#include + +#include +#include +#include + +#include "SolutionService.h" + +class Exception : public std::exception { + std::string _msg; + + public: + explicit Exception(const std::string& msg) : _msg(msg) {} + const char* what() const noexcept override { return _msg.c_str(); } +}; + +class SolutionRepositoryMock : public ISolutionRepository { + public: + ~SolutionRepositoryMock() override = default; + MOCK_METHOD(std::optional, getSolutionById, (size_t id), (override)); + MOCK_METHOD(std::vector, getSolutionsBySenderId, (size_t sender_id), (override)); + MOCK_METHOD(std::vector, getSolutionsByTaskId, (size_t task_id), (override)); + MOCK_METHOD(size_t, storeSolution, (Solution solution), (override)); + MOCK_METHOD(void, updateSolution, (Solution solution), (override)); + MOCK_METHOD(void, deleteSolutionById, (size_t id), (override)); + MOCK_METHOD(void, deleteSolution, (Solution solution), (override)); + MOCK_METHOD(std::optional, getOriginalSolution, (size_t id), (override)); + MOCK_METHOD(std::vector, getSolutionsByTaskIdAndSenderId, (size_t user_id, size_t task_id), (override)); + MOCK_METHOD(std::vector, getSolutionsByTaskIdAndLanguage, (size_t, std::string), (override)); +}; + +class TaskRepositoryMock : public ITaskRepository { + public: + ~TaskRepositoryMock() override = default; + MOCK_METHOD(std::optional, getTaskById, (size_t id), (override)); + MOCK_METHOD(void, updateTask, (const Task& task), (override)); + MOCK_METHOD(size_t, storeTask, (Task task), (override)); + MOCK_METHOD(void, deleteTask, (Task task), (override)); + MOCK_METHOD(void, deleteTaskById, (size_t id), (override)); + MOCK_METHOD(std::vector, getAllTasks, (), (override)); +}; + +struct SolutionServiceTest : public testing::Test { + SolutionService* ss; + SolutionRepositoryMock* solutionMockPtr; + TaskRepositoryMock* taskMockPtr; + + void SetUp() { + auto sMock = std::make_unique(); + solutionMockPtr = sMock.get(); + auto tMock = std::make_unique(); + taskMockPtr = tMock.get(); + ss = new SolutionService(std::move(sMock), std::move(tMock)); + } + void TearDown() { delete ss; } +}; + +ACTION(NoSolutionException) { throw Exception("no solution with this id in db"); } + +TEST_F(SolutionServiceTest, deleteSolution) { + EXPECT_CALL(*solutionMockPtr, deleteSolutionById(1)).Times(1); + ss->deleteSolutionById(1); +} + +TEST_F(SolutionServiceTest, deleteSolutionException) { + EXPECT_CALL(*solutionMockPtr, deleteSolutionById(-1)).Times(1).WillRepeatedly(NoSolutionException()); + EXPECT_THROW(ss->deleteSolutionById(-1), std::exception); +} + +TEST_F(SolutionServiceTest, getMetrics) { + EXPECT_CALL(*solutionMockPtr, getSolutionById(1)) + .Times(1) + .WillOnce(::testing::Return(Solution(1, "", 1, "", 1, "", "tokens", "astTree", 1, "cpp"))); + std::pair metrics = ss->getMetrics(1); + EXPECT_EQ(metrics.first, "tokens"); + EXPECT_EQ(metrics.second, "astTree"); +} + +TEST_F(SolutionServiceTest, getMetricsException) { + EXPECT_CALL(*solutionMockPtr, getSolutionById(-1)).Times(1).WillRepeatedly(NoSolutionException()); + EXPECT_THROW(ss->getMetrics(-1), std::exception); +} + +TEST_F(SolutionServiceTest, createSolutionPlagiat) { + EXPECT_CALL(*solutionMockPtr, storeSolution(Solution(0, "", 2, "source", 1, "", "", "", 1, "cpp"))) + .Times(1) + .WillRepeatedly(::testing::Return(1)); + + std::vector solutions; + solutions.push_back( + Solution(0, "", 1, "int main(){return 0;}", 1, "", "45 132 85 86 89 59 1 128 90 -1", "", -1, "cpp")); + EXPECT_CALL(*solutionMockPtr, getSolutionsByTaskIdAndLanguage(1, "cpp")) + .Times(1) + .WillOnce(::testing::Return(solutions)); + + EXPECT_CALL(*taskMockPtr, getTaskById(1)).Times(1).WillOnce(::testing::Return(Task(1, "desription", 0.5f, "name"))); + + EXPECT_CALL(*solutionMockPtr, getSolutionById(0)) + .Times(1) + .WillOnce(::testing::Return(std::make_optional( + Solution(0, "", 1, "int main(){return 0;}", 1, "", "45 132 85 86 89 59 1 128 90 -1", "", -1, "cpp")))); + + Solution sol = ss->createSolution(2, 1, "main.cpp", "size_t main(){return 1;}"); + EXPECT_EQ(sol.getId(), 1); + EXPECT_EQ( + sol.getResult(), + "\nНе, ну вы не палитесь. Плагиат.\nРезультаты метрик: 0.72\n\tАнализ текста: 0.54\n\tАнализ токенов: 0.89" + "\nОчень похоже на решение, отправленное до вас:\nint main(){return 0;}"); +} + +TEST_F(SolutionServiceTest, createSolutionNonPlagiat) { + EXPECT_CALL(*solutionMockPtr, storeSolution(Solution(0, "", 2, "source", 1, "", "", "", 1, "cpp"))) + .Times(1) + .WillRepeatedly(::testing::Return(1)); + + std::vector solutions; + solutions.push_back( + Solution(0, "", 1, "int main(){return 0;}", 1, "", "45 132 85 86 89 59 1 128 90 -1", "", -1, "cpp")); + EXPECT_CALL(*solutionMockPtr, getSolutionsByTaskIdAndLanguage(1, "cpp")) + .Times(1) + .WillOnce(::testing::Return(solutions)); + + EXPECT_CALL(*taskMockPtr, getTaskById(1)).Times(1).WillOnce(::testing::Return(Task(1, "desription", 0.5f, "name"))); + + Solution sol = ss->createSolution(1, 1, "main.cpp", "int main(){return 0;}"); + EXPECT_EQ(sol.getResult(), + "\nКрасивое решение. А главное уникальное !\nРезультаты метрик: 0.00\n\tАнализ текста: 0.00\n\tАнализ " + "токенов: 0.00"); +} diff --git a/server/internal/service/tests/TaskServiceTest.cpp b/server/internal/service/tests/TaskServiceTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..821e701300a53471663a028fbf4b49b1c67ec739 --- /dev/null +++ b/server/internal/service/tests/TaskServiceTest.cpp @@ -0,0 +1,71 @@ +#include +#include + +#include "TaskService.h" + +class Exception : public std::exception { + std::string _msg; + + public: + explicit Exception(const std::string& msg) : _msg(msg) {} + const char* what() const noexcept override { return _msg.c_str(); } +}; + +class TaskRepositoryMock : public ITaskRepository { + public: + ~TaskRepositoryMock() override = default; + MOCK_METHOD(std::optional, getTaskById, (size_t id), (override)); + MOCK_METHOD(void, updateTask, (const Task& task), (override)); + MOCK_METHOD(size_t, storeTask, (Task task), (override)); + MOCK_METHOD(void, deleteTask, (Task task), (override)); + MOCK_METHOD(void, deleteTaskById, (size_t id), (override)); + MOCK_METHOD(std::vector, getAllTasks, (), (override)); +}; + +struct TaskServiceTest : public testing::Test { + TaskService* ts; + TaskRepositoryMock* mock_ptr; + + void SetUp() { + auto mock = std::make_unique(); + mock_ptr = mock.get(); + ts = new TaskService(std::move(mock)); + } + void TearDown() { delete ts; } +}; + +ACTION(NoTaskException) { throw Exception("no task with this id in db"); } + +TEST_F(TaskServiceTest, deleteTaskById) { + EXPECT_CALL(*mock_ptr, deleteTaskById(1)).Times(1); + ts->deleteTask(1); +} + +TEST_F(TaskServiceTest, deleteTasWithInvalidId) { + EXPECT_CALL(*mock_ptr, deleteTaskById(1)).Times(1).WillRepeatedly(NoTaskException()); + EXPECT_THROW(ts->deleteTask(1), std::exception); +} + +TEST_F(TaskServiceTest, GetTaskByIdOK) { + EXPECT_CALL(*mock_ptr, getTaskById(1)).Times(1).WillOnce(::testing::Return(Task(1, "desription", 0.7f, "name"))); + Task t = ts->getTask(1); + EXPECT_EQ(t.getId(), 1); + EXPECT_EQ(t.getDescription(), "desription"); +} + +TEST_F(TaskServiceTest, GetTaskByIdEXEPTION) { + EXPECT_CALL(*mock_ptr, getTaskById(-1)).Times(1).WillRepeatedly(NoTaskException()); + EXPECT_THROW(ts->getTask(-1), std::exception); +} + +TEST_F(TaskServiceTest, CreateTask) { + EXPECT_CALL(*mock_ptr, storeTask(Task("desc", 0.5f, "name"))).Times(1).WillOnce(::testing::Return(1)); + Task t = ts->createTask("desc"); + EXPECT_EQ(t.getId(), 1); + EXPECT_EQ(t.getDescription(), "desc"); + + EXPECT_CALL(*mock_ptr, storeTask(Task("desc2", 0.8f, "name"))).Times(1).WillOnce(::testing::Return(2)); + t = ts->createTask("desc2"); + EXPECT_EQ(t.getId(), 2); + EXPECT_EQ(t.getDescription(), "desc2"); +} diff --git a/server/internal/service/tests/UserServiceTest.cpp b/server/internal/service/tests/UserServiceTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..afb6fe6abadfd0e832834957350ec7669af0d771 --- /dev/null +++ b/server/internal/service/tests/UserServiceTest.cpp @@ -0,0 +1,88 @@ +#include +#include + +#include "ServiceExceptions.h" +#include "UserService.h" + +class Exception : public std::exception { + std::string _msg; + + public: + explicit Exception(const std::string& msg) : _msg(msg) {} + const char* what() const noexcept override { return _msg.c_str(); } +}; + +class UserRepositoryMock : public IUserRepository { + public: + ~UserRepositoryMock() override = default; + MOCK_METHOD(std::optional, getUserById, (size_t id), (override)); + MOCK_METHOD(std::optional, getUserByLogin, (std::string login), (override)); + MOCK_METHOD(size_t, makeUser, (User user), (override)); + MOCK_METHOD(void, deleteUser, (User user), (override)); + MOCK_METHOD(void, deleteByUserId, (size_t id), (override)); + MOCK_METHOD(std::vector, getAllUsers, (), (override)); + MOCK_METHOD(void, update, (User user), (override)); +}; + +struct UserServiceTest : public testing::Test { + UserService* us; + UserRepositoryMock* mock_ptr; + + void SetUp() { + auto mock = std::make_unique(); + mock_ptr = mock.get(); + us = new UserService(std::move(mock)); + } + void TearDown() { delete us; } +}; + +ACTION(NoUserException) { throw Exception("no user with this id in db"); } + +TEST_F(UserServiceTest, deleteUser) { + EXPECT_CALL(*mock_ptr, deleteByUserId(1)).Times(1); + us->deleteUser(1); +} + +TEST_F(UserServiceTest, deleteUserWithInvalidId) { + EXPECT_CALL(*mock_ptr, deleteByUserId(1)).Times(1).WillRepeatedly(NoUserException()); + EXPECT_THROW(us->deleteUser(1), std::exception); +} + +TEST_F(UserServiceTest, loginOk) { + EXPECT_CALL(*mock_ptr, getUserByLogin("login")) + .Times(1) + .WillOnce(::testing::Return(User(1, "login", "password", "username"))); + User u = us->login("login", "password"); + EXPECT_EQ(u.getLogin(), "login"); + EXPECT_EQ(u.getId(), 1); + EXPECT_EQ(u.getPassword(), "password"); + EXPECT_EQ(u.getUsername(), "username"); +} + +TEST_F(UserServiceTest, loginInvalidLogin) { + EXPECT_CALL(*mock_ptr, getUserByLogin("loginnn")).Times(1).WillOnce(NoUserException()); + EXPECT_THROW(us->login("loginnn", "password"), std::exception); +} + +TEST_F(UserServiceTest, loginInvalidPass) { + EXPECT_CALL(*mock_ptr, getUserByLogin("login")) + .Times(1) + .WillOnce(::testing::Return(User(1, "login", "password", "username"))); + EXPECT_THROW(us->login("login", "password1"), std::exception); +} + +TEST_F(UserServiceTest, makeUserOk) { + EXPECT_CALL(*mock_ptr, makeUser(User("login@gmail.com", "password", "username"))) + .Times(1) + .WillOnce(::testing::Return(1)); + User u = us->createUser("login@gmail.com", "username", "password"); + EXPECT_EQ(u.getLogin(), "login@gmail.com"); + EXPECT_EQ(u.getId(), 1); + EXPECT_EQ(u.getPassword(), "password"); + EXPECT_EQ(u.getUsername(), "username"); +} + +TEST_F(UserServiceTest, makeUserInvalidData) { + EXPECT_CALL(*mock_ptr, makeUser(User("login", "password", "username"))).Times(0); + EXPECT_THROW(us->createUser("", "", ""), ValidateException); +} diff --git a/server/internal/service/tests/UserValidatorTest.cpp b/server/internal/service/tests/UserValidatorTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a831504f5ca4173cdd7356b8e768ea47a75e038e --- /dev/null +++ b/server/internal/service/tests/UserValidatorTest.cpp @@ -0,0 +1,16 @@ +#include +#include + +#include "UserValidator.h" + +TEST(UserValidatorTest, validateOK) { + EXPECT_TRUE(UserValidator::validate(User("login@gmail.com", "password", "username"))); +} + +TEST(UserValidatorTest, invalidLogin) { EXPECT_FALSE(UserValidator::validate(User("", "password", "username"))); } + +TEST(UserValidatorTest, invalidPassword) { EXPECT_FALSE(UserValidator::validate(User("login", "", "username"))); } + +TEST(UserValidatorTest, invalidUsername) { EXPECT_FALSE(UserValidator::validate(User("login", "password", ""))); } + +TEST(UserValidatorTest, invalidUserFields) { EXPECT_FALSE(UserValidator::validate(User("", "", ""))); } diff --git a/server/internal/service/tests/main.cpp b/server/internal/service/tests/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b0c2475bc547a7aea7d29fe243c6546d866ec30 --- /dev/null +++ b/server/internal/service/tests/main.cpp @@ -0,0 +1,8 @@ +#include +#include + +int main(int argc, char** argv) { + ::testing::InitGoogleMock(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/server/internal/service/virtual/IMockMetrics.h b/server/internal/service/virtual/IMockMetrics.h new file mode 100644 index 0000000000000000000000000000000000000000..fc0a93f5ae08f1f06eaebe2ced75c9e79e77f948 --- /dev/null +++ b/server/internal/service/virtual/IMockMetrics.h @@ -0,0 +1,6 @@ +#pragma once + +class IMockMetrics { + public: + virtual void countMetric(); +}; diff --git a/server/internal/service/virtual/ISolutionService.h b/server/internal/service/virtual/ISolutionService.h new file mode 100644 index 0000000000000000000000000000000000000000..7c1294d9ee88ed3773833f5df94510a595af8305 --- /dev/null +++ b/server/internal/service/virtual/ISolutionService.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#include "Solution.hpp" + +class ISolutionService { + public: + virtual ~ISolutionService() = default; + virtual Solution createSolution(size_t userId, size_t taskId, const std::string& filename, + const std::string& filedata) = 0; + virtual void deleteSolutionById(size_t solId) = 0; + virtual std::vector getSolutionsByUserAndTaskId(size_t user_id, size_t task_id) = 0; + virtual std::pair getMetrics(size_t solId) = 0; +}; diff --git a/server/internal/service/virtual/ITaskService.h b/server/internal/service/virtual/ITaskService.h new file mode 100644 index 0000000000000000000000000000000000000000..54bbb67d44f6907a0114db3b5e4f36d328dbbed1 --- /dev/null +++ b/server/internal/service/virtual/ITaskService.h @@ -0,0 +1,14 @@ +#pragma once +#include + +#include "Task.hpp" + +class ITaskService { + public: + virtual ~ITaskService() = default; + virtual Task createTask(const std::string& desc, const std::string& name = "Default name", + float treshold = 0.5f) = 0; + virtual Task getTask(size_t id) = 0; + virtual std::vector getAllTasks() = 0; + virtual void deleteTask(size_t id) = 0; +}; diff --git a/server/internal/service/virtual/IUserService.h b/server/internal/service/virtual/IUserService.h new file mode 100644 index 0000000000000000000000000000000000000000..c68315216afe325de074109b4d43cb54c143435a --- /dev/null +++ b/server/internal/service/virtual/IUserService.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include "User.hpp" + +class IUserService { + public: + virtual ~IUserService() = default; + virtual User createUser(const std::string& login, const std::string& username, const std::string& password) = 0; + virtual User login(const std::string& login, const std::string& password) = 0; + virtual void deleteUser(size_t id) = 0; +}; diff --git a/server/pkg/CMakeLists.txt b/server/pkg/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b1b20991842c95966cebb053e34bda7307c2b07c --- /dev/null +++ b/server/pkg/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(antlr) + +set(ANTLR4_LIB ${ANTLR4_LIB} PARENT_SCOPE) +set(ANTLR4_LIB_INCLUDE_DIRS ${ANTLR4_LIB_INCLUDE_DIRS} PARENT_SCOPE) diff --git a/server/pkg/antlr/CMakeLists.txt b/server/pkg/antlr/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0d9757459418b850937b443340511e5642587603 --- /dev/null +++ b/server/pkg/antlr/CMakeLists.txt @@ -0,0 +1,27 @@ +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +set(CMAKE_CXX_STANDARD 20) + + +# set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-lpthread") +set(THREADS_PREFER_PTHREAD_FLAG ON) + + +include(ExternalAntlr4Cpp) +include_directories(${ANTLR4_INCLUDE_DIRS}) + + +set(ANTLR_EXECUTABLE /usr/local/lib/antlr-4.12.0-complete.jar) +find_package(ANTLR REQUIRED) + +add_subdirectory(cpp14) +add_subdirectory(python3) + +message("CPP_ANTLR_LIBRARY=${CPP_ANTLR_LIBRARY}") +message("PYTHON3_ANTLR_LIBRARY=${PYTHON3_ANTLR_LIBRARY}") + +set(ANTLR4_LIB ${CPP_ANTLR_LIBRARY} ${PYTHON3_ANTLR_LIBRARY}) +set(ANTLR4_LIB ${ANTLR4_LIB} PARENT_SCOPE) +set(ANTLR4_LIB_INCLUDE_DIRS ${CPP_ANTLR_INCLUDE_DIRS} ${PYTHON3_ANTLR_INCLUDE_DIRS}) +set(ANTLR4_LIB_INCLUDE_DIRS ${ANTLR4_LIB_INCLUDE_DIRS} PARENT_SCOPE) + diff --git a/server/pkg/antlr/cmake/ExternalAntlr4Cpp.cmake b/server/pkg/antlr/cmake/ExternalAntlr4Cpp.cmake new file mode 100644 index 0000000000000000000000000000000000000000..54f874b86be4fe787a640d9e9b560dac0fb0417a --- /dev/null +++ b/server/pkg/antlr/cmake/ExternalAntlr4Cpp.cmake @@ -0,0 +1,177 @@ +cmake_minimum_required(VERSION 3.7) + +if(POLICY CMP0114) + cmake_policy(SET CMP0114 NEW) +endif() + +include(ExternalProject) + +set(ANTLR4_ROOT ${CMAKE_CURRENT_BINARY_DIR}/antlr4_runtime/src/antlr4_runtime) +set(ANTLR4_INCLUDE_DIRS ${ANTLR4_ROOT}/runtime/Cpp/runtime/src) +set(ANTLR4_GIT_REPOSITORY https://github.com/antlr/antlr4.git) +if(NOT DEFINED ANTLR4_TAG) + # Set to branch name to keep library updated at the cost of needing to rebuild after 'clean' + # Set to commit hash to keep the build stable and does not need to rebuild after 'clean' + set(ANTLR4_TAG master) +endif() + +# Ensure that the include dir already exists at configure time (to avoid cmake erroring +# on non-existent include dirs) +file(MAKE_DIRECTORY "${ANTLR4_INCLUDE_DIRS}") + +if(${CMAKE_GENERATOR} MATCHES "Visual Studio.*") + set(ANTLR4_OUTPUT_DIR ${ANTLR4_ROOT}/runtime/Cpp/dist/$(Configuration)) +elseif(${CMAKE_GENERATOR} MATCHES "Xcode.*") + set(ANTLR4_OUTPUT_DIR ${ANTLR4_ROOT}/runtime/Cpp/dist/$(CONFIGURATION)) +else() + set(ANTLR4_OUTPUT_DIR ${ANTLR4_ROOT}/runtime/Cpp/dist) +endif() + +if(MSVC) + set(ANTLR4_STATIC_LIBRARIES + ${ANTLR4_OUTPUT_DIR}/antlr4-runtime-static.lib) + set(ANTLR4_SHARED_LIBRARIES + ${ANTLR4_OUTPUT_DIR}/antlr4-runtime.lib) + set(ANTLR4_RUNTIME_LIBRARIES + ${ANTLR4_OUTPUT_DIR}/antlr4-runtime.dll) +else() + set(ANTLR4_STATIC_LIBRARIES + ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.a) + if(MINGW) + set(ANTLR4_SHARED_LIBRARIES + ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.dll.a) + set(ANTLR4_RUNTIME_LIBRARIES + ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.dll) + elseif(CYGWIN) + set(ANTLR4_SHARED_LIBRARIES + ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.dll.a) + set(ANTLR4_RUNTIME_LIBRARIES + ${ANTLR4_OUTPUT_DIR}/cygantlr4-runtime-4.12.0.dll) + elseif(APPLE) + set(ANTLR4_RUNTIME_LIBRARIES + ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.dylib) + else() + set(ANTLR4_RUNTIME_LIBRARIES + ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.so) + endif() +endif() + +if(${CMAKE_GENERATOR} MATCHES ".* Makefiles") + # This avoids + # 'warning: jobserver unavailable: using -j1. Add '+' to parent make rule.' + set(ANTLR4_BUILD_COMMAND $(MAKE)) +elseif(${CMAKE_GENERATOR} MATCHES "Visual Studio.*") + set(ANTLR4_BUILD_COMMAND + ${CMAKE_COMMAND} + --build . + --config $(Configuration) + --target) +elseif(${CMAKE_GENERATOR} MATCHES "Xcode.*") + set(ANTLR4_BUILD_COMMAND + ${CMAKE_COMMAND} + --build . + --config $(CONFIGURATION) + --target) +else() + set(ANTLR4_BUILD_COMMAND + ${CMAKE_COMMAND} + --build . + --target) +endif() + +if(NOT DEFINED ANTLR4_WITH_STATIC_CRT) + set(ANTLR4_WITH_STATIC_CRT ON) +endif() + +if(ANTLR4_ZIP_REPOSITORY) + ExternalProject_Add( + antlr4_runtime + PREFIX antlr4_runtime + URL ${ANTLR4_ZIP_REPOSITORY} + DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR} + BUILD_COMMAND "" + BUILD_IN_SOURCE 1 + SOURCE_DIR ${ANTLR4_ROOT} + SOURCE_SUBDIR runtime/Cpp + CMAKE_CACHE_ARGS + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DWITH_STATIC_CRT:BOOL=${ANTLR4_WITH_STATIC_CRT} + -DDISABLE_WARNINGS:BOOL=ON + # -DCMAKE_CXX_STANDARD:STRING=17 # if desired, compile the runtime with a different C++ standard + # -DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} # alternatively, compile the runtime with the same C++ standard as the outer project + INSTALL_COMMAND "" + EXCLUDE_FROM_ALL 1) +else() + ExternalProject_Add( + antlr4_runtime + PREFIX antlr4_runtime + GIT_REPOSITORY ${ANTLR4_GIT_REPOSITORY} + GIT_TAG ${ANTLR4_TAG} + DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR} + BUILD_COMMAND "" + BUILD_IN_SOURCE 1 + SOURCE_DIR ${ANTLR4_ROOT} + SOURCE_SUBDIR runtime/Cpp + CMAKE_CACHE_ARGS + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DWITH_STATIC_CRT:BOOL=${ANTLR4_WITH_STATIC_CRT} + -DDISABLE_WARNINGS:BOOL=ON + # -DCMAKE_CXX_STANDARD:STRING=17 # if desired, compile the runtime with a different C++ standard + # -DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} # alternatively, compile the runtime with the same C++ standard as the outer project + INSTALL_COMMAND "" + EXCLUDE_FROM_ALL 1) +endif() + +# Separate build step as rarely people want both +set(ANTLR4_BUILD_DIR ${ANTLR4_ROOT}) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0") + # CMake 3.14 builds in above's SOURCE_SUBDIR when BUILD_IN_SOURCE is true + set(ANTLR4_BUILD_DIR ${ANTLR4_ROOT}/runtime/Cpp) +endif() + +ExternalProject_Add_Step( + antlr4_runtime + build_static + COMMAND ${ANTLR4_BUILD_COMMAND} antlr4_static + # Depend on target instead of step (a custom command) + # to avoid running dependent steps concurrently + DEPENDS antlr4_runtime + BYPRODUCTS ${ANTLR4_STATIC_LIBRARIES} + EXCLUDE_FROM_MAIN 1 + WORKING_DIRECTORY ${ANTLR4_BUILD_DIR}) +ExternalProject_Add_StepTargets(antlr4_runtime build_static) + +add_library(antlr4_static STATIC IMPORTED) +add_dependencies(antlr4_static antlr4_runtime-build_static) +set_target_properties(antlr4_static PROPERTIES + IMPORTED_LOCATION ${ANTLR4_STATIC_LIBRARIES}) +target_include_directories(antlr4_static + INTERFACE + ${ANTLR4_INCLUDE_DIRS} +) + +ExternalProject_Add_Step( + antlr4_runtime + build_shared + COMMAND ${ANTLR4_BUILD_COMMAND} antlr4_shared + # Depend on target instead of step (a custom command) + # to avoid running dependent steps concurrently + DEPENDS antlr4_runtime + BYPRODUCTS ${ANTLR4_SHARED_LIBRARIES} ${ANTLR4_RUNTIME_LIBRARIES} + EXCLUDE_FROM_MAIN 1 + WORKING_DIRECTORY ${ANTLR4_BUILD_DIR}) +ExternalProject_Add_StepTargets(antlr4_runtime build_shared) + +add_library(antlr4_shared SHARED IMPORTED) +add_dependencies(antlr4_shared antlr4_runtime-build_shared) +set_target_properties(antlr4_shared PROPERTIES + IMPORTED_LOCATION ${ANTLR4_RUNTIME_LIBRARIES}) +target_include_directories(antlr4_shared + INTERFACE + ${ANTLR4_INCLUDE_DIRS} +) + +if(ANTLR4_SHARED_LIBRARIES) + set_target_properties(antlr4_shared PROPERTIES + IMPORTED_IMPLIB ${ANTLR4_SHARED_LIBRARIES}) +endif() diff --git a/server/pkg/antlr/cmake/FindANTLR.cmake b/server/pkg/antlr/cmake/FindANTLR.cmake new file mode 100644 index 0000000000000000000000000000000000000000..0ac8f8c79f939621c3a9405e51e0fa0648109107 --- /dev/null +++ b/server/pkg/antlr/cmake/FindANTLR.cmake @@ -0,0 +1,124 @@ +find_package(Java QUIET COMPONENTS Runtime) + +if(NOT ANTLR_EXECUTABLE) + find_program(ANTLR_EXECUTABLE + NAMES antlr.jar antlr4.jar antlr-4.jar antlr-4.12.0-complete.jar) +endif() + +if(ANTLR_EXECUTABLE AND Java_JAVA_EXECUTABLE) + execute_process( + COMMAND ${Java_JAVA_EXECUTABLE} -jar ${ANTLR_EXECUTABLE} + OUTPUT_VARIABLE ANTLR_COMMAND_OUTPUT + ERROR_VARIABLE ANTLR_COMMAND_ERROR + RESULT_VARIABLE ANTLR_COMMAND_RESULT + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(ANTLR_COMMAND_RESULT EQUAL 0) + string(REGEX MATCH "Version [0-9]+(\\.[0-9]+)*" ANTLR_VERSION ${ANTLR_COMMAND_OUTPUT}) + string(REPLACE "Version " "" ANTLR_VERSION ${ANTLR_VERSION}) + else() + message( + SEND_ERROR + "Command '${Java_JAVA_EXECUTABLE} -jar ${ANTLR_EXECUTABLE}' " + "failed with the output '${ANTLR_COMMAND_ERROR}'") + endif() + + macro(ANTLR_TARGET Name InputFile) + set(ANTLR_OPTIONS LEXER PARSER LISTENER VISITOR) + set(ANTLR_ONE_VALUE_ARGS PACKAGE OUTPUT_DIRECTORY DEPENDS_ANTLR) + set(ANTLR_MULTI_VALUE_ARGS COMPILE_FLAGS DEPENDS) + cmake_parse_arguments(ANTLR_TARGET + "${ANTLR_OPTIONS}" + "${ANTLR_ONE_VALUE_ARGS}" + "${ANTLR_MULTI_VALUE_ARGS}" + ${ARGN}) + + set(ANTLR_${Name}_INPUT ${InputFile}) + + get_filename_component(ANTLR_INPUT ${InputFile} NAME_WE) + + if(ANTLR_TARGET_OUTPUT_DIRECTORY) + set(ANTLR_${Name}_OUTPUT_DIR ${ANTLR_TARGET_OUTPUT_DIRECTORY}) + else() + set(ANTLR_${Name}_OUTPUT_DIR + ${CMAKE_CURRENT_BINARY_DIR}/antlr4cpp_generated_src/${ANTLR_INPUT}) + endif() + + unset(ANTLR_${Name}_CXX_OUTPUTS) + + if((ANTLR_TARGET_LEXER AND NOT ANTLR_TARGET_PARSER) OR + (ANTLR_TARGET_PARSER AND NOT ANTLR_TARGET_LEXER)) + list(APPEND ANTLR_${Name}_CXX_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}.cpp) + set(ANTLR_${Name}_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}.interp + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}.tokens) + else() + list(APPEND ANTLR_${Name}_CXX_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Lexer.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Lexer.cpp + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Parser.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Parser.cpp) + list(APPEND ANTLR_${Name}_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Lexer.interp + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Lexer.tokens) + endif() + + if(ANTLR_TARGET_LISTENER) + list(APPEND ANTLR_${Name}_CXX_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}BaseListener.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}BaseListener.cpp + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Listener.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Listener.cpp) + list(APPEND ANTLR_TARGET_COMPILE_FLAGS -listener) + endif() + + if(ANTLR_TARGET_VISITOR) + list(APPEND ANTLR_${Name}_CXX_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}BaseVisitor.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}BaseVisitor.cpp + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Visitor.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Visitor.cpp) + list(APPEND ANTLR_TARGET_COMPILE_FLAGS -visitor) + endif() + + if(ANTLR_TARGET_PACKAGE) + list(APPEND ANTLR_TARGET_COMPILE_FLAGS -package ${ANTLR_TARGET_PACKAGE}) + endif() + + list(APPEND ANTLR_${Name}_OUTPUTS ${ANTLR_${Name}_CXX_OUTPUTS}) + + if(ANTLR_TARGET_DEPENDS_ANTLR) + if(ANTLR_${ANTLR_TARGET_DEPENDS_ANTLR}_INPUT) + list(APPEND ANTLR_TARGET_DEPENDS + ${ANTLR_${ANTLR_TARGET_DEPENDS_ANTLR}_INPUT}) + list(APPEND ANTLR_TARGET_DEPENDS + ${ANTLR_${ANTLR_TARGET_DEPENDS_ANTLR}_OUTPUTS}) + else() + message(SEND_ERROR + "ANTLR target '${ANTLR_TARGET_DEPENDS_ANTLR}' not found") + endif() + endif() + + add_custom_command( + OUTPUT ${ANTLR_${Name}_OUTPUTS} + COMMAND ${Java_JAVA_EXECUTABLE} -jar ${ANTLR_EXECUTABLE} + ${InputFile} + -o ${ANTLR_${Name}_OUTPUT_DIR} + -no-listener + -Dlanguage=Cpp + ${ANTLR_TARGET_COMPILE_FLAGS} + DEPENDS ${InputFile} + ${ANTLR_TARGET_DEPENDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Building ${Name} with ANTLR ${ANTLR_VERSION}") + endmacro(ANTLR_TARGET) + +endif(ANTLR_EXECUTABLE AND Java_JAVA_EXECUTABLE) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + ANTLR + REQUIRED_VARS ANTLR_EXECUTABLE Java_JAVA_EXECUTABLE + VERSION_VAR ANTLR_VERSION) diff --git a/server/pkg/antlr/cmake/antlr4-generator.cmake.in b/server/pkg/antlr/cmake/antlr4-generator.cmake.in new file mode 100644 index 0000000000000000000000000000000000000000..63996514b0de9d619946f11b90585f8e0ecf7d95 --- /dev/null +++ b/server/pkg/antlr/cmake/antlr4-generator.cmake.in @@ -0,0 +1,181 @@ +set(ANTLR_VERSION @ANTLR_VERSION@) + +@PACKAGE_INIT@ + +if (NOT ANTLR4_CPP_GENERATED_SRC_DIR) + set(ANTLR4_GENERATED_SRC_DIR ${CMAKE_BINARY_DIR}/antlr4_generated_src) +endif() + +FIND_PACKAGE(Java COMPONENTS Runtime REQUIRED) + +# +# The ANTLR generator will output the following files given the input file f.g4 +# +# Input -> f.g4 +# Output -> f.h +# -> f.cpp +# +# the following files will only be produced if there is a parser contained +# Flag -visitor active +# Output -> BaseVisitor.h +# -> BaseVisitor.cpp +# -> Visitor.h +# -> Visitor.cpp +# +# Flag -listener active +# Output -> BaseListener.h +# -> BaseListener.cpp +# -> Listener.h +# -> Listener.cpp +# +# See documentation in markup +# +function(antlr4_generate + Antlr4_ProjectTarget + Antlr4_InputFile + Antlr4_GeneratorType + ) + + set( Antlr4_GeneratedSrcDir ${ANTLR4_GENERATED_SRC_DIR}/${Antlr4_ProjectTarget} ) + + get_filename_component(Antlr4_InputFileBaseName ${Antlr4_InputFile} NAME_WE ) + + list( APPEND Antlr4_GeneratorStatusMessage "Common Include-, Source- and Tokenfiles" ) + + if ( ${Antlr4_GeneratorType} STREQUAL "LEXER") + set(Antlr4_LexerBaseName "${Antlr4_InputFileBaseName}") + set(Antlr4_ParserBaseName "") + else() + if ( ${Antlr4_GeneratorType} STREQUAL "PARSER") + set(Antlr4_LexerBaseName "") + set(Antlr4_ParserBaseName "${Antlr4_InputFileBaseName}") + else() + if ( ${Antlr4_GeneratorType} STREQUAL "BOTH") + set(Antlr4_LexerBaseName "${Antlr4_InputFileBaseName}Lexer") + set(Antlr4_ParserBaseName "${Antlr4_InputFileBaseName}Parser") + else() + message(FATAL_ERROR "The third parameter must be LEXER, PARSER or BOTH") + endif () + endif () + endif () + + # Prepare list of generated targets + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_InputFileBaseName}.tokens" ) + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_InputFileBaseName}.interp" ) + list( APPEND DependentTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_InputFileBaseName}.tokens" ) + + if ( NOT ${Antlr4_LexerBaseName} STREQUAL "" ) + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_LexerBaseName}.h" ) + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_LexerBaseName}.cpp" ) + endif () + + if ( NOT ${Antlr4_ParserBaseName} STREQUAL "" ) + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_ParserBaseName}.h" ) + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_ParserBaseName}.cpp" ) + endif () + + # process optional arguments ... + + if ( ( ARGC GREATER_EQUAL 4 ) AND ARGV3 ) + set(Antlr4_BuildListenerOption "-listener") + + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_InputFileBaseName}BaseListener.h" ) + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_InputFileBaseName}BaseListener.cpp" ) + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_InputFileBaseName}Listener.h" ) + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_InputFileBaseName}Listener.cpp" ) + + list( APPEND Antlr4_GeneratorStatusMessage ", Listener Include- and Sourcefiles" ) + else() + set(Antlr4_BuildListenerOption "-no-listener") + endif () + + if ( ( ARGC GREATER_EQUAL 5 ) AND ARGV4 ) + set(Antlr4_BuildVisitorOption "-visitor") + + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_InputFileBaseName}BaseVisitor.h" ) + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_InputFileBaseName}BaseVisitor.cpp" ) + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_InputFileBaseName}Visitor.h" ) + list( APPEND Antlr4_GeneratedTargets "${Antlr4_GeneratedSrcDir}/${Antlr4_InputFileBaseName}Visitor.cpp" ) + + list( APPEND Antlr4_GeneratorStatusMessage ", Visitor Include- and Sourcefiles" ) + else() + set(Antlr4_BuildVisitorOption "-no-visitor") + endif () + + if ( (ARGC GREATER_EQUAL 6 ) AND (NOT ${ARGV5} STREQUAL "") ) + set(Antlr4_NamespaceOption "-package;${ARGV5}") + + list( APPEND Antlr4_GeneratorStatusMessage " in Namespace ${ARGV5}" ) + else() + set(Antlr4_NamespaceOption "") + endif () + + if ( (ARGC GREATER_EQUAL 7 ) AND (NOT ${ARGV6} STREQUAL "") ) + set(Antlr4_AdditionalDependencies ${ARGV6}) + else() + set(Antlr4_AdditionalDependencies "") + endif () + + if ( (ARGC GREATER_EQUAL 8 ) AND (NOT ${ARGV7} STREQUAL "") ) + set(Antlr4_LibOption "-lib;${ARGV7}") + + list( APPEND Antlr4_GeneratorStatusMessage " using Library ${ARGV7}" ) + else() + set(Antlr4_LibOption "") + endif () + + if(NOT Java_FOUND) + message(FATAL_ERROR "Java is required to process grammar or lexer files! - Use 'FIND_PACKAGE(Java COMPONENTS Runtime REQUIRED)'") + endif() + + if(NOT EXISTS "${ANTLR4_JAR_LOCATION}") + message(FATAL_ERROR "Unable to find antlr tool. ANTLR4_JAR_LOCATION:${ANTLR4_JAR_LOCATION}") + endif() + + # The call to generate the files + add_custom_command( + OUTPUT ${Antlr4_GeneratedTargets} + # Remove target directory + COMMAND + ${CMAKE_COMMAND} -E remove_directory ${Antlr4_GeneratedSrcDir} + # Create target directory + COMMAND + ${CMAKE_COMMAND} -E make_directory ${Antlr4_GeneratedSrcDir} + COMMAND + # Generate files + "${Java_JAVA_EXECUTABLE}" -jar "${ANTLR4_JAR_LOCATION}" -Werror -Dlanguage=Cpp ${Antlr4_BuildListenerOption} ${Antlr4_BuildVisitorOption} ${Antlr4_LibOption} ${ANTLR4_GENERATED_OPTIONS} -o "${Antlr4_GeneratedSrcDir}" ${Antlr4_NamespaceOption} "${Antlr4_InputFile}" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + MAIN_DEPENDENCY "${Antlr4_InputFile}" + DEPENDS ${Antlr4_AdditionalDependencies} + ) + + # set output variables in parent scope + set( ANTLR4_INCLUDE_DIR_${Antlr4_ProjectTarget} ${Antlr4_GeneratedSrcDir} PARENT_SCOPE) + set( ANTLR4_SRC_FILES_${Antlr4_ProjectTarget} ${Antlr4_GeneratedTargets} PARENT_SCOPE) + set( ANTLR4_TOKEN_FILES_${Antlr4_ProjectTarget} ${DependentTargets} PARENT_SCOPE) + set( ANTLR4_TOKEN_DIRECTORY_${Antlr4_ProjectTarget} ${Antlr4_GeneratedSrcDir} PARENT_SCOPE) + + # export generated cpp files into list + foreach(generated_file ${Antlr4_GeneratedTargets}) + + if (NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + set_source_files_properties( + ${generated_file} + PROPERTIES + COMPILE_FLAGS -Wno-overloaded-virtual + ) + endif () + + if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + set_source_files_properties( + ${generated_file} + PROPERTIES + COMPILE_FLAGS -wd4251 + ) + endif () + + endforeach(generated_file) + +message(STATUS "Antlr4 ${Antlr4_ProjectTarget} - Building " ${Antlr4_GeneratorStatusMessage} ) + +endfunction() diff --git a/server/pkg/antlr/cmake/antlr4-runtime.cmake.in b/server/pkg/antlr/cmake/antlr4-runtime.cmake.in new file mode 100644 index 0000000000000000000000000000000000000000..697b36c628172d5439476c4e28b67ea1fa64ca26 --- /dev/null +++ b/server/pkg/antlr/cmake/antlr4-runtime.cmake.in @@ -0,0 +1,13 @@ +set(ANTLR_VERSION @ANTLR_VERSION@) + +@PACKAGE_INIT@ + +set_and_check(ANTLR4_INCLUDE_DIR "@PACKAGE_ANTLR4_INCLUDE_DIR@") +set_and_check(ANTLR4_LIB_DIR "@PACKAGE_ANTLR4_LIB_DIR@") + +include(CMakeFindDependencyMacro) +find_dependency(Threads) + +include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) + +check_required_components(antlr) diff --git a/server/pkg/antlr/cpp14/CMakeLists.txt b/server/pkg/antlr/cpp14/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..30e332d3547259cd3a12865c39a952733a778cd7 --- /dev/null +++ b/server/pkg/antlr/cpp14/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.7) +project(cpp_antlr_lib) + +file(GLOB SOURCES ./src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/../virtual) + + +antlr_target(CPP14GrammarLexer CPP14Lexer.g4 LEXER + PACKAGE antlrcpptest) +antlr_target(CPP14GrammarParser CPP14Parser.g4 PARSER + PACKAGE antlrcpptest + DEPENDS_ANTLR CPP14GrammarLexer + COMPILE_FLAGS -lib ${ANTLR_CPP14GrammarLexer_OUTPUT_DIR}) + +include_directories(${ANTLR_CPP14GrammarLexer_OUTPUT_DIR}) +include_directories(${ANTLR_CPP14GrammarParser_OUTPUT_DIR}) + + +message("ANTLR_CPP14GrammarParser_OUTPUT_DIR=${ANTLR_CPP14GrammarParser_OUTPUT_DIR}") + +include_directories(${INCLUDE_DIRS}) +add_library(${PROJECT_NAME} ${SOURCES} ${ANTLR_CPP14GrammarLexer_CXX_OUTPUTS} ${ANTLR_CPP14GrammarParser_CXX_OUTPUTS}) + + +target_link_libraries(${PROJECT_NAME} antlr4_static Threads::Threads) + +set(CPP_ANTLR_LIBRARY ${PROJECT_NAME}) +set(CPP_ANTLR_LIBRARY ${CPP_ANTLR_LIBRARY} PARENT_SCOPE) + +set(CPP_ANTLR_INCLUDE_DIRS ${INCLUDE_DIRS} ${ANTLR_CPP14GrammarLexer_OUTPUT_DIR} ${ANTLR_CPP14GrammarParser_OUTPUT_DIR}) +set(CPP_ANTLR_INCLUDE_DIRS ${CPP_ANTLR_INCLUDE_DIRS} PARENT_SCOPE) + +message("CPP_ANTLR = ${CPP_ANTLR_INCLUDE_DIRS} ") \ No newline at end of file diff --git a/server/pkg/antlr/cpp14/CPP14Lexer.g4 b/server/pkg/antlr/cpp14/CPP14Lexer.g4 new file mode 100644 index 0000000000000000000000000000000000000000..860e6d20c0c0d7774707bd9d10ad1cb8d53fa5b7 --- /dev/null +++ b/server/pkg/antlr/cpp14/CPP14Lexer.g4 @@ -0,0 +1,409 @@ +lexer grammar CPP14Lexer; + +IntegerLiteral: + DecimalLiteral Integersuffix? + | OctalLiteral Integersuffix? + | HexadecimalLiteral Integersuffix? + | BinaryLiteral Integersuffix?; + +CharacterLiteral: + ('u' | 'U' | 'L')? '\'' Cchar+ '\''; + +FloatingLiteral: + Fractionalconstant Exponentpart? Floatingsuffix? + | Digitsequence Exponentpart Floatingsuffix?; + +StringLiteral: + Encodingprefix? + (Rawstring + |'"' Schar* '"'); + +BooleanLiteral: False_ | True_; + +PointerLiteral: Nullptr; + +UserDefinedLiteral: + UserDefinedIntegerLiteral + | UserDefinedFloatingLiteral + | UserDefinedStringLiteral + | UserDefinedCharacterLiteral; + +MultiLineMacro: + '#' (~[\n]*? '\\' '\r'? '\n')+ ~ [\n]+ -> channel (HIDDEN); + +Directive: '#' ~ [\n]* -> channel (HIDDEN); +/*Keywords*/ + +Alignas: 'alignas'; + +Alignof: 'alignof'; + +Asm: 'asm'; + +Auto: 'auto'; + +Bool: 'bool'; + +Break: 'break'; + +Case: 'case'; + +Catch: 'catch'; + +Char: 'char'; + +Char16: 'char16_t'; + +Char32: 'char32_t'; + +Class: 'class'; + +Const: 'const'; + +Constexpr: 'constexpr'; + +Const_cast: 'const_cast'; + +Continue: 'continue'; + +Decltype: 'decltype'; + +Default: 'default'; + +Delete: 'delete'; + +Do: 'do'; + +Double: 'double'; + +Dynamic_cast: 'dynamic_cast'; + +Else: 'else'; + +Enum: 'enum'; + +Explicit: 'explicit'; + +Export: 'export'; + +Extern: 'extern'; + +//DO NOT RENAME - PYTHON NEEDS True and False +False_: 'false'; + +Final: 'final'; + +Float: 'float'; + +For: 'for'; + +Friend: 'friend'; + +Goto: 'goto'; + +If: 'if'; + +Inline: 'inline'; + +Int: 'int'; + +Long: 'long'; + +Mutable: 'mutable'; + +Namespace: 'namespace'; + +New: 'new'; + +Noexcept: 'noexcept'; + +Nullptr: 'nullptr'; + +Operator: 'operator'; + +Override: 'override'; + +Private: 'private'; + +Protected: 'protected'; + +Public: 'public'; + +Register: 'register'; + +Reinterpret_cast: 'reinterpret_cast'; + +Return: 'return'; + +Short: 'short'; + +Signed: 'signed'; + +Sizeof: 'sizeof'; + +Static: 'static'; + +Static_assert: 'static_assert'; + +Static_cast: 'static_cast'; + +Struct: 'struct'; + +Switch: 'switch'; + +Template: 'template'; + +This: 'this'; + +Thread_local: 'thread_local'; + +Throw: 'throw'; + +//DO NOT RENAME - PYTHON NEEDS True and False +True_: 'true'; + +Try: 'try'; + +Typedef: 'typedef'; + +Typeid_: 'typeid'; + +Typename_: 'typename'; + +Union: 'union'; + +Unsigned: 'unsigned'; + +Using: 'using'; + +Virtual: 'virtual'; + +Void: 'void'; + +Volatile: 'volatile'; + +Wchar: 'wchar_t'; + +While: 'while'; +/*Operators*/ + +LeftParen: '('; + +RightParen: ')'; + +LeftBracket: '['; + +RightBracket: ']'; + +LeftBrace: '{'; + +RightBrace: '}'; + +Plus: '+'; + +Minus: '-'; + +Star: '*'; + +Div: '/'; + +Mod: '%'; + +Caret: '^'; + +And: '&'; + +Or: '|'; + +Tilde: '~'; + +Not: '!' | 'not'; + +Assign: '='; + +Less: '<'; + +Greater: '>'; + +PlusAssign: '+='; + +MinusAssign: '-='; + +StarAssign: '*='; + +DivAssign: '/='; + +ModAssign: '%='; + +XorAssign: '^='; + +AndAssign: '&='; + +OrAssign: '|='; + +LeftShiftAssign: '<<='; + +RightShiftAssign: '>>='; + +Equal: '=='; + +NotEqual: '!='; + +LessEqual: '<='; + +GreaterEqual: '>='; + +AndAnd: '&&' | 'and'; + +OrOr: '||' | 'or'; + +PlusPlus: '++'; + +MinusMinus: '--'; + +Comma: ','; + +ArrowStar: '->*'; + +Arrow: '->'; + +Question: '?'; + +Colon: ':'; + +Doublecolon: '::'; + +Semi: ';'; + +Dot: '.'; + +DotStar: '.*'; + +Ellipsis: '...'; + +fragment Hexquad: + HEXADECIMALDIGIT HEXADECIMALDIGIT HEXADECIMALDIGIT HEXADECIMALDIGIT; + +fragment Universalcharactername: + '\\u' Hexquad + | '\\U' Hexquad Hexquad; + +Identifier: + /* + Identifiernondigit | Identifier Identifiernondigit | Identifier DIGIT + */ + Identifiernondigit (Identifiernondigit | DIGIT)*; + +fragment Identifiernondigit: NONDIGIT | Universalcharactername; + +fragment NONDIGIT: [a-zA-Z_]; + +fragment DIGIT: [0-9]; + +DecimalLiteral: NONZERODIGIT ('\''? DIGIT)*; + +OctalLiteral: '0' ('\''? OCTALDIGIT)*; + +HexadecimalLiteral: ('0x' | '0X') HEXADECIMALDIGIT ( + '\''? HEXADECIMALDIGIT + )*; + +BinaryLiteral: ('0b' | '0B') BINARYDIGIT ('\''? BINARYDIGIT)*; + +fragment NONZERODIGIT: [1-9]; + +fragment OCTALDIGIT: [0-7]; + +fragment HEXADECIMALDIGIT: [0-9a-fA-F]; + +fragment BINARYDIGIT: [01]; + +Integersuffix: + Unsignedsuffix Longsuffix? + | Unsignedsuffix Longlongsuffix? + | Longsuffix Unsignedsuffix? + | Longlongsuffix Unsignedsuffix?; + +fragment Unsignedsuffix: [uU]; + +fragment Longsuffix: [lL]; + +fragment Longlongsuffix: 'll' | 'LL'; + +fragment Cchar: + ~ ['\\\r\n] + | Escapesequence + | Universalcharactername; + +fragment Escapesequence: + Simpleescapesequence + | Octalescapesequence + | Hexadecimalescapesequence; + +fragment Simpleescapesequence: + '\\\'' + | '\\"' + | '\\?' + | '\\\\' + | '\\a' + | '\\b' + | '\\f' + | '\\n' + | '\\r' + | ('\\' ('\r' '\n'? | '\n')) + | '\\t' + | '\\v'; + +fragment Octalescapesequence: + '\\' OCTALDIGIT + | '\\' OCTALDIGIT OCTALDIGIT + | '\\' OCTALDIGIT OCTALDIGIT OCTALDIGIT; + +fragment Hexadecimalescapesequence: '\\x' HEXADECIMALDIGIT+; + +fragment Fractionalconstant: + Digitsequence? '.' Digitsequence + | Digitsequence '.'; + +fragment Exponentpart: + 'e' SIGN? Digitsequence + | 'E' SIGN? Digitsequence; + +fragment SIGN: [+-]; + +fragment Digitsequence: DIGIT ('\''? DIGIT)*; + +fragment Floatingsuffix: [flFL]; + +fragment Encodingprefix: 'u8' | 'u' | 'U' | 'L'; + +fragment Schar: + ~ ["\\\r\n] + | Escapesequence + | Universalcharactername; + +fragment Rawstring: 'R"' (( '\\' ["()] )|~[\r\n (])*? '(' ~[)]*? ')' (( '\\' ["()]) | ~[\r\n "])*? '"'; + +UserDefinedIntegerLiteral: + DecimalLiteral Udsuffix + | OctalLiteral Udsuffix + | HexadecimalLiteral Udsuffix + | BinaryLiteral Udsuffix; + +UserDefinedFloatingLiteral: + Fractionalconstant Exponentpart? Udsuffix + | Digitsequence Exponentpart Udsuffix; + +UserDefinedStringLiteral: StringLiteral Udsuffix; + +UserDefinedCharacterLiteral: CharacterLiteral Udsuffix; + +fragment Udsuffix: Identifier; + +Whitespace: [ \t]+ -> skip; + +Newline: ('\r' '\n'? | '\n') -> skip; + +BlockComment: '/*' .*? '*/' -> skip; + +LineComment: '//' ~ [\r\n]* -> skip; diff --git a/server/pkg/antlr/cpp14/CPP14Parser.g4 b/server/pkg/antlr/cpp14/CPP14Parser.g4 new file mode 100644 index 0000000000000000000000000000000000000000..19c9aa5518ab77db29e555d76f7d910f4b87fc74 --- /dev/null +++ b/server/pkg/antlr/cpp14/CPP14Parser.g4 @@ -0,0 +1,823 @@ +/******************************************************************************* + * The MIT License (MIT) + * + * Copyright (c) 2015 Camilo Sanchez (Camiloasc1) 2020 Martin Mirchev (Marti2203) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * **************************************************************************** + */ +parser grammar CPP14Parser; +options { + tokenVocab = CPP14Lexer; +} +/*Basic concepts*/ + +translationUnit: declarationseq? EOF; +/*Expressions*/ + +primaryExpression: + literal+ + | This + | LeftParen expression RightParen + | idExpression + | lambdaExpression; + +idExpression: unqualifiedId | qualifiedId; + +unqualifiedId: + Identifier + | operatorFunctionId + | conversionFunctionId + | literalOperatorId + | Tilde (className | decltypeSpecifier) + | templateId; + +qualifiedId: nestedNameSpecifier Template? unqualifiedId; + +nestedNameSpecifier: + (theTypeName | namespaceName | decltypeSpecifier)? Doublecolon + | nestedNameSpecifier ( + Identifier + | Template? simpleTemplateId + ) Doublecolon; +lambdaExpression: + lambdaIntroducer lambdaDeclarator? compoundStatement; + +lambdaIntroducer: LeftBracket lambdaCapture? RightBracket; + +lambdaCapture: + captureList + | captureDefault (Comma captureList)?; + +captureDefault: And | Assign; + +captureList: capture (Comma capture)* Ellipsis?; + +capture: simpleCapture | initcapture; + +simpleCapture: And? Identifier | This; + +initcapture: And? Identifier initializer; + +lambdaDeclarator: + LeftParen parameterDeclarationClause? RightParen Mutable? exceptionSpecification? + attributeSpecifierSeq? trailingReturnType?; + +postfixExpression: + primaryExpression + | postfixExpression LeftBracket (expression | bracedInitList) RightBracket + | postfixExpression LeftParen expressionList? RightParen + | (simpleTypeSpecifier | typeNameSpecifier) ( + LeftParen expressionList? RightParen + | bracedInitList + ) + | postfixExpression (Dot | Arrow) ( + Template? idExpression + | pseudoDestructorName + ) + | postfixExpression (PlusPlus | MinusMinus) + | ( + Dynamic_cast + | Static_cast + | Reinterpret_cast + | Const_cast + ) Less theTypeId Greater LeftParen expression RightParen + | typeIdOfTheTypeId LeftParen (expression | theTypeId) RightParen; +/* + add a middle layer to eliminate duplicated function declarations + */ + +typeIdOfTheTypeId: Typeid_; + +expressionList: initializerList; + +pseudoDestructorName: + nestedNameSpecifier? (theTypeName Doublecolon)? Tilde theTypeName + | nestedNameSpecifier Template simpleTemplateId Doublecolon Tilde theTypeName + | Tilde decltypeSpecifier; + +unaryExpression: + postfixExpression + | (PlusPlus | MinusMinus | unaryOperator | Sizeof) unaryExpression + | Sizeof ( + LeftParen theTypeId RightParen + | Ellipsis LeftParen Identifier RightParen + ) + | Alignof LeftParen theTypeId RightParen + | noExceptExpression + | newExpression + | deleteExpression; + +unaryOperator: Or | Star | And | Plus | Tilde | Minus | Not; + +newExpression: + Doublecolon? New newPlacement? ( + newTypeId + | (LeftParen theTypeId RightParen) + ) newInitializer?; + +newPlacement: LeftParen expressionList RightParen; + +newTypeId: typeSpecifierSeq newDeclarator?; + +newDeclarator: + pointerOperator newDeclarator? + | noPointerNewDeclarator; + +noPointerNewDeclarator: + LeftBracket expression RightBracket attributeSpecifierSeq? + | noPointerNewDeclarator LeftBracket constantExpression RightBracket attributeSpecifierSeq?; + +newInitializer: + LeftParen expressionList? RightParen + | bracedInitList; + +deleteExpression: + Doublecolon? Delete (LeftBracket RightBracket)? castExpression; + +noExceptExpression: Noexcept LeftParen expression RightParen; + +castExpression: + unaryExpression + | LeftParen theTypeId RightParen castExpression; + +pointerMemberExpression: + castExpression ((DotStar | ArrowStar) castExpression)*; + +multiplicativeExpression: + pointerMemberExpression ( + (Star | Div | Mod) pointerMemberExpression + )*; + +additiveExpression: + multiplicativeExpression ( + (Plus | Minus) multiplicativeExpression + )*; + +shiftExpression: + additiveExpression (shiftOperator additiveExpression)*; + +shiftOperator: Greater Greater | Less Less; + +relationalExpression: + shiftExpression ( + (Less | Greater | LessEqual | GreaterEqual) shiftExpression + )*; + +equalityExpression: + relationalExpression ( + (Equal | NotEqual) relationalExpression + )*; + +andExpression: equalityExpression (And equalityExpression)*; + +exclusiveOrExpression: andExpression (Caret andExpression)*; + +inclusiveOrExpression: + exclusiveOrExpression (Or exclusiveOrExpression)*; + +logicalAndExpression: + inclusiveOrExpression (AndAnd inclusiveOrExpression)*; + +logicalOrExpression: + logicalAndExpression (OrOr logicalAndExpression)*; + +conditionalExpression: + logicalOrExpression ( + Question expression Colon assignmentExpression + )?; + +assignmentExpression: + conditionalExpression + | logicalOrExpression assignmentOperator initializerClause + | throwExpression; + +assignmentOperator: + Assign + | StarAssign + | DivAssign + | ModAssign + | PlusAssign + | MinusAssign + | RightShiftAssign + | LeftShiftAssign + | AndAssign + | XorAssign + | OrAssign; + +expression: assignmentExpression (Comma assignmentExpression)*; + +constantExpression: conditionalExpression; +/*Statements*/ + +statement: + labeledStatement + | declarationStatement + | attributeSpecifierSeq? ( + expressionStatement + | compoundStatement + | selectionStatement + | iterationStatement + | jumpStatement + | tryBlock + ); + +labeledStatement: + attributeSpecifierSeq? ( + Identifier + | Case constantExpression + | Default + ) Colon statement; + +expressionStatement: expression? Semi; + +compoundStatement: LeftBrace statementSeq? RightBrace; + +statementSeq: statement+; + +selectionStatement: + If LeftParen condition RightParen statement (Else statement)? + | Switch LeftParen condition RightParen statement; + +condition: + expression + | attributeSpecifierSeq? declSpecifierSeq declarator ( + Assign initializerClause + | bracedInitList + ); + +iterationStatement: + While LeftParen condition RightParen statement + | Do statement While LeftParen expression RightParen Semi + | For LeftParen ( + forInitStatement condition? Semi expression? + | forRangeDeclaration Colon forRangeInitializer + ) RightParen statement; + +forInitStatement: expressionStatement | simpleDeclaration; + +forRangeDeclaration: + attributeSpecifierSeq? declSpecifierSeq declarator; + +forRangeInitializer: expression | bracedInitList; + +jumpStatement: + ( + Break + | Continue + | Return (expression | bracedInitList)? + | Goto Identifier + ) Semi; + +declarationStatement: blockDeclaration; +/*Declarations*/ + +declarationseq: declaration+; + +declaration: + blockDeclaration + | functionDefinition + | templateDeclaration + | explicitInstantiation + | explicitSpecialization + | linkageSpecification + | namespaceDefinition + | emptyDeclaration + | attributeDeclaration; + +blockDeclaration: + simpleDeclaration + | asmDefinition + | namespaceAliasDefinition + | usingDeclaration + | usingDirective + | staticAssertDeclaration + | aliasDeclaration + | opaqueEnumDeclaration; + +aliasDeclaration: + Using Identifier attributeSpecifierSeq? Assign theTypeId Semi; + +simpleDeclaration: + declSpecifierSeq? initDeclaratorList? Semi + | attributeSpecifierSeq declSpecifierSeq? initDeclaratorList Semi; + +staticAssertDeclaration: + Static_assert LeftParen constantExpression Comma StringLiteral RightParen Semi; + +emptyDeclaration: Semi; + +attributeDeclaration: attributeSpecifierSeq Semi; + +declSpecifier: + storageClassSpecifier + | typeSpecifier + | functionSpecifier + | Friend + | Typedef + | Constexpr; + +declSpecifierSeq: declSpecifier+? attributeSpecifierSeq?; + +storageClassSpecifier: + Register + | Static + | Thread_local + | Extern + | Mutable; + +functionSpecifier: Inline | Virtual | Explicit; + +typedefName: Identifier; + +typeSpecifier: + trailingTypeSpecifier + | classSpecifier + | enumSpecifier; + +trailingTypeSpecifier: + simpleTypeSpecifier + | elaboratedTypeSpecifier + | typeNameSpecifier + | cvQualifier; + +typeSpecifierSeq: typeSpecifier+ attributeSpecifierSeq?; + +trailingTypeSpecifierSeq: + trailingTypeSpecifier+ attributeSpecifierSeq?; + +simpleTypeLengthModifier: + Short + | Long; + +simpleTypeSignednessModifier: + Unsigned + | Signed; + +simpleTypeSpecifier: + nestedNameSpecifier? theTypeName + | nestedNameSpecifier Template simpleTemplateId + | simpleTypeSignednessModifier + | simpleTypeSignednessModifier? simpleTypeLengthModifier+ + | simpleTypeSignednessModifier? Char + | simpleTypeSignednessModifier? Char16 + | simpleTypeSignednessModifier? Char32 + | simpleTypeSignednessModifier? Wchar + | Bool + | simpleTypeSignednessModifier? simpleTypeLengthModifier* Int + | Float + | simpleTypeLengthModifier? Double + | Void + | Auto + | decltypeSpecifier; + +theTypeName: + className + | enumName + | typedefName + | simpleTemplateId; + +decltypeSpecifier: + Decltype LeftParen (expression | Auto) RightParen; + +elaboratedTypeSpecifier: + classKey ( + attributeSpecifierSeq? nestedNameSpecifier? Identifier + | simpleTemplateId + | nestedNameSpecifier Template? simpleTemplateId + ) + | Enum nestedNameSpecifier? Identifier; + +enumName: Identifier; + +enumSpecifier: + enumHead LeftBrace (enumeratorList Comma?)? RightBrace; + +enumHead: + enumkey attributeSpecifierSeq? ( + nestedNameSpecifier? Identifier + )? enumbase?; + +opaqueEnumDeclaration: + enumkey attributeSpecifierSeq? Identifier enumbase? Semi; + +enumkey: Enum (Class | Struct)?; + +enumbase: Colon typeSpecifierSeq; + +enumeratorList: + enumeratorDefinition (Comma enumeratorDefinition)*; + +enumeratorDefinition: enumerator (Assign constantExpression)?; + +enumerator: Identifier; + +namespaceName: originalNamespaceName | namespaceAlias; + +originalNamespaceName: Identifier; + +namespaceDefinition: + Inline? Namespace (Identifier | originalNamespaceName)? LeftBrace namespaceBody = declarationseq + ? RightBrace; + +namespaceAlias: Identifier; + +namespaceAliasDefinition: + Namespace Identifier Assign qualifiednamespacespecifier Semi; + +qualifiednamespacespecifier: nestedNameSpecifier? namespaceName; + +usingDeclaration: + Using ((Typename_? nestedNameSpecifier) | Doublecolon) unqualifiedId Semi; + +usingDirective: + attributeSpecifierSeq? Using Namespace nestedNameSpecifier? namespaceName Semi; + +asmDefinition: Asm LeftParen StringLiteral RightParen Semi; + +linkageSpecification: + Extern StringLiteral ( + LeftBrace declarationseq? RightBrace + | declaration + ); + +attributeSpecifierSeq: attributeSpecifier+; + +attributeSpecifier: + LeftBracket LeftBracket attributeList? RightBracket RightBracket + | alignmentspecifier; + +alignmentspecifier: + Alignas LeftParen (theTypeId | constantExpression) Ellipsis? RightParen; + +attributeList: attribute (Comma attribute)* Ellipsis?; + +attribute: (attributeNamespace Doublecolon)? Identifier attributeArgumentClause?; + +attributeNamespace: Identifier; + +attributeArgumentClause: LeftParen balancedTokenSeq? RightParen; + +balancedTokenSeq: balancedtoken+; + +balancedtoken: + LeftParen balancedTokenSeq RightParen + | LeftBracket balancedTokenSeq RightBracket + | LeftBrace balancedTokenSeq RightBrace + | ~( + LeftParen + | RightParen + | LeftBrace + | RightBrace + | LeftBracket + | RightBracket + )+; +/*Declarators*/ + +initDeclaratorList: initDeclarator (Comma initDeclarator)*; + +initDeclarator: declarator initializer?; + +declarator: + pointerDeclarator + | noPointerDeclarator parametersAndQualifiers trailingReturnType; + +pointerDeclarator: (pointerOperator Const?)* noPointerDeclarator; + +noPointerDeclarator: + declaratorid attributeSpecifierSeq? + | noPointerDeclarator ( + parametersAndQualifiers + | LeftBracket constantExpression? RightBracket attributeSpecifierSeq? + ) + | LeftParen pointerDeclarator RightParen; + +parametersAndQualifiers: + LeftParen parameterDeclarationClause? RightParen cvqualifierseq? refqualifier? + exceptionSpecification? attributeSpecifierSeq?; + +trailingReturnType: + Arrow trailingTypeSpecifierSeq abstractDeclarator?; + +pointerOperator: + (And | AndAnd) attributeSpecifierSeq? + | nestedNameSpecifier? Star attributeSpecifierSeq? cvqualifierseq?; + +cvqualifierseq: cvQualifier+; + +cvQualifier: Const | Volatile; + +refqualifier: And | AndAnd; + +declaratorid: Ellipsis? idExpression; + +theTypeId: typeSpecifierSeq abstractDeclarator?; + +abstractDeclarator: + pointerAbstractDeclarator + | noPointerAbstractDeclarator? parametersAndQualifiers trailingReturnType + | abstractPackDeclarator; + +pointerAbstractDeclarator: + noPointerAbstractDeclarator + | pointerOperator+ noPointerAbstractDeclarator?; + +noPointerAbstractDeclarator: + noPointerAbstractDeclarator ( + parametersAndQualifiers + | noPointerAbstractDeclarator LeftBracket constantExpression? RightBracket + attributeSpecifierSeq? + ) + | parametersAndQualifiers + | LeftBracket constantExpression? RightBracket attributeSpecifierSeq? + | LeftParen pointerAbstractDeclarator RightParen; + +abstractPackDeclarator: + pointerOperator* noPointerAbstractPackDeclarator; + +noPointerAbstractPackDeclarator: + noPointerAbstractPackDeclarator ( + parametersAndQualifiers + | LeftBracket constantExpression? RightBracket attributeSpecifierSeq? + ) + | Ellipsis; + +parameterDeclarationClause: + parameterDeclarationList (Comma? Ellipsis)?; + +parameterDeclarationList: + parameterDeclaration (Comma parameterDeclaration)*; + +parameterDeclaration: + attributeSpecifierSeq? declSpecifierSeq ( + (declarator | abstractDeclarator?) ( + Assign initializerClause + )? + ); + +functionDefinition: + attributeSpecifierSeq? declSpecifierSeq? declarator virtualSpecifierSeq? functionBody; + +functionBody: + constructorInitializer? compoundStatement + | functionTryBlock + | Assign (Default | Delete) Semi; + +initializer: + braceOrEqualInitializer + | LeftParen expressionList RightParen; + +braceOrEqualInitializer: + Assign initializerClause + | bracedInitList; + +initializerClause: assignmentExpression | bracedInitList; + +initializerList: + initializerClause Ellipsis? ( + Comma initializerClause Ellipsis? + )*; + +bracedInitList: LeftBrace (initializerList Comma?)? RightBrace; +/*Classes*/ + +className: Identifier | simpleTemplateId; + +classSpecifier: + classHead LeftBrace memberSpecification? RightBrace; + +classHead: + classKey attributeSpecifierSeq? ( + classHeadName classVirtSpecifier? + )? baseClause? + | Union attributeSpecifierSeq? ( + classHeadName classVirtSpecifier? + )?; + +classHeadName: nestedNameSpecifier? className; + +classVirtSpecifier: Final; + +classKey: Class | Struct; + +memberSpecification: + (memberdeclaration | accessSpecifier Colon)+; + +memberdeclaration: + attributeSpecifierSeq? declSpecifierSeq? memberDeclaratorList? Semi + | functionDefinition + | usingDeclaration + | staticAssertDeclaration + | templateDeclaration + | aliasDeclaration + | emptyDeclaration; + +memberDeclaratorList: + memberDeclarator (Comma memberDeclarator)*; + +memberDeclarator: + declarator ( + virtualSpecifierSeq? pureSpecifier? + | braceOrEqualInitializer? + ) + | Identifier? attributeSpecifierSeq? Colon constantExpression; + +virtualSpecifierSeq: virtualSpecifier+; + +virtualSpecifier: Override | Final; +/* + purespecifier: Assign '0'//Conflicts with the lexer ; + */ + +pureSpecifier: + Assign val = OctalLiteral {if($val.text.compare("0")!=0) throw new InputMismatchException(this); + }; +/*Derived classes*/ + +baseClause: Colon baseSpecifierList; + +baseSpecifierList: + baseSpecifier Ellipsis? (Comma baseSpecifier Ellipsis?)*; + +baseSpecifier: + attributeSpecifierSeq? ( + baseTypeSpecifier + | Virtual accessSpecifier? baseTypeSpecifier + | accessSpecifier Virtual? baseTypeSpecifier + ); + +classOrDeclType: + nestedNameSpecifier? className + | decltypeSpecifier; + +baseTypeSpecifier: classOrDeclType; + +accessSpecifier: Private | Protected | Public; +/*Special member functions*/ + +conversionFunctionId: Operator conversionTypeId; + +conversionTypeId: typeSpecifierSeq conversionDeclarator?; + +conversionDeclarator: pointerOperator conversionDeclarator?; + +constructorInitializer: Colon memInitializerList; + +memInitializerList: + memInitializer Ellipsis? (Comma memInitializer Ellipsis?)*; + +memInitializer: + meminitializerid ( + LeftParen expressionList? RightParen + | bracedInitList + ); + +meminitializerid: classOrDeclType | Identifier; +/*Overloading*/ + +operatorFunctionId: Operator theOperator; + +literalOperatorId: + Operator ( + StringLiteral Identifier + | UserDefinedStringLiteral + ); +/*Templates*/ + +templateDeclaration: + Template Less templateparameterList Greater declaration; + +templateparameterList: + templateParameter (Comma templateParameter)*; + +templateParameter: typeParameter | parameterDeclaration; + +typeParameter: + ( + (Template Less templateparameterList Greater)? Class + | Typename_ + ) ((Ellipsis? Identifier?) | (Identifier? Assign theTypeId)); + +simpleTemplateId: + templateName Less templateArgumentList? Greater; + +templateId: + simpleTemplateId + | (operatorFunctionId | literalOperatorId) Less templateArgumentList? Greater; + +templateName: Identifier; + +templateArgumentList: + templateArgument Ellipsis? (Comma templateArgument Ellipsis?)*; + +templateArgument: theTypeId | constantExpression | idExpression; + +typeNameSpecifier: + Typename_ nestedNameSpecifier ( + Identifier + | Template? simpleTemplateId + ); + +explicitInstantiation: Extern? Template declaration; + +explicitSpecialization: Template Less Greater declaration; +/*Exception handling*/ + +tryBlock: Try compoundStatement handlerSeq; + +functionTryBlock: + Try constructorInitializer? compoundStatement handlerSeq; + +handlerSeq: handler+; + +handler: + Catch LeftParen exceptionDeclaration RightParen compoundStatement; + +exceptionDeclaration: + attributeSpecifierSeq? typeSpecifierSeq ( + declarator + | abstractDeclarator + )? + | Ellipsis; + +throwExpression: Throw assignmentExpression?; + +exceptionSpecification: + dynamicExceptionSpecification + | noeExceptSpecification; + +dynamicExceptionSpecification: + Throw LeftParen typeIdList? RightParen; + +typeIdList: theTypeId Ellipsis? (Comma theTypeId Ellipsis?)*; + +noeExceptSpecification: + Noexcept LeftParen constantExpression RightParen + | Noexcept; +/*Preprocessing directives*/ + +/*Lexer*/ + +theOperator: + New (LeftBracket RightBracket)? + | Delete (LeftBracket RightBracket)? + | Plus + | Minus + | Star + | Div + | Mod + | Caret + | And + | Or + | Tilde + | Not + | Assign + | Greater + | Less + | GreaterEqual + | PlusAssign + | MinusAssign + | StarAssign + | ModAssign + | XorAssign + | AndAssign + | OrAssign + | Less Less + | Greater Greater + | RightShiftAssign + | LeftShiftAssign + | Equal + | NotEqual + | LessEqual + | AndAnd + | OrOr + | PlusPlus + | MinusMinus + | Comma + | ArrowStar + | Arrow + | LeftParen RightParen + | LeftBracket RightBracket; + +literal: + IntegerLiteral + | CharacterLiteral + | FloatingLiteral + | StringLiteral + | BooleanLiteral + | PointerLiteral + | UserDefinedLiteral; + diff --git a/server/pkg/antlr/cpp14/include/MyCppAntlr.h b/server/pkg/antlr/cpp14/include/MyCppAntlr.h new file mode 100644 index 0000000000000000000000000000000000000000..8e2d32892937d16a74c27e9a3065e75a30376693 --- /dev/null +++ b/server/pkg/antlr/cpp14/include/MyCppAntlr.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#include "CPP14Lexer.h" +#include "CPP14Parser.h" +#include "IAntlrWrapper.h" + +class MyCppAntlr : public IAntlrWrapper { + private: + std::unique_ptr lexer_ptr; + std::unique_ptr parser_ptr; + std::unique_ptr input_ptr; + std::unique_ptr tokenStream_ptr; + + public: + MyCppAntlr(std::istream &in); + ~MyCppAntlr() override = default; + std::vector getTokens() override; + std::vector getTokensTypes() override; + std::pair getTokensAndTree() override; + std::string getTokensString() override; + std::string getTreeString() override; +}; diff --git a/server/pkg/antlr/cpp14/src/MyCppAntlr.cpp b/server/pkg/antlr/cpp14/src/MyCppAntlr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fcbc51928044a01cbc1ecec2fc22a74087c3af55 --- /dev/null +++ b/server/pkg/antlr/cpp14/src/MyCppAntlr.cpp @@ -0,0 +1,62 @@ +#include "MyCppAntlr.h" + +#include + +MyCppAntlr::MyCppAntlr(std::istream& in) { + input_ptr = std::make_unique(in); + lexer_ptr = std::make_unique(&(*input_ptr)); + tokenStream_ptr = std::make_unique(&(*lexer_ptr)); + parser_ptr = std::make_unique(&(*tokenStream_ptr)); +} + +std::vector MyCppAntlr::getTokens() { + tokenStream_ptr->fill(); + std::vector ans(tokenStream_ptr->size()); + + int i = 0; + for (antlr4::Token* token : tokenStream_ptr->getTokens()) { + ans[i] = token; + i++; + } + + return ans; +} + +std::vector MyCppAntlr::getTokensTypes() { + tokenStream_ptr->fill(); + std::vector ans(tokenStream_ptr->size()); + + int i = 0; + for (antlr4::Token* token : tokenStream_ptr->getTokens()) { + if (token->getText() == "") { + ans[i] = -1; + } else { + ans[i] = token->getType(); + } + i++; + } + + return ans; +} + +std::string MyCppAntlr::getTokensString() { + tokenStream_ptr->fill(); + std::string res; + + for (antlr4::Token* token : tokenStream_ptr->getTokens()) { + res += token->toString() + " "; + } + + return res; +} + +std::string MyCppAntlr::getTreeString() { + auto tree = parser_ptr->translationUnit(); + return tree->toStringTree(&(*parser_ptr)); +} + +std::pair MyCppAntlr::getTokensAndTree() { + std::string tokens = getTokensString(); + std::string astTree = getTreeString(); + return std::make_pair(tokens, astTree); +} diff --git a/server/pkg/antlr/python3/CMakeLists.txt b/server/pkg/antlr/python3/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..4a90e0b047399c98f603d970d8655e56bedcc3c5 --- /dev/null +++ b/server/pkg/antlr/python3/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.7) +project(python_antlr_lib) + +file(GLOB SOURCES ./src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/../virtual) + + +antlr_target(Python3Grammar Python3.g4 + PACKAGE antlrcpptest) + +include_directories(${ANTLR_Python3Grammar_OUTPUT_DIR}) + + +message("ANTLR_Python3Grammar_OUTPUT_DIR=${ANTLR_Python3Grammar_OUTPUT_DIR}") + +include_directories(${INCLUDE_DIRS}) +add_library(${PROJECT_NAME} ${SOURCES} ${ANTLR_Python3Grammar_CXX_OUTPUTS}) + + +target_link_libraries(${PROJECT_NAME} antlr4_static Threads::Threads) + +set(PYTHON3_ANTLR_LIBRARY ${PROJECT_NAME}) +set(PYTHON3_ANTLR_LIBRARY ${PYTHON3_ANTLR_LIBRARY} PARENT_SCOPE) + +set(PYTHON3_ANTLR_INCLUDE_DIRS ${INCLUDE_DIRS} ${ANTLR_Python3Grammar_OUTPUT_DIR}) +set(PYTHON3_ANTLR_INCLUDE_DIRS ${PYTHON3_ANTLR_INCLUDE_DIRS} PARENT_SCOPE) + +message("PYTHON3_ANTLR = ${PYTHON3_ANTLR_INCLUDE_DIRS} ") \ No newline at end of file diff --git a/server/pkg/antlr/python3/Python3.g4 b/server/pkg/antlr/python3/Python3.g4 new file mode 100644 index 0000000000000000000000000000000000000000..3f801e18042385ecd324faa3c156db9757796e73 --- /dev/null +++ b/server/pkg/antlr/python3/Python3.g4 @@ -0,0 +1,1182 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 by Bart Kiers + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Project : python3-parser; an ANTLR4 grammar for Python 3 + * https://github.com/bkiers/python3-parser + * Developed by : Bart Kiers, bart@big-o.nl + */ +grammar Python3; + +// All comments that start with "///" are copy-pasted from +// The Python Language Reference + +tokens { INDENT, DEDENT } + +@lexer::header { + #include "Python3Parser.h" +} + + +@lexer::members { + private: + // A queue where extra tokens are pushed on (see the NEWLINE lexer rule). + std::vector> m_tokens; + // The stack that keeps track of the indentation level. + std::stack m_indents; + // The amount of opened braces, brackets and parenthesis. + int m_opened = 0; + // The most recently produced token. + std::unique_ptr m_pLastToken = nullptr; + + public: + virtual void emit(std::unique_ptr newToken) override { + m_tokens.push_back(cloneToken(newToken)); + setToken(std::move(newToken)); + } + + std::unique_ptr nextToken() override { + // Check if the end-of-file is ahead and there are still some DEDENTS expected. + if (_input->LA(1) == EOF && !m_indents.empty()) { + // Remove any trailing EOF tokens from our buffer. + for (int i = m_tokens.size() - 1; i >= 0; i--) { + if (m_tokens[i]->getType() == EOF) { + m_tokens.erase(m_tokens.begin() + i); + } + } + + // First emit an extra line break that serves as the end of the statement. + emit(commonToken(Python3Parser::NEWLINE, "\n")); + + // Now emit as much DEDENT tokens as needed. + while (!m_indents.empty()) { + emit(createDedent()); + m_indents.pop(); + } + + // Put the EOF back on the token stream. + emit(commonToken(EOF, "")); + } + + std::unique_ptr next = Lexer::nextToken(); + + if (next->getChannel() == antlr4::Token::DEFAULT_CHANNEL) { + // Keep track of the last token on the default channel. + m_pLastToken = cloneToken(next); + } + + if (!m_tokens.empty()) + { + next = std::move(*m_tokens.begin()); + m_tokens.erase(m_tokens.begin()); + } + + return next; + } + + private: + std::unique_ptr createDedent() { + std::unique_ptr dedent = commonToken(Python3Parser::DEDENT, ""); + return dedent; + } + + std::unique_ptr commonToken(size_t type, const std::string& text) { + int stop = getCharIndex() - 1; + int start = text.empty() ? stop : stop - text.size() + 1; + return _factory->create({ this, _input }, type, text, DEFAULT_TOKEN_CHANNEL, start, stop, m_pLastToken ? m_pLastToken->getLine() : 0, m_pLastToken ? m_pLastToken->getCharPositionInLine() : 0); + } + + std::unique_ptr cloneToken(const std::unique_ptr& source) { + return _factory->create({ this, _input }, source->getType(), source->getText(), source->getChannel(), source->getStartIndex(), source->getStopIndex(), source->getLine(), source->getCharPositionInLine()); + } + + + // Calculates the indentation of the provided spaces, taking the + // following rules into account: + // + // "Tabs are replaced (from left to right) by one to eight spaces + // such that the total number of characters up to and including + // the replacement is a multiple of eight [...]" + // + // -- https://docs.python.org/3.1/reference/lexical_analysis.html#indentation + static int getIndentationCount(const std::string& spaces) { + int count = 0; + for (char ch : spaces) { + switch (ch) { + case '\t': + count += 8 - (count % 8); + break; + default: + // A normal space char. + count++; + } + } + + return count; + } + + bool atStartOfInput() { + return getCharPositionInLine() == 0 && getLine() == 1; + } +} + +/* + * parser rules + */ + +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE; +file_input: (NEWLINE | stmt)* EOF; +eval_input: testlist NEWLINE* EOF; + +decorator: '@' dotted_name ( '(' (arglist)? ')' )? NEWLINE; +decorators: decorator+; +decorated: decorators (classdef | funcdef | async_funcdef); + +async_funcdef: ASYNC funcdef; +funcdef: 'def' NAME parameters ('->' test)? ':' suite; + +parameters: '(' (typedargslist)? ')'; +typedargslist: (tfpdef ('=' test)? (',' tfpdef ('=' test)?)* (',' ( + '*' (tfpdef)? (',' tfpdef ('=' test)?)* (',' ('**' tfpdef (',')?)?)? + | '**' tfpdef (',')?)?)? + | '*' (tfpdef)? (',' tfpdef ('=' test)?)* (',' ('**' tfpdef (',')?)?)? + | '**' tfpdef (',')?); +tfpdef: NAME (':' test)?; +varargslist: (vfpdef ('=' test)? (',' vfpdef ('=' test)?)* (',' ( + '*' (vfpdef)? (',' vfpdef ('=' test)?)* (',' ('**' vfpdef (',')?)?)? + | '**' vfpdef (',')?)?)? + | '*' (vfpdef)? (',' vfpdef ('=' test)?)* (',' ('**' vfpdef (',')?)?)? + | '**' vfpdef (',')? +); +vfpdef: NAME; + +stmt: simple_stmt | compound_stmt; +simple_stmt: small_stmt (';' small_stmt)* (';')? NEWLINE; +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | nonlocal_stmt | assert_stmt); +expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*); +annassign: ':' test ('=' test)?; +testlist_star_expr: (test|star_expr) (',' (test|star_expr))* (',')?; +augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | + '<<=' | '>>=' | '**=' | '//='); +// For normal and annotated assignments, additional restrictions enforced by the interpreter +del_stmt: 'del' exprlist; +pass_stmt: 'pass'; +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt; +break_stmt: 'break'; +continue_stmt: 'continue'; +return_stmt: 'return' (testlist)?; +yield_stmt: yield_expr; +raise_stmt: 'raise' (test ('from' test)?)?; +import_stmt: import_name | import_from; +import_name: 'import' dotted_as_names; +// note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) + 'import' ('*' | '(' import_as_names ')' | import_as_names)); +import_as_name: NAME ('as' NAME)?; +dotted_as_name: dotted_name ('as' NAME)?; +import_as_names: import_as_name (',' import_as_name)* (',')?; +dotted_as_names: dotted_as_name (',' dotted_as_name)*; +dotted_name: NAME ('.' NAME)*; +global_stmt: 'global' NAME (',' NAME)*; +nonlocal_stmt: 'nonlocal' NAME (',' NAME)*; +assert_stmt: 'assert' test (',' test)?; + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt; +async_stmt: ASYNC (funcdef | with_stmt | for_stmt); +if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ('else' ':' suite)?; +while_stmt: 'while' test ':' suite ('else' ':' suite)?; +for_stmt: 'for' exprlist 'in' testlist ':' suite ('else' ':' suite)?; +try_stmt: ('try' ':' suite + ((except_clause ':' suite)+ + ('else' ':' suite)? + ('finally' ':' suite)? | + 'finally' ':' suite)); +with_stmt: 'with' with_item (',' with_item)* ':' suite; +with_item: test ('as' expr)?; +// NB compile.c makes sure that the default except clause is last +except_clause: 'except' (test ('as' NAME)?)?; +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT; + +test: or_test ('if' or_test 'else' test)? | lambdef; +test_nocond: or_test | lambdef_nocond; +lambdef: 'lambda' (varargslist)? ':' test; +lambdef_nocond: 'lambda' (varargslist)? ':' test_nocond; +or_test: and_test ('or' and_test)*; +and_test: not_test ('and' not_test)*; +not_test: 'not' not_test | comparison; +comparison: expr (comp_op expr)*; +// <> isn't actually a valid comparison operator in Python. It's here for the +// sake of a __future__ import described in PEP 401 (which really works :-) +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'; +star_expr: '*' expr; +expr: xor_expr ('|' xor_expr)*; +xor_expr: and_expr ('^' and_expr)*; +and_expr: shift_expr ('&' shift_expr)*; +shift_expr: arith_expr (('<<'|'>>') arith_expr)*; +arith_expr: term (('+'|'-') term)*; +term: factor (('*'|'@'|'/'|'%'|'//') factor)*; +factor: ('+'|'-'|'~') factor | power; +power: atom_expr ('**' factor)?; +atom_expr: (AWAIT)? atom trailer*; +atom: ('(' (yield_expr|testlist_comp)? ')' | + '[' (testlist_comp)? ']' | + '{' (dictorsetmaker)? '}' | + NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False'); +testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* (',')? ); +trailer: '(' (arglist)? ')' | '[' subscriptlist ']' | '.' NAME; +subscriptlist: subscript (',' subscript)* (',')?; +subscript: test | (test)? ':' (test)? (sliceop)?; +sliceop: ':' (test)?; +exprlist: (expr|star_expr) (',' (expr|star_expr))* (',')?; +testlist: test (',' test)* (',')?; +dictorsetmaker: ( ((test ':' test | '**' expr) + (comp_for | (',' (test ':' test | '**' expr))* (',')?)) | + ((test | star_expr) + (comp_for | (',' (test | star_expr))* (',')?)) ); + +classdef: 'class' NAME ('(' (arglist)? ')')? ':' suite; + +arglist: argument (',' argument)* (',')?; + +// The reason that keywords are test nodes instead of NAME is that using NAME +// results in an ambiguity. ast.c makes sure it's a NAME. +// "test '=' test" is really "keyword '=' test", but we have no such token. +// These need to be in a single rule to avoid grammar that is ambiguous +// to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, +// we explicitly match '*' here, too, to give it proper precedence. +// Illegal combinations and orderings are blocked in ast.c: +// multiple (test comp_for) arguments are blocked; keyword unpackings +// that precede iterable unpackings are blocked; etc. +argument: ( test (comp_for)? | + test '=' test | + '**' test | + '*' test ); + +comp_iter: comp_for | comp_if; +comp_for: (ASYNC)? 'for' exprlist 'in' or_test (comp_iter)?; +comp_if: 'if' test_nocond (comp_iter)?; + +// not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME; + +yield_expr: 'yield' (yield_arg)?; +yield_arg: 'from' test | testlist; + +/* + * lexer rules + */ + +STRING + : STRING_LITERAL + | BYTES_LITERAL + ; + +NUMBER + : INTEGER + | FLOAT_NUMBER + | IMAG_NUMBER + ; + +INTEGER + : DECIMAL_INTEGER + | OCT_INTEGER + | HEX_INTEGER + | BIN_INTEGER + ; + +DEF : 'def'; +RETURN : 'return'; +RAISE : 'raise'; +FROM : 'from'; +IMPORT : 'import'; +AS : 'as'; +GLOBAL : 'global'; +NONLOCAL : 'nonlocal'; +ASSERT : 'assert'; +IF : 'if'; +ELIF : 'elif'; +ELSE : 'else'; +WHILE : 'while'; +FOR : 'for'; +IN : 'in'; +TRY : 'try'; +FINALLY : 'finally'; +WITH : 'with'; +EXCEPT : 'except'; +LAMBDA : 'lambda'; +OR : 'or'; +AND : 'and'; +NOT : 'not'; +IS : 'is'; +NONE : 'None'; +TRUE : 'True'; +FALSE : 'False'; +CLASS : 'class'; +YIELD : 'yield'; +DEL : 'del'; +PASS : 'pass'; +CONTINUE : 'continue'; +BREAK : 'break'; +ASYNC : 'async'; +AWAIT : 'await'; + +NEWLINE + : ( {atStartOfInput()}? SPACES + | ( '\r'? '\n' | '\r' | '\f' ) SPACES? + ) + { + { + std::string newLine, spaces; + std::string text = getText(); + for(char c : text) + { + if ((c == '\r') || (c == '\n') || (c == '\f')) + newLine.push_back(c); + else + spaces.push_back(c); + } + + + // Strip newlines inside open clauses except if we are near EOF. We keep NEWLINEs near EOF to + // satisfy the final newline needed by the single_put rule used by the REPL. + int next = _input->LA(1); + int nextnext = _input->LA(2); + if (m_opened > 0 || (nextnext != -1 && (next == '\r' || next == '\n' || next == '\f' || next == '#'))) { + // If we're inside a list or on a blank line, ignore all indents, + // dedents and line breaks. + skip(); + } + else { + emit(commonToken(NEWLINE, newLine)); + int indent = getIndentationCount(spaces); + int previous = m_indents.empty() ? 0 : m_indents.top(); + if (indent == previous) { + // skip indents of the same size as the present indent-size + skip(); + } + else if (indent > previous) { + m_indents.push(indent); + emit(commonToken(Python3Parser::INDENT, spaces)); + } + else { + // Possibly emit more than 1 DEDENT token. + while(!m_indents.empty() && m_indents.top() > indent) { + emit(createDedent()); + m_indents.pop(); + } + } + } + } + } + ; + +/// identifier ::= id_start id_continue* +NAME + : ID_START ID_CONTINUE* + ; + +/// stringliteral ::= [stringprefix](shortstring | longstring) +/// stringprefix ::= "r" | "u" | "R" | "U" | "f" | "F" +/// | "fr" | "Fr" | "fR" | "FR" | "rf" | "rF" | "Rf" | "RF" +STRING_LITERAL + : ( [rR] | [uU] | [fF] | ( [fF] [rR] ) | ( [rR] [fF] ) )? ( SHORT_STRING | LONG_STRING ) + ; + +/// bytesliteral ::= bytesprefix(shortbytes | longbytes) +/// bytesprefix ::= "b" | "B" | "br" | "Br" | "bR" | "BR" | "rb" | "rB" | "Rb" | "RB" +BYTES_LITERAL + : ( [bB] | ( [bB] [rR] ) | ( [rR] [bB] ) ) ( SHORT_BYTES | LONG_BYTES ) + ; + +/// decimalinteger ::= nonzerodigit digit* | "0"+ +DECIMAL_INTEGER + : NON_ZERO_DIGIT DIGIT* + | '0'+ + ; + +/// octinteger ::= "0" ("o" | "O") octdigit+ +OCT_INTEGER + : '0' [oO] OCT_DIGIT+ + ; + +/// hexinteger ::= "0" ("x" | "X") hexdigit+ +HEX_INTEGER + : '0' [xX] HEX_DIGIT+ + ; + +/// bininteger ::= "0" ("b" | "B") bindigit+ +BIN_INTEGER + : '0' [bB] BIN_DIGIT+ + ; + +/// floatnumber ::= pointfloat | exponentfloat +FLOAT_NUMBER + : POINT_FLOAT + | EXPONENT_FLOAT + ; + +/// imagnumber ::= (floatnumber | intpart) ("j" | "J") +IMAG_NUMBER + : ( FLOAT_NUMBER | INT_PART ) [jJ] + ; + +DOT : '.'; +ELLIPSIS : '...'; +STAR : '*'; +OPEN_PAREN : '(' {m_opened++;}; +CLOSE_PAREN : ')' {m_opened--;}; +COMMA : ','; +COLON : ':'; +SEMI_COLON : ';'; +POWER : '**'; +ASSIGN : '='; +OPEN_BRACK : '[' {m_opened++;}; +CLOSE_BRACK : ']' {m_opened--;}; +OR_OP : '|'; +XOR : '^'; +AND_OP : '&'; +LEFT_SHIFT : '<<'; +RIGHT_SHIFT : '>>'; +ADD : '+'; +MINUS : '-'; +DIV : '/'; +MOD : '%'; +IDIV : '//'; +NOT_OP : '~'; +OPEN_BRACE : '{' {m_opened++;}; +CLOSE_BRACE : '}' {m_opened--;}; +LESS_THAN : '<'; +GREATER_THAN : '>'; +EQUALS : '=='; +GT_EQ : '>='; +LT_EQ : '<='; +NOT_EQ_1 : '<>'; +NOT_EQ_2 : '!='; +AT : '@'; +ARROW : '->'; +ADD_ASSIGN : '+='; +SUB_ASSIGN : '-='; +MULT_ASSIGN : '*='; +AT_ASSIGN : '@='; +DIV_ASSIGN : '/='; +MOD_ASSIGN : '%='; +AND_ASSIGN : '&='; +OR_ASSIGN : '|='; +XOR_ASSIGN : '^='; +LEFT_SHIFT_ASSIGN : '<<='; +RIGHT_SHIFT_ASSIGN : '>>='; +POWER_ASSIGN : '**='; +IDIV_ASSIGN : '//='; + +SKIP_ + : ( SPACES | COMMENT | LINE_JOINING ) -> skip + ; + +UNKNOWN_CHAR + : . + ; + +/* + * fragments + */ + +/// shortstring ::= "'" shortstringitem* "'" | '"' shortstringitem* '"' +/// shortstringitem ::= shortstringchar | stringescapeseq +/// shortstringchar ::= +fragment SHORT_STRING + : '\'' ( STRING_ESCAPE_SEQ | ~[\\\r\n\f'] )* '\'' + | '"' ( STRING_ESCAPE_SEQ | ~[\\\r\n\f"] )* '"' + ; +/// longstring ::= "'''" longstringitem* "'''" | '"""' longstringitem* '"""' +fragment LONG_STRING + : '\'\'\'' LONG_STRING_ITEM*? '\'\'\'' + | '"""' LONG_STRING_ITEM*? '"""' + ; + +/// longstringitem ::= longstringchar | stringescapeseq +fragment LONG_STRING_ITEM + : LONG_STRING_CHAR + | STRING_ESCAPE_SEQ + ; + +/// longstringchar ::= +fragment LONG_STRING_CHAR + : ~'\\' + ; + +/// stringescapeseq ::= "\" +fragment STRING_ESCAPE_SEQ + : '\\' . + | '\\' NEWLINE + ; + +/// nonzerodigit ::= "1"..."9" +fragment NON_ZERO_DIGIT + : [1-9] + ; + +/// digit ::= "0"..."9" +fragment DIGIT + : [0-9] + ; + +/// octdigit ::= "0"..."7" +fragment OCT_DIGIT + : [0-7] + ; + +/// hexdigit ::= digit | "a"..."f" | "A"..."F" +fragment HEX_DIGIT + : [0-9a-fA-F] + ; + +/// bindigit ::= "0" | "1" +fragment BIN_DIGIT + : [01] + ; + +/// pointfloat ::= [intpart] fraction | intpart "." +fragment POINT_FLOAT + : INT_PART? FRACTION + | INT_PART '.' + ; + +/// exponentfloat ::= (intpart | pointfloat) exponent +fragment EXPONENT_FLOAT + : ( INT_PART | POINT_FLOAT ) EXPONENT + ; + +/// intpart ::= digit+ +fragment INT_PART + : DIGIT+ + ; + +/// fraction ::= "." digit+ +fragment FRACTION + : '.' DIGIT+ + ; + +/// exponent ::= ("e" | "E") ["+" | "-"] digit+ +fragment EXPONENT + : [eE] [+-]? DIGIT+ + ; + +/// shortbytes ::= "'" shortbytesitem* "'" | '"' shortbytesitem* '"' +/// shortbytesitem ::= shortbyteschar | bytesescapeseq +fragment SHORT_BYTES + : '\'' ( SHORT_BYTES_CHAR_NO_SINGLE_QUOTE | BYTES_ESCAPE_SEQ )* '\'' + | '"' ( SHORT_BYTES_CHAR_NO_DOUBLE_QUOTE | BYTES_ESCAPE_SEQ )* '"' + ; + +/// longbytes ::= "'''" longbytesitem* "'''" | '"""' longbytesitem* '"""' +fragment LONG_BYTES + : '\'\'\'' LONG_BYTES_ITEM*? '\'\'\'' + | '"""' LONG_BYTES_ITEM*? '"""' + ; + +/// longbytesitem ::= longbyteschar | bytesescapeseq +fragment LONG_BYTES_ITEM + : LONG_BYTES_CHAR + | BYTES_ESCAPE_SEQ + ; + +/// shortbyteschar ::= +fragment SHORT_BYTES_CHAR_NO_SINGLE_QUOTE + : [\u0000-\u0009] + | [\u000B-\u000C] + | [\u000E-\u0026] + | [\u0028-\u005B] + | [\u005D-\u007F] + ; + +fragment SHORT_BYTES_CHAR_NO_DOUBLE_QUOTE + : [\u0000-\u0009] + | [\u000B-\u000C] + | [\u000E-\u0021] + | [\u0023-\u005B] + | [\u005D-\u007F] + ; + +/// longbyteschar ::= +fragment LONG_BYTES_CHAR + : [\u0000-\u005B] + | [\u005D-\u007F] + ; + +/// bytesescapeseq ::= "\" +fragment BYTES_ESCAPE_SEQ + : '\\' [\u0000-\u007F] + ; + +fragment SPACES + : [ \t]+ + ; + +fragment COMMENT + : '#' ~[\r\n\f]* + ; + +fragment LINE_JOINING + : '\\' SPACES? ( '\r'? '\n' | '\r' | '\f') + ; + +/// id_start ::= +fragment ID_START + : '_' + | [A-Z] + | [a-z] + | '\u00AA' + | '\u00B5' + | '\u00BA' + | [\u00C0-\u00D6] + | [\u00D8-\u00F6] + | [\u00F8-\u01BA] + | '\u01BB' + | [\u01BC-\u01BF] + | [\u01C0-\u01C3] + | [\u01C4-\u0241] + | [\u0250-\u02AF] + | [\u02B0-\u02C1] + | [\u02C6-\u02D1] + | [\u02E0-\u02E4] + | '\u02EE' + | '\u037A' + | '\u0386' + | [\u0388-\u038A] + | '\u038C' + | [\u038E-\u03A1] + | [\u03A3-\u03CE] + | [\u03D0-\u03F5] + | [\u03F7-\u0481] + | [\u048A-\u04CE] + | [\u04D0-\u04F9] + | [\u0500-\u050F] + | [\u0531-\u0556] + | '\u0559' + | [\u0561-\u0587] + | [\u05D0-\u05EA] + | [\u05F0-\u05F2] + | [\u0621-\u063A] + | '\u0640' + | [\u0641-\u064A] + | [\u066E-\u066F] + | [\u0671-\u06D3] + | '\u06D5' + | [\u06E5-\u06E6] + | [\u06EE-\u06EF] + | [\u06FA-\u06FC] + | '\u06FF' + | '\u0710' + | [\u0712-\u072F] + | [\u074D-\u076D] + | [\u0780-\u07A5] + | '\u07B1' + | [\u0904-\u0939] + | '\u093D' + | '\u0950' + | [\u0958-\u0961] + | '\u097D' + | [\u0985-\u098C] + | [\u098F-\u0990] + | [\u0993-\u09A8] + | [\u09AA-\u09B0] + | '\u09B2' + | [\u09B6-\u09B9] + | '\u09BD' + | '\u09CE' + | [\u09DC-\u09DD] + | [\u09DF-\u09E1] + | [\u09F0-\u09F1] + | [\u0A05-\u0A0A] + | [\u0A0F-\u0A10] + | [\u0A13-\u0A28] + | [\u0A2A-\u0A30] + | [\u0A32-\u0A33] + | [\u0A35-\u0A36] + | [\u0A38-\u0A39] + | [\u0A59-\u0A5C] + | '\u0A5E' + | [\u0A72-\u0A74] + | [\u0A85-\u0A8D] + | [\u0A8F-\u0A91] + | [\u0A93-\u0AA8] + | [\u0AAA-\u0AB0] + | [\u0AB2-\u0AB3] + | [\u0AB5-\u0AB9] + | '\u0ABD' + | '\u0AD0' + | [\u0AE0-\u0AE1] + | [\u0B05-\u0B0C] + | [\u0B0F-\u0B10] + | [\u0B13-\u0B28] + | [\u0B2A-\u0B30] + | [\u0B32-\u0B33] + | [\u0B35-\u0B39] + | '\u0B3D' + | [\u0B5C-\u0B5D] + | [\u0B5F-\u0B61] + | '\u0B71' + | '\u0B83' + | [\u0B85-\u0B8A] + | [\u0B8E-\u0B90] + | [\u0B92-\u0B95] + | [\u0B99-\u0B9A] + | '\u0B9C' + | [\u0B9E-\u0B9F] + | [\u0BA3-\u0BA4] + | [\u0BA8-\u0BAA] + | [\u0BAE-\u0BB9] + | [\u0C05-\u0C0C] + | [\u0C0E-\u0C10] + | [\u0C12-\u0C28] + | [\u0C2A-\u0C33] + | [\u0C35-\u0C39] + | [\u0C60-\u0C61] + | [\u0C85-\u0C8C] + | [\u0C8E-\u0C90] + | [\u0C92-\u0CA8] + | [\u0CAA-\u0CB3] + | [\u0CB5-\u0CB9] + | '\u0CBD' + | '\u0CDE' + | [\u0CE0-\u0CE1] + | [\u0D05-\u0D0C] + | [\u0D0E-\u0D10] + | [\u0D12-\u0D28] + | [\u0D2A-\u0D39] + | [\u0D60-\u0D61] + | [\u0D85-\u0D96] + | [\u0D9A-\u0DB1] + | [\u0DB3-\u0DBB] + | '\u0DBD' + | [\u0DC0-\u0DC6] + | [\u0E01-\u0E30] + | [\u0E32-\u0E33] + | [\u0E40-\u0E45] + | '\u0E46' + | [\u0E81-\u0E82] + | '\u0E84' + | [\u0E87-\u0E88] + | '\u0E8A' + | '\u0E8D' + | [\u0E94-\u0E97] + | [\u0E99-\u0E9F] + | [\u0EA1-\u0EA3] + | '\u0EA5' + | '\u0EA7' + | [\u0EAA-\u0EAB] + | [\u0EAD-\u0EB0] + | [\u0EB2-\u0EB3] + | '\u0EBD' + | [\u0EC0-\u0EC4] + | '\u0EC6' + | [\u0EDC-\u0EDD] + | '\u0F00' + | [\u0F40-\u0F47] + | [\u0F49-\u0F6A] + | [\u0F88-\u0F8B] + | [\u1000-\u1021] + | [\u1023-\u1027] + | [\u1029-\u102A] + | [\u1050-\u1055] + | [\u10A0-\u10C5] + | [\u10D0-\u10FA] + | '\u10FC' + | [\u1100-\u1159] + | [\u115F-\u11A2] + | [\u11A8-\u11F9] + | [\u1200-\u1248] + | [\u124A-\u124D] + | [\u1250-\u1256] + | '\u1258' + | [\u125A-\u125D] + | [\u1260-\u1288] + | [\u128A-\u128D] + | [\u1290-\u12B0] + | [\u12B2-\u12B5] + | [\u12B8-\u12BE] + | '\u12C0' + | [\u12C2-\u12C5] + | [\u12C8-\u12D6] + | [\u12D8-\u1310] + | [\u1312-\u1315] + | [\u1318-\u135A] + | [\u1380-\u138F] + | [\u13A0-\u13F4] + | [\u1401-\u166C] + | [\u166F-\u1676] + | [\u1681-\u169A] + | [\u16A0-\u16EA] + | [\u16EE-\u16F0] + | [\u1700-\u170C] + | [\u170E-\u1711] + | [\u1720-\u1731] + | [\u1740-\u1751] + | [\u1760-\u176C] + | [\u176E-\u1770] + | [\u1780-\u17B3] + | '\u17D7' + | '\u17DC' + | [\u1820-\u1842] + | '\u1843' + | [\u1844-\u1877] + | [\u1880-\u18A8] + | [\u1900-\u191C] + | [\u1950-\u196D] + | [\u1970-\u1974] + | [\u1980-\u19A9] + | [\u19C1-\u19C7] + | [\u1A00-\u1A16] + | [\u1D00-\u1D2B] + | [\u1D2C-\u1D61] + | [\u1D62-\u1D77] + | '\u1D78' + | [\u1D79-\u1D9A] + | [\u1D9B-\u1DBF] + | [\u1E00-\u1E9B] + | [\u1EA0-\u1EF9] + | [\u1F00-\u1F15] + | [\u1F18-\u1F1D] + | [\u1F20-\u1F45] + | [\u1F48-\u1F4D] + | [\u1F50-\u1F57] + | '\u1F59' + | '\u1F5B' + | '\u1F5D' + | [\u1F5F-\u1F7D] + | [\u1F80-\u1FB4] + | [\u1FB6-\u1FBC] + | '\u1FBE' + | [\u1FC2-\u1FC4] + | [\u1FC6-\u1FCC] + | [\u1FD0-\u1FD3] + | [\u1FD6-\u1FDB] + | [\u1FE0-\u1FEC] + | [\u1FF2-\u1FF4] + | [\u1FF6-\u1FFC] + | '\u2071' + | '\u207F' + | [\u2090-\u2094] + | '\u2102' + | '\u2107' + | [\u210A-\u2113] + | '\u2115' + | '\u2118' + | [\u2119-\u211D] + | '\u2124' + | '\u2126' + | '\u2128' + | [\u212A-\u212D] + | '\u212E' + | [\u212F-\u2131] + | [\u2133-\u2134] + | [\u2135-\u2138] + | '\u2139' + | [\u213C-\u213F] + | [\u2145-\u2149] + | [\u2160-\u2183] + | [\u2C00-\u2C2E] + | [\u2C30-\u2C5E] + | [\u2C80-\u2CE4] + | [\u2D00-\u2D25] + | [\u2D30-\u2D65] + | '\u2D6F' + | [\u2D80-\u2D96] + | [\u2DA0-\u2DA6] + | [\u2DA8-\u2DAE] + | [\u2DB0-\u2DB6] + | [\u2DB8-\u2DBE] + | [\u2DC0-\u2DC6] + | [\u2DC8-\u2DCE] + | [\u2DD0-\u2DD6] + | [\u2DD8-\u2DDE] + | '\u3005' + | '\u3006' + | '\u3007' + | [\u3021-\u3029] + | [\u3031-\u3035] + | [\u3038-\u303A] + | '\u303B' + | '\u303C' + | [\u3041-\u3096] + | [\u309B-\u309C] + | [\u309D-\u309E] + | '\u309F' + | [\u30A1-\u30FA] + | [\u30FC-\u30FE] + | '\u30FF' + | [\u3105-\u312C] + | [\u3131-\u318E] + | [\u31A0-\u31B7] + | [\u31F0-\u31FF] + | [\u3400-\u4DB5] + | [\u4E00-\u9FBB] + | [\uA000-\uA014] + | '\uA015' + | [\uA016-\uA48C] + | [\uA800-\uA801] + | [\uA803-\uA805] + | [\uA807-\uA80A] + | [\uA80C-\uA822] + | [\uAC00-\uD7A3] + | [\uF900-\uFA2D] + | [\uFA30-\uFA6A] + | [\uFA70-\uFAD9] + | [\uFB00-\uFB06] + | [\uFB13-\uFB17] + | '\uFB1D' + | [\uFB1F-\uFB28] + | [\uFB2A-\uFB36] + | [\uFB38-\uFB3C] + | '\uFB3E' + | [\uFB40-\uFB41] + | [\uFB43-\uFB44] + | [\uFB46-\uFBB1] + | [\uFBD3-\uFD3D] + | [\uFD50-\uFD8F] + | [\uFD92-\uFDC7] + | [\uFDF0-\uFDFB] + | [\uFE70-\uFE74] + | [\uFE76-\uFEFC] + | [\uFF21-\uFF3A] + | [\uFF41-\uFF5A] + | [\uFF66-\uFF6F] + | '\uFF70' + | [\uFF71-\uFF9D] + | [\uFF9E-\uFF9F] + | [\uFFA0-\uFFBE] + | [\uFFC2-\uFFC7] + | [\uFFCA-\uFFCF] + | [\uFFD2-\uFFD7] + | [\uFFDA-\uFFDC] + ; + +/// id_continue ::= +fragment ID_CONTINUE + : ID_START + | [0-9] + | [\u0300-\u036F] + | [\u0483-\u0486] + | [\u0591-\u05B9] + | [\u05BB-\u05BD] + | '\u05BF' + | [\u05C1-\u05C2] + | [\u05C4-\u05C5] + | '\u05C7' + | [\u0610-\u0615] + | [\u064B-\u065E] + | [\u0660-\u0669] + | '\u0670' + | [\u06D6-\u06DC] + | [\u06DF-\u06E4] + | [\u06E7-\u06E8] + | [\u06EA-\u06ED] + | [\u06F0-\u06F9] + | '\u0711' + | [\u0730-\u074A] + | [\u07A6-\u07B0] + | [\u0901-\u0902] + | '\u0903' + | '\u093C' + | [\u093E-\u0940] + | [\u0941-\u0948] + | [\u0949-\u094C] + | '\u094D' + | [\u0951-\u0954] + | [\u0962-\u0963] + | [\u0966-\u096F] + | '\u0981' + | [\u0982-\u0983] + | '\u09BC' + | [\u09BE-\u09C0] + | [\u09C1-\u09C4] + | [\u09C7-\u09C8] + | [\u09CB-\u09CC] + | '\u09CD' + | '\u09D7' + | [\u09E2-\u09E3] + | [\u09E6-\u09EF] + | [\u0A01-\u0A02] + | '\u0A03' + | '\u0A3C' + | [\u0A3E-\u0A40] + | [\u0A41-\u0A42] + | [\u0A47-\u0A48] + | [\u0A4B-\u0A4D] + | [\u0A66-\u0A6F] + | [\u0A70-\u0A71] + | [\u0A81-\u0A82] + | '\u0A83' + | '\u0ABC' + | [\u0ABE-\u0AC0] + | [\u0AC1-\u0AC5] + | [\u0AC7-\u0AC8] + | '\u0AC9' + | [\u0ACB-\u0ACC] + | '\u0ACD' + | [\u0AE2-\u0AE3] + | [\u0AE6-\u0AEF] + | '\u0B01' + | [\u0B02-\u0B03] + | '\u0B3C' + | '\u0B3E' + | '\u0B3F' + | '\u0B40' + | [\u0B41-\u0B43] + | [\u0B47-\u0B48] + | [\u0B4B-\u0B4C] + | '\u0B4D' + | '\u0B56' + | '\u0B57' + | [\u0B66-\u0B6F] + | '\u0B82' + | [\u0BBE-\u0BBF] + | '\u0BC0' + | [\u0BC1-\u0BC2] + | [\u0BC6-\u0BC8] + | [\u0BCA-\u0BCC] + | '\u0BCD' + | '\u0BD7' + | [\u0BE6-\u0BEF] + | [\u0C01-\u0C03] + | [\u0C3E-\u0C40] + | [\u0C41-\u0C44] + | [\u0C46-\u0C48] + | [\u0C4A-\u0C4D] + | [\u0C55-\u0C56] + | [\u0C66-\u0C6F] + | [\u0C82-\u0C83] + | '\u0CBC' + | '\u0CBE' + | '\u0CBF' + | [\u0CC0-\u0CC4] + | '\u0CC6' + | [\u0CC7-\u0CC8] + | [\u0CCA-\u0CCB] + | [\u0CCC-\u0CCD] + | [\u0CD5-\u0CD6] + | [\u0CE6-\u0CEF] + | [\u0D02-\u0D03] + | [\u0D3E-\u0D40] + | [\u0D41-\u0D43] + | [\u0D46-\u0D48] + | [\u0D4A-\u0D4C] + | '\u0D4D' + | '\u0D57' + | [\u0D66-\u0D6F] + | [\u0D82-\u0D83] + | '\u0DCA' + | [\u0DCF-\u0DD1] + | [\u0DD2-\u0DD4] + | '\u0DD6' + | [\u0DD8-\u0DDF] + | [\u0DF2-\u0DF3] + | '\u0E31' + | [\u0E34-\u0E3A] + | [\u0E47-\u0E4E] + | [\u0E50-\u0E59] + | '\u0EB1' + | [\u0EB4-\u0EB9] + | [\u0EBB-\u0EBC] + | [\u0EC8-\u0ECD] + | [\u0ED0-\u0ED9] + | [\u0F18-\u0F19] + | [\u0F20-\u0F29] + | '\u0F35' + | '\u0F37' + | '\u0F39' + | [\u0F3E-\u0F3F] + | [\u0F71-\u0F7E] + | '\u0F7F' + | [\u0F80-\u0F84] + | [\u0F86-\u0F87] + | [\u0F90-\u0F97] + | [\u0F99-\u0FBC] + | '\u0FC6' + | '\u102C' + | [\u102D-\u1030] + | '\u1031' + | '\u1032' + | [\u1036-\u1037] + | '\u1038' + | '\u1039' + | [\u1040-\u1049] + | [\u1056-\u1057] + | [\u1058-\u1059] + | '\u135F' + | [\u1369-\u1371] + | [\u1712-\u1714] + | [\u1732-\u1734] + | [\u1752-\u1753] + | [\u1772-\u1773] + | '\u17B6' + | [\u17B7-\u17BD] + | [\u17BE-\u17C5] + | '\u17C6' + | [\u17C7-\u17C8] + | [\u17C9-\u17D3] + | '\u17DD' + | [\u17E0-\u17E9] + | [\u180B-\u180D] + | [\u1810-\u1819] + | '\u18A9' + | [\u1920-\u1922] + | [\u1923-\u1926] + | [\u1927-\u1928] + | [\u1929-\u192B] + | [\u1930-\u1931] + | '\u1932' + | [\u1933-\u1938] + | [\u1939-\u193B] + | [\u1946-\u194F] + | [\u19B0-\u19C0] + | [\u19C8-\u19C9] + | [\u19D0-\u19D9] + | [\u1A17-\u1A18] + | [\u1A19-\u1A1B] + | [\u1DC0-\u1DC3] + | [\u203F-\u2040] + | '\u2054' + | [\u20D0-\u20DC] + | '\u20E1' + | [\u20E5-\u20EB] + | [\u302A-\u302F] + | [\u3099-\u309A] + | '\uA802' + | '\uA806' + | '\uA80B' + | [\uA823-\uA824] + | [\uA825-\uA826] + | '\uA827' + | '\uFB1E' + | [\uFE00-\uFE0F] + | [\uFE20-\uFE23] + | [\uFE33-\uFE34] + | [\uFE4D-\uFE4F] + | [\uFF10-\uFF19] + | '\uFF3F' + ; diff --git a/server/pkg/antlr/python3/include/PythonAntlr.h b/server/pkg/antlr/python3/include/PythonAntlr.h new file mode 100644 index 0000000000000000000000000000000000000000..aee2aa3dd36b3becf9e8069dcc9d335a80db19d1 --- /dev/null +++ b/server/pkg/antlr/python3/include/PythonAntlr.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +#include "IAntlrWrapper.h" +#include "Python3Lexer.h" +#include "Python3Parser.h" +#include "antlr4-runtime.h" + +class PythonAntlr : public IAntlrWrapper { + private: + std::unique_ptr lexer_ptr; + std::unique_ptr parser_ptr; + std::unique_ptr input_ptr; + std::unique_ptr tokenStream_ptr; + + public: + PythonAntlr(std::istream &in); + ~PythonAntlr() override = default; + std::vector getTokens() override; + std::vector getTokensTypes() override; + std::pair getTokensAndTree() override; + std::string getTokensString() override; + std::string getTreeString() override; +}; diff --git a/server/pkg/antlr/python3/src/PythonAntlr.cpp b/server/pkg/antlr/python3/src/PythonAntlr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..013966ff854f0149072303182d3fcbb00145d261 --- /dev/null +++ b/server/pkg/antlr/python3/src/PythonAntlr.cpp @@ -0,0 +1,62 @@ +#include "PythonAntlr.h" + +#include + +PythonAntlr::PythonAntlr(std::istream& in) { + input_ptr = std::make_unique(in); + lexer_ptr = std::make_unique(&(*input_ptr)); + tokenStream_ptr = std::make_unique(&(*lexer_ptr)); + parser_ptr = std::make_unique(&(*tokenStream_ptr)); +} + +std::vector PythonAntlr::getTokens() { + tokenStream_ptr->fill(); + std::vector ans(tokenStream_ptr->size()); + + int i = 0; + for (antlr4::Token* token : tokenStream_ptr->getTokens()) { + ans[i] = token; + i++; + } + + return ans; +} + +std::vector PythonAntlr::getTokensTypes() { + tokenStream_ptr->fill(); + std::vector ans(tokenStream_ptr->size()); + + int i = 0; + for (antlr4::Token* token : tokenStream_ptr->getTokens()) { + if (token->getText() == "") { + ans[i] = -1; + } else { + ans[i] = token->getType(); + } + i++; + } + + return ans; +} + +std::string PythonAntlr::getTokensString() { + tokenStream_ptr->fill(); + std::string res; + + for (antlr4::Token* token : tokenStream_ptr->getTokens()) { + res += token->toString() + " "; + } + + return res; +} + +std::string PythonAntlr::getTreeString() { + auto tree = parser_ptr->file_input(); + return tree->toStringTree(&(*parser_ptr)); +} + +std::pair PythonAntlr::getTokensAndTree() { + std::string tokens = getTokensString(); + std::string astTree = getTreeString(); + return std::make_pair(tokens, astTree); +} diff --git a/server/pkg/antlr/testprogs/cpp/test.cpp b/server/pkg/antlr/testprogs/cpp/test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d31b35438aa3641dce6ebb3a72dbcf95646a1f7 --- /dev/null +++ b/server/pkg/antlr/testprogs/cpp/test.cpp @@ -0,0 +1,10 @@ +#include + +int main() { + std::string res; + int a; + if (true && true) { + std::cout << "Hello World!"; + } + return 0; +} diff --git a/server/pkg/antlr/testprogs/python/test.py b/server/pkg/antlr/testprogs/python/test.py new file mode 100644 index 0000000000000000000000000000000000000000..b385b2a02f584f8a8e8a29fe0bb0842c6c32dfff --- /dev/null +++ b/server/pkg/antlr/testprogs/python/test.py @@ -0,0 +1,7 @@ +var_1 = 1567 +reverse = 0 +while var_1 > 0: + rest = var_1 % 10 + reverse = reverse * 10 + rest + var_1 = var_1 // 10 +print("Число в обратном порядке:", reverse) \ No newline at end of file diff --git a/server/pkg/antlr/virtual/IAntlrWrapper.h b/server/pkg/antlr/virtual/IAntlrWrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..824119e67d6d1ea283b23f9556308abd30eac966 --- /dev/null +++ b/server/pkg/antlr/virtual/IAntlrWrapper.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#include "antlr4-runtime.h" + +class IAntlrWrapper { + public: + virtual ~IAntlrWrapper() = default; + virtual std::vector getTokens() = 0; + virtual std::vector getTokensTypes() = 0; + virtual std::pair getTokensAndTree() = 0; + virtual std::string getTokensString() = 0; + virtual std::string getTreeString() = 0; +}; diff --git a/sql/database.sql b/sql/database.sql new file mode 100644 index 0000000000000000000000000000000000000000..5266ba29fb1382f95517cabc1e311fdf56803729 --- /dev/null +++ b/sql/database.sql @@ -0,0 +1,678 @@ +-- +-- PostgreSQL database cluster dump +-- + +-- Started on 2023-05-15 22:49:08 + +SET default_transaction_read_only = off; + +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; + +-- +-- Roles +-- + +CREATE ROLE postgres; +ALTER ROLE postgres WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN REPLICATION BYPASSRLS PASSWORD 'SCRAM-SHA-256$4096:nOmjHrNPdaIcnCeed7FgbQ==$U+pukPiYK1tBvbNsPjn4d9zV4IXLekf/OMW5+ZDzgrY=:1f9vBcPgoAcX5hmUUOkjkXLp0FRCqsnlQRmYs2U29pU='; + +-- +-- User Configurations +-- + + + + + + + + +-- +-- Databases +-- + +-- +-- Database "template1" dump +-- + +\connect template1 + +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 15.2 +-- Dumped by pg_dump version 15.2 + +-- Started on 2023-05-15 22:49:08 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- Completed on 2023-05-15 22:49:08 + +-- +-- PostgreSQL database dump complete +-- + +-- +-- Database "mydb" dump +-- + +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 15.2 +-- Dumped by pg_dump version 15.2 + +-- Started on 2023-05-15 22:49:08 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- TOC entry 3362 (class 1262 OID 16565) +-- Name: mydb; Type: DATABASE; Schema: -; Owner: postgres +-- + +CREATE DATABASE mydb WITH TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'Russian_Russia.1251'; + + +ALTER DATABASE mydb OWNER TO postgres; + +\connect mydb + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- TOC entry 215 (class 1259 OID 16574) +-- Name: users; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.users ( + id integer NOT NULL, + login character varying(255), + password character varying(255), + username character varying(255) +); + + +ALTER TABLE public.users OWNER TO postgres; + +-- +-- TOC entry 214 (class 1259 OID 16573) +-- Name: Users_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public."Users_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public."Users_id_seq" OWNER TO postgres; + +-- +-- TOC entry 3363 (class 0 OID 0) +-- Dependencies: 214 +-- Name: Users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public."Users_id_seq" OWNED BY public.users.id; + + +-- +-- TOC entry 219 (class 1259 OID 16613) +-- Name: metricstat; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.metricstat ( + id integer NOT NULL, + solution_id integer, + text_based_res real, + token_based_res real, + tree_based_res real, + verdict boolean, + mean_res real +); + + +ALTER TABLE public.metricstat OWNER TO postgres; + +-- +-- TOC entry 221 (class 1259 OID 16626) +-- Name: metricstat_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.metricstat_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + MAXVALUE 2147483647 + CACHE 1; + + +ALTER TABLE public.metricstat_id_seq OWNER TO postgres; + +-- +-- TOC entry 3364 (class 0 OID 0) +-- Dependencies: 221 +-- Name: metricstat_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.metricstat_id_seq OWNED BY public.metricstat.id; + + +-- +-- TOC entry 217 (class 1259 OID 16583) +-- Name: solutions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.solutions ( + id integer NOT NULL, + send_date date NOT NULL, + sender_id integer, + source character varying(255), + task_id integer, + result character varying, + tokens character varying, + asttree character varying, + original_solution_id integer +); + + +ALTER TABLE public.solutions OWNER TO postgres; + +-- +-- TOC entry 216 (class 1259 OID 16582) +-- Name: solutions_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.solutions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.solutions_id_seq OWNER TO postgres; + +-- +-- TOC entry 3365 (class 0 OID 0) +-- Dependencies: 216 +-- Name: solutions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.solutions_id_seq OWNED BY public.solutions.id; + + +-- +-- TOC entry 218 (class 1259 OID 16598) +-- Name: tasks; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.tasks ( + id integer NOT NULL, + description text, + treshold real +); + + +ALTER TABLE public.tasks OWNER TO postgres; + +-- +-- TOC entry 220 (class 1259 OID 16623) +-- Name: tasks_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.tasks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + MAXVALUE 2147483647 + CACHE 1; + + +ALTER TABLE public.tasks_id_seq OWNER TO postgres; + +-- +-- TOC entry 3366 (class 0 OID 0) +-- Dependencies: 220 +-- Name: tasks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.tasks_id_seq OWNED BY public.tasks.id; + + +-- +-- TOC entry 3191 (class 2604 OID 16628) +-- Name: metricstat id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metricstat ALTER COLUMN id SET DEFAULT nextval('public.metricstat_id_seq'::regclass); + + +-- +-- TOC entry 3189 (class 2604 OID 16586) +-- Name: solutions id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solutions ALTER COLUMN id SET DEFAULT nextval('public.solutions_id_seq'::regclass); + + +-- +-- TOC entry 3190 (class 2604 OID 16625) +-- Name: tasks id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.tasks ALTER COLUMN id SET DEFAULT nextval('public.tasks_id_seq'::regclass); + + +-- +-- TOC entry 3188 (class 2604 OID 16577) +-- Name: users id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.users ALTER COLUMN id SET DEFAULT nextval('public."Users_id_seq"'::regclass); + + +-- +-- TOC entry 3354 (class 0 OID 16613) +-- Dependencies: 219 +-- Data for Name: metricstat; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +COPY public.metricstat (id, solution_id, text_based_res, token_based_res, tree_based_res, verdict, mean_res) FROM stdin; +15 1 1 1 1 t 1 +\. + + +-- +-- TOC entry 3352 (class 0 OID 16583) +-- Dependencies: 217 +-- Data for Name: solutions; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +COPY public.solutions (id, send_date, sender_id, source, task_id, result, tokens, asttree, original_solution_id) FROM stdin; +1 2023-05-02 10 0.1 27 0.1 0.1 0.1 1 +\. + + +-- +-- TOC entry 3353 (class 0 OID 16598) +-- Dependencies: 218 +-- Data for Name: tasks; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +COPY public.tasks (id, description, treshold) FROM stdin; +27 orher_description 0.1 +1 description 0.5 +\. + + +-- +-- TOC entry 3350 (class 0 OID 16574) +-- Dependencies: 215 +-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +COPY public.users (id, login, password, username) FROM stdin; +1 qwerty200468@gmail.com 123 tolik +2 qwerty200468@gmail.com 123 tolik +3 qwerty200468@gmail.com 123 tolik +4 qwerty200468@gmail.com 123 tolik +5 qwerty200468@gmail.com 123 tolik +6 qwerty200468@gmail.com 123 tolik +7 qwerty200468@gmail.com 123 tolik +8 qwerty200468@gmail.com 123 tolik +9 qwerty200468@gmail.com 123 tolik +10 qwerty200468@gmail.com 123 tolik +52 qwerty200468@gmail.com 123 tolik +\. + + +-- +-- TOC entry 3367 (class 0 OID 0) +-- Dependencies: 214 +-- Name: Users_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public."Users_id_seq"', 78, true); + + +-- +-- TOC entry 3368 (class 0 OID 0) +-- Dependencies: 221 +-- Name: metricstat_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public.metricstat_id_seq', 36, true); + + +-- +-- TOC entry 3369 (class 0 OID 0) +-- Dependencies: 216 +-- Name: solutions_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public.solutions_id_seq', 150, true); + + +-- +-- TOC entry 3370 (class 0 OID 0) +-- Dependencies: 220 +-- Name: tasks_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public.tasks_id_seq', 34, true); + + +-- +-- TOC entry 3193 (class 2606 OID 16581) +-- Name: users Users_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT "Users_pkey" PRIMARY KEY (id); + + +-- +-- TOC entry 3202 (class 2606 OID 16617) +-- Name: metricstat metricStat_pk; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metricstat + ADD CONSTRAINT "metricStat_pk" PRIMARY KEY (id); + + +-- +-- TOC entry 3198 (class 2606 OID 16588) +-- Name: solutions solutions_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solutions + ADD CONSTRAINT solutions_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3200 (class 2606 OID 16604) +-- Name: tasks tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.tasks + ADD CONSTRAINT tasks_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3194 (class 1259 OID 16637) +-- Name: fki_original_solution_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX fki_original_solution_id ON public.solutions USING btree (original_solution_id); + + +-- +-- TOC entry 3195 (class 1259 OID 16594) +-- Name: fki_sender_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX fki_sender_id ON public.solutions USING btree (sender_id); + + +-- +-- TOC entry 3196 (class 1259 OID 16610) +-- Name: fki_task_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX fki_task_id ON public.solutions USING btree (task_id); + + +-- +-- TOC entry 3203 (class 2606 OID 16632) +-- Name: solutions original_solution_id; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solutions + ADD CONSTRAINT original_solution_id FOREIGN KEY (original_solution_id) REFERENCES public.solutions(id) ON UPDATE CASCADE ON DELETE CASCADE NOT VALID; + + +-- +-- TOC entry 3204 (class 2606 OID 16589) +-- Name: solutions sender_id; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solutions + ADD CONSTRAINT sender_id FOREIGN KEY (sender_id) REFERENCES public.users(id) ON UPDATE RESTRICT ON DELETE CASCADE NOT VALID; + + +-- +-- TOC entry 3206 (class 2606 OID 16618) +-- Name: metricstat solutions_id; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metricstat + ADD CONSTRAINT solutions_id FOREIGN KEY (solution_id) REFERENCES public.solutions(id); + + +-- +-- TOC entry 3205 (class 2606 OID 16605) +-- Name: solutions task_id; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solutions + ADD CONSTRAINT task_id FOREIGN KEY (task_id) REFERENCES public.tasks(id) NOT VALID; + + +-- Completed on 2023-05-15 22:49:09 + +-- +-- PostgreSQL database dump complete +-- + +-- +-- Database "postgres" dump +-- + +\connect postgres + +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 15.2 +-- Dumped by pg_dump version 15.2 + +-- Started on 2023-05-15 22:49:09 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- TOC entry 8 (class 2615 OID 16398) +-- Name: pgagent; Type: SCHEMA; Schema: -; Owner: postgres +-- + +CREATE SCHEMA pgagent; + + +ALTER SCHEMA pgagent OWNER TO postgres; + +-- +-- TOC entry 3437 (class 0 OID 0) +-- Dependencies: 8 +-- Name: SCHEMA pgagent; Type: COMMENT; Schema: -; Owner: postgres +-- + +COMMENT ON SCHEMA pgagent IS 'pgAgent system tables'; + + +-- +-- TOC entry 2 (class 3079 OID 16384) +-- Name: adminpack; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS adminpack WITH SCHEMA pg_catalog; + + +-- +-- TOC entry 3438 (class 0 OID 0) +-- Dependencies: 2 +-- Name: EXTENSION adminpack; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION adminpack IS 'administrative functions for PostgreSQL'; + + +-- +-- TOC entry 3 (class 3079 OID 16399) +-- Name: pgagent; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pgagent WITH SCHEMA pgagent; + + +-- +-- TOC entry 3439 (class 0 OID 0) +-- Dependencies: 3 +-- Name: EXTENSION pgagent; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION pgagent IS 'A PostgreSQL job scheduler'; + + +-- +-- TOC entry 3218 (class 0 OID 16400) +-- Dependencies: 219 +-- Data for Name: pga_jobagent; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_jobagent (jagpid, jaglogintime, jagstation) FROM stdin; +7380 2023-05-09 17:32:50.44529+03 DESKTOP-CLI5MDC +\. + + +-- +-- TOC entry 3219 (class 0 OID 16409) +-- Dependencies: 221 +-- Data for Name: pga_jobclass; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_jobclass (jclid, jclname) FROM stdin; +\. + + +-- +-- TOC entry 3220 (class 0 OID 16419) +-- Dependencies: 223 +-- Data for Name: pga_job; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_job (jobid, jobjclid, jobname, jobdesc, jobhostagent, jobenabled, jobcreated, jobchanged, jobagentid, jobnextrun, joblastrun) FROM stdin; +\. + + +-- +-- TOC entry 3222 (class 0 OID 16467) +-- Dependencies: 227 +-- Data for Name: pga_schedule; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_schedule (jscid, jscjobid, jscname, jscdesc, jscenabled, jscstart, jscend, jscminutes, jschours, jscweekdays, jscmonthdays, jscmonths) FROM stdin; +\. + + +-- +-- TOC entry 3223 (class 0 OID 16495) +-- Dependencies: 229 +-- Data for Name: pga_exception; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_exception (jexid, jexscid, jexdate, jextime) FROM stdin; +\. + + +-- +-- TOC entry 3224 (class 0 OID 16509) +-- Dependencies: 231 +-- Data for Name: pga_joblog; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_joblog (jlgid, jlgjobid, jlgstatus, jlgstart, jlgduration) FROM stdin; +\. + + +-- +-- TOC entry 3221 (class 0 OID 16443) +-- Dependencies: 225 +-- Data for Name: pga_jobstep; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_jobstep (jstid, jstjobid, jstname, jstdesc, jstenabled, jstkind, jstcode, jstconnstr, jstdbname, jstonerror, jscnextrun) FROM stdin; +\. + + +-- +-- TOC entry 3225 (class 0 OID 16525) +-- Dependencies: 233 +-- Data for Name: pga_jobsteplog; Type: TABLE DATA; Schema: pgagent; Owner: postgres +-- + +COPY pgagent.pga_jobsteplog (jslid, jsljlgid, jsljstid, jslstatus, jslresult, jslstart, jslduration, jsloutput) FROM stdin; +\. + + +-- Completed on 2023-05-15 22:49:09 + +-- +-- PostgreSQL database dump complete +-- + +-- Completed on 2023-05-15 22:49:09 + +-- +-- PostgreSQL database cluster dump complete +-- + diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index cbd2bd5cf24d419d22f50b5d29560a8591df4ecf..0000000000000000000000000000000000000000 --- a/src/main.cpp +++ /dev/null @@ -1,264 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace beast = boost::beast; // from -namespace http = beast::http; // from -namespace net = boost::asio; // from -using tcp = boost::asio::ip::tcp; // from - -//------------------------------------------------------------------------------ - -// Return a reasonable mime type based on the extension of a file. -beast::string_view mime_type(beast::string_view path) { - using beast::iequals; - auto const ext = [&path] { - auto const pos = path.rfind("."); - if (pos == beast::string_view::npos) return beast::string_view{}; - return path.substr(pos); - }(); - if (iequals(ext, ".htm")) return "text/html"; - if (iequals(ext, ".html")) return "text/html"; - if (iequals(ext, ".php")) return "text/html"; - if (iequals(ext, ".css")) return "text/css"; - if (iequals(ext, ".txt")) return "text/plain"; - if (iequals(ext, ".js")) return "application/javascript"; - if (iequals(ext, ".json")) return "application/json"; - if (iequals(ext, ".xml")) return "application/xml"; - if (iequals(ext, ".swf")) return "application/x-shockwave-flash"; - if (iequals(ext, ".flv")) return "video/x-flv"; - if (iequals(ext, ".png")) return "image/png"; - if (iequals(ext, ".jpe")) return "image/jpeg"; - if (iequals(ext, ".jpeg")) return "image/jpeg"; - if (iequals(ext, ".jpg")) return "image/jpeg"; - if (iequals(ext, ".gif")) return "image/gif"; - if (iequals(ext, ".bmp")) return "image/bmp"; - if (iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; - if (iequals(ext, ".tiff")) return "image/tiff"; - if (iequals(ext, ".tif")) return "image/tiff"; - if (iequals(ext, ".svg")) return "image/svg+xml"; - if (iequals(ext, ".svgz")) return "image/svg+xml"; - return "application/text"; -} - -// Append an HTTP rel-path to a local filesystem path. -// The returned path is normalized for the platform. -std::string path_cat(beast::string_view base, beast::string_view path) { - if (base.empty()) return std::string(path); - std::string result(base); -#ifdef BOOST_MSVC - char constexpr path_separator = '\\'; - if (result.back() == path_separator) result.resize(result.size() - 1); - result.append(path.data(), path.size()); - for (auto& c : result) - if (c == '/') c = path_separator; -#else - char constexpr path_separator = '/'; - if (result.back() == path_separator) result.resize(result.size() - 1); - result.append(path.data(), path.size()); -#endif - return result; -} - -// This function produces an HTTP response for the given -// request. The type of the response object depends on the -// contents of the request, so the interface requires the -// caller to pass a generic lambda for receiving the response. -template -void handle_request(beast::string_view doc_root, - http::request>&& req, - Send&& send) { - // Returns a bad request response - auto const bad_request = [&req](beast::string_view why) { - http::response res{http::status::bad_request, - req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = std::string(why); - res.prepare_payload(); - return res; - }; - - // Returns a not found response - auto const not_found = [&req](beast::string_view target) { - http::response res{http::status::not_found, - req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = "The resource '" + std::string(target) + "' was not found."; - res.prepare_payload(); - return res; - }; - - // Returns a server error response - auto const server_error = [&req](beast::string_view what) { - http::response res{http::status::internal_server_error, - req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = "An error occurred: '" + std::string(what) + "'"; - res.prepare_payload(); - return res; - }; - - // Make sure we can handle the method - if (req.method() != http::verb::get && req.method() != http::verb::head) - return send(bad_request("Unknown HTTP-method")); - - // Request path must be absolute and not contain "..". - if (req.target().empty() || req.target()[0] != '/' || - req.target().find("..") != beast::string_view::npos) - return send(bad_request("Illegal request-target")); - - // Build the path to the requested file - std::string path = path_cat(doc_root, req.target()); - if (req.target().back() == '/') path.append("index.html"); - - // Attempt to open the file - beast::error_code ec; - http::file_body::value_type body; - body.open(path.c_str(), beast::file_mode::scan, ec); - - // Handle the case where the file doesn't exist - if (ec == beast::errc::no_such_file_or_directory) - return send(not_found(req.target())); - - // Handle an unknown error - if (ec) return send(server_error(ec.message())); - - // Cache the size since we need it after the move - auto const size = body.size(); - - // Respond to HEAD request - if (req.method() == http::verb::head) { - http::response res{http::status::ok, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, mime_type(path)); - res.content_length(size); - res.keep_alive(req.keep_alive()); - return send(std::move(res)); - } - - // Respond to GET request - http::response res{ - std::piecewise_construct, std::make_tuple(std::move(body)), - std::make_tuple(http::status::ok, req.version())}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, mime_type(path)); - res.content_length(size); - res.keep_alive(req.keep_alive()); - return send(std::move(res)); -} - -//------------------------------------------------------------------------------ - -// Report a failure -void fail(beast::error_code ec, char const* what) { - std::cerr << what << ": " << ec.message() << "\n"; -} - -// This is the C++11 equivalent of a generic lambda. -// The function object is used to send an HTTP message. -template -struct send_lambda { - Stream& stream_; - bool& close_; - beast::error_code& ec_; - - explicit send_lambda(Stream& stream, bool& close, beast::error_code& ec) - : stream_(stream), close_(close), ec_(ec) {} - - template - void operator()(http::message&& msg) const { - // Determine if we should close the connection after - close_ = msg.need_eof(); - - // We need the serializer here because the serializer requires - // a non-const file_body, and the message oriented version of - // http::write only works with const messages. - http::serializer sr{msg}; - http::write(stream_, sr, ec_); - } -}; - -// Handles an HTTP server connection -void do_session(tcp::socket& socket, - std::shared_ptr const& doc_root) { - bool close = false; - beast::error_code ec; - - // This buffer is required to persist across reads - beast::flat_buffer buffer; - - // This lambda is used to send messages - send_lambda lambda{socket, close, ec}; - - for (;;) { - // Read a request - http::request req; - http::read(socket, buffer, req, ec); - if (ec == http::error::end_of_stream) break; - if (ec) return fail(ec, "read"); - - // Send the response - handle_request(*doc_root, std::move(req), lambda); - if (ec) return fail(ec, "write"); - if (close) { - // This means we should close the connection, usually because - // the response indicated the "Connection: close" semantic. - break; - } - } - - // Send a TCP shutdown - socket.shutdown(tcp::socket::shutdown_send, ec); - - // At this point the connection is closed gracefully -} - -//------------------------------------------------------------------------------ - -int main(int argc, char* argv[]) { - try { - // Check command line arguments. - if (argc != 4) { - std::cerr << "Usage: http-server-sync
\n" - << "Example:\n" - << " http-server-sync 0.0.0.0 8080 .\n"; - return EXIT_FAILURE; - } - auto const address = net::ip::make_address(argv[1]); - auto const port = static_cast(std::atoi(argv[2])); - auto const doc_root = std::make_shared(argv[3]); - - // The io_context is required for all I/O - net::io_context ioc{1}; - - // The acceptor receives incoming connections - tcp::acceptor acceptor{ioc, {address, port}}; - for (;;) { - // This will receive the new connection - tcp::socket socket{ioc}; - - // Block until we get a connection - acceptor.accept(socket); - - // Launch the session, transferring ownership of the socket - std::thread{std::bind(&do_session, std::move(socket), doc_root)}.detach(); - } - } catch (const std::exception& e) { - std::cerr << "Error: " << e.what() << std::endl; - return EXIT_FAILURE; - } -} diff --git a/text-basic-metrics/tbm_main.cpp b/text-basic-metrics/tbm_main.cpp deleted file mode 100644 index fdd4253768b6369af5fe434526e26cbb9fc30bcd..0000000000000000000000000000000000000000 --- a/text-basic-metrics/tbm_main.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// -// Created by march on 21.04.2023. -// - -#include -#include -#include -#include -#include -#include -#include - -#include - - -std::string deleteComms(const std::string& text){ - std::string modif; - std::string res; - - std::stringstream ss; - std::string line; - - ss << text; - - while(getline(ss, line)){ - line.pop_back(); - line.push_back('\0'); - modif += line; - } - - bool s_comm = false; - bool m_comm = false; - - for (int i = 0; i < modif.size(); i++){ - if (s_comm && modif[i] == '\0') - s_comm = false; - else if (m_comm && modif[i] == '*' && modif[i + 1] == '/') - m_comm = false, i++; - else if (s_comm || m_comm) - continue; - else if (modif[i] == '/' && modif[i+1] == '/') - s_comm = true, i++; - else if (modif[i] == '/' && modif[i+1] == '*') - m_comm = true, i++; - - else if (modif[i] != '\0') - res += modif[i]; - else{ - res += '\n'; - } - } - return res; -} - -std::vector tbm_tokenizer(const std::string &text){ - boost::char_separator sep(" {}();,\"\0\'"); - std::vector res; - boost::tokenizer < boost::char_separator > tokens(text, sep); - - for (const std::string &s: tokens) { - if (!s.empty() && s[0] != '\n' && s[0] != '\0'){ - res.push_back(s); - } - } - return res; -} - -// % = intersection(A, B) / union(A, B) -double Jaccard_metric(const std::vector & tokens1, const std::vector & tokens2){ - std::set s1; - std::set s2; - - for (auto &i : tokens1) s1.insert(i); - for (auto &i : tokens2) s2.insert(i); - - - std::set intersect_sets; - set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), - std::inserter(intersect_sets, intersect_sets.begin())); - - std::set union_sets; - set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), - std::inserter(union_sets, union_sets.begin())); - - std::cout << intersect_sets.size() << " " << union_sets.size() << std::endl; - - return static_cast (intersect_sets.size()) / static_cast (union_sets.size()); -} - -double Livenstain_dist(std::vector tokens1, std::vector tokens2){ - unsigned long n = tokens1.size(); - unsigned long m = tokens2.size(); - int x, y, z; - - std::vector > lev (n, std::vector (m, 0)); - - for (int i = 0; i < n; i++){ - for (int j = 0; j < m; j++){ - if (std::min(i, j) == 0){ - lev[i][j] = std::max(i, j); - } - else{ - x = lev[i-1][j]; - y = lev[i][j-1]; - z = lev[i-1][j-1]; - lev[i][j] = std::min(x, std::min(y, z)); - if (tokens1[i] != tokens2[j]){ - lev[i][j]++; - } - } - } - } - - return lev[n-1][m-1]; -} - -std::pair textCompare(std::istream& fin1, std::istream& fin2){ - std::string line; - - std::string text1( (std::istreambuf_iterator(fin1) ), - (std::istreambuf_iterator() ) ); - - std::string text2( (std::istreambuf_iterator(fin2) ), - (std::istreambuf_iterator() ) ); - - std::string non_comm_text1 = deleteComms(text1); - std::string non_comm_text2 = deleteComms(text2); - - std::vector tokens1 = tbm_tokenizer(non_comm_text1); - std::vector tokens2 = tbm_tokenizer(non_comm_text2); - - double res1 = Jaccard_metric(tokens1, tokens2); - double res2 = 1 - Livenstain_dist(tokens1, tokens2) / std::max(tokens1.size(), tokens2.size()); - - return {res1, res2}; -} - -int main(){ - - std::ifstream fin1; - fin1.open("text-basic-metrics/code1.txt"); - assert(fin1.is_open()); - - std::ifstream fin2; - fin2.open("text-basic-metrics/code2.txt"); - assert(fin2.is_open()); - - std::pair metrics_res = textCompare(fin1, fin2); - - std::cout << "Jaccard metric "<< metrics_res.first << "\nLivenstein distance: " << metrics_res.second; - fin1.close(); - fin2.close(); -} \ No newline at end of file