From 7ba279d29608095b0fa19638e2cc498f783ffe90 Mon Sep 17 00:00:00 2001 From: svyat935 Date: Sat, 10 Jul 2021 21:54:46 +0300 Subject: [PATCH] release-8.1.0 --- .../nginx/conf.d/reverse_80.nginx.template | 85 --- .../input-predictions-element.scss | 56 -- .../input-predictions-element.tsx | 80 --- .../input-training-element.scss | 160 ----- .../input-training-element.tsx | 188 ------ .../fields/settings_filter/setings_filter.tsx | 343 ---------- .../settings_filter/settings_filter.scss | 117 ---- .../settings_predictions.scss | 63 -- .../settings_predictions.tsx | 177 ----- .../settings_training/settings_training.scss | 204 ------ .../settings_training/settings_training.tsx | 559 ---------------- .../fields/settings_training/store/actions.ts | 26 - .../fields/settings_training/store/reducer.ts | 53 -- .../fields/settings_training/store/thunks.ts | 65 -- .../fields/settings_training/store/types.ts | 71 -- .../analysis_and_training/main/training.py | 604 ------------------ nostradamus/apps/authentication/apps.py | 5 - .../apps/authentication/main/__init__.py | 0 .../apps/authentication/main/common.py | 26 - .../apps/authentication/main/token_creator.py | 79 --- .../authentication/migrations/0001_initial.py | 203 ------ .../authentication/migrations/__init__.py | 0 .../apps/authentication/serializers.py | 103 --- nostradamus/apps/authentication/urls.py | 8 - nostradamus/apps/authentication/validators.py | 69 -- nostradamus/apps/authentication/views.py | 60 -- .../apps/extractor/migrations/__init__.py | 0 .../apps/qa_metrics/migrations/__init__.py | 0 .../apps/settings/migrations/0002_auto.py | 25 - .../settings/migrations/0003_new_tables.py | 150 ----- .../analysis_and_training_tests/test_train.py | 89 --- nostradamus/tests/auth_tests/test_register.py | 98 --- nostradamus/tests/auth_tests/test_signin.py | 134 ---- .../tests/views_tests/test_auth_views.py | 22 - 34 files changed, 3922 deletions(-) delete mode 100644 frontend/nginx/conf.d/reverse_80.nginx.template delete mode 100644 frontend/src/app/modules/settings/elements/input-predictions-element/input-predictions-element.scss delete mode 100644 frontend/src/app/modules/settings/elements/input-predictions-element/input-predictions-element.tsx delete mode 100644 frontend/src/app/modules/settings/elements/input-training-element/input-training-element.scss delete mode 100644 frontend/src/app/modules/settings/elements/input-training-element/input-training-element.tsx delete mode 100644 frontend/src/app/modules/settings/fields/settings_filter/setings_filter.tsx delete mode 100644 frontend/src/app/modules/settings/fields/settings_filter/settings_filter.scss delete mode 100644 frontend/src/app/modules/settings/fields/settings_predictions/settings_predictions.scss delete mode 100644 frontend/src/app/modules/settings/fields/settings_predictions/settings_predictions.tsx delete mode 100644 frontend/src/app/modules/settings/fields/settings_training/settings_training.scss delete mode 100644 frontend/src/app/modules/settings/fields/settings_training/settings_training.tsx delete mode 100644 frontend/src/app/modules/settings/fields/settings_training/store/actions.ts delete mode 100644 frontend/src/app/modules/settings/fields/settings_training/store/reducer.ts delete mode 100644 frontend/src/app/modules/settings/fields/settings_training/store/thunks.ts delete mode 100644 frontend/src/app/modules/settings/fields/settings_training/store/types.ts delete mode 100644 nostradamus/apps/analysis_and_training/main/training.py delete mode 100644 nostradamus/apps/authentication/apps.py delete mode 100644 nostradamus/apps/authentication/main/__init__.py delete mode 100644 nostradamus/apps/authentication/main/common.py delete mode 100644 nostradamus/apps/authentication/main/token_creator.py delete mode 100644 nostradamus/apps/authentication/migrations/0001_initial.py delete mode 100644 nostradamus/apps/authentication/migrations/__init__.py delete mode 100644 nostradamus/apps/authentication/serializers.py delete mode 100644 nostradamus/apps/authentication/urls.py delete mode 100644 nostradamus/apps/authentication/validators.py delete mode 100644 nostradamus/apps/authentication/views.py delete mode 100644 nostradamus/apps/extractor/migrations/__init__.py delete mode 100644 nostradamus/apps/qa_metrics/migrations/__init__.py delete mode 100644 nostradamus/apps/settings/migrations/0002_auto.py delete mode 100644 nostradamus/apps/settings/migrations/0003_new_tables.py delete mode 100644 nostradamus/tests/analysis_and_training_tests/test_train.py delete mode 100644 nostradamus/tests/auth_tests/test_register.py delete mode 100644 nostradamus/tests/auth_tests/test_signin.py delete mode 100644 nostradamus/tests/views_tests/test_auth_views.py diff --git a/frontend/nginx/conf.d/reverse_80.nginx.template b/frontend/nginx/conf.d/reverse_80.nginx.template deleted file mode 100644 index 4c64d4b..0000000 --- a/frontend/nginx/conf.d/reverse_80.nginx.template +++ /dev/null @@ -1,85 +0,0 @@ -upstream backendapp { - server nostradamus-core:8000; -} - -server { - listen 80; - server_name '${SERVER_NAME}'; - include /etc/nginx/conf.d/ssl_80.conf*; - access_log /var/log/nginx/reverse_80.access.log combined; - error_log /var/log/nginx/reverse_80.error.log; - - location / { - root /usr/share/nginx/html; - index index.html; - try_files $uri $uri/ /index.html; - add_header Access-Control-Allow-Origin "*"; - } - - location /api { - return 302 /api/; - } - - location /api_static/ { - rewrite /api_static/(.*) /api_static/$1 break; - proxy_pass http://backendapp/; -# proxy_read_timeout 90s; -# proxy_connect_timeout 90s; -# proxy_send_timeout 90s; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Proxy ""; - add_header Access-Control-Allow-Origin "*"; - add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"; - } - - location /flower/ { - rewrite ^/flower/?(.*)$ /$1 break; - proxy_pass http://flower:5555/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Proxy ""; - add_header Access-Control-Allow-Origin "*"; - add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"; - } - - location /virtual-assistant/ { - rewrite /virtual-assistant/(.*) /$1 break; - proxy_pass http://virtual-assistant-core:5005/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Proxy ""; - add_header Access-Control-Allow-Origin "*"; - add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"; - } - - location /api/ { - rewrite /api/(.*) /$1 break; - proxy_pass http://backendapp/; -# proxy_read_timeout 90s; -# proxy_connect_timeout 90s; -# proxy_send_timeout 90s; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Proxy ""; - add_header Access-Control-Allow-Origin "*"; - add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"; - } - - location /ws/ { - rewrite /ws/(.*) /$1 break; - proxy_pass http://channels:8001/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Proxy ""; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - add_header Access-Control-Allow-Origin "*"; - add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"; - } -} diff --git a/frontend/src/app/modules/settings/elements/input-predictions-element/input-predictions-element.scss b/frontend/src/app/modules/settings/elements/input-predictions-element/input-predictions-element.scss deleted file mode 100644 index 4df9863..0000000 --- a/frontend/src/app/modules/settings/elements/input-predictions-element/input-predictions-element.scss +++ /dev/null @@ -1,56 +0,0 @@ -@import "../../../../styles/colors"; - -.input-predictions-element { - display: flex; - align-items: flex-start; - justify-content: flex-start; - flex-wrap: wrap; - background-color: $veryLightGray; - color: $darkGray; - - &-block { - display: inline-flex; - align-items: center; - margin: 7px; - border-radius: 4px; - max-width: 95%; - cursor: pointer; - background-color: $grayDisabled; - - &_locked { - cursor: not-allowed; - } - - &__input { - background-color: transparent; - } - - &__position { - color: $darkBlue; - margin: 5px 10px; - } - - &__content { - margin: 5px 0; - width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - &__button { - color: $grayDisabledText; - background-color: transparent; - margin: 5px 10px; - padding: 0; - } - - &:hover { - background-color: $seaBlue; - - * { - color: white; - } - } - } -} diff --git a/frontend/src/app/modules/settings/elements/input-predictions-element/input-predictions-element.tsx b/frontend/src/app/modules/settings/elements/input-predictions-element/input-predictions-element.tsx deleted file mode 100644 index 53cd592..0000000 --- a/frontend/src/app/modules/settings/elements/input-predictions-element/input-predictions-element.tsx +++ /dev/null @@ -1,80 +0,0 @@ -/* eslint-disable jsx-a11y/no-noninteractive-tabindex */ -/* eslint-disable jsx-a11y/tabindex-no-positive */ -import React, { Component } from "react"; -import { PredictionTableData } from "app/common/store/settings/types"; -import Icon, { IconSize, IconType } from "app/common/components/icon/icon"; -import cn from "classnames"; -import "app/modules/settings/elements/input-predictions-element/input-predictions-element.scss"; - -interface InputPredictionsElementProps { - values: PredictionTableData[]; - onDeletePrediction: (index: number) => () => void; - onChangePredictionsOrder: (indexDrag: number, indexPaste: number) => void; -} - -export default class InputPredictionsElement extends Component { - predictionBlockRef: HTMLDivElement | undefined = undefined; - - predictionBlockDragStart = (e: React.DragEvent): void => { - const target = e.target as HTMLDivElement; - this.predictionBlockRef = target; - target.style.opacity = "0.25"; - }; - - predictionBlockDragEnd = (e: React.DragEvent): void => { - const target = e.target as HTMLDivElement; - target.style.opacity = "1"; - }; - - predictionBlockDragOver = (e: React.DragEvent): void => { - e.preventDefault(); - }; - - predictionBlockDrop = (e: React.DragEvent): void => { - if (!this.predictionBlockRef) return; - let target = e.target as HTMLDivElement; - - const { onChangePredictionsOrder } = this.props; - - while (target.classList[0] !== this.predictionBlockRef.classList[0]) - target = target.parentNode as HTMLDivElement; - onChangePredictionsOrder( - Array.from((target.parentNode as HTMLDivElement).children).indexOf(this.predictionBlockRef), - Array.from((target.parentNode as HTMLDivElement).children).indexOf(target) - ); - }; - - render() { - const { values, onDeletePrediction } = this.props; - - return ( -
- {values.map((item, index) => ( -
-

{item.position}

- -

{item.name}

- - -
- ))} -
- ); - } -} diff --git a/frontend/src/app/modules/settings/elements/input-training-element/input-training-element.scss b/frontend/src/app/modules/settings/elements/input-training-element/input-training-element.scss deleted file mode 100644 index bfc55c3..0000000 --- a/frontend/src/app/modules/settings/elements/input-training-element/input-training-element.scss +++ /dev/null @@ -1,160 +0,0 @@ -@import "../../../../styles/colors"; - -.input-training-element { - width: 100%; - position: relative; - - &-icons { - position: absolute; - left: 100%; - top: 50%; - background-color: transparent; - display: flex; - align-items: center; - transform: translate(calc(-100% - 15px), -50%); - - &__close { - background-color: transparent; - opacity: 0.5; - transition: opacity 0.2s ease-out; - &:hover { - opacity: 1; - } - } - - &__down { - pointer-events: none; - &_rotated { - transform: rotate(180deg); - } - } - } - - &-block-container { - height: 40px; - - font-weight: 500; - font-size: 16px; - line-height: 30px; - padding-right: 60px; - - overflow: hidden; - box-sizing: border-box; - - &_simple, - &_odd { - background-color: $veryLightGray; - } - - &_simple, &_disabled { - border-top-left-radius: 5px; - border-top-right-radius: 5px; - } - - &_simple { - border-bottom: 1px solid $deepDarkBlue; - } - - &_even { - background-color: $lightBlue; - } - - &_hovered { - background-color: $seaBlue; - color: $white; - } - - &_edited { - background-color: $lightSeaBlue; - border: 2px solid $seaBlue; - - .input-training-element-value-block { - margin: 3px; - } - } - - &_disabled { - pointer-events: none; - cursor: pointer; - background-color: $veryLightGray; - border-bottom: 1px solid $deepDarkBlue; - } - - &__placeholder { - pointer-events: none; - color: $gray; - margin-top: 20px; - margin-left: 10px; - transform: translateY(-50%); - } - } - - &-value-block { - width: calc(50% - 35px); - - background-color: $grayDisabled; - display: inline-block; - position: relative; - border-radius: 4px; - height: 30px; - margin: 5px; - - &__wrapper { - width: 100%; - height: 100%; - position: absolute; - top: 50%; - transform: translateY(-50%); - padding: 5px; - display: inline-flex; - align-items: center; - } - - &__number { - color: $darkBlue; - margin: 10px 5px; - } - - &__content { - color: $darkGray; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - &__close { - background-color: transparent; - margin: 10px 5px; - padding: 0; - opacity: 0.5; - transition: opacity 0.2s ease-out; - &:hover:not([disabled]) { - opacity: 1; - } - } - } - - &__spread-button { - max-width: 65px; - position: absolute; - font-size: 12px; - top: 50%; - left: 100%; - transform: translate(-110px, -50%); - background-color: transparent; - cursor: pointer; - - &__number { - display: inline; - } - } - - &__select-window { - position: absolute; - z-index: 10; - top: 100%; - right: 0; - width: 75%; - max-height: 250px; - } -} diff --git a/frontend/src/app/modules/settings/elements/input-training-element/input-training-element.tsx b/frontend/src/app/modules/settings/elements/input-training-element/input-training-element.tsx deleted file mode 100644 index 64a8570..0000000 --- a/frontend/src/app/modules/settings/elements/input-training-element/input-training-element.tsx +++ /dev/null @@ -1,188 +0,0 @@ -/* eslint-disable jsx-a11y/no-noninteractive-tabindex */ -import React, { Component } from "react"; -import cn from "classnames"; -import { - FilterElementType, - FilterDropdownType, -} from "app/modules/settings/elements/elements-types"; -import SelectWindow from "app/common/components/native-components/select-window/select-window"; -import Icon, { IconSize, IconType } from "app/common/components/icon/icon"; -import "app/modules/settings/elements/input-training-element/input-training-element.scss"; - -interface InputTrainingElementProps { - type: FilterElementType; - onChange: (value: string) => void; - onClear: (index: number) => void; - onClearAll: () => void; - values: string[]; - dropDownValues: string[]; -} - -interface InputTrainingElementState { - isSelectWindowOpen: boolean; - isSelectedListOpen: boolean; -} - -export default class InputTrainingElement extends Component< - InputTrainingElementProps, - InputTrainingElementState -> { - // eslint-disable-next-line react/static-property-placement - static defaultProps = { - type: FilterElementType.simple, - dropDownValues: Object.values(FilterDropdownType), - }; - - // eslint-disable-next-line no-undef - timerID: NodeJS.Timeout | null = null; - inputTrainingElementRef: React.RefObject = React.createRef(); - allowedEditing = false; - - constructor(props: InputTrainingElementProps) { - super(props); - - this.state = { - isSelectWindowOpen: false, - isSelectedListOpen: false, - }; - } - - onFocusTrainingElement = (): void => { - if (!this.allowedEditing) return; - if (this.timerID) clearTimeout(this.timerID); - this.setState({ isSelectWindowOpen: true }); - }; - - onBlurTrainingElement = (): void => { - this.timerID = setTimeout( - () => this.setState({ isSelectWindowOpen: false, isSelectedListOpen: false }), - 0 - ); - }; - - openSelectedValuesList = (): void => { - this.setState((state) => ({ - isSelectedListOpen: !state.isSelectedListOpen, - })); - }; - - selectDropdownValue = (value: string, isChecked: boolean) => (): void => { - const { values, onChange } = this.props; - if (isChecked) { - this.deleteValueBlock(values.findIndex((item) => item === value))(); - return; - } - onChange(value); - }; - - deleteValueBlock = (index: number) => (): void => { - const { values, onClear } = this.props; - const { isSelectedListOpen } = this.state; - - if (this.inputTrainingElementRef.current && values.length === 1 && isSelectedListOpen) { - this.inputTrainingElementRef.current.blur(); - } - onClear(index); - }; - - deleteAllValueBlocks = (): void => { - const { onClearAll } = this.props; - onClearAll(); - }; - - renderValueBlocks = (content: string, index: number): React.ReactNode => { - return ( -
-
-

{index + 1}

-

{content}

- {this.allowedEditing && ( - - )} -
-
- ); - }; - - isStrIncludesSubstr = (str: string, substr: string): boolean => - str.toLowerCase().includes(substr.toLowerCase()); - - render() { - const { values, dropDownValues, type } = this.props; - const { isSelectedListOpen, isSelectWindowOpen } = this.state; - - this.allowedEditing = [FilterElementType.simple, FilterElementType.edited].includes(type); - - const dropdownValues = isSelectedListOpen ? values : dropDownValues; - - return ( -
- {this.allowedEditing && ( -
- - -
- )} - -
- {values.length ? ( - [...values].splice(0, 2).map((item, index) => this.renderValueBlocks(item, index)) - ) : ( -

Entities Name

- )} -
- - {values.length > 2 && this.allowedEditing && ( - - )} - - {isSelectWindowOpen && ( -
- -
- )} -
- ); - } -} diff --git a/frontend/src/app/modules/settings/fields/settings_filter/setings_filter.tsx b/frontend/src/app/modules/settings/fields/settings_filter/setings_filter.tsx deleted file mode 100644 index 8611402..0000000 --- a/frontend/src/app/modules/settings/fields/settings_filter/setings_filter.tsx +++ /dev/null @@ -1,343 +0,0 @@ -import React, { Component } from "react"; -import cn from "classnames"; -import "app/modules/settings/fields/settings_filter/settings_filter.scss"; -import DropdownElement from "app/common/components/native-components/dropdown-element/dropdown-element"; -import { - FilterElementType, - FilterDropdownType, -} from "app/modules/settings/elements/elements-types"; -import Icon, { IconSize, IconType } from "app/common/components/icon/icon"; -import Button, { ButtonStyled } from "app/common/components/button/button"; -import { connect, ConnectedProps } from "react-redux"; -import { RootStore } from "app/common/types/store.types"; -import { SettingsSections } from "app/common/store/settings/types"; -import { sendSettingsData } from "app/common/store/settings/thunks"; -import { caseInsensitiveStringCompare } from "app/common/functions/helper"; - -interface SettingsFilterData { - [key: string]: string; - name: string; - filtration_type: string; -} - -interface SettingsFilterState { - [key: string]: - | boolean - | Array - | SettingsFilterData - | Array - | string[]; - names: string[]; - settings: Array; - dataInput: SettingsFilterData; - dataEdit: SettingsFilterData; - status: Array; - isSettingsDefault: boolean; -} - -interface SettingsFilterProps { - section: SettingsSections.filters | SettingsSections.qaFilters; -} - -class SettingsFilter extends Component { - constructor(props: Props) { - super(props); - this.state = this.getDefaultStateObject(); - } - - setFieldData = (keyField: "dataInput" | "dataEdit", valField: keyof SettingsFilterData) => ( - value: string - ) => { - this.setState((prevState) => { - const data: SettingsFilterData = prevState[keyField]; - data[valField] = value; - return { - [keyField]: data, - }; - }); - }; - - clearFieldData = ( - keyField: "dataInput" | "dataEdit", - valField?: keyof SettingsFilterData - ) => () => { - this.setState((prevState) => { - let data: SettingsFilterData = prevState[keyField]; - - if (valField) data[valField] = ""; - else data = { name: "", filtration_type: "" }; - - return { - [keyField]: data, - }; - }); - }; - - addTableRow = () => { - const { settings, status, dataInput } = this.state; - - settings.push({ ...dataInput }); - status.push(this.getTableRowParity(status.length)); - - this.setState({ - settings, - status, - }); - this.clearFieldData("dataInput")(); - this.detectIsSettingsDefault(); - }; - - changeTableRowHoverStatus = (index: number) => ({ type }: any) => { - const { status } = this.state; - - if (status[index] === FilterElementType.edited) return; - - switch (type) { - case "mouseenter": - status[index] = FilterElementType.hovered; - break; - default: - status[index] = this.getTableRowParity(index); - break; - } - - this.setState({ status }); - }; - - editTableRowData = (index: number) => () => { - const { status, settings } = this.state; - const dataEdit = { ...settings[index] }; - - status[index] = FilterElementType.edited; - - this.setState({ status, dataEdit }); - }; - - acceptTableRowEditing = (index: number) => () => { - const { settings, status, dataEdit } = this.state; - - settings[index] = { ...dataEdit }; - status[index] = this.getTableRowParity(index); - - this.setState({ - settings, - status, - }); - this.detectIsSettingsDefault(); - }; - - deleteTableRow = (index: number) => () => { - const { settings, status } = this.state; - settings.splice(index, 1); - status.pop(); - this.setState({ - settings, - status, - }); - this.detectIsSettingsDefault(); - }; - - setDefaultSettings = () => { - this.setState(this.getDefaultStateObject()); - }; - - saveSettings = () => { - const { sendSettingsData, section } = this.props; - const { settings } = this.state; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - sendSettingsData(section, settings); - this.detectIsSettingsDefault(true); - }; - - getDefaultStateObject = (): SettingsFilterState => { - const { defaultSettings, section } = this.props; - - return { - names: [...defaultSettings[section].names], - settings: this.sortTableRows(defaultSettings[section].filter_settings), - status: this.getDefaultTableRowsStatuses(defaultSettings[section].filter_settings.length), - dataInput: { - name: "", - filtration_type: "", - }, - dataEdit: { - name: "", - filtration_type: "", - }, - isSettingsDefault: true, - }; - }; - - getDefaultTableRowsStatuses = (length: number) => - [...new Array(length)].map((_, index) => this.getTableRowParity(index)); - - sortTableRows = (arr: Array) => - [...arr].sort((firstItem: SettingsFilterData, secondItem: SettingsFilterData) => - caseInsensitiveStringCompare(firstItem.name, secondItem.name) - ); - - getTableRowParity = (numb: number) => - numb % 2 === 1 ? FilterElementType.odd : FilterElementType.even; - - detectIsSettingsDefault = (isSettingsDefault = false) => this.setState({ isSettingsDefault }); - - isPositionAcceptButtonValid = (field: "dataInput" | "dataEdit") => { - // eslint-disable-next-line react/destructuring-assignment - const data: SettingsFilterData = this.state[field]; - return !(data.name && data.filtration_type); - }; - - render() { - const { settings, dataInput, names, status, dataEdit, isSettingsDefault } = this.state; - const excludeNames = settings.map((item) => item.name); - - return ( -
-

Filter

- -
-
-

Name

- -
- -
-

Filtration Type

-
- - - -
-
-
- -
- {settings.map(({ name, filtration_type }, index) => ( -
-
- exName !== name)} - /> -
- -
-
- - - {status[index] === FilterElementType.edited && ( - - )} -
-
- - {status[index] === FilterElementType.hovered && ( -
- - - -
- )} -
- ))} -
- -
-
-
- ); - } -} - -const mapStateToProps = ({ settings }: RootStore) => ({ - defaultSettings: settings.settingsStore.defaultSettings, -}); - -const mapDispatchToProps = { - sendSettingsData, -}; - -const connector = connect(mapStateToProps, mapDispatchToProps); - -type PropsFromRedux = ConnectedProps; -type Props = PropsFromRedux & SettingsFilterProps; - -export default connector(SettingsFilter); diff --git a/frontend/src/app/modules/settings/fields/settings_filter/settings_filter.scss b/frontend/src/app/modules/settings/fields/settings_filter/settings_filter.scss deleted file mode 100644 index 6cb42b2..0000000 --- a/frontend/src/app/modules/settings/fields/settings_filter/settings_filter.scss +++ /dev/null @@ -1,117 +0,0 @@ -@import "../../../../styles/colors"; - -.settings-filter { - font-weight: 500; - font-size: 14px; - line-height: 17px; - color: $darkGray; - - &__button { - &:disabled { - cursor: not-allowed; - } - } - &__title { - font-weight: 500; - font-size: 20px; - line-height: 22px; - color: $deepDarkBlue; - } - - &-name { - width: 40%; - - &_tabled { - border-right: 1px solid $darkBlue; - } - } - - &-type { - width: 60%; - &__dropdown-wrapper { - display: flex; - } - &__accept-button { - background-color: $seaBlue; - width: 60px; - color: white; - display: flex; - justify-content: center; - align-items: center; - } - } - - &-header { - display: flex; - margin-top: 15px; - - &__title { - margin-bottom: 5px; - } - - &__dropdown-wrapper { - display: flex; - align-items: center; - } - - &__add-position { - display: inline-block; - padding: 6px; - margin-left: 15px; - border: 1.5px solid $gray; - background-color: transparent; - border-radius: 100%; - opacity: 0.5; - cursor: pointer; - transform: rotate(45deg); - transition: opacity 0.2s ease-out, color 0.2s ease-out, border-color 0.2s ease-out; - - &:hover:not([disabled]) { - opacity: 1; - color: $seaBlue; - border-color: $seaBlue; - } - } - } - - &-main { - margin-top: 15px; - box-sizing: border-box; - - &__section-edit-wrapper { - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%); - display: flex; - } - - &__edit-button, - &__delete-button { - margin-right: 10px; - background-color: transparent; - color: white; - opacity: 0.5; - transition: opacity 0.2s ease-out; - - &:hover { - opacity: 1; - - &:disabled { - cursor: not-allowed; - } - } - } - - &__section { - display: flex; - position: relative; - } - } - - &-footer { - display: flex; - justify-content: space-between; - margin-top: 30px; - } -} diff --git a/frontend/src/app/modules/settings/fields/settings_predictions/settings_predictions.scss b/frontend/src/app/modules/settings/fields/settings_predictions/settings_predictions.scss deleted file mode 100644 index 9b9a8a5..0000000 --- a/frontend/src/app/modules/settings/fields/settings_predictions/settings_predictions.scss +++ /dev/null @@ -1,63 +0,0 @@ -@import "../../../../styles/colors"; - -.settings-predictions { - font-weight: 500; - font-size: 14px; - line-height: 17px; - color: $darkGray; - - &-header { - margin-top: 15px; - - &__title { - } - - &__wrapper { - display: flex; - align-items: center; - width: 50%; - margin-top: 5px; - } - } - - &-main { - margin-top: 15px; - } - - &__button { - &:disabled { - cursor: not-allowed; - } - } - - &__add-position { - display: inline-block; - padding: 6px; - margin-left: 15px; - border: 1.5px solid $gray; - background-color: transparent; - border-radius: 100%; - opacity: 0.5; - cursor: pointer; - transform: rotate(45deg); - transition: opacity 0.2s ease-out, color 0.2s ease-out, border-color 0.2s ease-out; - - &:hover:not([disabled]) { - opacity: 1; - color: $seaBlue; - border-color: $seaBlue; - } - } - - &__title { - font-size: 18px; - line-height: 22px; - color: $deepDarkBlue; - } - - &-footer { - display: flex; - justify-content: space-between; - margin-top: 30px; - } -} diff --git a/frontend/src/app/modules/settings/fields/settings_predictions/settings_predictions.tsx b/frontend/src/app/modules/settings/fields/settings_predictions/settings_predictions.tsx deleted file mode 100644 index 3001fa4..0000000 --- a/frontend/src/app/modules/settings/fields/settings_predictions/settings_predictions.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import React, { Component } from "react"; -import Icon, { IconSize, IconType } from "app/common/components/icon/icon"; -import Button, { ButtonStyled } from "app/common/components/button/button"; -import InputPredictionsElement from "app/modules/settings/elements/input-predictions-element/input-predictions-element"; -import cn from "classnames"; -import { connect, ConnectedProps } from "react-redux"; -import { RootStore } from "app/common/types/store.types"; -import { sendSettingsData } from "app/common/store/settings/thunks"; -import { PredictionTableData, SettingsSections } from "app/common/store/settings/types"; -import "app/modules/settings/fields/settings_predictions/settings_predictions.scss"; -import DropdownElement from "app/common/components/native-components/dropdown-element/dropdown-element"; - -interface SettingsPredictionsState { - names: string[]; - predictions: PredictionTableData[]; - dataInput: string; - isSettingsDefault: boolean; -} - -class SettingsPredictions extends Component { - constructor(props: SettingsPredictionsProps) { - super(props); - this.state = this.getDefaultStateObject(); - } - - detectIsSettingsDefault = (isSettingsDefault = false) => this.setState({ isSettingsDefault }); - - setInputData = (dataInput: string) => { - this.setState({ dataInput }); - }; - - clearInputData = () => { - this.setInputData(""); - }; - - addPredictionBlock = () => { - const { predictions, dataInput } = this.state; - - predictions.push({ - name: dataInput, - is_default: false, - position: predictions.length + 1, - settings: 0, - }); - - this.setState({ predictions }); - this.clearInputData(); - this.detectIsSettingsDefault(); - }; - - deletePredictionBlock = (index: number) => () => { - const { predictions } = this.state; - - predictions.splice(index, 1); - - this.setState({ predictions }); - this.fixPredictionsBlocksOrder(); - this.detectIsSettingsDefault(); - }; - - fixPredictionsBlocksOrder = () => { - let { predictions } = this.state; - predictions = predictions.map((item, index) => ({ ...item, position: index + 1 })); - this.setState({ predictions }); - }; - - changeValueBlocksOrder = (indexOfDraggedVal: number, indexOfNewPosition: number) => { - const { predictions } = this.state; - const val = predictions.splice(indexOfDraggedVal, 1)[0]; - - predictions.splice(indexOfNewPosition, 0, val); - - this.setState({ predictions }); - this.fixPredictionsBlocksOrder(); - this.detectIsSettingsDefault(); - }; - - saveSettings = () => { - const { sendSettingsData } = this.props; - const { predictions } = this.state; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - sendSettingsData(SettingsSections.predictions, predictions); - - this.detectIsSettingsDefault(true); - }; - - setDefaultSettings = () => { - this.setState(this.getDefaultStateObject()); - }; - - getDefaultStateObject = (): SettingsPredictionsState => { - const { predictions } = this.props; - - return { - names: [...predictions.field_names], - predictions: [...predictions.predictions_table_settings], - dataInput: "", - isSettingsDefault: true, - }; - }; - - render() { - const { predictions, dataInput, names, isSettingsDefault } = this.state; - const excludeNames = predictions.map((item) => item.name); - - return ( -
-

Predictions

- -
-

Add Own Element

-
- - -
-
- -
- -
- -
-
-
- ); - } -} - -const mapStateToProps = ({ settings }: RootStore) => ({ - predictions: settings.settingsStore.defaultSettings.predictions_table, -}); - -const mapDispatchToProps = { - sendSettingsData, -}; - -const connector = connect(mapStateToProps, mapDispatchToProps); - -type PropsFromRedux = ConnectedProps; -type SettingsPredictionsProps = PropsFromRedux & unknown; - -export default connector(SettingsPredictions); diff --git a/frontend/src/app/modules/settings/fields/settings_training/settings_training.scss b/frontend/src/app/modules/settings/fields/settings_training/settings_training.scss deleted file mode 100644 index af22a6f..0000000 --- a/frontend/src/app/modules/settings/fields/settings_training/settings_training.scss +++ /dev/null @@ -1,204 +0,0 @@ -@import "../../../../styles/colors"; - -.settings-training { - color: $darkGray; - - &__button { - &:disabled { - cursor: not-allowed; - } - } - - &__title, - &__subtitle { - font-weight: 500; - font-size: 20px; - line-height: 22px; - color: $deepDarkBlue; - } - - &__subtitle { - margin: 20px 0; - font-size: 18px; - color: $darkBlue; - } - - &__add-position { - display: inline-block; - padding: 6px; - margin-left: 15px; - border: 1.5px solid $gray; - background-color: transparent; - border-radius: 100%; - opacity: 0.5; - cursor: pointer; - transform: rotate(45deg); - transition: opacity 0.2s ease-out, color 0.2s ease-out, border-color 0.2s ease-out; - - &:hover:not([disabled]) { - opacity: 1; - color: $seaBlue; - border-color: $seaBlue; - } - } - - &-source { - display: flex; - align-items: center; - margin-top: 15px; - - &__title { - margin-right: 20px; - } - - &__arrow-left, - &__inscription, - &__check { - color: $orange; - margin: 0 20px; - } - - &__arrow-left { - animation: arrow_move 1s ease-in-out both alternate infinite; - } - - &__check { - color: $seaBlue; - } - - &__inscription { - margin-left: 0; - - &_success { - color: $seaBlue; - } - } - } - - &-table { - &-main { - margin-top: 15px; - - &__edit-button, - &__delete-button { - background-color: $seaBlue; - color: rgba(255, 255, 255, 0.5); - transition: color 0.2s ease-out; - - &:hover { - color: rgba(255, 255, 255, 1); - } - } - - &-section { - position: relative; - display: flex; - - &_select { - display: flex; - } - - &__edit-wrapper { - position: absolute; - right: 0; - display: flex; - top: 50%; - transform: translateY(-50%); - } - } - } - - &__accept-button { - background-color: $seaBlue; - width: 60px; - color: white; - display: flex; - justify-content: center; - align-items: center; - } - - &-input { - width: 40%; - &_tabled { - border-right: 1px solid $darkBlue; - } - } - - &-select { - width: 60%; - - &__select-wrapper { - display: flex; - align-items: center; - } - } - } - - &-table-header { - margin-top: 20px; - - &__wrapper { - margin-top: 15px; - display: flex; - } - - &__title { - font-size: 16px; - } - &__field-title { - font-size: 14px; - margin-bottom: 5px; - } - } - - &-bug-resolution { - margin-top: 25px; - - &__title { - font-weight: 500; - font-size: 18px; - color: $darkBlue; - } - - &-metric, - &-value { - display: flex; - align-items: center; - } - - &-wrapper { - display: flex; - margin-top: 15px; - } - - &-metric { - &__title { - margin-right: 20px; - } - } - - &-value { - &__title { - margin-right: 20px; - margin-left: 30px; - } - } - } - - &-footer { - margin-top: 30px; - padding-bottom: 10px; - display: flex; - align-items: center; - justify-content: space-between; - } -} - -@keyframes arrow_move { - from { - transform: translateX(10px); - } - to { - transform: translateX(-10px); - } -} diff --git a/frontend/src/app/modules/settings/fields/settings_training/settings_training.tsx b/frontend/src/app/modules/settings/fields/settings_training/settings_training.tsx deleted file mode 100644 index 83ea3b8..0000000 --- a/frontend/src/app/modules/settings/fields/settings_training/settings_training.tsx +++ /dev/null @@ -1,559 +0,0 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ -import React, { Component } from "react"; -import "app/modules/settings/fields/settings_training/settings_training.scss"; -import InputElement from "app/modules/settings/elements/input-element/input-element"; -import InputTrainingElement from "app/modules/settings/elements/input-training-element/input-training-element"; -import { FilterElementType } from "app/modules/settings/elements/elements-types"; -import Icon, { IconSize, IconType } from "app/common/components/icon/icon"; -import Button, { ButtonStyled } from "app/common/components/button/button"; -import { MarkUpEntities, SettingsSections } from "app/common/store/settings/types"; -import { connect, ConnectedProps } from "react-redux"; -import { RootStore } from "app/common/types/store.types"; -import { sendSettingsData } from "app/common/store/settings/thunks"; -import { - sendSettingsTrainingData, - uploadSettingsTrainingSubfieldData, -} from "app/modules/settings/fields/settings_training/store/thunks"; -import cn from "classnames"; -import { - MarkUpEntitiesData, - BugResolutionData, - SourceFieldData, - MarkUpEntitiesElement, - TrainingSubSection, - BugResolutionElement, -} from "app/modules/settings/fields/settings_training/store/types"; -import DropdownElement from "app/common/components/native-components/dropdown-element/dropdown-element"; - -interface SettingsTrainingState { - [key: string]: any; - - source_field: SourceFieldData; - markup_entities: MarkUpEntitiesData; - bug_resolution: BugResolutionData; - markUpEntitiesInputData: MarkUpEntitiesElement; - markUpEntitiesEditData: MarkUpEntitiesElement; - status: FilterElementType[]; - isSettingsDefault: boolean; - isAllowedEntitiesEditing: boolean; -} - -class SettingsTraining extends Component { - constructor(props: Props) { - super(props); - this.state = this.getDefaultStateObject(); - } - - setMarkUpEntitiesData = ( - keyField: "markUpEntitiesInputData" | "markUpEntitiesEditData", - valField: keyof MarkUpEntities - ) => (value: string) => { - this.setState((prevState) => { - const data: MarkUpEntitiesElement = prevState[keyField]; - if (valField === "area_of_testing") data.area_of_testing = value; - else data.entities.push(value); - return { [keyField]: data }; - }); - }; - - clearMarkUpEntitiesInputData = ( - keyField: "markUpEntitiesInputData" | "markUpEntitiesEditData" - ) => () => this.setMarkUpEntitiesData(keyField, "area_of_testing")(""); - - clearMarkUpEntitiesBlockValueData = ( - keyField: "markUpEntitiesInputData" | "markUpEntitiesEditData" - ) => (index: number) => { - this.setState((prevState) => { - const data = prevState[keyField]; - data.entities.splice(index, 1); - return { [keyField]: data }; - }); - }; - - clearAllMarkUpEntitiesBlockValueData = ( - keyField: "markUpEntitiesInputData" | "markUpEntitiesEditData" - ) => () => { - this.setState((prevState) => { - const data = prevState[keyField]; - data.entities = []; - return { [keyField]: data }; - }); - }; - - setSourceFieldData = (name: string, isAllowedEntitiesEditing = false) => { - const { source_field } = this.state; - source_field.source_field = name; - this.setState({ source_field, isAllowedEntitiesEditing }); - this.detectIsSettingsDefault(); - }; - - setMarkUpSource = (name: string) => { - this.setSourceFieldData(name); - setTimeout(() => { - const { sendSettingsTrainingData, uploadSettingsTrainingSubfieldData } = this.props; - - sendSettingsTrainingData({ source_field: name }, TrainingSubSection.source_field).then( - (isAllowedEntitiesEditing) => { - if (isAllowedEntitiesEditing) { - this.setState({ isAllowedEntitiesEditing }); - uploadSettingsTrainingSubfieldData(TrainingSubSection.markup_entities).then( - (markup) => { - if (markup) { - this.setState((prevState) => { - const { markup_entities } = prevState; - markup_entities.entity_names = [...markup.entity_names]; - return { markup_entities }; - }); - } else this.setState({ isAllowedEntitiesEditing: false }); - } - ); - } - } - ); - }, 1000); - }; - - clearMarkUpSource = () => { - this.setSourceFieldData(""); - }; - - changeBugResolutionValue = (index: number) => (value: string) => { - const { bug_resolution } = this.state; - - bug_resolution.resolution_settings[index] = { - metric: bug_resolution.resolution_settings[index].metric, - value, - }; - - this.setState({ bug_resolution }); - this.detectIsSettingsDefault(); - }; - - clearBugResolutionValue = (index: number) => () => { - this.changeBugResolutionValue(index)(""); - }; - - changeTableRowHoverStatus = (index: number) => ({ type }: any) => { - const { status, isAllowedEntitiesEditing } = this.state; - if (status[index] === FilterElementType.edited || !isAllowedEntitiesEditing) return; - - switch (type) { - case "mouseenter": - status[index] = FilterElementType.hovered; - break; - default: - status[index] = this.getTableRowParity(index); - break; - } - - this.setState({ status }); - }; - - addTableRow = () => { - const { markup_entities, status } = this.state; - let { markUpEntitiesInputData } = this.state; - markup_entities.mark_up_entities.push({ ...markUpEntitiesInputData }); - markUpEntitiesInputData = { - area_of_testing: "", - entities: [], - }; - status.push(this.getTableRowParity(markup_entities.mark_up_entities.length + 1)); - - this.setState({ - markup_entities, - markUpEntitiesInputData, - status, - }); - this.detectIsSettingsDefault(); - }; - - editTableRowData = (index: number) => () => { - const { status, markUpEntitiesEditData, markup_entities } = this.state; - - status[index] = FilterElementType.edited; - markUpEntitiesEditData.area_of_testing = - markup_entities.mark_up_entities[index].area_of_testing; - markUpEntitiesEditData.entities = [...markup_entities.mark_up_entities[index].entities]; - - this.setState({ status, markUpEntitiesEditData }); - }; - - acceptTableRowEditing = (index: number) => () => { - const { status, markup_entities } = this.state; - let { markUpEntitiesEditData } = this.state; - - status[index] = this.getTableRowParity(index); - markup_entities.mark_up_entities[index] = markUpEntitiesEditData; - markUpEntitiesEditData = { - area_of_testing: "", - entities: [], - }; - - this.setState({ - status, - markup_entities, - markUpEntitiesEditData, - }); - this.detectIsSettingsDefault(); - }; - - deleteTableRow = (index: number) => () => { - const { markup_entities, status } = this.state; - - markup_entities.mark_up_entities.splice(index, 1); - status.pop(); - - this.setState({ - markup_entities, - status, - }); - this.detectIsSettingsDefault(); - }; - - saveSettings = () => { - const { markup_entities, bug_resolution } = this.state; - const { sendSettingsData } = this.props; - - const settings = { - mark_up_entities: [...markup_entities.mark_up_entities], - bug_resolution: [...bug_resolution.resolution_settings], - }; - - sendSettingsData(SettingsSections.training, settings); - this.detectIsSettingsDefault(true); - }; - - setDefaultSettings = () => { - const newState = this.getDefaultStateObject(); - this.setState({ ...newState }); - }; - - detectIsSettingsDefault = (isSettingsDefault = false) => this.setState({ isSettingsDefault }); - - getTableRowParity = (index: number) => - index % 2 === 1 ? FilterElementType.odd : FilterElementType.even; - - getDefaultStateObject = () => { - const { training } = this.props; - const { bug_resolution } = training; - - const resolution_settings = [ - { metric: "Resolution", value: "" }, - { metric: "Resolution", value: "" }, - ].map((item: BugResolutionElement, index: number) => - training.bug_resolution.resolution_settings[index] - ? { ...training.bug_resolution.resolution_settings[index] } - : item - ); - - bug_resolution.resolution_settings = resolution_settings; - - return { - source_field: { ...training.source_field }, - markup_entities: { ...training.markup_entities }, - bug_resolution, - markUpEntitiesInputData: { - area_of_testing: "", - entities: [], - }, - markUpEntitiesEditData: { - area_of_testing: "", - entities: [], - }, - status: training.markup_entities.mark_up_entities.map((_: any, index: number) => - this.getTableRowParity(index) - ), - isSettingsDefault: true, - isAllowedEntitiesEditing: !!training.source_field.source_field.length, - }; - }; - - render() { - const { - bug_resolution, - source_field, - isAllowedEntitiesEditing, - markUpEntitiesInputData, - status, - markup_entities, - markUpEntitiesEditData, - isSettingsDefault, - } = this.state; - - const bugResolutionExcludeValues = bug_resolution.resolution_settings - ? bug_resolution.resolution_settings.map((item) => item.value) - : []; - - return ( -
-

Training

- -

Areas of Testing

- -
-

Source Field

- - - {isAllowedEntitiesEditing ? ( - - ) : ( - - )} - - - {isAllowedEntitiesEditing ? ( - <>Source Field Saved - ) : ( - <>Set Source Field first to add Entities - )} - -
- -
-
-
-

Name

- -
- -
-

Entities

-
- - -
-
-
-
- -
- {markup_entities.mark_up_entities.map((item: MarkUpEntitiesElement, index: number) => ( -
-
- -
-
- - {status[index] === FilterElementType.edited && ( - - )} -
- - {status[index] === FilterElementType.hovered && ( -
- - - -
- )} -
- ))} -
- -
-

Bug Resolution

- {Array(2) - .fill("") - .map((_, index) => ( - // eslint-disable-next-line react/no-array-index-key -
-
-

Metric

- -
-
-

Value

- item !== bug_resolution.resolution_settings[index].value - )} - value={bug_resolution.resolution_settings[index].value} - onChange={this.changeBugResolutionValue(index)} - onClear={this.clearBugResolutionValue(index)} - /> -
-
- ))} -
- -
-
-
- ); - } -} - -const mapStateToProps = ({ settings }: RootStore) => ({ - training: settings.settingsTrainingStore, -}); - -const mapDispatchToProps = { - sendSettingsData, - sendSettingsTrainingData, - uploadSettingsTrainingSubfieldData, -}; - -const connector = connect(mapStateToProps, mapDispatchToProps); - -type PropsFromRedux = ConnectedProps; -type Props = PropsFromRedux & unknown; - -export default connector(SettingsTraining); diff --git a/frontend/src/app/modules/settings/fields/settings_training/store/actions.ts b/frontend/src/app/modules/settings/fields/settings_training/store/actions.ts deleted file mode 100644 index e04ed19..0000000 --- a/frontend/src/app/modules/settings/fields/settings_training/store/actions.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { HttpStatus } from "app/common/types/http.types"; -import { - SettingsTrainingResponseType, - TrainingActionTypes, - TrainingSubSection, -} from "app/modules/settings/fields/settings_training/store/types"; - -export const setSettingsTrainingData = ( - data: SettingsTrainingResponseType, - section: TrainingSubSection -) => - ({ - data, - section, - type: TrainingActionTypes.setSettingsTrainingData, - } as const); - -export const setSettingsTrainingStatus = (status: HttpStatus, section: TrainingSubSection) => - ({ - section, - status, - type: TrainingActionTypes.setSettingsTrainingStatus, - } as const); - -export const clearSettingsTrainingData = () => - ({ type: TrainingActionTypes.clearSettingsTrainingData } as const); diff --git a/frontend/src/app/modules/settings/fields/settings_training/store/reducer.ts b/frontend/src/app/modules/settings/fields/settings_training/store/reducer.ts deleted file mode 100644 index cb18706..0000000 --- a/frontend/src/app/modules/settings/fields/settings_training/store/reducer.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { - TrainingActionTypes, - SettingTrainingStore, -} from "app/modules/settings/fields/settings_training/store/types"; -import { InferValueTypes } from "app/common/store/utils"; -import { HttpStatus } from "app/common/types/http.types"; - -import * as actions from "app/modules/settings/fields/settings_training/store/actions"; - -const initialState: SettingTrainingStore = { - status: { - source_field: HttpStatus.PREVIEW, - bug_resolution: HttpStatus.PREVIEW, - markup_entities: HttpStatus.PREVIEW, - }, - source_field: { - source_field: "", - source_field_names: [], - }, - bug_resolution: { - resolution_settings: [], - resolution_names: [], - }, - markup_entities: { - mark_up_entities: [], - entity_names: [], - }, -}; -type actionsSettingsTrainingTypes = ReturnType>; - -export default function settingsTrainingReducer( - state: SettingTrainingStore = initialState, - action: actionsSettingsTrainingTypes -): SettingTrainingStore { - const status = { ...state.status }; - - switch (action.type) { - case TrainingActionTypes.setSettingsTrainingData: - if (!Object.keys(action.data).length) return { ...state }; - return { ...state, [action.section]: action.data }; - - case TrainingActionTypes.setSettingsTrainingStatus: - status[action.section] = action.status; - return { ...state, status }; - - case TrainingActionTypes.clearSettingsTrainingData: - return { ...initialState }; - - default: - return { ...state }; - } -} diff --git a/frontend/src/app/modules/settings/fields/settings_training/store/thunks.ts b/frontend/src/app/modules/settings/fields/settings_training/store/thunks.ts deleted file mode 100644 index f1be659..0000000 --- a/frontend/src/app/modules/settings/fields/settings_training/store/thunks.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* eslint-disable consistent-return */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ - -// TODO: Declare static types for this thunk - -import { SettingsApi } from "app/common/api/settings.api"; -import { TrainingSubSection } from "app/modules/settings/fields/settings_training/store/types"; -import { SettingsSections } from "app/common/store/settings/types" -import { addToast } from "app/modules/toasts-overlay/store/actions"; -import { ToastStyle } from "app/modules/toasts-overlay/store/types"; -import { HttpStatus } from "app/common/types/http.types"; -import { - setSettingsTrainingData, - setSettingsTrainingStatus, -} from "app/modules/settings/fields/settings_training/store/actions"; - -export const uploadSettingsTrainingSubfieldData = (subfield: TrainingSubSection) => { - return async (dispatch: any) => { - let res; - try { - res = await SettingsApi.getSettingsData(SettingsSections.training, subfield); - } - catch (e) { - dispatch(addToast(e.message, ToastStyle.Error)); - dispatch(setSettingsTrainingStatus(HttpStatus.FAILED, subfield)); - return; - } - if (res.warning) { - dispatch(setSettingsTrainingStatus(HttpStatus.FAILED, subfield)); - return - } - dispatch(setSettingsTrainingData(res, subfield)); - dispatch(setSettingsTrainingStatus(HttpStatus.FINISHED, subfield)); - return res; - } -} - -export const sendSettingsTrainingData = (data: any, subfield: TrainingSubSection) => { - return async (dispatch: any) => { - try { - let res = await SettingsApi.sendSettingsData(SettingsSections.training, data, subfield); - return res.result === "success"; - } - catch (e) { - dispatch(addToast(e.message, ToastStyle.Error)); - } - } -} - -export const uploadSettingsTrainingData = () => { - return async (dispatch: any) => { - try { - Object.values(TrainingSubSection).forEach(item => { - dispatch(setSettingsTrainingStatus(HttpStatus.RELOADING, item)); - dispatch(uploadSettingsTrainingSubfieldData(item)); - }) - } - catch (e) { - dispatch(addToast(e.message, ToastStyle.Error)); - } - } -} diff --git a/frontend/src/app/modules/settings/fields/settings_training/store/types.ts b/frontend/src/app/modules/settings/fields/settings_training/store/types.ts deleted file mode 100644 index fe6cdbd..0000000 --- a/frontend/src/app/modules/settings/fields/settings_training/store/types.ts +++ /dev/null @@ -1,71 +0,0 @@ -// Api training routs - -import { HttpStatus } from "app/common/types/http.types"; - -export enum TrainingSubSection { - markup_entities = "markup_entities", - source_field = "source_field", - bug_resolution = "bug_resolution", -} - -// Action types -export enum TrainingActionTypes { - setSettingsTrainingData = "SET_SETTINGS_TRAINING_DATA", - setSettingsTrainingStatus = "SET_SETTINGS_TRAINING_STATUS", - clearSettingsTrainingData = "CLEAR_SETTINGS_TRAINING_DATA", -} - -// Source Field data type - -export interface SourceFieldData { - source_field: string; - source_field_names: string[]; -} - -// Mark Up Entities data type - -export interface MarkUpEntitiesElement { - area_of_testing: string; - entities: string[]; -} - -export interface MarkUpEntitiesData { - mark_up_entities: MarkUpEntitiesElement[]; - entity_names: string[]; -} - -// Bug Resolution data type - -export interface BugResolutionElement { - metric: string; - value: string; -} - -export interface BugResolutionData { - resolution_settings: BugResolutionElement[]; - resolution_names: string[]; -} - -// Training status type - -export interface TrainingStatus { - source_field: HttpStatus; - bug_resolution: HttpStatus; - markup_entities: HttpStatus; -} - -// Training state type - -export interface SettingTrainingStore { - [key: string]: TrainingStatus | SourceFieldData | BugResolutionData | MarkUpEntitiesData; - status: TrainingStatus; - source_field: SourceFieldData; - bug_resolution: BugResolutionData; - markup_entities: MarkUpEntitiesData; -} - -export type SettingsTrainingResponseType = SourceFieldData | BugResolutionData | MarkUpEntitiesData; - -export interface SettingsTrainingResponseError { - warning: string; -} diff --git a/nostradamus/apps/analysis_and_training/main/training.py b/nostradamus/apps/analysis_and_training/main/training.py deleted file mode 100644 index e03cf05..0000000 --- a/nostradamus/apps/analysis_and_training/main/training.py +++ /dev/null @@ -1,604 +0,0 @@ -import concurrent.futures -import json -import numpy -from typing import List, Dict, Union, Tuple - -from pandas import qcut, get_dummies, Categorical, DataFrame, Series, Interval -from sklearn import feature_selection -from sklearn.feature_selection import chi2, SelectKBest -from imblearn.pipeline import Pipeline -from pickle import dumps -from imblearn.over_sampling import SMOTE -from sklearn.svm import SVC -from django.db.models import Model - -from apps.analysis_and_training.main.common import ( - get_stop_words, - check_required_percentage, - check_bugs_count, -) -from apps.authentication.models import User - -from apps.settings.models import ( - UserTrainingParameters, - UserSettings, - UserTopTerms, - UserModels, -) -from utils.stemmed_tfidf_vectorizer import StemmedTfidfVectorizer -from utils.exceptions import ( - SmallNumberRepresentatives, - LittleDataToAnalyze, - ResolutionElementsMissed, - InconsistentGivenData, -) -from utils.warnings import ModelsNotTrainedWarning - -from sklearn.model_selection import GridSearchCV - -ModelParams = List[Dict[str, Dict[str, int]]] -TrainingParameters = Union[List[str], List[int], Dict[str, List[str]]] -ModelClasses = Dict[str, TrainingParameters] -ModelPipeline = Dict[str, Pipeline] - - -def compare_resolutions(issues: DataFrame, resolutions: List[str]) -> set: - """Checks for difference between required resolutions and those that are present in df. - - Parameters: - ---------- - issues: - Bug reports. - resolutions: - bugs resolution. - - Returns: - ---------- - The difference between the required resolutions and those that are present in the dataframe. - """ - return set(resolutions).difference(set(issues.Resolution.unique())) - - -def get_k_neighbors(series: Series) -> int: - """Calculates the number of k-nearest neighbors. - - Parameters: - ---------- - series: - data used for calculations. - - Returns: - ---------- - Number of k-nearest neighbors. - """ - unique_values_count = series.value_counts() - k_neighbors = ( - int(min(unique_values_count) / 2) - if len(unique_values_count) != 0 - else 0 - ) - - if k_neighbors <= 1: - raise SmallNumberRepresentatives( - f"Oops! Too small number of class representatives for {series.name}" - ) - - if len(set(unique_values_count)) == 1: - return 2 - - return k_neighbors - - -def stringify_ttr_intervals(intervals: List[Interval]) -> str: - """Stringifies list of ttr intervals. - - Parameters: - ---------- - intervals: - intervals. - - Returns: - ---------- - Stringified list of intervals. - """ - return ( - [ - str(intervals[0].left if intervals[0].left > 0 else 0) - + "-" - + str(intervals[0].right) - ] - + [ - str(intervals[el].left + 1) + "-" + str(intervals[el].right) - for el in range(1, len(intervals) - 1) - ] - + [">" + str(intervals[len(intervals) - 1].left)] - ) - - -def filter_classes( - issues: DataFrame, areas_of_testing: List[str], resolution: List[str] -) -> ModelClasses: - """Filters out classes with inadequate percentage of representatives. - - Parameters: - ---------- - issues: - Bug reports. - areas_of_testing: - areas of testing classes; - resolution: - resolution classes. - - Returns: - ---------- - Classes of models. - """ - classes = { - "areas_of_testing": areas_of_testing, - "Resolution": resolution, - "Priority": issues["Priority"].unique().tolist(), - "Time to Resolve": issues["Time to Resolve"].unique().tolist(), - } - - filtered_classes = {} - for metric in classes.keys(): - if metric == "areas_of_testing": - filtered_classes[metric] = [ - el - for el in classes[metric] - if check_required_percentage(issues[el], 1) - and len(set(issues[el])) != 1 - ] - else: - filtered_classes[metric] = sorted( - [ - el - for el in classes[metric] - if check_required_percentage(issues[metric], el) - and len(set(issues[metric])) != 1 - ] - ) - - return filtered_classes - - -def encode_series(issues: DataFrame) -> DataFrame: - """Encodes series classes. - - Parameters: - ---------- - issues: - Bug reports. - - Returns: - ---------- - Dataframe containing encoded series. - """ - - # TODO Investigation is required for imbalanced data - issues["Time to Resolve"] = qcut( - issues["Time to Resolve"].astype(int), q=4, duplicates="drop" - ) - issues["time to resolve_codes"] = issues[ - "Time to Resolve" - ].cat.rename_categories(range(len(issues["Time to Resolve"].unique()))) - - issues["priority_codes"] = Categorical( - issues["Priority"], ordered=True - ).codes - - issues = issues.reset_index(drop=True) - series_resolution = issues["Resolution"] - issues = get_dummies(issues, columns=["Resolution"]) - issues["Resolution"] = series_resolution - - return issues - - -def get_best_params(model, X: Series, Y: Series) -> dict: - """Selects optimal parameters for model. - - Parameters: - ---------- - model: - model; - X: - training vector; - Y: - target values. - - Returns: - ---------- - Best parameters for model. - """ - param_grid = {"clf__C": [1, 2], "clf__gamma": [2, 5]} - - gs = GridSearchCV( - model, param_grid, scoring="f1_weighted", cv=10, n_jobs=12 - ) - gs.fit(X, Y) - return gs.best_params_ - - -def train_imbalance( - descr_series: Series, - classes_codes: Series, - TFIDF_, - IMB_, - FS_, - req_percentage: int, - CLF_, - model_name: str, -) -> tuple: - """Trains models using handled setting and saves them as .sav objects. - - Parameters: - ---------- - instance: - Instance of User model. - descr_series: - description series. - classes_codes: - series with classes' codes. - TFIDF_: - vectorizer. - IMB_: - SMOTE instance. - FS_: - ranking terms method. - req_percentage: - percentage to be taken from the ranked list. - CLF_: - classifier. - model_name: - models name. - - Returns: - ---------- - Trained model in byte representation associated to its model name. - - """ - transformer = feature_selection.SelectPercentile(FS_) - clf_model = Pipeline( - [("tfidf", TFIDF_), ("imba", IMB_), ("fs", transformer), ("clf", CLF_)] - ) - - best_params = get_best_params(clf_model, descr_series, classes_codes) - print(f"{model_name}:{best_params}") - - clf_model.set_params( - fs__percentile=req_percentage, - clf__C=best_params["clf__C"], - clf__gamma=best_params["clf__gamma"], - ).fit(descr_series, classes_codes) - - return {model_name: clf_model}, {model_name: best_params} - - -def save_training_parameters( - user: Model, - classes: ModelClasses, - params: ModelParams, -): - """Saves training parameters to the database. - - Parameters: - ---------- - user: - User. - classes: - classes. - params: - params for model. - """ - training_settings = {} - - for class_ in classes: - if class_ == "Resolution": - training_settings["Resolution"] = {} - resolution_settings = training_settings["Resolution"] - for resolution in classes[class_]: - resolution_settings[resolution] = [ - "not " + resolution, - resolution, - ] - else: - training_settings[class_] = classes[class_] - - training_settings["model_params"] = params - - user_settings = UserSettings.objects.get(user=user) - for ( - training_settings_name, - training_settings_value, - ) in training_settings.items(): - UserTrainingParameters.objects.create( - name=training_settings_name, - training_parameters=json.dumps(training_settings_value), - settings=user_settings, - ) - - -def train( - instance: Model, - issues: DataFrame, - areas_of_testing: List[str], - resolution: List[str], -): - """Train models. - - Parameters: - ---------- - instance: - Instance of User model. - issues: - Bug reports. - areas_of_testing: - areas of testing. - resolution: - resolution. - """ - - def _params_producer() -> Tuple[Series, Series, SMOTE, str]: - """Generates parameters for imbalance training. - - Returns: - ---------- - Bugs description, classes codes, SMOTE instance and model name. - """ - for metric in filtered_classes.keys(): - if metric in ["Priority", "Time to Resolve"]: - filtered_df = issues[ - issues[metric].isin(filtered_classes[metric]) - ] - smt = SMOTE( - ratio="minority", - random_state=0, - kind="borderline1", - n_jobs=4, - ) - smt.k_neighbors = get_k_neighbors( - filtered_df[metric.lower() + "_codes"] - ) - classes_codes = filtered_df[metric.lower() + "_codes"] - model_name = metric.split("_")[0] - - yield filtered_df.Description_tr, classes_codes, smt, model_name - else: - for class_ in filtered_classes[metric]: - df_index = ( - "Resolution_" + class_ - if metric == "Resolution" - else class_ - ) - smt = SMOTE( - ratio="minority", - random_state=0, - kind="borderline1", - n_jobs=4, - ) - smt.k_neighbors = get_k_neighbors(issues[df_index]) - - yield issues.Description_tr, issues[df_index], smt, class_ - - issues = issues[ - (issues["Resolution"] != "Unresolved") - & (issues["Resolved"].isna() is not True) - & (issues["Resolved"].notnull()) - ] - issues = issues.reset_index() - - if not check_bugs_count(issues): - raise LittleDataToAnalyze - - issues = encode_series(issues) - filtered_classes = filter_classes(issues, areas_of_testing, resolution) - - # TODO: remove unnecessary resolution verification - # when settings will be linked to data - missing_resolutions = compare_resolutions(issues, resolution) - if missing_resolutions: - raise ResolutionElementsMissed( - f"Oops! These Resolution elements are missed: {missing_resolutions}. Models can't be trained." - ) - - filtered_elements = ( - set(resolution).union(set(areas_of_testing)) - ).difference( - set(filtered_classes.get("Resolution")).union( - set(filtered_classes.get("areas_of_testing")) - ) - ) - - if filtered_elements and filtered_elements != {"Other"}: - raise SmallNumberRepresentatives( - f"Oops! Too little number of class representatives for: {filtered_elements}. Models can't be trained." - ) - - svm_imb = SVC(gamma=2, C=1, probability=True, class_weight="balanced") - sw = get_stop_words(issues) - tfidf = StemmedTfidfVectorizer(stop_words=sw) - - try: - with concurrent.futures.ProcessPoolExecutor() as executor: - models_and_params = { - executor.submit( - train_imbalance, - description, - classes, - tfidf, - smote, - chi2, - 50, - svm_imb, - model_name, - ) - for description, classes, smote, model_name in _params_producer() - } - models = [ - model.result()[0] - for model in concurrent.futures.as_completed(models_and_params) - ] - params = [ - param.result()[1] - for param in concurrent.futures.as_completed(models_and_params) - ] - except ValueError: - raise InconsistentGivenData - - save_models(user=instance, models=models) - - filtered_classes["Time to Resolve"] = stringify_ttr_intervals( - filtered_classes["Time to Resolve"] - ) - filtered_classes["binary"] = [0, 1] - - save_training_parameters( - user=instance, classes=filtered_classes, params=params - ) - - resolutions = [ - "Resolution_" + resol for resol in filtered_classes["Resolution"] - ] - - save_top_terms( - user=instance, - issues=issues, - resolutions=resolutions, - priorities=filtered_classes["Priority"], - areas_of_testing=filtered_classes["areas_of_testing"], - ) - - -def get_top_terms(issues: DataFrame, metric: str) -> dict: - """Calculates top terms. - - Parameters: - ---------- - issues: - Bug reports. - metric: - Value which is used for calculations. - - Returns: - ---------- - Object with calculated terms. - """ - chi2 = feature_selection.chi2 - - sw = get_stop_words(issues) - tfidf = StemmedTfidfVectorizer(stop_words=sw) - tfs = tfidf.fit_transform(issues["Description_tr"]) - - y = issues[metric] - selector = SelectKBest(score_func=chi2, k="all") - selector.fit_transform(tfs, y) - - return dict(zip(tfidf.get_feature_names(), selector.scores_)) - - -def calculate_top_terms(issues: DataFrame, metric: str) -> list: - """Calculates top terms which are based on significance weights. - - Parameters: - ---------- - issues: - Bug reports. - metric: - field which is used for calculation. - - Returns: - ---------- - list of the calculated terms. - - """ - terms = get_top_terms(issues, metric) - - terms = {k: v for (k, v) in terms.items() if v > 1} - return [ - k for (k, v) in terms.items() if v > numpy.mean(list(terms.values())) - ] - - -def save_top_terms( - user: Model, - issues: DataFrame, - resolutions: List[str], - priorities: List[str], - areas_of_testing: List[str], -): - """Saves calculation results to database. - - Parameters: - ---------- - user: - User. - issues: - Bug reports. - resolutions: - Resolutions. - priorities: - Priorities derived after models' training. - areas_of_testing: - Areas of testing derived after models' training. - - """ - binarized_df = get_dummies( - issues, - prefix=["Priority"], - columns=["Priority"], - ) - top_terms = {} - - metrics = ( - ["Priority_" + priority for priority in priorities] - + resolutions - + [area for area in areas_of_testing if area != "Other"] - ) - - for metric in metrics: - top_terms[metric] = calculate_top_terms(binarized_df, metric) - - top_terms = DataFrame( - {metric: Series(terms) for metric, terms in top_terms.items()} - ) - - user_settings = UserSettings.objects.get(user=user) - UserTopTerms.objects.filter(settings=user_settings).delete() - UserTopTerms.objects.create( - top_terms_object=dumps(top_terms), - settings=user_settings, - ) - - -def check_training_files(user: User) -> None: - """Check whether the model is trained. - - Parameters: - ---------- - user: - User. - """ - user_settings = UserSettings.objects.get(user=user) - if not UserTrainingParameters.objects.filter(settings=user_settings): - raise ModelsNotTrainedWarning - - -def save_models(user: Model, models: List[ModelPipeline]): - """Saves models to database. - - Parameters: - ---------- - user: - User. - models: - Models pipelines. - """ - user_settings = UserSettings.objects.get(user=user) - for model in models: - for model_name, model_obj in model.items(): - UserModels.objects.create( - name=model_name, - model=dumps(model_obj), - settings=user_settings, - ) diff --git a/nostradamus/apps/authentication/apps.py b/nostradamus/apps/authentication/apps.py deleted file mode 100644 index 372ba81..0000000 --- a/nostradamus/apps/authentication/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class AuthenticationConfig(AppConfig): - name = "authentication" diff --git a/nostradamus/apps/authentication/main/__init__.py b/nostradamus/apps/authentication/main/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nostradamus/apps/authentication/main/common.py b/nostradamus/apps/authentication/main/common.py deleted file mode 100644 index 42c3436..0000000 --- a/nostradamus/apps/authentication/main/common.py +++ /dev/null @@ -1,26 +0,0 @@ -from apps.authentication.models import Role, TeamMember - -DEFAULT_ROLE = "QA" - - -def bind_user_to_team(user_instance, team) -> None: - """Binds user with team. - - Parameters: - ---------- - user_instance: - User instance. - team: - Team to which user will be attached. - """ - team_member_instance = TeamMember( - user=user_instance, - team=team, - role=get_default_role(), - ) - team_member_instance.save() - - -def get_default_role() -> None: - """Returns default role.""" - return Role.objects.get(name=DEFAULT_ROLE) diff --git a/nostradamus/apps/authentication/main/token_creator.py b/nostradamus/apps/authentication/main/token_creator.py deleted file mode 100644 index ccaf58b..0000000 --- a/nostradamus/apps/authentication/main/token_creator.py +++ /dev/null @@ -1,79 +0,0 @@ -from rest_framework_simplejwt.tokens import AccessToken - -from apps.authentication.models import User, Team, Role, TeamMember -from utils.exceptions import IncorrectUserCredentials - - -class TokenCreator: - def __init__(self, credentials, password): - self.password = password - self.credentials = credentials - self.__user = self.__get_user() - - def create_jwt(self) -> dict: - """Generates jwt with user data if input credentials are valid. - - Returns: - ---------- - Jwt with 15 weeks expiration date. - """ - - if self.__check_password(): - payload = self.__create_jwt_payload() - payload["token"] = str(AccessToken.for_user(self.__user)) - - return payload - - def __create_jwt_payload(self) -> dict: - """Receives all information about __user object. - - Returns: - ---------- - User information. - """ - - payload = { - "id": self.__user.id, - "name": self.__user.name, - "email": self.__user.email, - } - - team_member = TeamMember.objects.filter(user=self.__user).first() - team = Team.objects.filter(id=team_member.team.id).first().name - role = Role.objects.filter(id=team_member.role.id).first().name - - payload["team"] = team - payload["role"] = role - - return payload - - def __check_password(self) -> bool: - """Checks that user and user password are valid. - - Returns: - ---------- - True if user sent valid credentials otherwise False. - """ - - if self.__user and self.__user.check_password(self.password): - return True - - raise IncorrectUserCredentials - - def __get_user(self) -> User: - """Generates private User object for password checking. - - Returns: - ---------- - User object which can be accessed only inside TokenCreator class. - """ - - query = User.objects.filter(name__iexact=self.credentials) - - if query: - return query.first() - - query = User.objects.filter(email__iexact=self.credentials) - - if query: - return query.first() diff --git a/nostradamus/apps/authentication/migrations/0001_initial.py b/nostradamus/apps/authentication/migrations/0001_initial.py deleted file mode 100644 index dc9018d..0000000 --- a/nostradamus/apps/authentication/migrations/0001_initial.py +++ /dev/null @@ -1,203 +0,0 @@ -# Generated by Django 3.0.3 on 2020-03-20 14:47 - -from django.conf import settings -import django.contrib.auth.models -import django.contrib.auth.validators -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ("auth", "0011_update_proxy_permissions"), - ] - - operations = [ - migrations.CreateModel( - name="User", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "password", - models.CharField(max_length=128, verbose_name="password"), - ), - ( - "last_login", - models.DateTimeField( - blank=True, null=True, verbose_name="last login" - ), - ), - ( - "is_superuser", - models.BooleanField( - default=False, - help_text="Designates that this user has all permissions without explicitly assigning them.", - verbose_name="superuser status", - ), - ), - ( - "username", - models.CharField( - error_messages={ - "unique": "A user with that username already exists." - }, - help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", - max_length=150, - unique=True, - validators=[ - django.contrib.auth.validators.UnicodeUsernameValidator() - ], - verbose_name="username", - ), - ), - ( - "first_name", - models.CharField( - blank=True, max_length=30, verbose_name="first name" - ), - ), - ( - "last_name", - models.CharField( - blank=True, max_length=150, verbose_name="last name" - ), - ), - ( - "is_staff", - models.BooleanField( - default=False, - help_text="Designates whether the user can log into this admin site.", - verbose_name="staff status", - ), - ), - ( - "is_active", - models.BooleanField( - default=True, - help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", - verbose_name="active", - ), - ), - ( - "date_joined", - models.DateTimeField( - default=django.utils.timezone.now, - verbose_name="date joined", - ), - ), - ("name", models.CharField(max_length=64, unique=True)), - ("email", models.EmailField(max_length=254, unique=True)), - ( - "groups", - models.ManyToManyField( - blank=True, - help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", - related_name="user_set", - related_query_name="user", - to="auth.Group", - verbose_name="groups", - ), - ), - ( - "user_permissions", - models.ManyToManyField( - blank=True, - help_text="Specific permissions for this user.", - related_name="user_set", - related_query_name="user", - to="auth.Permission", - verbose_name="user permissions", - ), - ), - ], - options={ - "verbose_name": "User", - }, - managers=[ - ("objects", django.contrib.auth.models.UserManager()), - ], - ), - migrations.CreateModel( - name="Role", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=64, unique=True)), - ], - options={ - "verbose_name": "Role", - }, - ), - migrations.CreateModel( - name="Team", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=64, unique=True)), - ], - options={ - "verbose_name": "Team", - }, - ), - migrations.CreateModel( - name="TeamMember", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "role", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="authentication.Role", - ), - ), - ( - "team", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="authentication.Team", - ), - ), - ( - "user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL, - ), - ), - ], - ), - ] diff --git a/nostradamus/apps/authentication/migrations/__init__.py b/nostradamus/apps/authentication/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nostradamus/apps/authentication/serializers.py b/nostradamus/apps/authentication/serializers.py deleted file mode 100644 index 20f6e3c..0000000 --- a/nostradamus/apps/authentication/serializers.py +++ /dev/null @@ -1,103 +0,0 @@ -from collections import OrderedDict - -from django.core.validators import MinLengthValidator, MaxLengthValidator -from rest_framework import serializers - -from apps.authentication.models import Team, User -from apps.authentication.validators import ( - password_whitespaces_validator, - name_whitespaces_validator, - email_whitespaces_validator, - name_special_symbols_validator, - email_validator, - unique_user_name, - unique_user_email, -) - - -class TeamSerializer(serializers.ModelSerializer): - class Meta: - model = Team - fields = ( - "id", - "name", - ) - - -class UserSerializer(serializers.ModelSerializer): - password = serializers.CharField( - write_only=True, - validators=[ - MinLengthValidator(limit_value=6), - MaxLengthValidator(limit_value=254), - password_whitespaces_validator, - ], - ) - name = serializers.CharField( - validators=[ - name_whitespaces_validator, - unique_user_name, - MaxLengthValidator(limit_value=64), - name_special_symbols_validator, - ] - ) - email = serializers.EmailField( - validators=[ - email_whitespaces_validator, - unique_user_email, - MaxLengthValidator(limit_value=254), - email_validator, - ] - ) - team = serializers.IntegerField(required=True) - - class Meta: - model = User - fields = ("id", "name", "email", "password", "team") - - def create(self, validated_data: OrderedDict) -> User: - """ Creates new User instance when calling serializer.save() method. - - Parameters: - ---------- - validated_data: - Serializer property created while user_serializer.is_valid() method - calling. - - Returns: - ---------- - User instance. - """ - - user_instance = User( - name=validated_data["name"], - username=validated_data["name"], - email=validated_data["email"], - ) - user_instance.set_password(validated_data["password"]) - user_instance.save() - - return user_instance - - def get_team(self) -> Team: - return Team.objects.get(id=self.validated_data["team"]) - - -class AuthRequestSerializer(serializers.Serializer): - credentials = serializers.CharField(required=True) - password = serializers.CharField(required=True) - - def get_credentials(self) -> tuple: - return ( - self.validated_data["credentials"].lower(), - self.validated_data["password"], - ) - - -class AuthResponseSerializer(serializers.Serializer): - id = serializers.IntegerField() - name = serializers.CharField() - email = serializers.CharField() - team = serializers.CharField() - role = serializers.CharField() - token = serializers.CharField() diff --git a/nostradamus/apps/authentication/urls.py b/nostradamus/apps/authentication/urls.py deleted file mode 100644 index 9cb8649..0000000 --- a/nostradamus/apps/authentication/urls.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.urls import path - -from .views import AuthenticationView, RegistrationView - -urlpatterns = [ - path("signin/", AuthenticationView.as_view()), - path("register/", RegistrationView.as_view()), -] diff --git a/nostradamus/apps/authentication/validators.py b/nostradamus/apps/authentication/validators.py deleted file mode 100644 index 778cb1d..0000000 --- a/nostradamus/apps/authentication/validators.py +++ /dev/null @@ -1,69 +0,0 @@ -import re - -from rest_framework import serializers -from string import whitespace - -from apps.authentication.models import User - - -def name_whitespaces_validator(value: str) -> None: - whitespaces_regex = re.compile(f"[{whitespace}]") - - if whitespaces_regex.search(value): - raise serializers.ValidationError( - "Ensure that username doesn't have whitespaces." - ) - - -def password_whitespaces_validator(value: str) -> None: - whitespaces_regex = re.compile(f"[{whitespace}]") - - if whitespaces_regex.search(value): - raise serializers.ValidationError( - "Ensure that password doesn't have whitespaces." - ) - - -def email_whitespaces_validator(value: str) -> None: - whitespaces_regex = re.compile(f"[{whitespace}]") - - if whitespaces_regex.search(value): - raise serializers.ValidationError( - "Ensure that email doesn't have whitespaces." - ) - - -def name_special_symbols_validator(value: str) -> None: - special_symbols_regex = re.compile(r"[-№`/!-,:-@[-^{-~]") - - if special_symbols_regex.search(value): - raise serializers.ValidationError( - "Ensure that name doesn't have special symbols." - ) - - -def email_validator(value: str) -> None: - email_regex = re.compile(r"[№/!--:-?[-`{-~]") - - if email_regex.search(value): - raise serializers.ValidationError( - "Ensure that email doesn't have special characters." - ) - - -def unique_user_name(value: str) -> None: - user = User.objects.filter(name__iexact=value) - - if user: - raise serializers.ValidationError( - "User with this name already exists." - ) - - -def unique_user_email(value: str) -> None: - user = User.objects.filter(email__iexact=value) - - if user: - raise serializers.ValidationError( - "User with this email already exists." - ) diff --git a/nostradamus/apps/authentication/views.py b/nostradamus/apps/authentication/views.py deleted file mode 100644 index 9be54e6..0000000 --- a/nostradamus/apps/authentication/views.py +++ /dev/null @@ -1,60 +0,0 @@ -from rest_framework.permissions import AllowAny - -from rest_framework.response import Response -from rest_framework.views import APIView - -from drf_yasg.utils import swagger_auto_schema - -from apps.authentication.main.common import bind_user_to_team - -from apps.authentication.serializers import ( - TeamSerializer, - UserSerializer, - AuthRequestSerializer, - AuthResponseSerializer, -) -from apps.authentication.main.token_creator import TokenCreator -from apps.authentication.models import Team - - -class AuthenticationView(APIView): - permission_classes = [AllowAny] - - @swagger_auto_schema( - query_serializer=AuthRequestSerializer, - responses={200: AuthResponseSerializer}, - ) - def post(self, request): - credentials_serializer = AuthRequestSerializer(data=request.data) - credentials_serializer.is_valid(raise_exception=True) - credentials, password = credentials_serializer.get_credentials() - - token_payload = TokenCreator(credentials, password).create_jwt() - - return Response(token_payload) - - -class RegistrationView(APIView): - permission_classes = [AllowAny] - - @swagger_auto_schema(responses={200: TeamSerializer}) - def get(self, request): - teams = Team.objects.all() - - teams_serializer = TeamSerializer(teams, many=True) - data = teams_serializer.data - return Response(data) - - @swagger_auto_schema( - query_serializer=UserSerializer, responses={200: "success"} - ) - def post(self, request): - user_serializer = UserSerializer(data=request.data) - - user_serializer.is_valid(raise_exception=True) - team = user_serializer.get_team() - user = user_serializer.save() - - bind_user_to_team(user, team) - - return Response({"result": "success"}) diff --git a/nostradamus/apps/extractor/migrations/__init__.py b/nostradamus/apps/extractor/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nostradamus/apps/qa_metrics/migrations/__init__.py b/nostradamus/apps/qa_metrics/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/nostradamus/apps/settings/migrations/0002_auto.py b/nostradamus/apps/settings/migrations/0002_auto.py deleted file mode 100644 index 6536526..0000000 --- a/nostradamus/apps/settings/migrations/0002_auto.py +++ /dev/null @@ -1,25 +0,0 @@ -from django.db import migrations - -from apps.authentication.models import Team - - -def create_nostradamus_team(apps, schema_editor): - Team.objects.create(name="Nostradamus") - - -def create_qa_role(apps, schema_editor): - qa_table = apps.get_model("authentication", "Role") - qa_table.objects.create(name="QA") - - -class Migration(migrations.Migration): - - dependencies = [("settings", "0001_initial")] - operations = [ - migrations.RunPython( - create_nostradamus_team, - ), - migrations.RunPython( - create_qa_role, - ), - ] diff --git a/nostradamus/apps/settings/migrations/0003_new_tables.py b/nostradamus/apps/settings/migrations/0003_new_tables.py deleted file mode 100644 index c66e44b..0000000 --- a/nostradamus/apps/settings/migrations/0003_new_tables.py +++ /dev/null @@ -1,150 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-26 19:04 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("settings", "0002_auto"), - ] - - operations = [ - migrations.CreateModel( - name="UserTopTerms", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("top_terms_object", models.BinaryField()), - ( - "settings", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="settings.UserSettings", - ), - ), - ], - ), - migrations.CreateModel( - name="UserTrainingParameters", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=128)), - ("training_parameters", models.TextField()), - ( - "settings", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="settings.UserSettings", - ), - ), - ], - ), - migrations.CreateModel( - name="UserSourceField", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=128)), - ( - "settings", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="settings.UserSettings", - ), - ), - ], - ), - migrations.CreateModel( - name="UserModels", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=128)), - ("model", models.BinaryField()), - ( - "settings", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="settings.UserSettings", - ), - ), - ], - ), - migrations.CreateModel( - name="UserMarkUpEntity", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=128)), - ("entities", models.TextField()), - ( - "settings", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="settings.UserSettings", - ), - ), - ], - ), - migrations.CreateModel( - name="UserBugResolution", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("metric", models.CharField(max_length=128)), - ("value", models.CharField(max_length=128)), - ( - "settings", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="settings.UserSettings", - ), - ), - ], - ), - ] diff --git a/nostradamus/tests/analysis_and_training_tests/test_train.py b/nostradamus/tests/analysis_and_training_tests/test_train.py deleted file mode 100644 index cd0c031..0000000 --- a/nostradamus/tests/analysis_and_training_tests/test_train.py +++ /dev/null @@ -1,89 +0,0 @@ -import pandas as pd -import pytest - -from apps.analysis_and_training.main.training import ( - compare_resolutions, - get_k_neighbors, - stringify_ttr_intervals, -) - -from utils.exceptions import SmallNumberRepresentatives - - -def test_compare_resolutions_positive(train_df): - resolutions = ["Duplicated", "Resolved"] - assert not compare_resolutions(train_df, resolutions) - - -def test_compare_resolutions_positive_2(train_df): - resolutions = ["Duplicated"] - assert not compare_resolutions(train_df, resolutions) - - -def test_compare_resolutions_negative(train_df): - resolutions = ["Duplicated", "Fixed", "Resolved"] - assert compare_resolutions(train_df, resolutions) == set(["Fixed"]) - - -def test_get_k_neighbors_positive(train_df): - assert get_k_neighbors(train_df["Description_tr"]) == 2 - - -def test_get_k_neighbors_positive_2(train_df): - len_df = len(train_df) - test_df = train_df - for i in range(len_df, len_df + 4): - test_df.loc[i] = [ - i, - "test", - "defect,analysis,coffee", - 8, - pd.to_datetime("08-01-2020", utc=True, dayfirst=True), - 1, - 0, - 10, - "Duplicated", - "Low", - ] - - assert get_k_neighbors(test_df["Description_tr"]) == 2 - - -def test_get_k_neighbors_negative(train_df): - test_df = train_df - test_df.loc[len(train_df)] = [ - len(train_df), - "test", - "defect,analysis,coffee", - 8, - pd.to_datetime("08-01-2020", utc=True, dayfirst=True), - 1, - 0, - 10, - "Duplicated", - "Low", - ] - with pytest.raises(SmallNumberRepresentatives): - get_k_neighbors(test_df["Description_tr"]) - - -def test_stringify_ttr_intervals_positive(): - intervals = [ - pd.Interval(0, 4), - pd.Interval(4, 6), - pd.Interval(6, 9), - pd.Interval(9, 10), - ] - expected_result = ["0-4", "5-6", "7-9", ">9"] - assert stringify_ttr_intervals(intervals) == expected_result - - -def test_stringify_ttr_intervals_negative(): - intervals = [ - pd.Interval(-0.01, 4), - pd.Interval(4, 6), - pd.Interval(6, 9), - pd.Interval(9, 10), - ] - expected_result = ["0-4", "5-6", "7-9", ">9"] - assert stringify_ttr_intervals(intervals) == expected_result diff --git a/nostradamus/tests/auth_tests/test_register.py b/nostradamus/tests/auth_tests/test_register.py deleted file mode 100644 index 3a66089..0000000 --- a/nostradamus/tests/auth_tests/test_register.py +++ /dev/null @@ -1,98 +0,0 @@ -import requests -import unittest -import pytest -import os - -os.environ["DJANGO_SETTINGS_MODULE"] = "nostradamus.settings" - -import django - -django.setup() - -from apps.authentication.models import User - - -@pytest.mark.usefixtures( - "sql_conn", "test_user_1", "test_user_2", "host", "register_url" -) -class TestRegister(unittest.TestCase): - def teardown_method(self, _): - User.objects.filter(name=self.test_user_1["name"]).delete() - User.objects.filter(name=self.test_user_2["name"]).delete() - - def test_get_teams(self): - request = requests.get(self.host + self.register_url) - teams = {team["name"] for team in request.json()} - - self.conn.execute("SELECT name FROM authentication_team") - db_teams = set(*self.conn.fetchall()) - - assert db_teams == teams - - def test_register_success(self): - request = requests.post( - self.host + self.register_url, data=self.test_user_1 - ) - - assert request.json() == {"result": "success"} - - def test_registered_already(self): - requests.post(self.host + self.register_url, data=self.test_user_2) - request = requests.post( - self.host + self.register_url, data=self.test_user_2 - ) - assert request.status_code == 400 - - def test_check_registered_user(self): - self.conn.execute( - f"SELECT * FROM authentication_user WHERE name='{self.test_user_1['name']}' AND email='{self.test_user_1['email']}'", - ) - - result = self.conn.fetchall() - - assert result is not None - - def test_special_symbols(self): - test_user = self.test_user_1.copy() - test_user.update( - {"name": "test#$%user", "email": "test.user1@test.com"} - ) - - request = requests.post(self.host + self.register_url, data=test_user) - assert ( - request.json()["exception"]["fields"][0]["name"] == "name" - and request.json()["exception"]["fields"][0]["errors"][0] - == "Ensure that name doesn't have special symbols." - ) - - def test_password_length(self): - test_user = self.test_user_1.copy() - test_user.update( - { - "name": "test.user.2", - "email": "test.user.2@test.com", - "password": 123, - } - ) - request = requests.post(self.host + self.register_url, data=test_user) - - assert ( - request.status_code == 400 - and request.json()["exception"]["fields"][0]["name"] == "password" - ) - - def test_whitespaces_credentials(self): - test_user = self.test_user_1.copy() - test_user.update( - { - "name": "test.user 3", - "email": "test.user3@test.com", - } - ) - request = requests.post(self.host + self.register_url, data=test_user) - - assert ( - request.json()["exception"]["fields"][0]["name"] == "name" - and request.json()["exception"]["fields"][0]["errors"][0] - == "Ensure that username doesn't have whitespaces." - ) diff --git a/nostradamus/tests/auth_tests/test_signin.py b/nostradamus/tests/auth_tests/test_signin.py deleted file mode 100644 index 40b1585..0000000 --- a/nostradamus/tests/auth_tests/test_signin.py +++ /dev/null @@ -1,134 +0,0 @@ -import os -import requests -import unittest -import pytest - -os.environ["DJANGO_SETTINGS_MODULE"] = "nostradamus.settings" - -import django - -django.setup() - -from apps.authentication.models import User - - -@pytest.mark.usefixtures( - "sql_conn", "test_user_1", "host", "signin_url", "register_url" -) -class TestRegister(unittest.TestCase): - def teardown_method(self, method): - User.objects.filter(name=self.test_user_1["name"]).delete() - - def test_auth_by_username(self): - requests.post( - self.host + self.register_url, data=self.test_user_1 - ).json() - - test_user = { - "credentials": self.test_user_1["name"], - "password": self.test_user_1["password"], - } - - request = requests.post(self.host + self.signin_url, data=test_user) - data = request.json() - - assert data is not None and "exception" not in data - - def test_auth_by_email(self): - requests.post( - self.host + self.register_url, data=self.test_user_1 - ).json() - - test_user = { - "credentials": self.test_user_1["email"], - "password": self.test_user_1["password"], - } - - request = requests.post(self.host + self.signin_url, data=test_user) - data = request.json() - - assert data is not None and "exception" not in data - - def test_auth_error(self): - requests.post( - self.host + self.register_url, data=self.test_user_1 - ).json() - - test_user = { - "credentials": self.test_user_1["email"], - "password": "1234Pass", - } - - request = requests.post(self.host + self.signin_url, data=test_user) - data = request.json() - - assert data is not None and "exception" in data - - def test_auth_data(self): - requests.post( - self.host + self.register_url, data=self.test_user_1 - ).json() - - test_user = { - "credentials": self.test_user_1["name"], - "password": self.test_user_1["password"], - } - - self.conn.execute( - f"SELECT name FROM authentication_team WHERE id=%s", - (self.test_user_1["team"],), - ) - team = self.conn.fetchone()[0] - - self.conn.execute( - f"SELECT id FROM authentication_user WHERE email=%s", - (self.test_user_1["email"],), - ) - user_id = self.conn.fetchone()[0] - - self.conn.execute( - f""" - SELECT - name - FROM - authentication_role AS r - INNER JOIN - authentication_teammember AS tm - ON r.id = tm.role_id - WHERE - tm.user_id = '{user_id}' - """, - ) - role = self.conn.fetchone()[0] - - request = requests.post(self.host + self.signin_url, data=test_user) - data = request.json() - - assert all( - [ - data["id"] == user_id, - data["name"] == self.test_user_1["name"], - data["email"] == self.test_user_1["email"], - data["team"] == team, - data["role"] == role, - ] - ) - - def test_auth_token(self): - requests.post( - self.host + self.register_url, data=self.test_user_1 - ).json() - - filter_route = "analysis_and_training/" - test_user = { - "credentials": self.test_user_1["name"], - "password": self.test_user_1["password"], - } - - request = requests.post(self.host + self.signin_url, data=test_user) - token = "JWT " + request.json()["token"] - headers = {"Authorization": token} - - request = requests.get(self.host + filter_route, headers=headers) - - assert request.status_code == 200 diff --git a/nostradamus/tests/views_tests/test_auth_views.py b/nostradamus/tests/views_tests/test_auth_views.py deleted file mode 100644 index 2312219..0000000 --- a/nostradamus/tests/views_tests/test_auth_views.py +++ /dev/null @@ -1,22 +0,0 @@ -import requests - -from pytest import mark -from unittest import TestCase - - -@mark.usefixtures("host", "auth_route") -class TestAuthViews(TestCase): - def test_get_register(self): - request = requests.head(f"{self.host}{self.auth_route}register/") - - assert "GET" in request.headers["Allow"] - - def test_post_register(self): - request = requests.head(f"{self.host}{self.auth_route}register/") - - assert "POST" in request.headers["Allow"] - - def test_post_signin(self): - request = requests.head(f"{self.host}{self.auth_route}signin/") - - assert "POST" in request.headers["Allow"]