From c47080971256d01b23b54d27be679ccdb6689afd Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Wed, 24 Feb 2021 16:30:33 +0100 Subject: [PATCH 01/73] Enable editing an existing matching solution --- .../AddAlgorithmDialog.View.tsx | 9 +-- .../AddAlgorithmDialog/AddAlgorithmDialog.tsx | 5 +- .../AddAlgorithmDialogProps.ts | 1 + app/src/components/AddFab/AddAlgorithmFab.tsx | 4 +- .../AlgorithmCard/AlgorithmCard.View.tsx | 11 ++- .../AlgorithmCard/AlgorithmCard.tsx | 4 ++ .../AlgorithmCard/AlgorithmCardProps.ts | 1 + .../actions/AddAlgorithmDialogStoreActions.ts | 68 +++++++++++++++++-- app/src/store/actions/actionTypes.ts | 3 +- app/src/store/models.ts | 1 + .../reducers/AddAlgorithmDialogReducer.ts | 13 +++- app/src/utils/statusMessages.ts | 1 + 12 files changed, 105 insertions(+), 16 deletions(-) diff --git a/app/src/components/AddAlgorithmDialog/AddAlgorithmDialog.View.tsx b/app/src/components/AddAlgorithmDialog/AddAlgorithmDialog.View.tsx index 2f0ec0fb..e4f89f49 100644 --- a/app/src/components/AddAlgorithmDialog/AddAlgorithmDialog.View.tsx +++ b/app/src/components/AddAlgorithmDialog/AddAlgorithmDialog.View.tsx @@ -11,13 +11,14 @@ import { } from '@ionic/react'; import { AddAlgorithmDialogProps } from 'components/AddAlgorithmDialog/AddAlgorithmDialogProps'; import ModalDialog from 'components/ModalDialog/ModalDialog'; -import { addCircleOutline, closeCircleOutline } from 'ionicons/icons'; +import { addCircleOutline, closeCircleOutline, pencil } from 'ionicons/icons'; import React from 'react'; const AddAlgorithmDialogView = ({ algorithmDescription, algorithmName, isOpen, + isAddDialog, clickOnCancel, clickOnAdd, changeAlgorithmDescription, @@ -25,7 +26,7 @@ const AddAlgorithmDialogView = ({ closeDialog, }: AddAlgorithmDialogProps): JSX.Element => ( @@ -51,8 +52,8 @@ const AddAlgorithmDialogView = ({
- - Add + + {isAddDialog ? 'Add' : 'Update'} ({ + isAddDialog: state.AddAlgorithmDialogStore.algorithmId === null, isOpen: state.AddAlgorithmDialogStore.isOpen, algorithmDescription: state.AddAlgorithmDialogStore.algorithmDescription, algorithmName: state.AddAlgorithmDialogStore.algorithmName, @@ -30,7 +31,7 @@ const mapDispatchToProps = ( changeAlgorithmDescription: (event: IonChangeEvent): void => dispatch(changeAlgorithmDescription(event.detail.value as string)), clickOnAdd(): void { - dispatch(addAlgorithm()).then(); + dispatch(addOrUpdateAlgorithm()).then(); }, }); diff --git a/app/src/components/AddAlgorithmDialog/AddAlgorithmDialogProps.ts b/app/src/components/AddAlgorithmDialog/AddAlgorithmDialogProps.ts index a8d8f71f..915239f1 100644 --- a/app/src/components/AddAlgorithmDialog/AddAlgorithmDialogProps.ts +++ b/app/src/components/AddAlgorithmDialog/AddAlgorithmDialogProps.ts @@ -4,6 +4,7 @@ export interface AddAlgorithmDialogStateProps { algorithmName: string; isOpen: boolean; algorithmDescription: string; + isAddDialog: boolean; } export interface AddAlgorithmDialogDispatchProps { diff --git a/app/src/components/AddFab/AddAlgorithmFab.tsx b/app/src/components/AddFab/AddAlgorithmFab.tsx index e15b8a35..a8fc523b 100644 --- a/app/src/components/AddFab/AddAlgorithmFab.tsx +++ b/app/src/components/AddFab/AddAlgorithmFab.tsx @@ -1,13 +1,13 @@ import AddFabView from 'components/AddFab/AddFab.View'; import { AddFabDispatchProps } from 'components/AddFab/AddFabProps'; import { connect } from 'react-redux'; -import { openDialog } from 'store/actions/AddAlgorithmDialogStoreActions'; +import { openAddDialog } from 'store/actions/AddAlgorithmDialogStoreActions'; import { SnowmanDispatch } from 'store/messages'; const mapDispatchToProps = ( dispatch: SnowmanDispatch ): AddFabDispatchProps => ({ - clickOnFab: (): void => dispatch(openDialog()), + clickOnFab: (): void => dispatch(openAddDialog()), }); const AddAlgorithmFab = connect(null, mapDispatchToProps)(AddFabView); diff --git a/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx b/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx index cb7864f2..954b8d63 100644 --- a/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx +++ b/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx @@ -7,12 +7,13 @@ import { IonIcon, } from '@ionic/react'; import { AlgorithmCardProps } from 'components/AlgorithmCard/AlgorithmCardProps'; -import { trash } from 'ionicons/icons'; +import { pencil, trash } from 'ionicons/icons'; import React from 'react'; const AlgorithmCardView = ({ algorithm, deleteAlgorithm, + editAlgorithm, }: AlgorithmCardProps): JSX.Element => ( @@ -27,6 +28,14 @@ const AlgorithmCardView = ({ > + + + {algorithm.description} diff --git a/app/src/components/AlgorithmCard/AlgorithmCard.tsx b/app/src/components/AlgorithmCard/AlgorithmCard.tsx index 9894fff3..93802e3b 100644 --- a/app/src/components/AlgorithmCard/AlgorithmCard.tsx +++ b/app/src/components/AlgorithmCard/AlgorithmCard.tsx @@ -4,6 +4,7 @@ import { AlgorithmCardOwnProps, } from 'components/AlgorithmCard/AlgorithmCardProps'; import { connect } from 'react-redux'; +import { openChangeDialog } from 'store/actions/AddAlgorithmDialogStoreActions'; import { deleteAlgorithm } from 'store/actions/AlgorithmsStoreActions'; import { SnowmanDispatch } from 'store/messages'; @@ -14,6 +15,9 @@ const mapDispatchToProps = ( deleteAlgorithm() { dispatch(deleteAlgorithm(ownProps.algorithm.id)).then(); }, + editAlgorithm() { + dispatch(openChangeDialog(ownProps.algorithm.id)).then(); + }, }); const AlgorithmCard = connect(null, mapDispatchToProps)(AlgorithmCardView); diff --git a/app/src/components/AlgorithmCard/AlgorithmCardProps.ts b/app/src/components/AlgorithmCard/AlgorithmCardProps.ts index 53c5db2e..cc33b696 100644 --- a/app/src/components/AlgorithmCard/AlgorithmCardProps.ts +++ b/app/src/components/AlgorithmCard/AlgorithmCardProps.ts @@ -5,6 +5,7 @@ export interface AlgorithmCardOwnProps { } export interface AlgorithmCardDispatchProps { deleteAlgorithm(): void; + editAlgorithm(): void; } export type AlgorithmCardProps = AlgorithmCardOwnProps & diff --git a/app/src/store/actions/AddAlgorithmDialogStoreActions.ts b/app/src/store/actions/AddAlgorithmDialogStoreActions.ts index 0e9af11d..6bc3d05d 100644 --- a/app/src/store/actions/AddAlgorithmDialogStoreActions.ts +++ b/app/src/store/actions/AddAlgorithmDialogStoreActions.ts @@ -1,22 +1,46 @@ -import { AlgorithmApi } from 'api'; +import { Algorithm, AlgorithmApi } from 'api'; import { AddAlgorithmDialogStoreActionTypes as DialogActions } from 'store/actions/actionTypes'; import { getAlgorithms } from 'store/actions/AlgorithmsStoreActions'; import { SnowmanDispatch, SnowmanThunkAction } from 'store/messages'; import { store } from 'store/store'; +import { MagicNotPossibleId } from 'utils/constants'; import { easyPrimitiveAction, easyPrimitiveActionReturn, } from 'utils/easyActionsFactory'; import RequestHandler from 'utils/requestHandler'; -import { SUCCESS_TO_ADD_NEW_ALGORITHM } from 'utils/statusMessages'; +import { + SUCCESS_TO_ADD_NEW_ALGORITHM, + SUCCESS_UPDATE_ALGORITHM, +} from 'utils/statusMessages'; -export const openDialog = (): easyPrimitiveActionReturn => +export const openAddDialog = (): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.OPEN_DIALOG, + type: DialogActions.OPEN_ADD_DIALOG, // reducer ignores payload payload: false, }); +export const openChangeDialog = ( + algorithmId: number +): SnowmanThunkAction> => async ( + dispatch: SnowmanDispatch +): Promise => { + return RequestHandler( + (): Promise => + new AlgorithmApi() + .getAlgorithm({ algorithmId: algorithmId }) + .then((anAlgorithm: Algorithm) => + dispatch({ + type: DialogActions.OPEN_CHANGE_DIALOG, + payload: anAlgorithm, + }) + ) + .then(), + dispatch + ); +}; + export const closeDialog = (): easyPrimitiveActionReturn => easyPrimitiveAction({ type: DialogActions.CLOSE_DIALOG, @@ -45,7 +69,7 @@ export const resetDialog = (): easyPrimitiveActionReturn => payload: false, }); -export const addAlgorithm = (): SnowmanThunkAction> => async ( +const addAlgorithm = (): SnowmanThunkAction> => async ( dispatch: SnowmanDispatch ): Promise => RequestHandler( @@ -68,3 +92,37 @@ export const addAlgorithm = (): SnowmanThunkAction> => async ( dispatch, SUCCESS_TO_ADD_NEW_ALGORITHM ); + +const updateAlgorithm = (): SnowmanThunkAction> => async ( + dispatch: SnowmanDispatch +): Promise => + RequestHandler( + (): Promise => + new AlgorithmApi() + .setAlgorithm({ + algorithmId: + store.getState().AddAlgorithmDialogStore.algorithmId ?? + MagicNotPossibleId, + algorithmValues: { + description: store.getState().AddAlgorithmDialogStore + .algorithmDescription, + name: store.getState().AddAlgorithmDialogStore.algorithmName, + }, + }) + .then((): void => dispatch(resetDialog())) + .finally((): void => { + dispatch(closeDialog()); + dispatch(getAlgorithms()); + }), + dispatch, + SUCCESS_UPDATE_ALGORITHM + ); + +export const addOrUpdateAlgorithm = (): SnowmanThunkAction< + Promise +> => async (dispatch: SnowmanDispatch): Promise => { + if (store.getState().AddAlgorithmDialogStore.algorithmId === null) { + return dispatch(addAlgorithm()); + } + return dispatch(updateAlgorithm()); +}; diff --git a/app/src/store/actions/actionTypes.ts b/app/src/store/actions/actionTypes.ts index 6fb30efa..7c867d0d 100644 --- a/app/src/store/actions/actionTypes.ts +++ b/app/src/store/actions/actionTypes.ts @@ -45,7 +45,8 @@ export const AddExperimentDialogStoreActionTypes = { }; export const AddAlgorithmDialogStoreActionTypes = { - OPEN_DIALOG: 'ADD_ALGORITHM_DIALOG_STORE-OPEN_DIALOG', + OPEN_ADD_DIALOG: 'ADD_ALGORITHM_DIALOG_STORE-OPEN_ADD_DIALOG', + OPEN_CHANGE_DIALOG: 'ADD_ALGORITHM_DIALOG_STORE-OPEN_CHANGE_DIALOG', CLOSE_DIALOG: 'ADD_ALGORITHM_DIALOG_STORE-CLOSE_DIALOG', CHANGE_ALGORITHM_NAME: 'ADD_ALGORITHM_DIALOG_STORE-CHANGE_ALGORITHM_NAME', CHANGE_ALGORITHM_DESCRIPTION: diff --git a/app/src/store/models.ts b/app/src/store/models.ts index f97356ae..79f17125 100644 --- a/app/src/store/models.ts +++ b/app/src/store/models.ts @@ -10,6 +10,7 @@ import experimentFileFormatEnum from 'types/ExperimentFileFormats'; import { MetricsTuplesCategories } from 'types/MetricsTuplesCategories'; export interface AddAlgorithmDialogStore { + algorithmId: number | null; algorithmName: string; algorithmDescription: string; isOpen: boolean; diff --git a/app/src/store/reducers/AddAlgorithmDialogReducer.ts b/app/src/store/reducers/AddAlgorithmDialogReducer.ts index d48d071f..432f429f 100644 --- a/app/src/store/reducers/AddAlgorithmDialogReducer.ts +++ b/app/src/store/reducers/AddAlgorithmDialogReducer.ts @@ -1,9 +1,11 @@ +import { Algorithm } from 'api'; import { AddAlgorithmDialogStoreActionTypes as DialogActions } from 'store/actions/actionTypes'; import { SnowmanAction } from 'store/messages'; import { AddAlgorithmDialogStore } from 'store/models'; const initialState: AddAlgorithmDialogStore = { isOpen: false, + algorithmId: null, algorithmName: '', algorithmDescription: '', }; @@ -13,15 +15,24 @@ export const AddAlgorithmDialogReducer = ( action: SnowmanAction ): AddAlgorithmDialogStore => { switch (action.type) { - case DialogActions.OPEN_DIALOG: + case DialogActions.OPEN_ADD_DIALOG: return { ...state, isOpen: true, }; + case DialogActions.OPEN_CHANGE_DIALOG: + return { + ...state, + isOpen: true, + algorithmId: (action.payload as Algorithm).id, + algorithmName: (action.payload as Algorithm).name, + algorithmDescription: (action.payload as Algorithm).description ?? '', + }; case DialogActions.CLOSE_DIALOG: return { ...state, isOpen: false, + algorithmId: null, }; case DialogActions.CHANGE_ALGORITHM_NAME: return { diff --git a/app/src/utils/statusMessages.ts b/app/src/utils/statusMessages.ts index 8ac77543..b235b48e 100644 --- a/app/src/utils/statusMessages.ts +++ b/app/src/utils/statusMessages.ts @@ -16,3 +16,4 @@ export const SUCCESS_LOAD_METRICS_TUPLES = 'Successfully loaded metrics tuples!'; export const SUCCESS_LOAD_BINARY_METRICS = 'Successfully loaded binary metrics!'; +export const SUCCESS_UPDATE_ALGORITHM = 'Successfully updated algorithm'; From f5820faef940d2ecf2f4bd7bcb4f200a3098b8da Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Wed, 24 Feb 2021 16:38:11 +0100 Subject: [PATCH 02/73] Rename AddAlgorithmDialog to AlgorithmDialog to represent its update feature --- app/src/components/AddFab/AddAlgorithmFab.tsx | 2 +- .../AlgorithmCard/AlgorithmCard.tsx | 2 +- .../AddAlgorithmDialogProps.ts | 8 +++--- .../AlgorithmDialog.View.tsx} | 8 +++--- .../AlgorithmDialog.css} | 0 .../AlgorithmDialog.tsx} | 28 +++++++++---------- .../AlgorithmsPage/AlgorithmsPage.View.tsx | 4 +-- ...ions.ts => AlgorithmDialogStoreActions.ts} | 14 +++++----- app/src/store/actions/actionTypes.ts | 14 +++++----- app/src/store/models.ts | 4 +-- ...ogReducer.ts => AlgorithmDialogReducer.ts} | 12 ++++---- app/src/store/reducers/rootReducer.ts | 4 +-- 12 files changed, 50 insertions(+), 50 deletions(-) rename app/src/components/{AddAlgorithmDialog => AlgorithmDialog}/AddAlgorithmDialogProps.ts (62%) rename app/src/components/{AddAlgorithmDialog/AddAlgorithmDialog.View.tsx => AlgorithmDialog/AlgorithmDialog.View.tsx} (88%) rename app/src/components/{AddAlgorithmDialog/AddAlgorithmDialogStyles.css => AlgorithmDialog/AlgorithmDialog.css} (100%) rename app/src/components/{AddAlgorithmDialog/AddAlgorithmDialog.tsx => AlgorithmDialog/AlgorithmDialog.tsx} (52%) rename app/src/store/actions/{AddAlgorithmDialogStoreActions.ts => AlgorithmDialogStoreActions.ts} (86%) rename app/src/store/reducers/{AddAlgorithmDialogReducer.ts => AlgorithmDialogReducer.ts} (77%) diff --git a/app/src/components/AddFab/AddAlgorithmFab.tsx b/app/src/components/AddFab/AddAlgorithmFab.tsx index a8fc523b..220db447 100644 --- a/app/src/components/AddFab/AddAlgorithmFab.tsx +++ b/app/src/components/AddFab/AddAlgorithmFab.tsx @@ -1,7 +1,7 @@ import AddFabView from 'components/AddFab/AddFab.View'; import { AddFabDispatchProps } from 'components/AddFab/AddFabProps'; import { connect } from 'react-redux'; -import { openAddDialog } from 'store/actions/AddAlgorithmDialogStoreActions'; +import { openAddDialog } from 'store/actions/AlgorithmDialogStoreActions'; import { SnowmanDispatch } from 'store/messages'; const mapDispatchToProps = ( diff --git a/app/src/components/AlgorithmCard/AlgorithmCard.tsx b/app/src/components/AlgorithmCard/AlgorithmCard.tsx index 93802e3b..f4276621 100644 --- a/app/src/components/AlgorithmCard/AlgorithmCard.tsx +++ b/app/src/components/AlgorithmCard/AlgorithmCard.tsx @@ -4,7 +4,7 @@ import { AlgorithmCardOwnProps, } from 'components/AlgorithmCard/AlgorithmCardProps'; import { connect } from 'react-redux'; -import { openChangeDialog } from 'store/actions/AddAlgorithmDialogStoreActions'; +import { openChangeDialog } from 'store/actions/AlgorithmDialogStoreActions'; import { deleteAlgorithm } from 'store/actions/AlgorithmsStoreActions'; import { SnowmanDispatch } from 'store/messages'; diff --git a/app/src/components/AddAlgorithmDialog/AddAlgorithmDialogProps.ts b/app/src/components/AlgorithmDialog/AddAlgorithmDialogProps.ts similarity index 62% rename from app/src/components/AddAlgorithmDialog/AddAlgorithmDialogProps.ts rename to app/src/components/AlgorithmDialog/AddAlgorithmDialogProps.ts index 915239f1..ca107bcd 100644 --- a/app/src/components/AddAlgorithmDialog/AddAlgorithmDialogProps.ts +++ b/app/src/components/AlgorithmDialog/AddAlgorithmDialogProps.ts @@ -1,13 +1,13 @@ import { IonChangeEvent } from 'types/IonChangeEvent'; -export interface AddAlgorithmDialogStateProps { +export interface AlgorithmDialogStateProps { algorithmName: string; isOpen: boolean; algorithmDescription: string; isAddDialog: boolean; } -export interface AddAlgorithmDialogDispatchProps { +export interface AlgorithmDialogDispatchProps { clickOnCancel(): void; closeDialog(): void; changeAlgorithmName(event: IonChangeEvent): void; @@ -15,5 +15,5 @@ export interface AddAlgorithmDialogDispatchProps { clickOnAdd(): void; } -export type AddAlgorithmDialogProps = AddAlgorithmDialogStateProps & - AddAlgorithmDialogDispatchProps; +export type AddAlgorithmDialogProps = AlgorithmDialogStateProps & + AlgorithmDialogDispatchProps; diff --git a/app/src/components/AddAlgorithmDialog/AddAlgorithmDialog.View.tsx b/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx similarity index 88% rename from app/src/components/AddAlgorithmDialog/AddAlgorithmDialog.View.tsx rename to app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx index e4f89f49..02ac692a 100644 --- a/app/src/components/AddAlgorithmDialog/AddAlgorithmDialog.View.tsx +++ b/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx @@ -1,4 +1,4 @@ -import 'components/AddAlgorithmDialog/AddAlgorithmDialogStyles.css'; +import 'components/AlgorithmDialog/AlgorithmDialog.css'; import { IonButton, @@ -9,12 +9,12 @@ import { IonList, IonTextarea, } from '@ionic/react'; -import { AddAlgorithmDialogProps } from 'components/AddAlgorithmDialog/AddAlgorithmDialogProps'; +import { AddAlgorithmDialogProps } from 'components/AlgorithmDialog/AddAlgorithmDialogProps'; import ModalDialog from 'components/ModalDialog/ModalDialog'; import { addCircleOutline, closeCircleOutline, pencil } from 'ionicons/icons'; import React from 'react'; -const AddAlgorithmDialogView = ({ +const AlgorithmDialogView = ({ algorithmDescription, algorithmName, isOpen, @@ -67,4 +67,4 @@ const AddAlgorithmDialogView = ({ ); -export default AddAlgorithmDialogView; +export default AlgorithmDialogView; diff --git a/app/src/components/AddAlgorithmDialog/AddAlgorithmDialogStyles.css b/app/src/components/AlgorithmDialog/AlgorithmDialog.css similarity index 100% rename from app/src/components/AddAlgorithmDialog/AddAlgorithmDialogStyles.css rename to app/src/components/AlgorithmDialog/AlgorithmDialog.css diff --git a/app/src/components/AddAlgorithmDialog/AddAlgorithmDialog.tsx b/app/src/components/AlgorithmDialog/AlgorithmDialog.tsx similarity index 52% rename from app/src/components/AddAlgorithmDialog/AddAlgorithmDialog.tsx rename to app/src/components/AlgorithmDialog/AlgorithmDialog.tsx index 087a3125..383621cd 100644 --- a/app/src/components/AddAlgorithmDialog/AddAlgorithmDialog.tsx +++ b/app/src/components/AlgorithmDialog/AlgorithmDialog.tsx @@ -1,29 +1,29 @@ -import AddAlgorithmDialogView from 'components/AddAlgorithmDialog/AddAlgorithmDialog.View'; import { - AddAlgorithmDialogDispatchProps, - AddAlgorithmDialogStateProps, -} from 'components/AddAlgorithmDialog/AddAlgorithmDialogProps'; + AlgorithmDialogDispatchProps, + AlgorithmDialogStateProps, +} from 'components/AlgorithmDialog/AddAlgorithmDialogProps'; +import AlgorithmDialogView from 'components/AlgorithmDialog/AlgorithmDialog.View'; import { connect } from 'react-redux'; import { addOrUpdateAlgorithm, changeAlgorithmDescription, changeAlgorithmName, closeDialog, -} from 'store/actions/AddAlgorithmDialogStoreActions'; +} from 'store/actions/AlgorithmDialogStoreActions'; import { SnowmanDispatch } from 'store/messages'; import { Store } from 'store/models'; import { IonChangeEvent } from 'types/IonChangeEvent'; -const mapStateToProps = (state: Store): AddAlgorithmDialogStateProps => ({ - isAddDialog: state.AddAlgorithmDialogStore.algorithmId === null, - isOpen: state.AddAlgorithmDialogStore.isOpen, - algorithmDescription: state.AddAlgorithmDialogStore.algorithmDescription, - algorithmName: state.AddAlgorithmDialogStore.algorithmName, +const mapStateToProps = (state: Store): AlgorithmDialogStateProps => ({ + isAddDialog: state.AlgorithmDialogStore.algorithmId === null, + isOpen: state.AlgorithmDialogStore.isOpen, + algorithmDescription: state.AlgorithmDialogStore.algorithmDescription, + algorithmName: state.AlgorithmDialogStore.algorithmName, }); const mapDispatchToProps = ( dispatch: SnowmanDispatch -): AddAlgorithmDialogDispatchProps => ({ +): AlgorithmDialogDispatchProps => ({ clickOnCancel: (): void => dispatch(closeDialog()), closeDialog: (): void => dispatch(closeDialog()), changeAlgorithmName: (event: IonChangeEvent): void => @@ -35,9 +35,9 @@ const mapDispatchToProps = ( }, }); -const AddAlgorithmDialog = connect( +const AlgorithmDialog = connect( mapStateToProps, mapDispatchToProps -)(AddAlgorithmDialogView); +)(AlgorithmDialogView); -export default AddAlgorithmDialog; +export default AlgorithmDialog; diff --git a/app/src/pages/AlgorithmsPage/AlgorithmsPage.View.tsx b/app/src/pages/AlgorithmsPage/AlgorithmsPage.View.tsx index 0c8f9054..7ac58213 100644 --- a/app/src/pages/AlgorithmsPage/AlgorithmsPage.View.tsx +++ b/app/src/pages/AlgorithmsPage/AlgorithmsPage.View.tsx @@ -1,8 +1,8 @@ import { IonCol, IonGrid, IonRow } from '@ionic/react'; import { Algorithm } from 'api'; -import AddAlgorithmDialog from 'components/AddAlgorithmDialog/AddAlgorithmDialog'; import AddAlgorithmFab from 'components/AddFab/AddAlgorithmFab'; import AlgorithmCard from 'components/AlgorithmCard/AlgorithmCard'; +import AlgorithmDialog from 'components/AlgorithmDialog/AlgorithmDialog'; import PageStruct from 'components/PageStruct/PageStruct'; import { AlgorithmsPageProps } from 'pages/AlgorithmsPage/AlgorithmsPageProps'; import React, { useEffect } from 'react'; @@ -24,7 +24,7 @@ const AlgorithmsPageView = ({ - + ); }; diff --git a/app/src/store/actions/AddAlgorithmDialogStoreActions.ts b/app/src/store/actions/AlgorithmDialogStoreActions.ts similarity index 86% rename from app/src/store/actions/AddAlgorithmDialogStoreActions.ts rename to app/src/store/actions/AlgorithmDialogStoreActions.ts index 6bc3d05d..c9a80c65 100644 --- a/app/src/store/actions/AddAlgorithmDialogStoreActions.ts +++ b/app/src/store/actions/AlgorithmDialogStoreActions.ts @@ -1,5 +1,5 @@ import { Algorithm, AlgorithmApi } from 'api'; -import { AddAlgorithmDialogStoreActionTypes as DialogActions } from 'store/actions/actionTypes'; +import { AlgorithmDialogStoreActionTypes as DialogActions } from 'store/actions/actionTypes'; import { getAlgorithms } from 'store/actions/AlgorithmsStoreActions'; import { SnowmanDispatch, SnowmanThunkAction } from 'store/messages'; import { store } from 'store/store'; @@ -77,8 +77,8 @@ const addAlgorithm = (): SnowmanThunkAction> => async ( new AlgorithmApi() .addAlgorithm({ algorithmValues: { - name: store.getState().AddAlgorithmDialogStore.algorithmName, - description: store.getState().AddAlgorithmDialogStore + name: store.getState().AlgorithmDialogStore.algorithmName, + description: store.getState().AlgorithmDialogStore .algorithmDescription, }, }) @@ -101,12 +101,12 @@ const updateAlgorithm = (): SnowmanThunkAction> => async ( new AlgorithmApi() .setAlgorithm({ algorithmId: - store.getState().AddAlgorithmDialogStore.algorithmId ?? + store.getState().AlgorithmDialogStore.algorithmId ?? MagicNotPossibleId, algorithmValues: { - description: store.getState().AddAlgorithmDialogStore + description: store.getState().AlgorithmDialogStore .algorithmDescription, - name: store.getState().AddAlgorithmDialogStore.algorithmName, + name: store.getState().AlgorithmDialogStore.algorithmName, }, }) .then((): void => dispatch(resetDialog())) @@ -121,7 +121,7 @@ const updateAlgorithm = (): SnowmanThunkAction> => async ( export const addOrUpdateAlgorithm = (): SnowmanThunkAction< Promise > => async (dispatch: SnowmanDispatch): Promise => { - if (store.getState().AddAlgorithmDialogStore.algorithmId === null) { + if (store.getState().AlgorithmDialogStore.algorithmId === null) { return dispatch(addAlgorithm()); } return dispatch(updateAlgorithm()); diff --git a/app/src/store/actions/actionTypes.ts b/app/src/store/actions/actionTypes.ts index 7c867d0d..53f0c314 100644 --- a/app/src/store/actions/actionTypes.ts +++ b/app/src/store/actions/actionTypes.ts @@ -44,14 +44,14 @@ export const AddExperimentDialogStoreActionTypes = { 'ADD_EXPERIMENT_DIALOG_STORE_ACTION-CLICK_ON_MATCHING_SOLUTION_TAG', }; -export const AddAlgorithmDialogStoreActionTypes = { - OPEN_ADD_DIALOG: 'ADD_ALGORITHM_DIALOG_STORE-OPEN_ADD_DIALOG', - OPEN_CHANGE_DIALOG: 'ADD_ALGORITHM_DIALOG_STORE-OPEN_CHANGE_DIALOG', - CLOSE_DIALOG: 'ADD_ALGORITHM_DIALOG_STORE-CLOSE_DIALOG', - CHANGE_ALGORITHM_NAME: 'ADD_ALGORITHM_DIALOG_STORE-CHANGE_ALGORITHM_NAME', +export const AlgorithmDialogStoreActionTypes = { + OPEN_ADD_DIALOG: 'ALGORITHM_DIALOG_STORE-OPEN_ADD_DIALOG', + OPEN_CHANGE_DIALOG: 'ALGORITHM_DIALOG_STORE-OPEN_CHANGE_DIALOG', + CLOSE_DIALOG: 'ALGORITHM_DIALOG_STORE-CLOSE_DIALOG', + CHANGE_ALGORITHM_NAME: 'ALGORITHM_DIALOG_STORE-CHANGE_ALGORITHM_NAME', CHANGE_ALGORITHM_DESCRIPTION: - 'ADD_ALGORITHM_DIALOG_STORE-CHANGE_ALGORITHM_DESCRIPTION', - RESET_DIALOG: 'ADD_ALGORITHM_DIALOG_STORE-RESET_DIALOG', + 'ALGORITHM_DIALOG_STORE-CHANGE_ALGORITHM_DESCRIPTION', + RESET_DIALOG: 'ALGORITHM_DIALOG_STORE-RESET_DIALOG', }; export const GlobalIndicatorStoreActionTypes = { diff --git a/app/src/store/models.ts b/app/src/store/models.ts index 79f17125..7946061c 100644 --- a/app/src/store/models.ts +++ b/app/src/store/models.ts @@ -9,7 +9,7 @@ import { DatasetTypes } from 'types/DatasetTypes'; import experimentFileFormatEnum from 'types/ExperimentFileFormats'; import { MetricsTuplesCategories } from 'types/MetricsTuplesCategories'; -export interface AddAlgorithmDialogStore { +export interface AlgorithmDialogStore { algorithmId: number | null; algorithmName: string; algorithmDescription: string; @@ -80,7 +80,7 @@ export interface Store { AlgorithmsStore: AlgorithmsStore; AddDatasetDialogStore: AddDatasetDialogStore; AddExperimentDialogStore: AddExperimentDialogStore; - AddAlgorithmDialogStore: AddAlgorithmDialogStore; + AlgorithmDialogStore: AlgorithmDialogStore; GlobalIndicatorStore: GlobalIndicatorStore; MetricsStore: MetricsStore; InputChipStore: InputChipStore; diff --git a/app/src/store/reducers/AddAlgorithmDialogReducer.ts b/app/src/store/reducers/AlgorithmDialogReducer.ts similarity index 77% rename from app/src/store/reducers/AddAlgorithmDialogReducer.ts rename to app/src/store/reducers/AlgorithmDialogReducer.ts index 432f429f..4ba50e50 100644 --- a/app/src/store/reducers/AddAlgorithmDialogReducer.ts +++ b/app/src/store/reducers/AlgorithmDialogReducer.ts @@ -1,19 +1,19 @@ import { Algorithm } from 'api'; -import { AddAlgorithmDialogStoreActionTypes as DialogActions } from 'store/actions/actionTypes'; +import { AlgorithmDialogStoreActionTypes as DialogActions } from 'store/actions/actionTypes'; import { SnowmanAction } from 'store/messages'; -import { AddAlgorithmDialogStore } from 'store/models'; +import { AlgorithmDialogStore } from 'store/models'; -const initialState: AddAlgorithmDialogStore = { +const initialState: AlgorithmDialogStore = { isOpen: false, algorithmId: null, algorithmName: '', algorithmDescription: '', }; -export const AddAlgorithmDialogReducer = ( - state: AddAlgorithmDialogStore = initialState, +export const AlgorithmDialogReducer = ( + state: AlgorithmDialogStore = initialState, action: SnowmanAction -): AddAlgorithmDialogStore => { +): AlgorithmDialogStore => { switch (action.type) { case DialogActions.OPEN_ADD_DIALOG: return { diff --git a/app/src/store/reducers/rootReducer.ts b/app/src/store/reducers/rootReducer.ts index be0f5fbd..4ee96844 100644 --- a/app/src/store/reducers/rootReducer.ts +++ b/app/src/store/reducers/rootReducer.ts @@ -1,7 +1,7 @@ import { combineReducers } from 'redux'; -import { AddAlgorithmDialogReducer } from 'store/reducers/AddAlgorithmDialogReducer'; import { AddDatasetDialogReducer } from 'store/reducers/AddDatasetDialogReducer'; import { AddExperimentDialogReducer } from 'store/reducers/AddExperimentDialogReducer'; +import { AlgorithmDialogReducer } from 'store/reducers/AlgorithmDialogReducer'; import { AlgorithmsReducer } from 'store/reducers/AlgorithmsReducer'; import { DatasetsReducer } from 'store/reducers/DatasetsReducer'; import { ExperimentsReducer } from 'store/reducers/ExperimentsReducer'; @@ -15,7 +15,7 @@ export const rootReducer = combineReducers({ AlgorithmsStore: AlgorithmsReducer, AddDatasetDialogStore: AddDatasetDialogReducer, AddExperimentDialogStore: AddExperimentDialogReducer, - AddAlgorithmDialogStore: AddAlgorithmDialogReducer, + AlgorithmDialogStore: AlgorithmDialogReducer, GlobalIndicatorStore: GlobalIndicatorReducer, MetricsStore: MetricsReducer, InputChipStore: InputChipReducer, From 0ff812699460caa55ab23a10dc0a45bcb9b0c99c Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Wed, 24 Feb 2021 16:43:20 +0100 Subject: [PATCH 03/73] Improve color of edit button --- app/src/components/AlgorithmCard/AlgorithmCard.View.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx b/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx index 954b8d63..9909ea52 100644 --- a/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx +++ b/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx @@ -31,6 +31,7 @@ const AlgorithmCardView = ({ From 52dcb41755e6b51aae3e46e634c60ad9d136e048 Mon Sep 17 00:00:00 2001 From: phpfs Date: Wed, 24 Feb 2021 18:43:13 +0100 Subject: [PATCH 04/73] Visual clean-up and optimization --- .../AlgorithmCard/AlgorithmCard.View.tsx | 53 +++++++++++-------- .../AlgorithmDialog/AlgorithmDialog.View.tsx | 11 +++- app/src/components/OptionCard/OptionCard.tsx | 3 +- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx b/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx index 9909ea52..ceb451b8 100644 --- a/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx +++ b/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx @@ -4,7 +4,10 @@ import { IonCardContent, IonCardHeader, IonCardTitle, + IonCol, + IonGrid, IonIcon, + IonRow, } from '@ionic/react'; import { AlgorithmCardProps } from 'components/AlgorithmCard/AlgorithmCardProps'; import { pencil, trash } from 'ionicons/icons'; @@ -17,29 +20,37 @@ const AlgorithmCardView = ({ }: AlgorithmCardProps): JSX.Element => ( - - {algorithm.name} - - - - - - - + {algorithm.name} {algorithm.description} + + + + + + Edit + + + + + + Delete + + + + ); diff --git a/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx b/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx index 02ac692a..23598da9 100644 --- a/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx +++ b/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx @@ -11,7 +11,11 @@ import { } from '@ionic/react'; import { AddAlgorithmDialogProps } from 'components/AlgorithmDialog/AddAlgorithmDialogProps'; import ModalDialog from 'components/ModalDialog/ModalDialog'; -import { addCircleOutline, closeCircleOutline, pencil } from 'ionicons/icons'; +import { + addCircleOutline, + checkmarkCircleOutline, + closeCircleOutline, +} from 'ionicons/icons'; import React from 'react'; const AlgorithmDialogView = ({ @@ -52,7 +56,10 @@ const AlgorithmDialogView = ({
- + {isAddDialog ? 'Add' : 'Update'} ) : null} - + Delete From 0da3b9ceb7c845dd7d10ca7a41b792c95c5034ee Mon Sep 17 00:00:00 2001 From: phpfs Date: Wed, 24 Feb 2021 18:56:27 +0100 Subject: [PATCH 05/73] Change success message --- app/src/utils/statusMessages.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/utils/statusMessages.ts b/app/src/utils/statusMessages.ts index b235b48e..e0cd3d78 100644 --- a/app/src/utils/statusMessages.ts +++ b/app/src/utils/statusMessages.ts @@ -16,4 +16,5 @@ export const SUCCESS_LOAD_METRICS_TUPLES = 'Successfully loaded metrics tuples!'; export const SUCCESS_LOAD_BINARY_METRICS = 'Successfully loaded binary metrics!'; -export const SUCCESS_UPDATE_ALGORITHM = 'Successfully updated algorithm'; +export const SUCCESS_UPDATE_ALGORITHM = + 'Successfully updated the designated matching solution!'; From 6643b7f9c209f028096463d460ffc0e2840c5f9c Mon Sep 17 00:00:00 2001 From: phpfs Date: Wed, 24 Feb 2021 20:49:52 +0100 Subject: [PATCH 06/73] Implemented a possible solution --- .../BinaryMetricsPage.View.tsx | 18 ++++++++++++------ .../BinaryMetricsPageStyles.css | 4 ++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx index ba0df285..0b2caad5 100644 --- a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx +++ b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx @@ -29,6 +29,11 @@ export const BinaryMetricsPageView = ({ }: BinaryMetricsPageProps): JSX.Element => { useEffect(() => loadMetrics(), [loadMetrics]); useEffect(() => loadTuples(), [loadTuples]); + useEffect(() => { + // Triggered on every component update! + ReactTooltip.rebuild(); + }); + return ( @@ -42,13 +47,14 @@ export const BinaryMetricsPageView = ({
- + {value !== undefined ? value.toPrecision(2) : '?'} - + {name} @@ -73,7 +79,7 @@ export const BinaryMetricsPageView = ({ /> - + ); }; diff --git a/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css b/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css index 1dc33fb6..008e616c 100644 --- a/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css +++ b/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css @@ -14,3 +14,7 @@ height: 500px; padding-bottom: 20px; } + +.tooltip-fixed { + max-width: 250px; +} From 1129b85a91a93d1d10559c9c2a34c3a1e6047963 Mon Sep 17 00:00:00 2001 From: phpfs Date: Wed, 24 Feb 2021 21:12:15 +0100 Subject: [PATCH 07/73] Adding katex --- app/package-lock.json | 21 +++++++++++++++++++++ app/package.json | 2 ++ 2 files changed, 23 insertions(+) diff --git a/app/package-lock.json b/app/package-lock.json index 2813d2ed..e00438c9 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -2348,6 +2348,12 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=" }, + "@types/katex": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.11.0.tgz", + "integrity": "sha512-27BfE8zASRLYfSBNMk5/+KIjr2CBBrH0i5lhsO04fca4TGirIIMay73v3zNkzqmsaeIa/Mi5kejWDcxPLAmkvA==", + "dev": true + }, "@types/lodash": { "version": "4.14.167", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.167.tgz", @@ -9839,6 +9845,21 @@ "object.assign": "^4.1.2" } }, + "katex": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.12.0.tgz", + "integrity": "sha512-y+8btoc/CK70XqcHqjxiGWBOeIL8upbS0peTPXTvgrh21n1RiWWcIpSWM+4uXq+IAgNh9YYQWdc7LVDPDAEEAg==", + "requires": { + "commander": "^2.19.0" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + } + } + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", diff --git a/app/package.json b/app/package.json index 3d210fbf..cf216e0c 100644 --- a/app/package.json +++ b/app/package.json @@ -11,6 +11,7 @@ "@ionic/react-router": "^5.0.7", "immutable": "^4.0.0-rc.12", "ionicons": "^5.0.0", + "katex": "^0.12.0", "lodash": "^4.17.20", "react": "^17.0.1", "react-dom": "^17.0.1", @@ -32,6 +33,7 @@ "@testing-library/react": "^11.2.2", "@testing-library/user-event": "^12.6.0", "@types/jest": "^26.0.19", + "@types/katex": "^0.11.0", "@types/lodash": "^4.14.167", "@types/papaparse": "^5.2.5", "@types/react": "^16.14.2", From 082b1bc9b1b8b19caf978726f867d30d9d162838 Mon Sep 17 00:00:00 2001 From: phpfs Date: Wed, 24 Feb 2021 21:12:33 +0100 Subject: [PATCH 08/73] First example with katex --- .../pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx index 0b2caad5..ee773f3d 100644 --- a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx +++ b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx @@ -13,6 +13,7 @@ import DataViewer from 'components/DataViewer/DataViewer'; import PageStruct from 'components/PageStruct/PageStruct'; import PaneButtonRow from 'components/PaneButtonRow/PaneButtonRow'; import StyledCarousel from 'components/StyledCarousel/StyledCarousel'; +import { renderToString } from 'katex'; import { BinaryMetricsPageProps } from 'pages/BinaryMetricsPage/BinaryMetricsPageProps'; import React, { useEffect } from 'react'; import ReactTooltip from 'react-tooltip'; @@ -54,7 +55,12 @@ export const BinaryMetricsPageView = ({ > {value !== undefined ? value.toPrecision(2) : '?'} - + {name} @@ -79,7 +85,7 @@ export const BinaryMetricsPageView = ({ /> - + ); }; From e99a6e066dd1a5b231dd7beeefd0915566a74692 Mon Sep 17 00:00:00 2001 From: phpfs Date: Wed, 24 Feb 2021 21:20:28 +0100 Subject: [PATCH 09/73] Improve styling --- app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx index ee773f3d..cde080a8 100644 --- a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx +++ b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx @@ -59,6 +59,8 @@ export const BinaryMetricsPageView = ({ class="metric-name" data-tip={renderToString(description.replaceAll('$', ''), { throwOnError: false, + displayMode: true, + output: 'html', })} > {name} From 3755b26dd50dd0c3902252bb029ea10399c96499 Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Wed, 24 Feb 2021 21:47:13 +0100 Subject: [PATCH 10/73] Fix form state after switching between add and change dialog --- app/src/store/models.ts | 2 ++ .../store/reducers/AlgorithmDialogReducer.ts | 17 ++++++++++++----- app/src/types/DialogTypes.ts | 4 ++++ 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 app/src/types/DialogTypes.ts diff --git a/app/src/store/models.ts b/app/src/store/models.ts index 7946061c..d6c0efb6 100644 --- a/app/src/store/models.ts +++ b/app/src/store/models.ts @@ -6,6 +6,7 @@ import { Metric, } from 'api'; import { DatasetTypes } from 'types/DatasetTypes'; +import { DialogTypes } from 'types/DialogTypes'; import experimentFileFormatEnum from 'types/ExperimentFileFormats'; import { MetricsTuplesCategories } from 'types/MetricsTuplesCategories'; @@ -14,6 +15,7 @@ export interface AlgorithmDialogStore { algorithmName: string; algorithmDescription: string; isOpen: boolean; + dialogType: DialogTypes; } export interface AddDatasetDialogStore { diff --git a/app/src/store/reducers/AlgorithmDialogReducer.ts b/app/src/store/reducers/AlgorithmDialogReducer.ts index 4ba50e50..3b4370f6 100644 --- a/app/src/store/reducers/AlgorithmDialogReducer.ts +++ b/app/src/store/reducers/AlgorithmDialogReducer.ts @@ -2,12 +2,14 @@ import { Algorithm } from 'api'; import { AlgorithmDialogStoreActionTypes as DialogActions } from 'store/actions/actionTypes'; import { SnowmanAction } from 'store/messages'; import { AlgorithmDialogStore } from 'store/models'; +import { DialogTypes } from 'types/DialogTypes'; const initialState: AlgorithmDialogStore = { isOpen: false, algorithmId: null, algorithmName: '', algorithmDescription: '', + dialogType: DialogTypes.ADD_DIALOG, }; export const AlgorithmDialogReducer = ( @@ -19,6 +21,7 @@ export const AlgorithmDialogReducer = ( return { ...state, isOpen: true, + dialogType: DialogTypes.ADD_DIALOG, }; case DialogActions.OPEN_CHANGE_DIALOG: return { @@ -27,13 +30,17 @@ export const AlgorithmDialogReducer = ( algorithmId: (action.payload as Algorithm).id, algorithmName: (action.payload as Algorithm).name, algorithmDescription: (action.payload as Algorithm).description ?? '', + dialogType: DialogTypes.CHANGE_DIALOG, }; case DialogActions.CLOSE_DIALOG: - return { - ...state, - isOpen: false, - algorithmId: null, - }; + if (state.dialogType === DialogTypes.ADD_DIALOG) + return { + ...state, + isOpen: false, + }; + else { + return initialState; + } case DialogActions.CHANGE_ALGORITHM_NAME: return { ...state, diff --git a/app/src/types/DialogTypes.ts b/app/src/types/DialogTypes.ts new file mode 100644 index 00000000..ecb695b6 --- /dev/null +++ b/app/src/types/DialogTypes.ts @@ -0,0 +1,4 @@ +export enum DialogTypes { + ADD_DIALOG, + CHANGE_DIALOG, +} From 317939a4d4e1337e5b624ad0943fa00645dc8576 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Thu, 25 Feb 2021 08:23:07 +0100 Subject: [PATCH 11/73] Better error messages --- wrapper/src/api/providers/algorithm/algorithmProvider.ts | 4 ++-- .../experiment/experimentProvider/file/csvInserter.ts | 4 +++- .../experiment/experimentProvider/file/getter.ts | 4 ++-- .../providers/experiment/experimentProvider/file/index.ts | 6 +++++- wrapper/src/api/server/services/BenchmarkService.ts | 8 ++------ 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/wrapper/src/api/providers/algorithm/algorithmProvider.ts b/wrapper/src/api/providers/algorithm/algorithmProvider.ts index 42280ce9..7c638a33 100644 --- a/wrapper/src/api/providers/algorithm/algorithmProvider.ts +++ b/wrapper/src/api/providers/algorithm/algorithmProvider.ts @@ -60,7 +60,7 @@ export class AlgorithmProvider extends BaseAlgorithmProvider { getAlgorithm(id: AlgorithmId): Algorithm { const algorithm = this.getAlgorithmQuery.all(id); if (algorithm.length === 0) { - throw new Error(`An algorithm with the id ${id} does not exist.`); + throw new Error(`A matching solution with the id ${id} does not exist.`); } return algorithm[0]; } @@ -90,7 +90,7 @@ export class AlgorithmProvider extends BaseAlgorithmProvider { ) .join(', '); throw new Error( - `The algorithm ${algorithm.name} (${algorithm.id}) is used by the experiments ${experiments}.` + `The matching solution ${algorithm.name} (${algorithm.id}) is used by the experiments ${experiments}.` ); } } diff --git a/wrapper/src/api/providers/experiment/experimentProvider/file/csvInserter.ts b/wrapper/src/api/providers/experiment/experimentProvider/file/csvInserter.ts index ac078c86..47b8d57b 100644 --- a/wrapper/src/api/providers/experiment/experimentProvider/file/csvInserter.ts +++ b/wrapper/src/api/providers/experiment/experimentProvider/file/csvInserter.ts @@ -37,7 +37,9 @@ export abstract class CSVInserter throw new Error( `The uploaded experiment is invalid as it does not contain the required columns ${[ ...unseenRequiredColumns.values(), - ].join(', ')}.` + ].join( + ', ' + )}. Make sure you have selected the correct experiment file format.` ); } } diff --git a/wrapper/src/api/providers/experiment/experimentProvider/file/getter.ts b/wrapper/src/api/providers/experiment/experimentProvider/file/getter.ts index c55b86b8..284d22a6 100644 --- a/wrapper/src/api/providers/experiment/experimentProvider/file/getter.ts +++ b/wrapper/src/api/providers/experiment/experimentProvider/file/getter.ts @@ -26,7 +26,7 @@ export class ExperimentFileGetter { ): IterableIterator { startAt = startAt ?? 0; limit = limit ?? -1; - if (sortBy && !(sortBy in this.table.schema.columns)) { + if (sortBy) { if ( !(experimentCustomColumnPrefix + sortBy in this.table.schema.columns) ) { @@ -37,7 +37,7 @@ export class ExperimentFileGetter { sortBy = experimentCustomColumnPrefix + sortBy; } } else { - sortBy = sortBy ?? this.table.schema.columns.id1.name; + sortBy = this.table.schema.columns.id1.name; } yield this.columns.map((column) => column.name.startsWith(experimentCustomColumnPrefix) diff --git a/wrapper/src/api/providers/experiment/experimentProvider/file/index.ts b/wrapper/src/api/providers/experiment/experimentProvider/file/index.ts index 12466a3f..409f8c59 100644 --- a/wrapper/src/api/providers/experiment/experimentProvider/file/index.ts +++ b/wrapper/src/api/providers/experiment/experimentProvider/file/index.ts @@ -28,7 +28,11 @@ export function getExperimentInserter( ): InstantiableExperimentInserter { const ExperimentLoader = experimentInserters.get(formatId); if (!ExperimentLoader) { - throw new Error('Unknown experiment format'); + throw new Error( + `Unknown experiment file format: ${formatId}. Known formats are: ${experimentFormats + .map(([format]) => format) + .join(', ')}` + ); } return ExperimentLoader; } diff --git a/wrapper/src/api/server/services/BenchmarkService.ts b/wrapper/src/api/server/services/BenchmarkService.ts index d71e0714..1a342936 100644 --- a/wrapper/src/api/server/services/BenchmarkService.ts +++ b/wrapper/src/api/server/services/BenchmarkService.ts @@ -34,9 +34,7 @@ export async function calculateExperimentIntersectionCount({ ).data.length; } else { throw new Error( - 'Intersection for' + - body.length + - 'experiments is not supported so far! Please provide exactly two experiments' + `Intersection for ${body.length} experiments is not supported so far! Please provide exactly two experiments` ); } }, @@ -82,9 +80,7 @@ export async function calculateExperimentIntersectionRecords({ ); } else { throw new Error( - 'Intersection for' + - body.length + - 'experiments is not supported so far! Please provide exactly two experiments' + `Intersection for ${body.length} experiments is not supported so far! Please provide exactly two experiments` ); } }, From b52ff6837aa065d9d26dcfac4313d9b22b066a26 Mon Sep 17 00:00:00 2001 From: phpfs Date: Thu, 25 Feb 2021 11:23:31 +0100 Subject: [PATCH 12/73] Improve appearance --- app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx | 7 ++++--- .../pages/BinaryMetricsPage/BinaryMetricsPageStyles.css | 2 +- app/src/utils/latex.ts | 5 +++++ 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 app/src/utils/latex.ts diff --git a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx index cde080a8..1751ab39 100644 --- a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx +++ b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx @@ -17,6 +17,7 @@ import { renderToString } from 'katex'; import { BinaryMetricsPageProps } from 'pages/BinaryMetricsPage/BinaryMetricsPageProps'; import React, { useEffect } from 'react'; import ReactTooltip from 'react-tooltip'; +import { trimMathExpr } from 'utils/latex'; export const BinaryMetricsPageView = ({ loadMetrics, @@ -57,10 +58,10 @@ export const BinaryMetricsPageView = ({ {name} @@ -87,7 +88,7 @@ export const BinaryMetricsPageView = ({ /> - + ); }; diff --git a/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css b/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css index 008e616c..acee336e 100644 --- a/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css +++ b/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css @@ -16,5 +16,5 @@ } .tooltip-fixed { - max-width: 250px; + max-width: 300px; } diff --git a/app/src/utils/latex.ts b/app/src/utils/latex.ts new file mode 100644 index 00000000..a8a6fd5e --- /dev/null +++ b/app/src/utils/latex.ts @@ -0,0 +1,5 @@ +export const trimMathExpr = (aLatexString: string): string => { + return aLatexString + .replace(new RegExp(/^\$/), '') + .replace(new RegExp(/\$$/), ''); +}; From f892781b62c0f45f558370d3ab49983bf36093c7 Mon Sep 17 00:00:00 2001 From: phpfs Date: Thu, 25 Feb 2021 12:33:26 +0100 Subject: [PATCH 13/73] Improve metric formulae --- .../benchmark/benchmarkProvider/metrics/advanced/accuracy.ts | 3 +-- .../benchmarkProvider/metrics/advanced/balancedAccuracy.ts | 2 +- .../metrics/advanced/bookmaker_informedness.ts | 2 +- .../benchmark/benchmarkProvider/metrics/advanced/f1_score.ts | 3 +-- .../metrics/advanced/fowlkes-mallows_index.ts | 2 +- .../benchmarkProvider/metrics/advanced/markedness.ts | 2 +- .../metrics/advanced/matthews_correlation_coeffecient.ts | 4 ++-- .../benchmarkProvider/metrics/basic/false_discovery_rate.ts | 2 +- .../benchmarkProvider/metrics/basic/false_negative_rate.ts | 2 +- .../benchmarkProvider/metrics/basic/false_omission_rate.ts | 2 +- .../benchmarkProvider/metrics/basic/false_positive_rate.ts | 2 +- .../metrics/basic/negative_predictive_value.ts | 2 +- .../benchmark/benchmarkProvider/metrics/basic/precision.ts | 2 +- .../benchmarkProvider/metrics/basic/prevalenceThreshold.ts | 2 +- .../benchmark/benchmarkProvider/metrics/basic/recall.ts | 2 +- .../benchmark/benchmarkProvider/metrics/basic/specificity.ts | 2 +- .../benchmark/benchmarkProvider/metrics/basic/threat_score.ts | 3 +-- 17 files changed, 18 insertions(+), 21 deletions(-) diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/accuracy.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/accuracy.ts index 7451ddd1..972d666f 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/accuracy.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/accuracy.ts @@ -2,8 +2,7 @@ import { BaseMetric } from '../base'; export class Accuracy extends BaseMetric { name = 'accuracy'; - description = - '$(truePositives + trueNegatives)/(truePositives + trueNegatives + falsePositives + falseNegatives)$'; + description = '\\frac{TPos + TNeg}{TPos + TNeg + FPos + FNeg}'; range: [number, number] = [0, 1]; get value(): number { return ( diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/balancedAccuracy.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/balancedAccuracy.ts index f422cb3f..06163f8d 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/balancedAccuracy.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/balancedAccuracy.ts @@ -5,7 +5,7 @@ import { Specificity } from '../basic/specificity'; export class BalancedAccuracy extends BaseMetric { name = 'balanced accuracy'; range: [number, number] = [0, 1]; - description = '$(truePositiveRate + trueNegativeRate) / 2$'; + description = '\\frac{TruePosRate + TrueNegRate}{2}'; get value(): number { const truePositiveRate = new Recall(this.matrix).value; const trueNegativeRate = new Specificity(this.matrix).value; diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/bookmaker_informedness.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/bookmaker_informedness.ts index df415a4b..c717b7b7 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/bookmaker_informedness.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/bookmaker_informedness.ts @@ -5,7 +5,7 @@ import { Specificity } from '../basic/specificity'; export class BookmakerInformedness extends BaseMetric { name = 'bookmaker informedness'; range: [number, number] = [0, 1]; - description = '$truePositiveRate + trueNegativeRate - 1$'; + description = 'TruePosRate + TrueNegRate - 1'; get value(): number { const truePositiveRate = new Recall(this.matrix).value; const trueNegativeRate = new Specificity(this.matrix).value; diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts index 0e1e2eef..bf05c26c 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts @@ -3,8 +3,7 @@ import { BaseMetric } from '../base'; export class FMeasure extends BaseMetric { name = 'f1 score'; range: [number, number] = [0, 1]; - description = - '$(2*truePositives)/ (2*truePositives + falsePositives + falseNegatives)$'; + description = '2 * \\frac{precision * recall}{precision + recall}'; get value(): number { return ( (2 * this.matrix.truePositives) / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/fowlkes-mallows_index.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/fowlkes-mallows_index.ts index 1fe86a91..af1ce966 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/fowlkes-mallows_index.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/fowlkes-mallows_index.ts @@ -4,7 +4,7 @@ export class FowlkesMallowsIndex extends BaseMetric { name = 'fowlkes mallows index'; range: [number, number] = [0, 1]; description = - '$\\sqrt{truePositives/(truePositives + falsePositives) * truePositives / (truePositives + falseNegatives)$'; + '\\sqrt{\\frac{TruePos}{TruePos + FalsePos} * \\frac{TruePos}{TruePos + FalseNeg}}'; get value(): number { return Math.sqrt( (this.matrix.truePositives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/markedness.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/markedness.ts index 8ee247f8..2757e0d4 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/markedness.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/markedness.ts @@ -5,7 +5,7 @@ import { Precision } from '../basic/precision'; export class Markedness extends BaseMetric { name = 'markedness'; range: [number, number] = [0, 1]; - description = '$positivePredectiveValue + negativePredictiveValue - 1$'; + description = 'PosPredictiveValue + NegPredictiveValue - 1'; get value(): number { const ppv = new Precision(this.matrix).value; const npv = new NegativePredictiveValue(this.matrix).value; diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/matthews_correlation_coeffecient.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/matthews_correlation_coeffecient.ts index 09c0299f..22963511 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/matthews_correlation_coeffecient.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/matthews_correlation_coeffecient.ts @@ -1,10 +1,10 @@ import { BaseMetric } from '../base'; export class MatthewsCorrelationCoefficient extends BaseMetric { - name = 'matthews correlation coefficient'; + name = 'matthews correlation coeff.'; range: [number, number] = [0, 1]; description = - '$truePositives * trueNegatives - falsePositives * falseNegatives\\sqrt{(truePositives+falsePositives) * (truePositives+falseNegatives)*(trueNegatives + falsePositives)+(trueNegatives*falseNegatives)}$'; + '\\frac{TPos * TNeg - FPos * FNeg}{\\sqrt{(TP + FP) * (TP + FN) * (TN + FP) * (TN + FN)}}'; get value(): number { return ( diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_discovery_rate.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_discovery_rate.ts index fceebef6..e4bf8a6e 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_discovery_rate.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_discovery_rate.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class FalseDiscoveryRate extends BaseMetric { name = 'false discovery rate'; range: [number, number] = [0, 1]; - description = '$falsePositives / (truePositives + falsePositives)$'; + description = '\\frac{falsePositives}{truePositives + falsePositives}'; get value(): number { return ( this.matrix.falsePositives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_negative_rate.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_negative_rate.ts index a7e88ee2..f7aeb708 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_negative_rate.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_negative_rate.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class FalseNegativeRate extends BaseMetric { name = 'false negative rate'; range: [number, number] = [0, 1]; - description = '$falseNegatives / (falseNegatives + truePositives)$'; + description = '\\frac{falseNegatives}{falseNegatives + truePositives}'; get value(): number { return ( this.matrix.falseNegatives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_omission_rate.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_omission_rate.ts index b5c78592..d0740d88 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_omission_rate.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_omission_rate.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class FalseOmissionRate extends BaseMetric { name = 'false omission rate'; range: [number, number] = [0, 1]; - description = '$falseNegatives / (trueNegatives + falseNegatives)$'; + description = '\\frac{falseNegatives}{trueNegatives + falseNegatives}'; get value(): number { return ( this.matrix.falseNegatives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_positive_rate.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_positive_rate.ts index 2546e48f..ed75a01c 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_positive_rate.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_positive_rate.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class FalsePositiveRate extends BaseMetric { name = 'false positive rate'; range: [number, number] = [0, 1]; - description = '$falsePositives / (falsePositives + trueNegatives)$'; + description = '\\frac{falsePositives}{falsePositives + trueNegatives}'; get value(): number { return ( this.matrix.falsePositives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/negative_predictive_value.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/negative_predictive_value.ts index c3913641..91396650 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/negative_predictive_value.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/negative_predictive_value.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class NegativePredictiveValue extends BaseMetric { name = 'negative predictive value'; range: [number, number] = [0, 1]; - description = '$trueNegatives / (trueNegatives + falseNegatives)$'; + description = '\\frac{trueNegatives}{trueNegatives + falseNegatives}'; get value(): number { return ( this.matrix.trueNegatives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/precision.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/precision.ts index 9e5ceb23..1987da06 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/precision.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/precision.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class Precision extends BaseMetric { name = 'precision'; range: [number, number] = [0, 1]; - description = '$truePositives / (truePositives + falsePositives)$'; + description = '\\frac{truePositives}{truePositives + falsePositives}'; get value(): number { return ( this.matrix.truePositives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/prevalenceThreshold.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/prevalenceThreshold.ts index 797d9fca..7ea97483 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/prevalenceThreshold.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/prevalenceThreshold.ts @@ -6,7 +6,7 @@ export class PrevalenceThreshold extends BaseMetric { name = 'prevalence threshold'; range: [number, number] = [0, 1]; description = - '$\\sqrt{truePositiveRate*(- trueNegativeRate + 1)} + trueNegativeRate - 1 / (truePositiveRate + trueNegativeRate - 1)$'; + '\\frac{\\sqrt{TPosRate * (1 - TNegRate)} + TNegRate - 1}{TPosRate + TNegRate - 1}'; get value(): number { const truePositiveRate = new Recall(this.matrix).value; const trueNegativeRate = new Specificity(this.matrix).value; diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/recall.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/recall.ts index e737eb43..b442f0e4 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/recall.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/recall.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class Recall extends BaseMetric { name = 'recall'; range: [number, number] = [0, 1]; - description = '$truePositives / (truePositives + falseNegatives)$'; + description = '\\frac{truePositives}{truePositives + falseNegatives}'; get value(): number { return ( this.matrix.truePositives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/specificity.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/specificity.ts index 804c8277..859ebcc0 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/specificity.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/specificity.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class Specificity extends BaseMetric { name = 'specificity'; range: [number, number] = [0, 1]; - description = '$trueNegatives / (trueNegatives + falsePositives)$'; + description = '\\frac{trueNegatives}{trueNegatives + falsePositives}'; get value(): number { return ( this.matrix.trueNegatives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/threat_score.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/threat_score.ts index 1ab0a2b3..c4c9df6c 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/threat_score.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/threat_score.ts @@ -3,8 +3,7 @@ import { BaseMetric } from '../base'; export class ThreatScore extends BaseMetric { name = 'threat score'; range: [number, number] = [0, 1]; - description = - '$truePositives / (truePositives + falseNegatives + falsePositives)$'; + description = '\\frac{truePositives}{TruePos + FalseNeg + FalsePos}'; get value(): number { return ( this.matrix.truePositives / From 6a29ad2382bf2883e211a32e98e9d90747fa4488 Mon Sep 17 00:00:00 2001 From: phpfs Date: Thu, 25 Feb 2021 12:39:35 +0100 Subject: [PATCH 14/73] Finalize visuals --- app/src/app/App.View.tsx | 2 ++ app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx | 6 ++---- app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css | 4 ---- app/src/theme/overwrites.css | 5 +++++ app/src/utils/latex.ts | 5 ----- 5 files changed, 9 insertions(+), 13 deletions(-) delete mode 100644 app/src/utils/latex.ts diff --git a/app/src/app/App.View.tsx b/app/src/app/App.View.tsx index 170465e4..1f9f60f4 100644 --- a/app/src/app/App.View.tsx +++ b/app/src/app/App.View.tsx @@ -26,6 +26,7 @@ import SideMenu from 'components/SideMenu/SideMenu'; import React, { useEffect } from 'react'; import { Redirect, Route } from 'react-router-dom'; import { ToastContainer } from 'react-toastify'; +import ReactTooltip from 'react-tooltip'; import history from 'utils/history'; import { getEmptyPath, @@ -56,6 +57,7 @@ const AppView = ({ loadInitialState }: AppProps): JSX.Element => { + -

+

Binary Metrics

@@ -58,7 +57,7 @@ export const BinaryMetricsPageView = ({ - ); }; diff --git a/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css b/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css index acee336e..1dc33fb6 100644 --- a/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css +++ b/app/src/pages/BinaryMetricsPage/BinaryMetricsPageStyles.css @@ -14,7 +14,3 @@ height: 500px; padding-bottom: 20px; } - -.tooltip-fixed { - max-width: 300px; -} diff --git a/app/src/theme/overwrites.css b/app/src/theme/overwrites.css index 6b52e397..e4358061 100644 --- a/app/src/theme/overwrites.css +++ b/app/src/theme/overwrites.css @@ -56,3 +56,8 @@ .Toastify__toast--error { background-color: var(--ion-color-danger); } + +/* Tooltip */ +.tooltip-fixed { + max-width: 350px; +} diff --git a/app/src/utils/latex.ts b/app/src/utils/latex.ts deleted file mode 100644 index a8a6fd5e..00000000 --- a/app/src/utils/latex.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const trimMathExpr = (aLatexString: string): string => { - return aLatexString - .replace(new RegExp(/^\$/), '') - .replace(new RegExp(/\$$/), ''); -}; From 68c1af56ec7b94af20ce0472e5b1e4f8f389b3f4 Mon Sep 17 00:00:00 2001 From: phpfs Date: Thu, 25 Feb 2021 12:44:32 +0100 Subject: [PATCH 15/73] Fix benchmarkProvider.spec.ts --- wrapper/src/api/providers/benchmark/benchmarkProvider.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider.spec.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider.spec.ts index 90c1d11e..9195b8c8 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider.spec.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider.spec.ts @@ -176,7 +176,7 @@ describe('test benchmark functions', () => { { name: 'false positive rate', value: '0.0055' }, { name: 'fowlkes mallows index', value: '0.2500' }, { name: 'markedness', value: '0.4628' }, - { name: 'matthews correlation coefficient', value: '0.2352' }, + { name: 'matthews correlation coeff.', value: '0.2352' }, { name: 'negative predictive value', value: '0.9628' }, { name: 'precision', value: '0.5000' }, { name: 'prevalence threshold', value: '0.1733' }, From 30ba176823e76a8e15b9b4d361c6f008a382d5f4 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Thu, 25 Feb 2021 13:48:47 +0100 Subject: [PATCH 16/73] Refactor command line parameters --- wrapper/src/api/config.ts | 8 --- wrapper/src/api/main.ts | 16 +++-- .../server/{expressServer.ts => apiServer.ts} | 53 +++++++-------- wrapper/src/api/tools/cli.ts | 67 +++++++++++++++---- wrapper/src/api/tools/logger.ts | 6 +- wrapper/src/main.ts | 20 +++++- 6 files changed, 110 insertions(+), 60 deletions(-) rename wrapper/src/api/server/{expressServer.ts => apiServer.ts} (56%) diff --git a/wrapper/src/api/config.ts b/wrapper/src/api/config.ts index 4c397e2c..430ae569 100644 --- a/wrapper/src/api/config.ts +++ b/wrapper/src/api/config.ts @@ -1,7 +1,5 @@ import path from 'path'; -import { cliArgs } from './tools/cli'; - export const ASSETS_DIR = path.join(__dirname, '../../assets'); export const OPENAPI_YAML_PATH = path.join( ASSETS_DIR, @@ -9,10 +7,4 @@ export const OPENAPI_YAML_PATH = path.join( ); export const DATABASE_SETUP_DIR = path.join(ASSETS_DIR, 'database_setup'); -export const STORAGE_DIR = - cliArgs.path ?? path.join(__dirname, '../../storage'); - -export const PORT = 8123; -export const HOST = '127.0.0.1'; - export const INSERT_BATCH_SIZE = 500; diff --git a/wrapper/src/api/main.ts b/wrapper/src/api/main.ts index 7dd0c813..7f8d3827 100644 --- a/wrapper/src/api/main.ts +++ b/wrapper/src/api/main.ts @@ -1,18 +1,22 @@ -import { STORAGE_DIR } from './config'; import { setupDatabase } from './database'; -import { ExpressServer } from './server/expressServer'; +import { ExpressServer as ApiServer } from './server/apiServer'; +import { cliArgs } from './tools/cli'; import { logger } from './tools/logger'; async function launch(): Promise { - logger.info(`Using storage directory ${STORAGE_DIR}`); + if (cliArgs.inMemory) { + logger.info(`Using in memory database`); + } else { + logger.info(`Using storage directory ${cliArgs.storageDirectory}`); + } logger.info('Preparing database...'); await setupDatabase({ - temporary: false, - appPath: STORAGE_DIR, + temporary: cliArgs.inMemory, + appPath: cliArgs.storageDirectory, loadExampleEntries: true, }); logger.info('Starting webserver...'); - await ExpressServer.launch(); + ApiServer.launch(); logger.info('Ready'); } diff --git a/wrapper/src/api/server/expressServer.ts b/wrapper/src/api/server/apiServer.ts similarity index 56% rename from wrapper/src/api/server/expressServer.ts rename to wrapper/src/api/server/apiServer.ts index 0669f5b0..816e1658 100644 --- a/wrapper/src/api/server/expressServer.ts +++ b/wrapper/src/api/server/apiServer.ts @@ -6,28 +6,27 @@ import http from 'http'; import { join } from 'path'; import path from 'path'; -import { HOST, OPENAPI_YAML_PATH, PORT } from '../config'; +import { OPENAPI_YAML_PATH } from '../config'; +import { cliArgs } from '../tools/cli'; import { identifyResponse } from './identifyResponse'; export class ExpressServer { - private readonly host: string = HOST; - private readonly port: number = PORT; - private readonly openApiPath: string = OPENAPI_YAML_PATH; - private app: express.Express; - private server: http.Server | undefined; - constructor() { + protected readonly app: express.Express; + protected server?: http.Server; + + protected constructor() { this.app = express(); this.setupMiddleware(); } - setupMiddleware(): void { + protected setupMiddleware(): void { this.app.use(cors()); this.app.use(express.json({ limit: '14MB' })); this.app.use(express.urlencoded({ extended: false })); this.app.use(cookieParser()); - //Send the openapi document *AS GENERATED BY THE GENERATOR* - this.app.get('/openapi', (req: express.Request, res: express.Response) => - res.sendFile(this.openApiPath) + + this.app.get('/openapi', (_req: express.Request, res: express.Response) => + res.sendFile(OPENAPI_YAML_PATH) ); this.app.get('/api', (_req: express.Request, res: express.Response) => { res.status(200).send('Snowman API'); @@ -38,26 +37,21 @@ export class ExpressServer { res.status(200).send(identifyResponse); } ); - this.app.use('/', express.static(join(__dirname, '../../../', 'app'))); - } - launch(): void { + this.app.use('/', express.static(join(__dirname, '..', '..', '..', 'app'))); this.app.use( OpenApiValidator.middleware({ - apiSpec: this.openApiPath, + apiSpec: OPENAPI_YAML_PATH, operationHandlers: path.join(__dirname), }) ); - - // eslint-disable-next-line no-unused-vars this.app.use( ( err: { status: number; message?: string; errors?: string }, - req: express.Request, + _req: express.Request, res: express.Response, - next: express.NextFunction + _next: express.NextFunction ) => { - // format errors res.status(err.status || 500).json({ message: err.message || err, errors: err.errors || '', @@ -65,10 +59,14 @@ export class ExpressServer { } ); this.app.use((_req, res) => { - // Route everything that's remaining to index.html - res.sendFile(join(__dirname, '../../../', 'app', 'index.html')); + res.sendFile(join(__dirname, '..', '..', '..', 'app', 'index.html')); }); - this.server = http.createServer(this.app).listen(this.port, this.host); + } + + protected launch(): void { + this.server = http + .createServer(this.app) + .listen(cliArgs.port, cliArgs.hostname); } close(): void { @@ -77,12 +75,13 @@ export class ExpressServer { } } - static async launch(): Promise { - const expressServer = new ExpressServer(); + static launch(): ExpressServer { + const server = new ExpressServer(); try { - expressServer.launch(); + server.launch(); + return server; } catch (error) { - expressServer.close(); + server.close(); throw error; } } diff --git a/wrapper/src/api/tools/cli.ts b/wrapper/src/api/tools/cli.ts index 88cc9ca5..3d911041 100644 --- a/wrapper/src/api/tools/cli.ts +++ b/wrapper/src/api/tools/cli.ts @@ -1,22 +1,63 @@ import * as dashdash from 'dashdash'; +import path from 'path'; -interface IArgs { - path?: string; +interface IArgs extends dashdash.Results { + storageDirectory: string; + inMemory: boolean; + hostname: string; + port: number; + headless: boolean; } -const cliOptions: dashdash.Option[] = [ +export const STORAGE_DIRECTORY_CLI_FLAG = 'storageDirectory'; + +dashdash.addOptionType({ + name: 'port', + takesArg: true, + helpArg: '', + parseArg: (_option, _optstr, arg) => { + const port = parseInt(arg); + if (port < 1 || port > 65535) { + throw new Error( + `port needs to be in the range from 1 to 65535 (received ${arg})` + ); + } + return port; + }, +}); + +export const cliOptions: dashdash.OptionWithoutAliases[] = [ + { + name: STORAGE_DIRECTORY_CLI_FLAG, + type: 'string', + default: path.join(__dirname, '../../storage'), + help: 'Where the database and configuration files live.', + }, + { + name: 'inMemory', + type: 'bool', + default: (false as unknown) as string, + help: + 'If present, an in memory database will be used. This increases the performance. Keep in mind that any changes made will be lost when the process is stopped.', + }, { - name: 'path', + name: 'hostname', type: 'string', + default: 'localhost', + help: 'The API will be available on this hostname only.', + }, + { + name: 'port', + type: 'port', + default: (8123 as unknown) as string, + help: 'The API will be available on this port.', + }, + { + name: 'headless', + type: 'bool', + default: (false as unknown) as string, + help: 'If present, does not show the UI but starts the API directly.', }, ]; -function getArgs(): IArgs { - try { - return dashdash.parse({ options: cliOptions }) as IArgs; - } catch (_) { - return {} as IArgs; - } -} - -export const cliArgs = getArgs(); +export const cliArgs = dashdash.parse({ options: cliOptions }) as IArgs; diff --git a/wrapper/src/api/tools/logger.ts b/wrapper/src/api/tools/logger.ts index 85ff4604..70df0b9c 100644 --- a/wrapper/src/api/tools/logger.ts +++ b/wrapper/src/api/tools/logger.ts @@ -1,7 +1,7 @@ import path from 'path'; import { createLogger, format, transports } from 'winston'; -import { STORAGE_DIR } from '../config'; +import { cliArgs } from './cli'; export const logger = createLogger({ level: 'info', @@ -10,7 +10,7 @@ export const logger = createLogger({ format: format.simple(), }), new transports.File({ - filename: path.join(STORAGE_DIR, 'error.log'), + filename: path.join(cliArgs.storageDirectory, 'error.log'), level: 'error', format: format.combine( format.timestamp(), @@ -24,7 +24,7 @@ export const logger = createLogger({ format.metadata(), format.json() ), - filename: path.join(STORAGE_DIR, 'combined.log'), + filename: path.join(cliArgs.storageDirectory, 'combined.log'), }), ], }); diff --git a/wrapper/src/main.ts b/wrapper/src/main.ts index c560f56d..8c34e984 100644 --- a/wrapper/src/main.ts +++ b/wrapper/src/main.ts @@ -2,10 +2,16 @@ import axios from 'axios'; import * as proc from 'child_process'; import { app, BrowserWindow, ipcMain } from 'electron'; import path from 'path'; +import { argv } from 'process'; import { URL } from 'url'; import waitForLocalhost from 'wait-for-localhost'; import { identifyResponse } from './api/server/identifyResponse'; +import { + cliArgs, + cliOptions, + STORAGE_DIRECTORY_CLI_FLAG, +} from './api/tools/cli'; import { headlessMessage } from './headlessMessage'; let mainWindow: BrowserWindow; @@ -42,9 +48,18 @@ function spawnLocalServer() { if (backend !== undefined) { console.error('Failed to start backend as it is already running.'); } + if (!argv.includes('--' + STORAGE_DIRECTORY_CLI_FLAG)) { + cliArgs.storageDirectory = app.getPath('userData'); + } backend = proc.fork( path.join(app.getAppPath(), './dist/api/main.js'), - ['--path', app.getPath('userData')], + cliOptions.flatMap((option) => + option.type === 'bool' + ? cliArgs[option.name] + ? ['--' + option.name] + : [] + : ['--' + option.name, cliArgs[option.name].toString()] + ), {} ); } @@ -57,7 +72,6 @@ function killLocalServer() { } function createWindow() { - // Create the browser window. mainWindow = new BrowserWindow({ width: 1000, height: 700, @@ -118,7 +132,7 @@ app.on('activate', () => { }); app.on('ready', () => { - if (app.commandLine.hasSwitch('headless')) { + if (cliArgs.headless) { console.log(headlessMessage); spawnLocalServer(); } else { From fc994400c6d5521ed9ecab015aedd9d6a8035b39 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Thu, 25 Feb 2021 13:57:43 +0100 Subject: [PATCH 17/73] Rename ExpressServer -> APIServer --- wrapper/src/api/main.ts | 4 ++-- wrapper/src/api/server/apiServer.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/wrapper/src/api/main.ts b/wrapper/src/api/main.ts index 7f8d3827..c9316f45 100644 --- a/wrapper/src/api/main.ts +++ b/wrapper/src/api/main.ts @@ -1,5 +1,5 @@ import { setupDatabase } from './database'; -import { ExpressServer as ApiServer } from './server/apiServer'; +import { APIServer } from './server/apiServer'; import { cliArgs } from './tools/cli'; import { logger } from './tools/logger'; @@ -16,7 +16,7 @@ async function launch(): Promise { loadExampleEntries: true, }); logger.info('Starting webserver...'); - ApiServer.launch(); + APIServer.launch(); logger.info('Ready'); } diff --git a/wrapper/src/api/server/apiServer.ts b/wrapper/src/api/server/apiServer.ts index 816e1658..e22658e4 100644 --- a/wrapper/src/api/server/apiServer.ts +++ b/wrapper/src/api/server/apiServer.ts @@ -10,7 +10,7 @@ import { OPENAPI_YAML_PATH } from '../config'; import { cliArgs } from '../tools/cli'; import { identifyResponse } from './identifyResponse'; -export class ExpressServer { +export class APIServer { protected readonly app: express.Express; protected server?: http.Server; @@ -75,8 +75,8 @@ export class ExpressServer { } } - static launch(): ExpressServer { - const server = new ExpressServer(); + static launch(): APIServer { + const server = new APIServer(); try { server.launch(); return server; From 022e4b8fc0e360330127d8a6832ccb2298997d77 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Thu, 25 Feb 2021 14:09:41 +0100 Subject: [PATCH 18/73] Reorder metrics by importance + rename FMeasure -> F1Score --- .../benchmarkProvider/benchmarkProvider.ts | 22 ++++++++++--------- .../metrics/advanced/f1_score.ts | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts index ec5e5edc..04d470b2 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts @@ -13,11 +13,11 @@ import { Accuracy, BalancedAccuracy, BookmakerInformedness, + F1Score, FalseDiscoveryRate, FalseNegativeRate, FalseOmissionRate, FalsePositiveRate, - FMeasure, FowlkesMallowsIndex, Markedness, MatthewsCorrelationCoefficient, @@ -65,21 +65,23 @@ export class BenchmarkProvider extends BaseBenchmarkProvider { calculateMetrics(goldstandardId: number, experimentId: number): Metric[] { const metrics = [ Accuracy, - BalancedAccuracy, - BookmakerInformedness, - FalseDiscoveryRate, + Precision, + Recall, + F1Score, + + FalsePositiveRate, FalseNegativeRate, + FalseDiscoveryRate, FalseOmissionRate, - FalsePositiveRate, - FMeasure, + NegativePredictiveValue, + Specificity, + + BalancedAccuracy, + BookmakerInformedness, FowlkesMallowsIndex, Markedness, MatthewsCorrelationCoefficient, - NegativePredictiveValue, - Precision, PrevalenceThreshold, - Recall, - Specificity, ThreatScore, ]; diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts index bf05c26c..8d4bcb1e 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts @@ -1,6 +1,6 @@ import { BaseMetric } from '../base'; -export class FMeasure extends BaseMetric { +export class F1Score extends BaseMetric { name = 'f1 score'; range: [number, number] = [0, 1]; description = '2 * \\frac{precision * recall}{precision + recall}'; From 80b4f1cc67d47fc17ce01aca5a30507bbc21437e Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Thu, 25 Feb 2021 14:40:44 +0100 Subject: [PATCH 19/73] Spilt info and formula from description --- app/src/api/apis/ExperimentsApi.ts | 2 +- app/src/api/models/ExperimentIntersection.ts | 6 +- app/src/api/models/Metric.ts | 24 +++++-- .../BinaryMetricsPage.View.tsx | 13 +++- docs/api_specification.yaml | 13 ++-- wrapper/assets/api_specification.yaml | 64 ++++++++++++------- .../benchmarkProvider/benchmarkProvider.ts | 6 +- .../metrics/advanced/accuracy.ts | 2 +- .../metrics/advanced/balancedAccuracy.ts | 2 +- .../advanced/bookmaker_informedness.ts | 2 +- .../metrics/advanced/f1_score.ts | 5 +- .../metrics/advanced/fowlkes-mallows_index.ts | 2 +- .../metrics/advanced/markedness.ts | 2 +- .../matthews_correlation_coeffecient.ts | 2 +- .../benchmarkProvider/metrics/base.ts | 5 +- .../metrics/basic/false_discovery_rate.ts | 2 +- .../metrics/basic/false_negative_rate.ts | 2 +- .../metrics/basic/false_omission_rate.ts | 2 +- .../metrics/basic/false_positive_rate.ts | 2 +- .../basic/negative_predictive_value.ts | 2 +- .../metrics/basic/precision.ts | 2 +- .../metrics/basic/prevalenceThreshold.ts | 2 +- .../benchmarkProvider/metrics/basic/recall.ts | 2 +- .../metrics/basic/specificity.ts | 2 +- .../metrics/basic/threat_score.ts | 2 +- wrapper/src/api/server/types/Metric.ts | 18 +++++- 26 files changed, 128 insertions(+), 60 deletions(-) diff --git a/app/src/api/apis/ExperimentsApi.ts b/app/src/api/apis/ExperimentsApi.ts index d99e431c..03b0bd29 100644 --- a/app/src/api/apis/ExperimentsApi.ts +++ b/app/src/api/apis/ExperimentsApi.ts @@ -341,5 +341,5 @@ export enum SetExperimentFileFormatEnum { Magellan = 'magellan', ClusterEr = 'clusterER', Pilot = 'pilot', - Sigmod = 'sigmod2021' + Sigmod2021 = 'sigmod2021' } diff --git a/app/src/api/models/ExperimentIntersection.ts b/app/src/api/models/ExperimentIntersection.ts index 05771485..7cc16d5b 100644 --- a/app/src/api/models/ExperimentIntersection.ts +++ b/app/src/api/models/ExperimentIntersection.ts @@ -42,9 +42,9 @@ export function ExperimentIntersectionFromJSONTyped(json: any, ignoreDiscriminat return json; } return { - - 'header': !exists(json, 'header') ? undefined : json['header'], - 'data': !exists(json, 'data') ? undefined : json['data'], + + 'header': json['header'], + 'data': json['data'], }; } diff --git a/app/src/api/models/Metric.ts b/app/src/api/models/Metric.ts index 965c113a..e0ae24e7 100644 --- a/app/src/api/models/Metric.ts +++ b/app/src/api/models/Metric.ts @@ -26,11 +26,23 @@ export interface Metric { */ name: string; /** - * can contain latex math expressions denoted by surrounding dollar symbols ($$) + * + * @type {string} + * @memberof Metric + */ + formula: string; + /** + * + * @type {string} + * @memberof Metric + */ + info?: string; + /** + * * @type {string} * @memberof Metric */ - description: string; + infoLink?: string; /** * * @type {number} @@ -56,7 +68,9 @@ export function MetricFromJSONTyped(json: any, ignoreDiscriminator: boolean): Me return { 'name': json['name'], - 'description': json['description'], + 'formula': json['formula'], + 'info': !exists(json, 'info') ? undefined : json['info'], + 'infoLink': !exists(json, 'infoLink') ? undefined : json['infoLink'], 'value': json['value'], 'range': json['range'], }; @@ -72,7 +86,9 @@ export function MetricToJSON(value?: Metric | null): any { return { 'name': value.name, - 'description': value.description, + 'formula': value.formula, + 'info': value.info, + 'infoLink': value.infoLink, 'value': value.value, 'range': value.range, }; diff --git a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx index 350f9310..8ca29b5a 100644 --- a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx +++ b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx @@ -44,7 +44,14 @@ export const BinaryMetricsPageView = ({ {metrics.map( - ({ name, description, range, value }: Metric): JSX.Element => ( + ({ + name, + formula, + range, + value, + info, + infoLink, + }: Metric): JSX.Element => (
@@ -57,7 +64,7 @@ export const BinaryMetricsPageView = ({ -

Example Pairs

+

Confusion Matrix

$) + example: https://link.springer.com/article/10.1007/s11222-017-9746-6 value: type: number example: 0.75 @@ -738,7 +743,7 @@ components: example: [0, 1] required: - name - - description + - formula - value - range Metrics: diff --git a/wrapper/assets/api_specification.yaml b/wrapper/assets/api_specification.yaml index e6fdb844..e23bb313 100644 --- a/wrapper/assets/api_specification.yaml +++ b/wrapper/assets/api_specification.yaml @@ -8,7 +8,7 @@ info: Comparing data matching algorithms is still an unsolved topic in both industry and research. With snowman, developers and researchers will be able to compare the performance of different data matching solutions or improve new algorithms. license: - name: LICENSE + name: LICENSE (MIT) url: https://github.com title: Snowman API version: 1.0.0 @@ -582,7 +582,7 @@ paths: required: true schema: enum: - - Magellan + - magellan - clusterER - pilot - sigmod2021 @@ -853,20 +853,27 @@ components: Metric: example: name: precision - description: $truePositives / (truePositives + falsePositives)$ + formula: \frac{truePositives}{truePositives + falsePositives} range: - 0 - 1 value: 0.75 + info: The meaningfulness of this metric is discussed in the research community. + infoLink: https://link.springer.com/article/10.1007/s11222-017-9746-6 properties: name: example: precision type: string - description: - description: - can contain latex math expressions denoted by surrounding dollar - symbols ($$) - example: $truePositives / (truePositives + falsePositives)$ + formula: + example: \frac{truePositives}{truePositives + falsePositives} + type: string + info: + example: + The meaningfulness of this metric is discussed in the research + community. + type: string + infoLink: + example: https://link.springer.com/article/10.1007/s11222-017-9746-6 type: string value: example: 0.75 @@ -881,7 +888,7 @@ components: minItems: 2 type: array required: - - description + - formula - name - range - value @@ -909,31 +916,44 @@ components: type: object type: array ExperimentIntersection: - type: object + example: + data: + - - - "1" + - hallo + - - "1" + - hallo + - - - "1" + - hallo + - - "1" + - hallo + header: + - id + - testColumn1 + - testColumn2 properties: header: - type: array - items: - type: string example: - id - - name - - telephone - data: + - testColumn1 + - testColumn2 + items: + type: string type: array + data: items: - type: array items: - type: array example: - "1" - - "Ben" - - "+123 4567890" + - hallo items: type: string + type: array + type: array + type: array required: - - header - data + - header + type: object ExperimentIntersectionCount: example: count: 37 @@ -958,7 +978,7 @@ components: example: 1 type: integer numberOfUploadedRecords: - example: 2008 + example: 2005 type: integer required: - id diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts index 04d470b2..18ed8958 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts @@ -96,12 +96,14 @@ export class BenchmarkProvider extends BaseBenchmarkProvider { ).confusionMatrixCounts; return metrics .map((Metric) => new Metric(matrix)) - .map(({ value, name, description, range }) => { + .map(({ value, formula, name, range, info, infoLink }) => { return { value, + formula, name, - description, range, + info, + infoLink, }; }); } diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/accuracy.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/accuracy.ts index 972d666f..80783ba4 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/accuracy.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/accuracy.ts @@ -2,7 +2,7 @@ import { BaseMetric } from '../base'; export class Accuracy extends BaseMetric { name = 'accuracy'; - description = '\\frac{TPos + TNeg}{TPos + TNeg + FPos + FNeg}'; + formula = '\\frac{TPos + TNeg}{TPos + TNeg + FPos + FNeg}'; range: [number, number] = [0, 1]; get value(): number { return ( diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/balancedAccuracy.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/balancedAccuracy.ts index 06163f8d..60db3a64 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/balancedAccuracy.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/balancedAccuracy.ts @@ -5,7 +5,7 @@ import { Specificity } from '../basic/specificity'; export class BalancedAccuracy extends BaseMetric { name = 'balanced accuracy'; range: [number, number] = [0, 1]; - description = '\\frac{TruePosRate + TrueNegRate}{2}'; + formula = '\\frac{TruePosRate + TrueNegRate}{2}'; get value(): number { const truePositiveRate = new Recall(this.matrix).value; const trueNegativeRate = new Specificity(this.matrix).value; diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/bookmaker_informedness.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/bookmaker_informedness.ts index c717b7b7..d3b040a8 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/bookmaker_informedness.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/bookmaker_informedness.ts @@ -5,7 +5,7 @@ import { Specificity } from '../basic/specificity'; export class BookmakerInformedness extends BaseMetric { name = 'bookmaker informedness'; range: [number, number] = [0, 1]; - description = 'TruePosRate + TrueNegRate - 1'; + formula = 'TruePosRate + TrueNegRate - 1'; get value(): number { const truePositiveRate = new Recall(this.matrix).value; const trueNegativeRate = new Specificity(this.matrix).value; diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts index 8d4bcb1e..1d410c2b 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/f1_score.ts @@ -3,7 +3,10 @@ import { BaseMetric } from '../base'; export class F1Score extends BaseMetric { name = 'f1 score'; range: [number, number] = [0, 1]; - description = '2 * \\frac{precision * recall}{precision + recall}'; + formula = '2 * \\frac{precision * recall}{precision + recall}'; + info = + 'The meaningfulness of the f1 score is debated in the research community.'; + infoLink = 'https://link.springer.com/article/10.1007/s11222-017-9746-6'; get value(): number { return ( (2 * this.matrix.truePositives) / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/fowlkes-mallows_index.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/fowlkes-mallows_index.ts index af1ce966..e0fdebe6 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/fowlkes-mallows_index.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/fowlkes-mallows_index.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class FowlkesMallowsIndex extends BaseMetric { name = 'fowlkes mallows index'; range: [number, number] = [0, 1]; - description = + formula = '\\sqrt{\\frac{TruePos}{TruePos + FalsePos} * \\frac{TruePos}{TruePos + FalseNeg}}'; get value(): number { return Math.sqrt( diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/markedness.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/markedness.ts index 2757e0d4..984989ff 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/markedness.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/markedness.ts @@ -5,7 +5,7 @@ import { Precision } from '../basic/precision'; export class Markedness extends BaseMetric { name = 'markedness'; range: [number, number] = [0, 1]; - description = 'PosPredictiveValue + NegPredictiveValue - 1'; + formula = 'PosPredictiveValue + NegPredictiveValue - 1'; get value(): number { const ppv = new Precision(this.matrix).value; const npv = new NegativePredictiveValue(this.matrix).value; diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/matthews_correlation_coeffecient.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/matthews_correlation_coeffecient.ts index 22963511..cde55e94 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/matthews_correlation_coeffecient.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/advanced/matthews_correlation_coeffecient.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class MatthewsCorrelationCoefficient extends BaseMetric { name = 'matthews correlation coeff.'; range: [number, number] = [0, 1]; - description = + formula = '\\frac{TPos * TNeg - FPos * FNeg}{\\sqrt{(TP + FP) * (TP + FN) * (TN + FP) * (TN + FN)}}'; get value(): number { diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/base.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/base.ts index 0187fee6..3b4353d7 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/base.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/base.ts @@ -6,6 +6,9 @@ export abstract class BaseMetric implements Metric { abstract name: string; abstract range: [number, number]; - abstract description: string; + abstract formula: string; abstract value: number; + + info?: string; + infoLink?: string; } diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_discovery_rate.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_discovery_rate.ts index e4bf8a6e..428268d0 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_discovery_rate.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_discovery_rate.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class FalseDiscoveryRate extends BaseMetric { name = 'false discovery rate'; range: [number, number] = [0, 1]; - description = '\\frac{falsePositives}{truePositives + falsePositives}'; + formula = '\\frac{falsePositives}{truePositives + falsePositives}'; get value(): number { return ( this.matrix.falsePositives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_negative_rate.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_negative_rate.ts index f7aeb708..ccd97639 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_negative_rate.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_negative_rate.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class FalseNegativeRate extends BaseMetric { name = 'false negative rate'; range: [number, number] = [0, 1]; - description = '\\frac{falseNegatives}{falseNegatives + truePositives}'; + formula = '\\frac{falseNegatives}{falseNegatives + truePositives}'; get value(): number { return ( this.matrix.falseNegatives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_omission_rate.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_omission_rate.ts index d0740d88..77eec3c1 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_omission_rate.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_omission_rate.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class FalseOmissionRate extends BaseMetric { name = 'false omission rate'; range: [number, number] = [0, 1]; - description = '\\frac{falseNegatives}{trueNegatives + falseNegatives}'; + formula = '\\frac{falseNegatives}{trueNegatives + falseNegatives}'; get value(): number { return ( this.matrix.falseNegatives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_positive_rate.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_positive_rate.ts index ed75a01c..a6af0a83 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_positive_rate.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/false_positive_rate.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class FalsePositiveRate extends BaseMetric { name = 'false positive rate'; range: [number, number] = [0, 1]; - description = '\\frac{falsePositives}{falsePositives + trueNegatives}'; + formula = '\\frac{falsePositives}{falsePositives + trueNegatives}'; get value(): number { return ( this.matrix.falsePositives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/negative_predictive_value.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/negative_predictive_value.ts index 91396650..92cc7b94 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/negative_predictive_value.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/negative_predictive_value.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class NegativePredictiveValue extends BaseMetric { name = 'negative predictive value'; range: [number, number] = [0, 1]; - description = '\\frac{trueNegatives}{trueNegatives + falseNegatives}'; + formula = '\\frac{trueNegatives}{trueNegatives + falseNegatives}'; get value(): number { return ( this.matrix.trueNegatives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/precision.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/precision.ts index 1987da06..e7ea67c3 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/precision.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/precision.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class Precision extends BaseMetric { name = 'precision'; range: [number, number] = [0, 1]; - description = '\\frac{truePositives}{truePositives + falsePositives}'; + formula = '\\frac{truePositives}{truePositives + falsePositives}'; get value(): number { return ( this.matrix.truePositives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/prevalenceThreshold.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/prevalenceThreshold.ts index 7ea97483..ec28ad74 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/prevalenceThreshold.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/prevalenceThreshold.ts @@ -5,7 +5,7 @@ import { Specificity } from './specificity'; export class PrevalenceThreshold extends BaseMetric { name = 'prevalence threshold'; range: [number, number] = [0, 1]; - description = + formula = '\\frac{\\sqrt{TPosRate * (1 - TNegRate)} + TNegRate - 1}{TPosRate + TNegRate - 1}'; get value(): number { const truePositiveRate = new Recall(this.matrix).value; diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/recall.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/recall.ts index b442f0e4..5670c4e2 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/recall.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/recall.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class Recall extends BaseMetric { name = 'recall'; range: [number, number] = [0, 1]; - description = '\\frac{truePositives}{truePositives + falseNegatives}'; + formula = '\\frac{truePositives}{truePositives + falseNegatives}'; get value(): number { return ( this.matrix.truePositives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/specificity.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/specificity.ts index 859ebcc0..4a3fb970 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/specificity.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/specificity.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class Specificity extends BaseMetric { name = 'specificity'; range: [number, number] = [0, 1]; - description = '\\frac{trueNegatives}{trueNegatives + falsePositives}'; + formula = '\\frac{trueNegatives}{trueNegatives + falsePositives}'; get value(): number { return ( this.matrix.trueNegatives / diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/threat_score.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/threat_score.ts index c4c9df6c..29aeffcf 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/threat_score.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/metrics/basic/threat_score.ts @@ -3,7 +3,7 @@ import { BaseMetric } from '../base'; export class ThreatScore extends BaseMetric { name = 'threat score'; range: [number, number] = [0, 1]; - description = '\\frac{truePositives}{TruePos + FalseNeg + FalsePos}'; + formula = '\\frac{truePositives}{TruePos + FalseNeg + FalsePos}'; get value(): number { return ( this.matrix.truePositives / diff --git a/wrapper/src/api/server/types/Metric.ts b/wrapper/src/api/server/types/Metric.ts index 37580156..714e2b55 100644 --- a/wrapper/src/api/server/types/Metric.ts +++ b/wrapper/src/api/server/types/Metric.ts @@ -25,11 +25,23 @@ export interface Metric { */ name: string; /** - * can contain latex math expressions denoted by surrounding dollar symbols ($$) + * + * @type {string} + * @memberof Metric + */ + formula: string; + /** + * + * @type {string} + * @memberof Metric + */ + info?: string; + /** + * * @type {string} * @memberof Metric */ - description: string; + infoLink?: string; /** * * @type {number} @@ -42,4 +54,4 @@ export interface Metric { * @memberof Metric */ range: Array; -} +} \ No newline at end of file From 892afefdc7507443a524c4039b00f3e37290ac98 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Thu, 25 Feb 2021 14:56:04 +0100 Subject: [PATCH 20/73] Add comments at unclear positions --- wrapper/src/main.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wrapper/src/main.ts b/wrapper/src/main.ts index 8c34e984..b79bbf8d 100644 --- a/wrapper/src/main.ts +++ b/wrapper/src/main.ts @@ -48,11 +48,13 @@ function spawnLocalServer() { if (backend !== undefined) { console.error('Failed to start backend as it is already running.'); } + // we do not have access to app in the subprocess and therefore need to overwrite the default storage directory from here. if (!argv.includes('--' + STORAGE_DIRECTORY_CLI_FLAG)) { cliArgs.storageDirectory = app.getPath('userData'); } backend = proc.fork( path.join(app.getAppPath(), './dist/api/main.js'), + // bool flags do not take a value and are true if present. Therefore we need to handle them differently. cliOptions.flatMap((option) => option.type === 'bool' ? cliArgs[option.name] @@ -120,7 +122,7 @@ ipcMain.on('open_benchmark', (event, url) => { } }); -ipcMain.on('reset_launcher', (event) => { +ipcMain.on('reset_launcher', () => { initOccured = false; showLauncherPage(); }); From 66cda554e6a5e408312b03a11bf9bb6ba0c7be63 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Thu, 25 Feb 2021 14:57:22 +0100 Subject: [PATCH 21/73] rename IArgs -> CommandLineParameters --- wrapper/src/api/tools/cli.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wrapper/src/api/tools/cli.ts b/wrapper/src/api/tools/cli.ts index 3d911041..e2bb6080 100644 --- a/wrapper/src/api/tools/cli.ts +++ b/wrapper/src/api/tools/cli.ts @@ -1,7 +1,7 @@ import * as dashdash from 'dashdash'; import path from 'path'; -interface IArgs extends dashdash.Results { +interface CommandLineArguments extends dashdash.Results { storageDirectory: string; inMemory: boolean; hostname: string; @@ -60,4 +60,6 @@ export const cliOptions: dashdash.OptionWithoutAliases[] = [ }, ]; -export const cliArgs = dashdash.parse({ options: cliOptions }) as IArgs; +export const cliArgs = dashdash.parse({ + options: cliOptions, +}) as CommandLineArguments; From a49dcfa1456972d4d7b9b4effc1a2a7f4e3b8298 Mon Sep 17 00:00:00 2001 From: phpfs Date: Thu, 25 Feb 2021 15:15:37 +0100 Subject: [PATCH 22/73] Make latex browser-independent --- app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx index 8ca29b5a..c520f219 100644 --- a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx +++ b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.View.tsx @@ -1,4 +1,5 @@ import 'pages/BinaryMetricsPage/BinaryMetricsPageStyles.css'; +import 'katex/dist/katex.min.css'; import { IonCard, @@ -67,7 +68,7 @@ export const BinaryMetricsPageView = ({ data-tip={renderToString(formula, { throwOnError: false, displayMode: true, - output: 'mathml', + output: 'html', })} > {name} From 9ae27493261ebc31bbd07ab75f8a0ea0d2532e6b Mon Sep 17 00:00:00 2001 From: Lasklu Date: Thu, 25 Feb 2021 15:16:34 +0100 Subject: [PATCH 23/73] preloads computer dataset from sigmodcontest --- .../datasets/computers_sigmod.csv | 44 + .../computers_sigmod_goldstandard.csv | 904 ++++++++++++++++++ .../api/database/setup/examples/datasets.ts | 16 + .../database/setup/examples/experiments.ts | 18 + 4 files changed, 982 insertions(+) create mode 100644 wrapper/assets/database_setup/datasets/computers_sigmod.csv create mode 100644 wrapper/assets/database_setup/experiments/computers_sigmod_goldstandard.csv diff --git a/wrapper/assets/database_setup/datasets/computers_sigmod.csv b/wrapper/assets/database_setup/datasets/computers_sigmod.csv new file mode 100644 index 00000000..6d8a537f --- /dev/null +++ b/wrapper/assets/database_setup/datasets/computers_sigmod.csv @@ -0,0 +1,44 @@ +cpu_brand,battery_chemistry,ram_type,cpu_frequency,hdd_capacity,dimensions_height,brand,cpu_type,title,cpu_cache,battery_life,dimensions_depth,dimensions,display_size,instance_id,cpu_model +,Lithium-Ion ( 43Wh ),,,,Not Specified By Manufacturer,,,"Dell Inspiron 15 i5547-3751sLV 15.6"" I5547-3751SLV B&H",,Lithium-Ion ( 43Wh ),Not Specified By Manufacturer,Not Specified By Manufacturer,,www.bhphotovideo.com//284, +"Asus UX31A-XB52 13.3 '' Laptop with Core i5 , 4GB RAM , 256GB SSD HD , Windows 7",,"Asus UX31A-XB52 13.3 '' Laptop with Core i5 , 4GB RAM , 256GB SSD HD , Windows 7","Asus UX31A-XB52 13.3 '' Laptop with Core i5 , 4GB RAM , 256GB SSD HD , Windows 7",,,Asus,"Asus UX31A-XB52 13.3 '' Laptop with Core i5 , 4GB RAM , 256GB SSD HD , Windows 7","Asus UX31A-XB52 13.3"" Laptop on sale for $1414.95",,,,,"Asus UX31A-XB52 13.3 '' Laptop with Core i5 , 4GB RAM , 256GB SSD HD , Windows 7",www.gosale.com//99,"Asus UX31A-XB52 13.3 '' Laptop with Core i5 , 4GB RAM , 256GB SSD HD , Windows 7" +"Acer Aspire S7-392-6807 Signature Edition 13.3 '' WQHD Touchscreen Ultrabook with Core i5 , 8GB RAM , 128GB SSD , Windows 8.1",,"Acer Aspire S7-392-6807 Signature Edition 13.3 '' WQHD Touchscreen Ultrabook with Core i5 , 8GB RAM , 128GB SSD , Windows 8.1","Acer Aspire S7-392-6807 Signature Edition 13.3 '' WQHD Touchscreen Ultrabook with Core i5 , 8GB RAM , 128GB SSD , Windows 8.1",,,Acer,"Acer Aspire S7-392-6807 Signature Edition 13.3 '' WQHD Touchscreen Ultrabook with Core i5 , 8GB RAM , 128GB SSD , Windows 8.1",Acer Aspire S7-392-6807 Signature Edition on sale for $1092.99,,,,,"Acer Aspire S7-392-6807 Signature Edition 13.3 '' WQHD Touchscreen Ultrabook with Core i5 , 8GB RAM , 128GB SSD , Windows 8.1",www.gosale.com//74,"Acer Aspire S7-392-6807 Signature Edition 13.3 '' WQHD Touchscreen Ultrabook with Core i5 , 8GB RAM , 128GB SSD , Windows 8.1" +"Asus Zenbook Prime UX31A 13.3 Ultrabook with Intel Core i5-3317U , 128GB SSD , 4GB RAM , Windows 7 ( # UX31A-R5102H )",,"Asus Zenbook Prime UX31A 13.3 Ultrabook with Intel Core i5-3317U , 128GB SSD , 4GB RAM , Windows 7 ( # UX31A-R5102H )","Asus Zenbook Prime UX31A 13.3 Ultrabook with Intel Core i5-3317U , 128GB SSD , 4GB RAM , Windows 7 ( # UX31A-R5102H )",,,Asus,"Asus Zenbook Prime UX31A 13.3 Ultrabook with Intel Core i5-3317U , 128GB SSD , 4GB RAM , Windows 7 ( # UX31A-R5102H )",Asus Zenbook Prime UX31A 13.3 Ultrabook on sale for $749.99,,,,,"Asus Zenbook Prime UX31A 13.3 Ultrabook with Intel Core i5-3317U , 128GB SSD , 4GB RAM , Windows 7 ( # UX31A-R5102H )",www.gosale.com//93,"Asus Zenbook Prime UX31A 13.3 Ultrabook with Intel Core i5-3317U , 128GB SSD , 4GB RAM , Windows 7 ( # UX31A-R5102H )" +1.7GHz Intel Core i5-3317U Dual-Core. Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory,6-Cell Providing up to 7 Hours per Charge ( 50Wh ). Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory,,1.7GHz Intel Core i5-3317U Dual-Core. Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory,Installed : 256GB SSD. Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory,Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory,,1.7GHz Intel Core i5-3317U Dual-Core. Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory,"Best Asus ZENBOOK Prime UX31A-XB52-Core i5-3317U 1.70 GHz-4GB RAM-256GB SSD-Win 7 Pro-13.3""Ultrabook-Silver Aluminum | TopEndElectronics UK",L3 : 3MB. Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory,Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory,Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory,Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory,Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory,www.topendelectronic.co.uk//258,1.7GHz Intel Core i5-3317U Dual-Core. Performance Processor 1.7GHz Intel Core i5-3317U Dual-Core Cache L3 : 3MB System Bus 5GT/s Memory +2.40GHz Intel Core i7-3630QM. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,9-Cell Lithium-Ion ( 100Wh ). Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,,2.40GHz Intel Core i7-3630QM. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Installed : 500GB 7200rpm. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,,2.40GHz Intel Core i7-3630QM. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,"Best HP EliteBook 8470w-C7A68UT-i7-3630QM-8GB-500GB HDD-Win 7-14""LED Notebook | TopEndElectronics UK",L3 : 6MB. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,www.topendelectronic.co.uk//139,2.40GHz Intel Core i7-3630QM. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory +Intel Core i5 Intel Core i5. Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion,6-cell lithium ion. Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion,4GB,3320M ( 2.60GHz ). Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion,180GB SSD. Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion,Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion,ThinkPad,Intel Core i5 Intel Core i5. Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion,"Best Lenovo ThinkPad X230 34355AU-i5-3320M 2.6GHz-4GB-180GB SSD-Win 7-12.5"" LED Notebook-Black | TopEndElectronics UK",Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion,Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion,Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion,Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion,12.5 ''. Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion,www.topendelectronic.co.uk//472,3320M ( 2.60GHz ). Model Brand ThinkPad Model X230 ( 34355AU ) General Operating System Windows 7 Pro 64-bit CPU Type Intel Core i5 Screen 12.5 '' Memory Size 4GB Hard Disk 180GB SSD Webcam Yes Graphics Card Intel HD Graphics 4000 CPU CPU Type Intel Core i5 CPU Speed 3320M ( 2.60GHz ) Display Resolution 1366 x 768 Screen Size 12.5 '' Operating Systems Operating System Windows 7 Pro 64-bit Communications LAN 10/100/1000Mbps WLAN IEEE 802.11a/b/g/n Bluetooth Bluetooth 4.0 Ports DisplayPort 1 Power Battery 6-cell lithium ion +2.40GHz Intel Core i7-3630QM. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,9-Cell Lithium-Ion ( 100Wh ). Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,,2.40GHz Intel Core i7-3630QM. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Installed : 128GB. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,,2.40GHz Intel Core i7-3630QM. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,"Best HP EliteBook 8470w-C6Z03UT-i7-3630QM-8GB-128GB SSD-Win 7-14"" LED Notebook | TopEndElectronics UK",L3 : 6MB. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory,www.topendelectronic.co.uk//238,2.40GHz Intel Core i7-3630QM. Performance Processor 2.40GHz Intel Core i7-3630QM Cache L3 : 6MB Memory +Intel Celeron. Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion,6-cell lithium ion. Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion,,1.90GHz. Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion,500GB. Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion,Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion,Acer,Intel Celeron. Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion,"Best Acer Aspire E1-731-10054G50Mnii-Celeron 1005M-4GB-500GB HDD-Win 7-17.3"" LED Notebook | TopEndElectronics UK",Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion,3 Hour. Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion,Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion,Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion,17.3 ''. Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion,www.topendelectronic.co.uk//79,1.90GHz. Model Brand Acer Series Aspire General Battery Life 3 Hour CPU CPU Type Intel Celeron CPU Speed 1.90GHz Display Screen Size 17.3 '' Display Type HD+ Operating Systems Operating System Windows 7 Home Premium Graphics GPU/VPU Graphics Media Accelerator HD Hard Drive HDD 500GB Communications WLAN 802.11b/g/n Wireless LAN Bluetooth Yes Memory Memory 4GB Max Memory Supported 8GB Optical Drive Optical Drive Type DVD±R/RW Ports HDMI 1 x HDMI Supplemental Drive Webcam Yes Power Battery 6-cell lithium ion +"Intel. Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","Lithium Ion ( Li-Ion ). Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture",DDR3 SDRAM. DDR3 SDRAM,"2.40 GHz. Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","500 GB. Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","1.4 ''. Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","Acer. Acer , Inc","Dual-core ( 2 Core ). i3-3110M. Core i3. Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","Best Acer Aspire E1-771-33116G50Mnii-i3-3110M-6GB-500GB HDD-Win 7-17.3"" LED Notebook | TopEndElectronics UK","3 MB. Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","3 Hour. Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","10.8 ''. Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","10.8 ''. 1.4 ''. Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","17.30 ``. Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture",www.topendelectronic.co.uk//448,"i3-3110M. Pricing Part Number NX.MG7AA.006 Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.006 UPC Code 43211503 Brand Name Acer Product Model E1-771-33116G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3110M Processor Speed 2.40 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture" +Intel. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad,Lithium Ion ( Li-Ion ). Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad,DDR3 SDRAM. DDR3L SDRAM,1 GHz. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad,500 GB. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad,0.8 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad,Acer. Acer,Dual-core ( 2 Core ). 1019Y. Celeron. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad,"Best Acer Aspire V5-132P-10194G50nss-Celeron 1019Y-4GB-500GB HDD-Win 8-11.6""Touchscreen LED Notebook | TopEndElectronics UK",2 MB. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad,3.50 Hour. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad,8.1 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad,8.1 ''. 0.8 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad,11.60 ``. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad,www.topendelectronic.co.uk//470,1019Y. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2640 mAh Maximum Battery Run Time 3.50 Hour Interfaces/Ports Total Number of USB Ports 2 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 1 Audio Line Out Yes Display & Graphics Screen Size 11.60 `` Touchscreen Yes Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED Multi-touch Screen Yes Graphics Controller Manufacturer Intel Graphics Controller Model Graphics Media Accelerator HD Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type No General Information Manufacturer Acer Manufacturer Part Number NX.MDRAA.001 Brand Name Acer Product Model V5-132P-10194G50nss Product Type Notebook Product Line Aspire Product Series V5-132P Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 4 GB Maximum Memory 6 GB Memory Technology DDR3L SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Broadcom Wireless LAN Model 43228 Wireless LAN Standard IEEE 802.11b/g/n Bluetooth Yes Physical Characteristics Height 0.8 '' Width 11.4 '' Depth 8.1 '' Weight ( Approximate ) 48.64 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Celeron Processor Model 1019Y Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Cache 2 MB Chipset Manufacturer Intel Chipset Model HM70 Express Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad +AMD. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,Lithium Ion ( Li-Ion ). Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,,1 GHz. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,500 GB. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,0.9 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,Acer,Dual-core ( 2 Core ). E1-2100. E-Series. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,"Best Acer Aspire V5-123-12104G50nkk-E-Series E1-2100-4GB-500GB HDD-Win 8-11.6"" LED Notebook-Red | TopEndElectronics UK",Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,4 Hour. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,8.0 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,8.0 ''. 0.9 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,11.60 ``. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,www.topendelectronic.co.uk//393,E1-2100. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture +AMD. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,Lithium Ion ( Li-Ion ). Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,,1 GHz. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,500 GB. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,0.9 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,Acer,Dual-core ( 2 Core ). E1-2100. E-Series. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,"Best Acer Aspire V5-123-12104G50nkk-E-Series E1-2100-4GB-500GB HDD-Win 8-11.6"" LED Notebook-Black | TopEndElectronics UK",Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,4 Hour. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,8.0 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,8.0 ''. 0.9 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,11.60 ``. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture,www.topendelectronic.co.uk//181,E1-2100. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 2500 mAh Maximum Battery Run Time 4 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 Display & Graphics Screen Size 11.60 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer AMD Graphics Controller Model Radeon HD 8210 Graphics Memory Accessibility Shared Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 General Information Brand Name Acer Product Model V5-123-12104G50nkk Product Type Notebook Product Line Aspire Product Series V5-123 Memory Standard Memory 4 GB Number of Total Memory Slots 1 Network & Communication Wireless LAN Yes Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 0.9 '' Width 11.2 '' Depth 8.0 '' Weight ( Approximate ) 42.4 oz Built-in Devices Webcam Yes Speakers Yes Processor & Chipset Processor Manufacturer AMD Processor Type E-Series Processor Model E1-2100 Processor Speed 1 GHz Processor Core Dual-core ( 2 Core ) Software Operating System Windows 8 Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture +"Intel. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","Lithium Ion ( Li-Ion ). Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture",DDR3 SDRAM. DDR3 SDRAM,"2.60 GHz. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","500 GB. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","1.4 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","Acer. Acer , Inc","Dual-core ( 2 Core ). i5-3230M. Core i5. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","Best Acer Aspire E1-771-53236G50Mnii-6GB-500GB HDD-Win 7-17.3""LED Notebook | TopEndElectronics UK","3 MB. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","3 Hour. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","10.8 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","10.8 ''. 1.4 ''. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","17.30 ``. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture",www.topendelectronic.co.uk//93,"i5-3230M. Battery Information Battery Chemistry Lithium Ion ( Li-Ion ) Battery Capacity 4400 mAh Maximum Battery Run Time 3 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 3 Number of USB 2.0 Ports 1 Number of USB 3.0 Ports 2 VGA Yes Audio Line In Yes Audio Line Out Yes Display & Graphics Screen Size 17.30 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD+ Screen Resolution 1600 x 900 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.MG7AA.005 UPC Code 43211503 Brand Name Acer Product Model E1-771-53236G50Mnii Product Type Notebook Product Line Aspire Product Series E1-771 Memory Memory Card Supported Secure Digital ( SD ) Card Standard Memory 6 GB Maximum Memory 8 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 2 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Physical Characteristics Height 1.4 '' Width 16.3 '' Depth 10.8 '' Weight ( Approximate ) 112.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i5 Processor Model i5-3230M Processor Speed 2.60 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture" +AMD. Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes,Lithium Ion ( Li-Ion ). Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes,DDR3 SDRAM. DDR3L SDRAM,1.80 GHz. Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes,500 GB. Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes,1.2 ''. 1 ''. 1.19 ''. Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes,,Quad-core ( 4 Core ). A4-6210. A-Series. Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes,"Best Acer Aspire E5-521-435W-AMD A-Series A4-6210 1.80GHz-4GB -500GB HDD-Win 8.1 64-bit-15.6"" LED Notebook -Black | TopEndElectronics UK",2 MB. Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes,Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes,10.1 ''. Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes,10.1 ''. 1.2 ''. 1 ''. 1.19 ''. Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes,15.6 ''. Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes,www.topendelectronic.co.uk//110,A4-6210. Product Name Aspire E5-521-435W A4-6210 1.8GHz 4GB 500GB DVD/RW 15.6 '' W8.1 Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer AMD Graphics Controller Manufacturer AMD Bluetooth Yes Wireless LAN Yes Optical Drive Type DVD-Writer Graphics Memory Accessibility Shared HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire E5-521 Operating System Architecture 64-bit Processor Model A4-6210 Color Black Operating System Windows 8.1 Processor Speed 1.80 GHz Processor Type A-Series Screen Size 15.6 '' Number of Cells 6-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) Number of Total Memory Slots Optical Media Supported DVD-RAM/±R/ ±RW Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports Height 1.2 '' Width 15 '' Depth 10.1 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series E5-521 Product Model E5-521-435W Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 0.672916667 Maximum Memory 16 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 2500 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 VGA Yes Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes Number of USB 2.0 Ports 2 Number of USB 3.0 Ports 1 TouchPad Features Multi-touch Gesture Height ( Front ) 1 '' Height ( Rear ) 1.19 '' Memory Card Supported Secure Digital ( SD ) Card Certifications & Standards ACPI 3.0 Keyboard Yes Microphone Yes Speakers Yes +"Intel. Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","Lithium Polymer ( Li-Polymer ). Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture",DDR3 SDRAM. DDR3 SDRAM,"1.90 GHz. Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","500 GB. Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","0.8 ''. Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","Acer. Acer , Inc","Dual-core ( 2 Core ). i3-3227U. Core i3. Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","Best Acer Aspire M5-481T-33226G52Mtss-i3-3227U-6GB-500GB HDD+20 GB SSD-Win 7-14""LED Ultrabook | TopEndElectronics UK","3 MB. Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","8 Hour. Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","9.6 ''. Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","9.6 ''. 0.8 ''. Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture","14.00 ``. Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture",www.topendelectronic.co.uk//492,"i3-3227U. Battery Information Battery Chemistry Lithium Polymer ( Li-Polymer ) Battery Capacity 4850 mAh Maximum Battery Run Time 8 Hour Interfaces/Ports HDMI Yes Total Number of USB Ports 2 Number of USB 3.0 Ports 2 Audio Line Out Yes Display & Graphics Screen Size 14.00 `` Display Screen Type Active Matrix TFT Color LCD Aspect Ratio 16 : 9 Screen Mode HD Screen Resolution 1366 x 768 Backlight Technology LED HDCP Supported Yes Graphics Controller Manufacturer Intel Graphics Controller Model HD 4000 Graphics Memory Technology DDR3 SDRAM Graphics Memory Accessibility Shared TV Card No Storage Storage Size 500 GB Hard Drive Interface Serial ATA Hard Drive RPM 5400 Solid State Drive Capacity 20 GB Optical Drive Type DVD-Writer General Information Manufacturer Acer , Inc Manufacturer Part Number NX.M26AA.009 Brand Name Acer Product Model M5-481T-33226G52Mtss Product Type Ultrabook Product Line Aspire Product Series M5-481T Memory Memory Card Supported Secure Digital ( SD ) Card , MultiMediaCard ( MMC ) Standard Memory 6 GB Maximum Memory 6 GB Memory Technology DDR3 SDRAM Number of Total Memory Slots 1 Memory Card Reader Yes Network & Communication Wireless LAN Yes Wireless LAN Manufacturer Intel Wireless LAN Model Centrino Advanced-N 6235 Wireless LAN Standard IEEE 802.11a/b/g/n Ethernet Technology Gigabit Ethernet Bluetooth Yes Bluetooth Standard Bluetooth 4.0 + HS Physical Characteristics Height 0.8 '' Width 13.4 '' Depth 9.6 '' Weight ( Approximate ) 68.8 oz Built-in Devices Webcam Yes Microphone Yes Finger Print Reader No Speakers Yes Processor & Chipset Processor Manufacturer Intel Processor Type Core i3 Processor Model i3-3227U Processor Speed 1.90 GHz Processor Core Dual-core ( 2 Core ) Cache 3 MB Chipset Manufacturer Intel Chipset Model HM77 Express Software Operating System Windows 7 Home Premium Operating System Architecture 64-bit Input Devices Keyboard Backlight Yes Pointing Device Type TouchPad TouchPad Features Multi-touch Gesture" +Intel. Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes,Lithium Ion ( Li-Ion ). Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes,DDR3 SDRAM. DDR3L SDRAM,1.83 GHz. Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes,500 GB. Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes,1.1 ''. 0.95 ''. 1.07 ''. Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes,,Quad-core ( 4 Core ). N2930. Celeron. Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes,"Best Acer Aspire ES1-511-C665-Intel N2930 1.83GHz - 4GB- 500GB HDD-Win 8.1 64-bit-15.6"" LED Notebook-Black | TopEndElectronics UK",2 MB. Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes,6 Hour. Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes,10.2 ''. Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes,10.2 ''. 1.1 ''. 0.95 ''. 1.07 ''. Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes,15.6 ''. Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes,www.topendelectronic.co.uk//297,N2930. Product Name Aspire ES1-511-C665 N2930 1.83GHz 4GB 500GB 15.6 '' W8.1 - Black Product Type Notebook Hard Drive Capacity 500 GB Processor Manufacturer Intel Graphics Controller Manufacturer Intel Graphics Controller Model HD Graphics Bluetooth Yes Wireless LAN Yes Optical Drive Type No Graphics Memory Accessibility Shared Maximum Battery Run Time 6 Hour Finger Print Reader No HDMI Yes Webcam Yes Backlight Technology LED Product Family Aspire ES1-511 Operating System Architecture 64-bit Processor Model N2930 Operating System Windows 8.1 Processor Speed 1.83 GHz Processor Type Celeron Screen Size 15.6 '' Number of Cells 4-cell Screen Resolution 1366 x 768 Standard Memory 4 GB Cache 2 MB Processor Core Quad-core ( 4 Core ) 64-bit Processing Yes Number of Total Memory Slots 1 Display Screen Type Active Matrix TFT Color LCD Wireless LAN Standard IEEE 802.11b/g/n Ethernet Technology Gigabit Ethernet Total Number of USB Ports 3 Height 1.1 '' Width 15 '' Depth 10.2 '' Screen Mode HD Memory Technology DDR3L SDRAM Product Series ES1-511 Product Model ES1-511-C665 Pointing Device Type TouchPad Product Line Aspire Aspect Ratio 16.9 Maximum Memory 8 GB Battery Chemistry Lithium Ion ( Li-Ion ) HDCP Supported Yes Memory Card Reader Yes Battery Capacity 3220 mAh Graphics Memory Technology DDR3 SDRAM Wireless LAN Manufacturer Atheros Wireless LAN Model WB335 Hyper-Threading No TV Card No Network ( RJ-45 ) Yes Maximum Power Supply Wattage 40 W Audio Line Out Yes TouchPad Features Multi-touch Gesture Height ( Front ) 0.95 '' Height ( Rear ) 1.07 '' Memory Card Supported Secure Digital ( SD ) Card Keyboard Yes Microphone Yes Speakers Yes +Intel. Intel Dual-Core i5-3230M Processor,"Power Cord , Battery. Lithium Ion",,2.60 GHz,500 GB,16.3 x 10.8 x 1.2,,Intel Dual-Core i5-3230M Processor,"Acer Aspire Laptop PC, E1-771-53236G50Mnii - Walmart.com",,3 hours,16.3 x 10.8 x 1.2,16.3 x 10.8 x 1.2,17.3 inch,wallmartphoto.com//411,Intel Dual-Core i5-3230M Processor +Intel. Intel Core i3-3227U Processor,"Power Cord , Battery. Lithium Ion",,1.90 GHz,20 GB,15.5 x 11.4 x 3.6,,Intel Core i3-3227U Processor,"Acer Silver Aspire M5-481T-33226G52Mtss 14"" Laptop PC - Walmart.com",,8 hours,15.5 x 11.4 x 3.6,15.5 x 11.4 x 3.6,14.0 inch,wallmartphoto.com//599,Intel Core i3-3227U Processor +Intel. Intel Celeron 2955U Dual-Core Processor,"Power Cord , Battery. Lithium Ion",,1.40 GHz,500 GB,15.0 x 10.1 x 0.5,,Intel Celeron 2955U Dual-Core Processor,"Acer Aspire Laptop PC, E1-532-29574G50Mnrr - Walmart.com",,4 hours,15.0 x 10.1 x 0.5,15.0 x 10.1 x 0.5,15.6 inch,wallmartphoto.com//344,Intel Celeron 2955U Dual-Core Processor +Intel. Intel Dual-Core i3-3110M Processor,"Power Cord , Battery. Lithium Ion",,2.40 GHz,500 GB,16.3 x 10.8 x 1.2,,Intel Dual-Core i3-3110M Processor,"Acer Aspire Laptop PC, E1-771-33116G50Mnii - Walmart.com",,3 hours,16.3 x 10.8 x 1.2,16.3 x 10.8 x 1.2,17.3 inch,wallmartphoto.com//813,Intel Dual-Core i3-3110M Processor +Intel. Intel Celeron 1005M Dual-Core Processor,"Power Cord , Battery. Lithium Ion",,1.90 GHz,500 GB,16.3 x 10.8 x 1.2,,Intel Celeron 1005M Dual-Core Processor,"Acer Aspire Laptop PC, E1-731-10054G50Mnii - Walmart.com",,3 hours,16.3 x 10.8 x 1.2,16.3 x 10.8 x 1.2,17.3 inch,wallmartphoto.com//432,Intel Celeron 1005M Dual-Core Processor +Intel. Intel Pentium 3558U Dual-Core Processor,"Power Cord , Battery. Lithium Ion",,1.70 GHz,500 GB,15.0 x 10.1 x 0.5,,Intel Pentium 3558U Dual-Core Processor,"Acer Aspire Laptop PC, E1-532-35584G50Mnkk - Walmart.com",,4 hours,15.0 x 10.1 x 0.5,15.0 x 10.1 x 0.5,15.6 inch,wallmartphoto.com//209,Intel Pentium 3558U Dual-Core Processor +Intel. Intel Core i5-4210U Processor,"Power Cord , Battery. Lithium Ion",,2.70 GHz,1 TB,14.9 x 10.0 x 0.9,,Intel Core i5-4210U Processor,"Dell Silver 15.6"" Inspiron 15 5547 Laptop PC with Intel Core i5-4210U Processor, 6GB Memory, Touchscreen, 1TB Hard Drive and Windows 8.1: Computers : Walmart.com",,7 hours 17 minutes,14.9 x 10.0 x 0.9,14.9 x 10.0 x 0.9,15.6 inch,wallmartphoto.com//596,Intel Core i5-4210U Processor +Intel. Intel Dual-Core i5-4200U Processor,"Power Cord , Battery. Lithium Ion",,"1.60 GHz , with a Max Turbo Speed of 2.60 GHz",128 GB,17.5 x 10.9 x 3.3,,Intel Dual-Core i5-4200U Processor,"Acer 13.3"" Aspire Laptop PC, Crystal White, S7-392-6845 - Walmart.com",,8 hours,17.5 x 10.9 x 3.3,17.5 x 10.9 x 3.3,13.3 inch,wallmartphoto.com//187,Intel Dual-Core i5-4200U Processor +Intel. Intel Pentium 3558U Dual-Core Processor,"Power Cord , Battery. Lithium Ion",,1.70 GHz,500 GB,15.0 x 10.1 x 0.5,,Intel Pentium 3558U Dual-Core Processor,"Acer Aspire Laptop PC, E1-532-35584G50Mnrr - Walmart.com",,4 hours,15.0 x 10.1 x 0.5,15.0 x 10.1 x 0.5,15.6 inch,wallmartphoto.com//33,Intel Pentium 3558U Dual-Core Processor +AMD. AMD Dual-Core E1-2100 Processor,"Power Cord , Battery. Lithium Ion",,1.0 GHz,500 GB,15.0 x 9.4 x 3.0,,AMD Dual-Core E1-2100 Processor,"Acer Glossy Black 11.6"" Aspire V5-123-3466 Laptop PC with AMD E1-2100 Dual-Core Processor, 4GB Memory, 500GB Hard Drive and Windows 8: Computers : Walmart.com",,4 hours,15.0 x 9.4 x 3.0,15.0 x 9.4 x 3.0,11.6 inch,wallmartphoto.com//309,AMD Dual-Core E1-2100 Processor +,,,,,,,,"Miniprice.ca - Lenovo ThinkPad X230 34355AF Tablet PC - 12.5"" - In-plane Switching (IPS) Technology - Wireless LAN - Intel Core i5 i5-3320M 2.60 GHz - Black - 4 GB RAM - 180 GB SSD - Windows 7 Professional 64-bit (34355AF-DDO)",,,,,,www.pricequebec.com//824, +Intel,Lithium Polymer ( Li-Polymer ),DDR3 SDRAM. DDR3 SDRAM,1.90 GHz,500 GB,0.8 '',"Acer. Acer , Inc",i3-3227U. Core i3,"Acer Aspire M5-481T-33226G52Mtss 14"" LED Ultrabook - Intel Core i3 i3-3227U 1.90 GHz - NXM26AA009 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",3 MB,8 Hour,9.6 '',9.6 ''. 0.8 '',14 '',www.thenerds.net//885,i3-3227U +Intel,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3L SDRAM,1.70 GHz,500 GB,1 ''. 0.48 ''. 1 '',"Acer. Acer , Inc",3558U. Pentium,"Acer Aspire E1-532-35584G50Mnrr 15.6"" LED Notebook - Intel Pentium 3558U 1.70 GHz - Red - NXMHGAA002 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",2 MB,4 Hour,10.1 '',10.1 ''. 1 ''. 0.48 ''. 1 '',15.6 '',www.thenerds.net//1234,3558U +AMD,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3 SDRAM,1 GHz,500 GB,0.9 ''. 0.93 ''. 1.08 '',"Acer. Acer , Inc",E1-2100. E-Series,"Acer Aspire V5-123-12104G50nrr 11.6"" LED (ComfyView) Notebook - AMD E-Series E1-2100 1 GHz - Red - NXML2AA001 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",1 MB,4 Hour,8 '',8 ''. 0.9 ''. 0.93 ''. 1.08 '',11.6 '',www.thenerds.net//956,E1-2100 +Intel,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3L SDRAM,1.70 GHz,500 GB,1 ''. 0.48 ''. 1 '',"Acer. Acer , Inc",3558U. Pentium,"Acer Aspire E1-532-35584G50Mnkk 15.6"" LED Notebook - Intel Pentium 3558U 1.70 GHz - Black - NXMFVAA005 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",2 MB,4 Hour,10.1 '',10.1 ''. 1 ''. 0.48 ''. 1 '',15.6 '',www.thenerds.net//1034,3558U +Intel,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3L SDRAM,1.40 GHz,500 GB,1 ''. 0.48 ''. 1 '',"Acer. Acer , Inc",2957U. Celeron,"Acer Aspire E1-532-29574G50Mnkk 15.6"" LED Notebook - Intel Celeron 2957U 1.40 GHz - Black - NXMFVAA006 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",2 MB,4 Hour,10.1 '',10.1 ''. 1 ''. 0.48 ''. 1 '',15.6 '',www.thenerds.net//718,2957U +Intel,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3L SDRAM,1.40 GHz,500 GB,1 ''. 0.48 ''. 1 '',"Acer. Acer , Inc",2957U. Celeron,"Acer Aspire E1-532-29574G50Mnrr 15.6"" LED Notebook - Intel Celeron 2957U 1.40 GHz - Red - NXMHGAA001 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",2 MB,4 Hour,10.1 '',10.1 ''. 1 ''. 0.48 ''. 1 '',15.6 '',www.thenerds.net//827,2957U +AMD,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3 SDRAM,1 GHz,500 GB,0.9 ''. 0.93 ''. 1.08 '',"Acer. Acer , Inc",E1-2100. E-Series,"Acer Aspire V5-123-12104G50nkk 11.6"" LED (ComfyView) Notebook - AMD E-Series E1-2100 1 GHz - Black - NXMFQAA005 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",1 MB,4 Hour,8 '',8 ''. 0.9 ''. 0.93 ''. 1.08 '',11.6 '',www.thenerds.net//1516,E1-2100 +Intel,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3L SDRAM,1.70 GHz,500 GB,1 '',"Acer. Acer , Inc",i3-4010U. Core i3,"Acer Aspire E1-572-34014G50Mnkk 15.6"" LED Notebook - Intel Core i3 i3-4010U 1.70 GHz - Black - NXM8EAA007 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",3 MB,4 Hour,10.1 '',10.1 ''. 1 '',15.6 '',www.thenerds.net//1396,i3-4010U +Intel,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3L SDRAM,1.83 GHz,500 GB,1.1 ''. 0.95 ''. 1.07 '',"Acer. Acer , Inc",N2930. Celeron,"Acer Aspire ES1-511-C665 15.6"" LED Notebook - Intel Celeron N2930 1.83 GHz - NXMMLAA015 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",2 MB,6 Hour,10.2 '',10.2 ''. 1.1 ''. 0.95 ''. 1.07 '',15.6 '',www.thenerds.net//1504,N2930 +Intel,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3 SDRAM,2.40 GHz,500 GB,1.4 ''. 1.22 ''. 1.36 '',"Acer. Acer , Inc",i3-3110M. Core i3,"Acer Aspire E1-771-33116G50Mnii 17.3"" LED Notebook - Intel Core i3 i3-3110M 2.40 GHz - NXMG7AA006 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",3 MB,3 Hour,10.8 '',10.8 ''. 1.4 ''. 1.22 ''. 1.36 '',17.3 '',www.thenerds.net//1394,i3-3110M +Intel,Lithium Polymer ( Li-Polymer ),DDR3 SDRAM. DDR3L SDRAM,1.60 GHz,,0.5 ''. 0.51 ''. 0.51 '',"Acer. Acer , Inc",i5-4200U. Core i5,"Acer Aspire S7-392-54208G25tws 13.3"" Touchscreen LED (In-plane Switching (IPS) Technology) Ultrabook - Intel Core i5 i5-4200U 1.60 GHz - NXMBKAA023 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",3 MB,8 Hour,8.8 '',8.8 ''. 0.5 ''. 0.51 ''. 0.51 '',13.3 '',www.thenerds.net//1037,i5-4200U +Intel,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3 SDRAM,2.60 GHz,500 GB,1.4 ''. 1.22 ''. 1.36 '',"Acer. Acer , Inc",i5-3230M. Core i5,"Acer Aspire E1-771-53236G50Mnii 17.3"" LED Notebook - Intel Core i5 i5-3230M 2.60 GHz - NXMG7AA005 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",3 MB,3 Hour,10.8 '',10.8 ''. 1.4 ''. 1.22 ''. 1.36 '',17.3 '',www.thenerds.net//1265,i5-3230M +Intel,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3L SDRAM,1 GHz,500 GB,0.8 ''. 0.77 ''. 0.83 '',"Acer. Acer , Inc",1019Y. Celeron,"Acer Aspire V5-132P-10194G50nss 11.6"" Touchscreen LED Notebook - Intel Celeron 1019Y 1 GHz - Silver - NXMDRAA001 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",2 MB,3.50 Hour,8.1 '',8.1 ''. 0.8 ''. 0.77 ''. 0.83 '',11.6 '',www.thenerds.net//1290,1019Y +AMD,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3L SDRAM,1.80 GHz,500 GB,1.2 ''. 1 ''. 1.19 '',"Acer. Acer , Inc",A4-6210. A-Series,"Acer Aspire E5-521-435W 15.6"" LED Notebook - AMD A-Series A4-6210 1.80 GHz - Black - NXMLFAA010 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",2 MB,,10.1 '',10.1 ''. 1.2 ''. 1 ''. 1.19 '',15.6 '',www.thenerds.net//1254,A4-6210 +Intel,Lithium Ion ( Li-Ion ),DDR3 SDRAM. DDR3L SDRAM,1.70 GHz,500 GB,1 '',"Acer. Acer , Inc",i3-4010U. Core i3,"Acer Aspire E1-572-34014G50Mnrr 15.6"" LED Notebook - Intel Core i3 i3-4010U 1.70 GHz - Red - NXMHFAA002 - Laptops & Notebooks - ACER Laptops & Notebooks - TheNerds.net",3 MB,4 Hour,10.1 '',10.1 ''. 1 '',15.6 '',www.thenerds.net//1129,i3-4010U diff --git a/wrapper/assets/database_setup/experiments/computers_sigmod_goldstandard.csv b/wrapper/assets/database_setup/experiments/computers_sigmod_goldstandard.csv new file mode 100644 index 00000000..5d76162a --- /dev/null +++ b/wrapper/assets/database_setup/experiments/computers_sigmod_goldstandard.csv @@ -0,0 +1,904 @@ +left_instance_id,right_instance_id,label +www.thenerds.net//1129,www.thenerds.net//1396,1 +www.topendelectronic.co.uk//93,wallmartphoto.com//813,1 +www.topendelectronic.co.uk//93,www.thenerds.net//1394,1 +www.topendelectronic.co.uk//448,wallmartphoto.com//813,1 +wallmartphoto.com//813,www.thenerds.net//1394,1 +www.topendelectronic.co.uk//448,www.thenerds.net//1394,1 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//448,1 +wallmartphoto.com//411,www.thenerds.net//1265,1 +www.thenerds.net//1254,www.topendelectronic.co.uk//110,1 +www.thenerds.net//1504,www.topendelectronic.co.uk//297,1 +www.topendelectronic.co.uk//492,wallmartphoto.com//599,1 +wallmartphoto.com//599,www.thenerds.net//885,1 +www.topendelectronic.co.uk//492,www.thenerds.net//885,1 +www.gosale.com//93,www.gosale.com//99,1 +www.topendelectronic.co.uk//258,www.gosale.com//99,1 +www.topendelectronic.co.uk//258,www.gosale.com//93,1 +www.gosale.com//74,www.thenerds.net//1037,1 +wallmartphoto.com//187,www.thenerds.net//1037,1 +wallmartphoto.com//187,www.gosale.com//74,1 +www.bhphotovideo.com//284,wallmartphoto.com//596,1 +www.topendelectronic.co.uk//181,wallmartphoto.com//309,1 +www.thenerds.net//956,www.thenerds.net//1516,1 +www.topendelectronic.co.uk//181,www.topendelectronic.co.uk//393,1 +www.thenerds.net//956,wallmartphoto.com//309,1 +www.thenerds.net//956,www.topendelectronic.co.uk//393,1 +www.topendelectronic.co.uk//393,www.thenerds.net//1516,1 +www.topendelectronic.co.uk//181,www.thenerds.net//956,1 +www.topendelectronic.co.uk//393,wallmartphoto.com//309,1 +www.thenerds.net//1516,wallmartphoto.com//309,1 +www.topendelectronic.co.uk//181,www.thenerds.net//1516,1 +www.thenerds.net//1290,www.topendelectronic.co.uk//470,1 +www.topendelectronic.co.uk//472,www.pricequebec.com//824,1 +wallmartphoto.com//432,www.topendelectronic.co.uk//79,1 +www.topendelectronic.co.uk//139,www.topendelectronic.co.uk//238,1 +www.thenerds.net//827,www.thenerds.net//718,1 +wallmartphoto.com//344,www.thenerds.net//718,1 +www.thenerds.net//827,wallmartphoto.com//344,1 +www.thenerds.net//1234,www.thenerds.net//1034,1 +www.thenerds.net//1234,wallmartphoto.com//209,1 +www.thenerds.net//1234,wallmartphoto.com//33,1 +wallmartphoto.com//33,wallmartphoto.com//209,1 +wallmartphoto.com//33,www.thenerds.net//1034,1 +wallmartphoto.com//209,www.thenerds.net//1034,1 +www.thenerds.net//1396,www.thenerds.net//1394,0 +www.thenerds.net//1129,www.thenerds.net//1394,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//93,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//93,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//448,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//448,0 +www.thenerds.net//1396,wallmartphoto.com//813,0 +www.thenerds.net//1129,wallmartphoto.com//813,0 +www.thenerds.net//1396,www.thenerds.net//1265,0 +www.thenerds.net//1396,wallmartphoto.com//411,0 +www.thenerds.net//1129,www.thenerds.net//1265,0 +www.thenerds.net//1129,wallmartphoto.com//411,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//110,0 +www.thenerds.net//1396,www.thenerds.net//1254,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//110,0 +www.thenerds.net//1129,www.thenerds.net//1254,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//297,0 +www.thenerds.net//1129,www.thenerds.net//1504,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//297,0 +www.thenerds.net//1396,www.thenerds.net//1504,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//492,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//492,0 +www.thenerds.net//1396,wallmartphoto.com//599,0 +www.thenerds.net//1129,www.thenerds.net//885,0 +www.thenerds.net//1129,wallmartphoto.com//599,0 +www.thenerds.net//1396,www.thenerds.net//885,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//258,0 +www.thenerds.net//1129,www.gosale.com//93,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//258,0 +www.thenerds.net//1396,www.gosale.com//93,0 +www.thenerds.net//1396,www.gosale.com//99,0 +www.thenerds.net//1129,www.gosale.com//99,0 +www.thenerds.net//1396,wallmartphoto.com//187,0 +www.thenerds.net//1129,wallmartphoto.com//187,0 +www.thenerds.net//1129,www.thenerds.net//1037,0 +www.thenerds.net//1396,www.thenerds.net//1037,0 +www.thenerds.net//1129,www.gosale.com//74,0 +www.thenerds.net//1396,www.gosale.com//74,0 +www.thenerds.net//1396,wallmartphoto.com//596,0 +www.thenerds.net//1129,www.bhphotovideo.com//284,0 +www.thenerds.net//1129,wallmartphoto.com//596,0 +www.thenerds.net//1396,www.bhphotovideo.com//284,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//393,0 +www.thenerds.net//1129,wallmartphoto.com//309,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//393,0 +www.thenerds.net//1396,wallmartphoto.com//309,0 +www.thenerds.net//1396,www.thenerds.net//1516,0 +www.thenerds.net//1129,www.thenerds.net//1516,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//181,0 +www.thenerds.net//1129,www.thenerds.net//956,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//181,0 +www.thenerds.net//1396,www.thenerds.net//956,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//470,0 +www.thenerds.net//1396,www.thenerds.net//1290,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//470,0 +www.thenerds.net//1129,www.thenerds.net//1290,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//472,0 +www.thenerds.net//1396,www.pricequebec.com//824,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//472,0 +www.thenerds.net//1129,www.pricequebec.com//824,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//79,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//79,0 +www.thenerds.net//1129,wallmartphoto.com//432,0 +www.thenerds.net//1396,wallmartphoto.com//432,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//238,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//139,0 +www.thenerds.net//1129,www.topendelectronic.co.uk//238,0 +www.thenerds.net//1396,www.topendelectronic.co.uk//139,0 +www.thenerds.net//1129,www.thenerds.net//718,0 +www.thenerds.net//1129,www.thenerds.net//827,0 +www.thenerds.net//1396,www.thenerds.net//718,0 +www.thenerds.net//1396,www.thenerds.net//827,0 +www.thenerds.net//1396,wallmartphoto.com//344,0 +www.thenerds.net//1129,wallmartphoto.com//344,0 +www.thenerds.net//1129,wallmartphoto.com//33,0 +www.thenerds.net//1129,wallmartphoto.com//209,0 +www.thenerds.net//1396,wallmartphoto.com//209,0 +www.thenerds.net//1396,www.thenerds.net//1234,0 +www.thenerds.net//1129,www.thenerds.net//1234,0 +www.thenerds.net//1129,www.thenerds.net//1034,0 +www.thenerds.net//1396,wallmartphoto.com//33,0 +www.thenerds.net//1396,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//93,wallmartphoto.com//411,0 +www.topendelectronic.co.uk//93,www.thenerds.net//1265,0 +www.thenerds.net//1394,wallmartphoto.com//411,0 +www.topendelectronic.co.uk//448,www.thenerds.net//1265,0 +www.thenerds.net//1394,www.thenerds.net//1265,0 +wallmartphoto.com//813,www.thenerds.net//1265,0 +wallmartphoto.com//813,wallmartphoto.com//411,0 +www.topendelectronic.co.uk//448,wallmartphoto.com//411,0 +wallmartphoto.com//813,www.thenerds.net//1254,0 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//110,0 +www.topendelectronic.co.uk//448,www.thenerds.net//1254,0 +www.thenerds.net//1394,www.thenerds.net//1254,0 +www.topendelectronic.co.uk//93,www.thenerds.net//1254,0 +www.thenerds.net//1394,www.topendelectronic.co.uk//110,0 +www.topendelectronic.co.uk//448,www.topendelectronic.co.uk//110,0 +wallmartphoto.com//813,www.topendelectronic.co.uk//110,0 +wallmartphoto.com//813,www.topendelectronic.co.uk//297,0 +www.topendelectronic.co.uk//448,www.thenerds.net//1504,0 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//297,0 +wallmartphoto.com//813,www.thenerds.net//1504,0 +www.thenerds.net//1394,www.topendelectronic.co.uk//297,0 +www.topendelectronic.co.uk//448,www.topendelectronic.co.uk//297,0 +www.topendelectronic.co.uk//93,www.thenerds.net//1504,0 +www.thenerds.net//1394,www.thenerds.net//1504,0 +www.topendelectronic.co.uk//93,wallmartphoto.com//599,0 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//492,0 +www.thenerds.net//1394,www.thenerds.net//885,0 +www.thenerds.net//1394,wallmartphoto.com//599,0 +www.thenerds.net//1394,www.topendelectronic.co.uk//492,0 +wallmartphoto.com//813,www.topendelectronic.co.uk//492,0 +www.topendelectronic.co.uk//93,www.thenerds.net//885,0 +wallmartphoto.com//813,wallmartphoto.com//599,0 +www.topendelectronic.co.uk//448,www.topendelectronic.co.uk//492,0 +www.topendelectronic.co.uk//448,www.thenerds.net//885,0 +www.topendelectronic.co.uk//448,wallmartphoto.com//599,0 +wallmartphoto.com//813,www.thenerds.net//885,0 +www.thenerds.net//1394,www.gosale.com//99,0 +www.topendelectronic.co.uk//93,www.gosale.com//93,0 +wallmartphoto.com//813,www.gosale.com//99,0 +www.thenerds.net//1394,www.gosale.com//93,0 +www.topendelectronic.co.uk//448,www.gosale.com//99,0 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//258,0 +www.topendelectronic.co.uk//448,www.topendelectronic.co.uk//258,0 +www.thenerds.net//1394,www.topendelectronic.co.uk//258,0 +www.topendelectronic.co.uk//93,www.gosale.com//99,0 +wallmartphoto.com//813,www.topendelectronic.co.uk//258,0 +www.topendelectronic.co.uk//448,www.gosale.com//93,0 +wallmartphoto.com//813,www.gosale.com//93,0 +www.topendelectronic.co.uk//448,www.thenerds.net//1037,0 +wallmartphoto.com//813,www.thenerds.net//1037,0 +www.thenerds.net//1394,www.thenerds.net//1037,0 +www.topendelectronic.co.uk//93,wallmartphoto.com//187,0 +www.topendelectronic.co.uk//448,www.gosale.com//74,0 +wallmartphoto.com//813,www.gosale.com//74,0 +www.topendelectronic.co.uk//93,www.thenerds.net//1037,0 +www.thenerds.net//1394,wallmartphoto.com//187,0 +wallmartphoto.com//813,wallmartphoto.com//187,0 +www.topendelectronic.co.uk//93,www.gosale.com//74,0 +www.topendelectronic.co.uk//448,wallmartphoto.com//187,0 +www.thenerds.net//1394,www.gosale.com//74,0 +www.topendelectronic.co.uk//93,wallmartphoto.com//596,0 +www.topendelectronic.co.uk//93,www.bhphotovideo.com//284,0 +www.topendelectronic.co.uk//448,www.bhphotovideo.com//284,0 +www.thenerds.net//1394,wallmartphoto.com//596,0 +wallmartphoto.com//813,www.bhphotovideo.com//284,0 +www.thenerds.net//1394,www.bhphotovideo.com//284,0 +wallmartphoto.com//813,wallmartphoto.com//596,0 +www.topendelectronic.co.uk//448,wallmartphoto.com//596,0 +www.topendelectronic.co.uk//93,www.thenerds.net//1516,0 +wallmartphoto.com//813,www.topendelectronic.co.uk//181,0 +www.topendelectronic.co.uk//448,www.topendelectronic.co.uk//181,0 +www.thenerds.net//1394,www.topendelectronic.co.uk//181,0 +wallmartphoto.com//813,www.thenerds.net//1516,0 +www.topendelectronic.co.uk//448,www.thenerds.net//956,0 +www.topendelectronic.co.uk//93,wallmartphoto.com//309,0 +www.thenerds.net//1394,www.thenerds.net//1516,0 +wallmartphoto.com//813,www.thenerds.net//956,0 +www.thenerds.net//1394,wallmartphoto.com//309,0 +www.thenerds.net//1394,www.topendelectronic.co.uk//393,0 +www.topendelectronic.co.uk//448,www.topendelectronic.co.uk//393,0 +www.topendelectronic.co.uk//448,wallmartphoto.com//309,0 +www.topendelectronic.co.uk//93,www.thenerds.net//956,0 +wallmartphoto.com//813,www.topendelectronic.co.uk//393,0 +wallmartphoto.com//813,wallmartphoto.com//309,0 +www.thenerds.net//1394,www.thenerds.net//956,0 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//181,0 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//393,0 +www.topendelectronic.co.uk//448,www.thenerds.net//1516,0 +www.topendelectronic.co.uk//448,www.thenerds.net//1290,0 +www.thenerds.net//1394,www.thenerds.net//1290,0 +wallmartphoto.com//813,www.thenerds.net//1290,0 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//470,0 +www.thenerds.net//1394,www.topendelectronic.co.uk//470,0 +wallmartphoto.com//813,www.topendelectronic.co.uk//470,0 +www.topendelectronic.co.uk//448,www.topendelectronic.co.uk//470,0 +www.topendelectronic.co.uk//93,www.thenerds.net//1290,0 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//472,0 +www.topendelectronic.co.uk//93,www.pricequebec.com//824,0 +www.thenerds.net//1394,www.topendelectronic.co.uk//472,0 +www.thenerds.net//1394,www.pricequebec.com//824,0 +wallmartphoto.com//813,www.pricequebec.com//824,0 +www.topendelectronic.co.uk//448,www.pricequebec.com//824,0 +www.topendelectronic.co.uk//448,www.topendelectronic.co.uk//472,0 +wallmartphoto.com//813,www.topendelectronic.co.uk//472,0 +www.thenerds.net//1394,www.topendelectronic.co.uk//79,0 +www.topendelectronic.co.uk//448,www.topendelectronic.co.uk//79,0 +wallmartphoto.com//813,www.topendelectronic.co.uk//79,0 +wallmartphoto.com//813,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//79,0 +www.topendelectronic.co.uk//448,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//93,wallmartphoto.com//432,0 +www.thenerds.net//1394,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//139,0 +wallmartphoto.com//813,www.topendelectronic.co.uk//139,0 +www.topendelectronic.co.uk//448,www.topendelectronic.co.uk//139,0 +wallmartphoto.com//813,www.topendelectronic.co.uk//238,0 +www.thenerds.net//1394,www.topendelectronic.co.uk//238,0 +www.topendelectronic.co.uk//448,www.topendelectronic.co.uk//238,0 +www.topendelectronic.co.uk//93,www.topendelectronic.co.uk//238,0 +www.thenerds.net//1394,www.topendelectronic.co.uk//139,0 +wallmartphoto.com//813,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//93,www.thenerds.net//827,0 +www.topendelectronic.co.uk//448,wallmartphoto.com//344,0 +www.thenerds.net//1394,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//93,www.thenerds.net//718,0 +www.topendelectronic.co.uk//93,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//448,www.thenerds.net//827,0 +www.thenerds.net//1394,www.thenerds.net//718,0 +wallmartphoto.com//813,www.thenerds.net//827,0 +www.topendelectronic.co.uk//448,www.thenerds.net//718,0 +www.thenerds.net//1394,www.thenerds.net//827,0 +wallmartphoto.com//813,www.thenerds.net//718,0 +www.topendelectronic.co.uk//93,wallmartphoto.com//33,0 +www.thenerds.net//1394,www.thenerds.net//1234,0 +wallmartphoto.com//813,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//448,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//448,www.thenerds.net//1234,0 +wallmartphoto.com//813,wallmartphoto.com//33,0 +www.thenerds.net//1394,wallmartphoto.com//209,0 +wallmartphoto.com//813,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//448,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//93,wallmartphoto.com//209,0 +www.topendelectronic.co.uk//93,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//93,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//448,wallmartphoto.com//209,0 +www.thenerds.net//1394,www.thenerds.net//1034,0 +wallmartphoto.com//813,wallmartphoto.com//209,0 +www.thenerds.net//1394,wallmartphoto.com//33,0 +wallmartphoto.com//411,www.thenerds.net//1254,0 +www.thenerds.net//1265,www.thenerds.net//1254,0 +wallmartphoto.com//411,www.topendelectronic.co.uk//110,0 +www.thenerds.net//1265,www.topendelectronic.co.uk//110,0 +www.thenerds.net//1265,www.thenerds.net//1504,0 +www.thenerds.net//1265,www.topendelectronic.co.uk//297,0 +wallmartphoto.com//411,www.topendelectronic.co.uk//297,0 +wallmartphoto.com//411,www.thenerds.net//1504,0 +www.thenerds.net//1265,www.topendelectronic.co.uk//492,0 +wallmartphoto.com//411,www.topendelectronic.co.uk//492,0 +wallmartphoto.com//411,wallmartphoto.com//599,0 +wallmartphoto.com//411,www.thenerds.net//885,0 +www.thenerds.net//1265,wallmartphoto.com//599,0 +www.thenerds.net//1265,www.thenerds.net//885,0 +www.thenerds.net//1265,www.topendelectronic.co.uk//258,0 +wallmartphoto.com//411,www.topendelectronic.co.uk//258,0 +wallmartphoto.com//411,www.gosale.com//93,0 +www.thenerds.net//1265,www.gosale.com//99,0 +wallmartphoto.com//411,www.gosale.com//99,0 +www.thenerds.net//1265,www.gosale.com//93,0 +www.thenerds.net//1265,wallmartphoto.com//187,0 +wallmartphoto.com//411,wallmartphoto.com//187,0 +www.thenerds.net//1265,www.gosale.com//74,0 +wallmartphoto.com//411,www.thenerds.net//1037,0 +www.thenerds.net//1265,www.thenerds.net//1037,0 +wallmartphoto.com//411,www.gosale.com//74,0 +wallmartphoto.com//411,wallmartphoto.com//596,0 +www.thenerds.net//1265,wallmartphoto.com//596,0 +www.thenerds.net//1265,www.bhphotovideo.com//284,0 +wallmartphoto.com//411,www.bhphotovideo.com//284,0 +wallmartphoto.com//411,www.topendelectronic.co.uk//393,0 +www.thenerds.net//1265,wallmartphoto.com//309,0 +wallmartphoto.com//411,wallmartphoto.com//309,0 +www.thenerds.net//1265,www.topendelectronic.co.uk//393,0 +wallmartphoto.com//411,www.thenerds.net//1516,0 +www.thenerds.net//1265,www.thenerds.net//956,0 +www.thenerds.net//1265,www.topendelectronic.co.uk//181,0 +wallmartphoto.com//411,www.topendelectronic.co.uk//181,0 +www.thenerds.net//1265,www.thenerds.net//1516,0 +wallmartphoto.com//411,www.thenerds.net//956,0 +wallmartphoto.com//411,www.thenerds.net//1290,0 +wallmartphoto.com//411,www.topendelectronic.co.uk//470,0 +www.thenerds.net//1265,www.topendelectronic.co.uk//470,0 +www.thenerds.net//1265,www.thenerds.net//1290,0 +www.thenerds.net//1265,www.pricequebec.com//824,0 +wallmartphoto.com//411,www.pricequebec.com//824,0 +wallmartphoto.com//411,www.topendelectronic.co.uk//472,0 +www.thenerds.net//1265,www.topendelectronic.co.uk//472,0 +wallmartphoto.com//411,wallmartphoto.com//432,0 +www.thenerds.net//1265,wallmartphoto.com//432,0 +wallmartphoto.com//411,www.topendelectronic.co.uk//79,0 +www.thenerds.net//1265,www.topendelectronic.co.uk//79,0 +wallmartphoto.com//411,www.topendelectronic.co.uk//238,0 +www.thenerds.net//1265,www.topendelectronic.co.uk//238,0 +www.thenerds.net//1265,www.topendelectronic.co.uk//139,0 +wallmartphoto.com//411,www.topendelectronic.co.uk//139,0 +www.thenerds.net//1265,www.thenerds.net//718,0 +wallmartphoto.com//411,www.thenerds.net//718,0 +wallmartphoto.com//411,www.thenerds.net//827,0 +www.thenerds.net//1265,www.thenerds.net//827,0 +wallmartphoto.com//411,wallmartphoto.com//344,0 +www.thenerds.net//1265,wallmartphoto.com//344,0 +www.thenerds.net//1265,wallmartphoto.com//209,0 +www.thenerds.net//1265,wallmartphoto.com//33,0 +www.thenerds.net//1265,www.thenerds.net//1234,0 +wallmartphoto.com//411,wallmartphoto.com//209,0 +wallmartphoto.com//411,www.thenerds.net//1234,0 +www.thenerds.net//1265,www.thenerds.net//1034,0 +wallmartphoto.com//411,wallmartphoto.com//33,0 +wallmartphoto.com//411,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//110,www.thenerds.net//1504,0 +www.topendelectronic.co.uk//110,www.topendelectronic.co.uk//297,0 +www.thenerds.net//1254,www.thenerds.net//1504,0 +www.thenerds.net//1254,www.topendelectronic.co.uk//297,0 +www.topendelectronic.co.uk//110,www.thenerds.net//885,0 +www.thenerds.net//1254,wallmartphoto.com//599,0 +www.thenerds.net//1254,www.thenerds.net//885,0 +www.topendelectronic.co.uk//110,wallmartphoto.com//599,0 +www.thenerds.net//1254,www.topendelectronic.co.uk//492,0 +www.topendelectronic.co.uk//110,www.topendelectronic.co.uk//492,0 +www.topendelectronic.co.uk//110,www.gosale.com//93,0 +www.topendelectronic.co.uk//110,www.topendelectronic.co.uk//258,0 +www.thenerds.net//1254,www.gosale.com//93,0 +www.thenerds.net//1254,www.topendelectronic.co.uk//258,0 +www.thenerds.net//1254,www.gosale.com//99,0 +www.topendelectronic.co.uk//110,www.gosale.com//99,0 +www.thenerds.net//1254,www.gosale.com//74,0 +www.topendelectronic.co.uk//110,www.thenerds.net//1037,0 +www.thenerds.net//1254,www.thenerds.net//1037,0 +www.thenerds.net//1254,wallmartphoto.com//187,0 +www.topendelectronic.co.uk//110,wallmartphoto.com//187,0 +www.topendelectronic.co.uk//110,www.gosale.com//74,0 +www.topendelectronic.co.uk//110,wallmartphoto.com//596,0 +www.topendelectronic.co.uk//110,www.bhphotovideo.com//284,0 +www.thenerds.net//1254,wallmartphoto.com//596,0 +www.thenerds.net//1254,www.bhphotovideo.com//284,0 +www.thenerds.net//1254,www.topendelectronic.co.uk//181,0 +www.thenerds.net//1254,www.thenerds.net//956,0 +www.topendelectronic.co.uk//110,www.topendelectronic.co.uk//181,0 +www.thenerds.net//1254,www.thenerds.net//1516,0 +www.topendelectronic.co.uk//110,www.thenerds.net//1516,0 +www.topendelectronic.co.uk//110,wallmartphoto.com//309,0 +www.topendelectronic.co.uk//110,www.topendelectronic.co.uk//393,0 +www.thenerds.net//1254,wallmartphoto.com//309,0 +www.thenerds.net//1254,www.topendelectronic.co.uk//393,0 +www.topendelectronic.co.uk//110,www.thenerds.net//956,0 +www.topendelectronic.co.uk//110,www.thenerds.net//1290,0 +www.thenerds.net//1254,www.topendelectronic.co.uk//470,0 +www.thenerds.net//1254,www.thenerds.net//1290,0 +www.topendelectronic.co.uk//110,www.topendelectronic.co.uk//470,0 +www.thenerds.net//1254,www.topendelectronic.co.uk//472,0 +www.thenerds.net//1254,www.pricequebec.com//824,0 +www.topendelectronic.co.uk//110,www.topendelectronic.co.uk//472,0 +www.topendelectronic.co.uk//110,www.pricequebec.com//824,0 +www.thenerds.net//1254,www.topendelectronic.co.uk//79,0 +www.topendelectronic.co.uk//110,www.topendelectronic.co.uk//79,0 +www.topendelectronic.co.uk//110,wallmartphoto.com//432,0 +www.thenerds.net//1254,wallmartphoto.com//432,0 +www.thenerds.net//1254,www.topendelectronic.co.uk//139,0 +www.topendelectronic.co.uk//110,www.topendelectronic.co.uk//238,0 +www.topendelectronic.co.uk//110,www.topendelectronic.co.uk//139,0 +www.thenerds.net//1254,www.topendelectronic.co.uk//238,0 +www.thenerds.net//1254,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//110,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//110,www.thenerds.net//827,0 +www.topendelectronic.co.uk//110,www.thenerds.net//718,0 +www.thenerds.net//1254,www.thenerds.net//827,0 +www.thenerds.net//1254,www.thenerds.net//718,0 +www.thenerds.net//1254,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//110,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//110,wallmartphoto.com//209,0 +www.thenerds.net//1254,wallmartphoto.com//209,0 +www.thenerds.net//1254,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//110,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//110,wallmartphoto.com//33,0 +www.thenerds.net//1254,www.thenerds.net//1034,0 +www.thenerds.net//1504,wallmartphoto.com//599,0 +www.topendelectronic.co.uk//297,www.topendelectronic.co.uk//492,0 +www.topendelectronic.co.uk//297,www.thenerds.net//885,0 +www.topendelectronic.co.uk//297,wallmartphoto.com//599,0 +www.thenerds.net//1504,www.thenerds.net//885,0 +www.thenerds.net//1504,www.topendelectronic.co.uk//492,0 +www.thenerds.net//1504,www.gosale.com//93,0 +www.topendelectronic.co.uk//297,www.topendelectronic.co.uk//258,0 +www.topendelectronic.co.uk//297,www.gosale.com//93,0 +www.thenerds.net//1504,www.gosale.com//99,0 +www.topendelectronic.co.uk//297,www.gosale.com//99,0 +www.thenerds.net//1504,www.topendelectronic.co.uk//258,0 +www.topendelectronic.co.uk//297,wallmartphoto.com//187,0 +www.topendelectronic.co.uk//297,www.gosale.com//74,0 +www.thenerds.net//1504,www.gosale.com//74,0 +www.topendelectronic.co.uk//297,www.thenerds.net//1037,0 +www.thenerds.net//1504,www.thenerds.net//1037,0 +www.thenerds.net//1504,wallmartphoto.com//187,0 +www.thenerds.net//1504,www.bhphotovideo.com//284,0 +www.thenerds.net//1504,wallmartphoto.com//596,0 +www.topendelectronic.co.uk//297,www.bhphotovideo.com//284,0 +www.topendelectronic.co.uk//297,wallmartphoto.com//596,0 +www.topendelectronic.co.uk//297,www.topendelectronic.co.uk//393,0 +www.thenerds.net//1504,www.thenerds.net//1516,0 +www.topendelectronic.co.uk//297,wallmartphoto.com//309,0 +www.thenerds.net//1504,www.topendelectronic.co.uk//393,0 +www.topendelectronic.co.uk//297,www.thenerds.net//1516,0 +www.thenerds.net//1504,www.thenerds.net//956,0 +www.thenerds.net//1504,www.topendelectronic.co.uk//181,0 +www.topendelectronic.co.uk//297,www.topendelectronic.co.uk//181,0 +www.thenerds.net//1504,wallmartphoto.com//309,0 +www.topendelectronic.co.uk//297,www.thenerds.net//956,0 +www.thenerds.net//1504,www.topendelectronic.co.uk//470,0 +www.topendelectronic.co.uk//297,www.thenerds.net//1290,0 +www.topendelectronic.co.uk//297,www.topendelectronic.co.uk//470,0 +www.thenerds.net//1504,www.thenerds.net//1290,0 +www.thenerds.net//1504,www.topendelectronic.co.uk//472,0 +www.topendelectronic.co.uk//297,www.pricequebec.com//824,0 +www.topendelectronic.co.uk//297,www.topendelectronic.co.uk//472,0 +www.thenerds.net//1504,www.pricequebec.com//824,0 +www.thenerds.net//1504,www.topendelectronic.co.uk//79,0 +www.topendelectronic.co.uk//297,www.topendelectronic.co.uk//79,0 +www.thenerds.net//1504,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//297,wallmartphoto.com//432,0 +www.thenerds.net//1504,www.topendelectronic.co.uk//139,0 +www.thenerds.net//1504,www.topendelectronic.co.uk//238,0 +www.topendelectronic.co.uk//297,www.topendelectronic.co.uk//139,0 +www.topendelectronic.co.uk//297,www.topendelectronic.co.uk//238,0 +www.topendelectronic.co.uk//297,www.thenerds.net//827,0 +www.topendelectronic.co.uk//297,www.thenerds.net//718,0 +www.thenerds.net//1504,www.thenerds.net//827,0 +www.topendelectronic.co.uk//297,wallmartphoto.com//344,0 +www.thenerds.net//1504,www.thenerds.net//718,0 +www.thenerds.net//1504,wallmartphoto.com//344,0 +www.thenerds.net//1504,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//297,wallmartphoto.com//209,0 +www.thenerds.net//1504,www.thenerds.net//1234,0 +www.thenerds.net//1504,www.thenerds.net//1034,0 +www.thenerds.net//1504,wallmartphoto.com//209,0 +www.topendelectronic.co.uk//297,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//297,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//297,wallmartphoto.com//33,0 +www.thenerds.net//885,www.gosale.com//93,0 +www.topendelectronic.co.uk//492,www.gosale.com//93,0 +wallmartphoto.com//599,www.topendelectronic.co.uk//258,0 +wallmartphoto.com//599,www.gosale.com//93,0 +www.thenerds.net//885,www.topendelectronic.co.uk//258,0 +www.topendelectronic.co.uk//492,www.gosale.com//99,0 +www.thenerds.net//885,www.gosale.com//99,0 +wallmartphoto.com//599,www.gosale.com//99,0 +www.topendelectronic.co.uk//492,www.topendelectronic.co.uk//258,0 +wallmartphoto.com//599,wallmartphoto.com//187,0 +www.thenerds.net//885,wallmartphoto.com//187,0 +www.thenerds.net//885,www.thenerds.net//1037,0 +wallmartphoto.com//599,www.thenerds.net//1037,0 +www.topendelectronic.co.uk//492,www.gosale.com//74,0 +www.topendelectronic.co.uk//492,www.thenerds.net//1037,0 +www.thenerds.net//885,www.gosale.com//74,0 +wallmartphoto.com//599,www.gosale.com//74,0 +www.topendelectronic.co.uk//492,wallmartphoto.com//187,0 +www.thenerds.net//885,wallmartphoto.com//596,0 +www.topendelectronic.co.uk//492,wallmartphoto.com//596,0 +www.thenerds.net//885,www.bhphotovideo.com//284,0 +wallmartphoto.com//599,wallmartphoto.com//596,0 +www.topendelectronic.co.uk//492,www.bhphotovideo.com//284,0 +wallmartphoto.com//599,www.bhphotovideo.com//284,0 +www.topendelectronic.co.uk//492,wallmartphoto.com//309,0 +www.thenerds.net//885,www.thenerds.net//1516,0 +www.topendelectronic.co.uk//492,www.thenerds.net//1516,0 +wallmartphoto.com//599,www.topendelectronic.co.uk//393,0 +wallmartphoto.com//599,wallmartphoto.com//309,0 +wallmartphoto.com//599,www.thenerds.net//1516,0 +www.topendelectronic.co.uk//492,www.topendelectronic.co.uk//181,0 +www.thenerds.net//885,wallmartphoto.com//309,0 +www.topendelectronic.co.uk//492,www.topendelectronic.co.uk//393,0 +wallmartphoto.com//599,www.topendelectronic.co.uk//181,0 +www.thenerds.net//885,www.thenerds.net//956,0 +www.topendelectronic.co.uk//492,www.thenerds.net//956,0 +www.thenerds.net//885,www.topendelectronic.co.uk//181,0 +wallmartphoto.com//599,www.thenerds.net//956,0 +www.thenerds.net//885,www.topendelectronic.co.uk//393,0 +www.thenerds.net//885,www.topendelectronic.co.uk//470,0 +www.topendelectronic.co.uk//492,www.topendelectronic.co.uk//470,0 +wallmartphoto.com//599,www.topendelectronic.co.uk//470,0 +www.topendelectronic.co.uk//492,www.thenerds.net//1290,0 +wallmartphoto.com//599,www.thenerds.net//1290,0 +www.thenerds.net//885,www.thenerds.net//1290,0 +wallmartphoto.com//599,www.pricequebec.com//824,0 +www.thenerds.net//885,www.topendelectronic.co.uk//472,0 +www.topendelectronic.co.uk//492,www.topendelectronic.co.uk//472,0 +www.thenerds.net//885,www.pricequebec.com//824,0 +wallmartphoto.com//599,www.topendelectronic.co.uk//472,0 +www.topendelectronic.co.uk//492,www.pricequebec.com//824,0 +www.thenerds.net//885,www.topendelectronic.co.uk//79,0 +www.thenerds.net//885,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//492,wallmartphoto.com//432,0 +wallmartphoto.com//599,www.topendelectronic.co.uk//79,0 +wallmartphoto.com//599,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//492,www.topendelectronic.co.uk//79,0 +wallmartphoto.com//599,www.topendelectronic.co.uk//238,0 +www.topendelectronic.co.uk//492,www.topendelectronic.co.uk//139,0 +www.thenerds.net//885,www.topendelectronic.co.uk//139,0 +www.topendelectronic.co.uk//492,www.topendelectronic.co.uk//238,0 +www.thenerds.net//885,www.topendelectronic.co.uk//238,0 +wallmartphoto.com//599,www.topendelectronic.co.uk//139,0 +www.topendelectronic.co.uk//492,www.thenerds.net//718,0 +www.thenerds.net//885,www.thenerds.net//827,0 +wallmartphoto.com//599,www.thenerds.net//718,0 +wallmartphoto.com//599,www.thenerds.net//827,0 +www.topendelectronic.co.uk//492,www.thenerds.net//827,0 +www.thenerds.net//885,wallmartphoto.com//344,0 +www.thenerds.net//885,www.thenerds.net//718,0 +wallmartphoto.com//599,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//492,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//492,wallmartphoto.com//33,0 +www.thenerds.net//885,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//492,www.thenerds.net//1234,0 +wallmartphoto.com//599,wallmartphoto.com//209,0 +wallmartphoto.com//599,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//492,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//492,wallmartphoto.com//209,0 +www.thenerds.net//885,wallmartphoto.com//209,0 +www.thenerds.net//885,www.thenerds.net//1234,0 +www.thenerds.net//885,www.thenerds.net//1034,0 +wallmartphoto.com//599,wallmartphoto.com//33,0 +wallmartphoto.com//599,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//258,wallmartphoto.com//187,0 +www.gosale.com//93,www.thenerds.net//1037,0 +www.gosale.com//99,www.thenerds.net//1037,0 +www.gosale.com//99,www.gosale.com//74,0 +www.topendelectronic.co.uk//258,www.gosale.com//74,0 +www.gosale.com//99,wallmartphoto.com//187,0 +www.gosale.com//93,wallmartphoto.com//187,0 +www.topendelectronic.co.uk//258,www.thenerds.net//1037,0 +www.gosale.com//93,www.gosale.com//74,0 +www.topendelectronic.co.uk//258,www.bhphotovideo.com//284,0 +www.gosale.com//99,www.bhphotovideo.com//284,0 +www.gosale.com//93,wallmartphoto.com//596,0 +www.gosale.com//99,wallmartphoto.com//596,0 +www.topendelectronic.co.uk//258,wallmartphoto.com//596,0 +www.gosale.com//93,www.bhphotovideo.com//284,0 +www.topendelectronic.co.uk//258,wallmartphoto.com//309,0 +www.gosale.com//93,www.topendelectronic.co.uk//181,0 +www.topendelectronic.co.uk//258,www.topendelectronic.co.uk//393,0 +www.gosale.com//99,www.topendelectronic.co.uk//181,0 +www.gosale.com//99,www.thenerds.net//956,0 +www.topendelectronic.co.uk//258,www.thenerds.net//956,0 +www.gosale.com//93,www.thenerds.net//1516,0 +www.topendelectronic.co.uk//258,www.topendelectronic.co.uk//181,0 +www.gosale.com//93,wallmartphoto.com//309,0 +www.gosale.com//93,www.topendelectronic.co.uk//393,0 +www.gosale.com//99,www.topendelectronic.co.uk//393,0 +www.gosale.com//99,wallmartphoto.com//309,0 +www.gosale.com//99,www.thenerds.net//1516,0 +www.topendelectronic.co.uk//258,www.thenerds.net//1516,0 +www.gosale.com//93,www.thenerds.net//956,0 +www.gosale.com//93,www.thenerds.net//1290,0 +www.gosale.com//99,www.thenerds.net//1290,0 +www.gosale.com//93,www.topendelectronic.co.uk//470,0 +www.topendelectronic.co.uk//258,www.thenerds.net//1290,0 +www.gosale.com//99,www.topendelectronic.co.uk//470,0 +www.topendelectronic.co.uk//258,www.topendelectronic.co.uk//470,0 +www.topendelectronic.co.uk//258,www.pricequebec.com//824,0 +www.gosale.com//93,www.topendelectronic.co.uk//472,0 +www.gosale.com//99,www.pricequebec.com//824,0 +www.gosale.com//93,www.pricequebec.com//824,0 +www.gosale.com//99,www.topendelectronic.co.uk//472,0 +www.topendelectronic.co.uk//258,www.topendelectronic.co.uk//472,0 +www.gosale.com//93,wallmartphoto.com//432,0 +www.gosale.com//99,www.topendelectronic.co.uk//79,0 +www.gosale.com//93,www.topendelectronic.co.uk//79,0 +www.gosale.com//99,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//258,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//258,www.topendelectronic.co.uk//79,0 +www.topendelectronic.co.uk//258,www.topendelectronic.co.uk//238,0 +www.topendelectronic.co.uk//258,www.topendelectronic.co.uk//139,0 +www.gosale.com//99,www.topendelectronic.co.uk//139,0 +www.gosale.com//99,www.topendelectronic.co.uk//238,0 +www.gosale.com//93,www.topendelectronic.co.uk//238,0 +www.gosale.com//93,www.topendelectronic.co.uk//139,0 +www.gosale.com//99,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//258,www.thenerds.net//718,0 +www.gosale.com//93,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//258,www.thenerds.net//827,0 +www.gosale.com//93,www.thenerds.net//827,0 +www.gosale.com//93,www.thenerds.net//718,0 +www.topendelectronic.co.uk//258,wallmartphoto.com//344,0 +www.gosale.com//99,www.thenerds.net//718,0 +www.gosale.com//99,www.thenerds.net//827,0 +www.gosale.com//99,wallmartphoto.com//209,0 +www.gosale.com//99,www.thenerds.net//1234,0 +www.gosale.com//93,www.thenerds.net//1234,0 +www.gosale.com//93,wallmartphoto.com//209,0 +www.topendelectronic.co.uk//258,wallmartphoto.com//33,0 +www.gosale.com//99,wallmartphoto.com//33,0 +www.gosale.com//99,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//258,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//258,www.thenerds.net//1034,0 +www.gosale.com//93,www.thenerds.net//1034,0 +www.gosale.com//93,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//258,wallmartphoto.com//209,0 +www.gosale.com//74,wallmartphoto.com//596,0 +wallmartphoto.com//187,wallmartphoto.com//596,0 +wallmartphoto.com//187,www.bhphotovideo.com//284,0 +www.thenerds.net//1037,www.bhphotovideo.com//284,0 +www.gosale.com//74,www.bhphotovideo.com//284,0 +www.thenerds.net//1037,wallmartphoto.com//596,0 +wallmartphoto.com//187,wallmartphoto.com//309,0 +www.gosale.com//74,www.thenerds.net//1516,0 +wallmartphoto.com//187,www.thenerds.net//1516,0 +www.gosale.com//74,wallmartphoto.com//309,0 +www.gosale.com//74,www.topendelectronic.co.uk//393,0 +www.thenerds.net//1037,www.thenerds.net//956,0 +www.thenerds.net//1037,www.topendelectronic.co.uk//181,0 +wallmartphoto.com//187,www.topendelectronic.co.uk//181,0 +www.gosale.com//74,www.thenerds.net//956,0 +wallmartphoto.com//187,www.topendelectronic.co.uk//393,0 +www.thenerds.net//1037,www.topendelectronic.co.uk//393,0 +www.thenerds.net//1037,wallmartphoto.com//309,0 +www.gosale.com//74,www.topendelectronic.co.uk//181,0 +wallmartphoto.com//187,www.thenerds.net//956,0 +www.thenerds.net//1037,www.thenerds.net//1516,0 +www.thenerds.net//1037,www.thenerds.net//1290,0 +wallmartphoto.com//187,www.topendelectronic.co.uk//470,0 +wallmartphoto.com//187,www.thenerds.net//1290,0 +www.gosale.com//74,www.thenerds.net//1290,0 +www.thenerds.net//1037,www.topendelectronic.co.uk//470,0 +www.gosale.com//74,www.topendelectronic.co.uk//470,0 +www.gosale.com//74,www.pricequebec.com//824,0 +wallmartphoto.com//187,www.topendelectronic.co.uk//472,0 +www.thenerds.net//1037,www.pricequebec.com//824,0 +www.thenerds.net//1037,www.topendelectronic.co.uk//472,0 +wallmartphoto.com//187,www.pricequebec.com//824,0 +www.gosale.com//74,www.topendelectronic.co.uk//472,0 +www.thenerds.net//1037,www.topendelectronic.co.uk//79,0 +www.thenerds.net//1037,wallmartphoto.com//432,0 +www.gosale.com//74,wallmartphoto.com//432,0 +wallmartphoto.com//187,wallmartphoto.com//432,0 +www.gosale.com//74,www.topendelectronic.co.uk//79,0 +wallmartphoto.com//187,www.topendelectronic.co.uk//79,0 +wallmartphoto.com//187,www.topendelectronic.co.uk//139,0 +www.gosale.com//74,www.topendelectronic.co.uk//238,0 +wallmartphoto.com//187,www.topendelectronic.co.uk//238,0 +www.gosale.com//74,www.topendelectronic.co.uk//139,0 +www.thenerds.net//1037,www.topendelectronic.co.uk//139,0 +www.thenerds.net//1037,www.topendelectronic.co.uk//238,0 +wallmartphoto.com//187,www.thenerds.net//718,0 +www.gosale.com//74,www.thenerds.net//827,0 +www.gosale.com//74,www.thenerds.net//718,0 +www.thenerds.net//1037,wallmartphoto.com//344,0 +wallmartphoto.com//187,www.thenerds.net//827,0 +www.thenerds.net//1037,www.thenerds.net//718,0 +www.thenerds.net//1037,www.thenerds.net//827,0 +www.gosale.com//74,wallmartphoto.com//344,0 +wallmartphoto.com//187,wallmartphoto.com//344,0 +wallmartphoto.com//187,wallmartphoto.com//33,0 +www.thenerds.net//1037,www.thenerds.net//1234,0 +www.gosale.com//74,www.thenerds.net//1034,0 +www.thenerds.net//1037,www.thenerds.net//1034,0 +www.gosale.com//74,wallmartphoto.com//33,0 +wallmartphoto.com//187,www.thenerds.net//1234,0 +www.thenerds.net//1037,wallmartphoto.com//33,0 +wallmartphoto.com//187,www.thenerds.net//1034,0 +wallmartphoto.com//187,wallmartphoto.com//209,0 +www.gosale.com//74,www.thenerds.net//1234,0 +www.gosale.com//74,wallmartphoto.com//209,0 +www.thenerds.net//1037,wallmartphoto.com//209,0 +wallmartphoto.com//596,www.topendelectronic.co.uk//393,0 +wallmartphoto.com//596,www.thenerds.net//1516,0 +www.bhphotovideo.com//284,www.thenerds.net//1516,0 +www.bhphotovideo.com//284,www.topendelectronic.co.uk//393,0 +www.bhphotovideo.com//284,www.topendelectronic.co.uk//181,0 +wallmartphoto.com//596,wallmartphoto.com//309,0 +www.bhphotovideo.com//284,www.thenerds.net//956,0 +wallmartphoto.com//596,www.topendelectronic.co.uk//181,0 +wallmartphoto.com//596,www.thenerds.net//956,0 +www.bhphotovideo.com//284,wallmartphoto.com//309,0 +wallmartphoto.com//596,www.topendelectronic.co.uk//470,0 +www.bhphotovideo.com//284,www.thenerds.net//1290,0 +www.bhphotovideo.com//284,www.topendelectronic.co.uk//470,0 +wallmartphoto.com//596,www.thenerds.net//1290,0 +www.bhphotovideo.com//284,www.pricequebec.com//824,0 +www.bhphotovideo.com//284,www.topendelectronic.co.uk//472,0 +wallmartphoto.com//596,www.topendelectronic.co.uk//472,0 +wallmartphoto.com//596,www.pricequebec.com//824,0 +wallmartphoto.com//596,www.topendelectronic.co.uk//79,0 +www.bhphotovideo.com//284,www.topendelectronic.co.uk//79,0 +wallmartphoto.com//596,wallmartphoto.com//432,0 +www.bhphotovideo.com//284,wallmartphoto.com//432,0 +wallmartphoto.com//596,www.topendelectronic.co.uk//139,0 +wallmartphoto.com//596,www.topendelectronic.co.uk//238,0 +www.bhphotovideo.com//284,www.topendelectronic.co.uk//139,0 +www.bhphotovideo.com//284,www.topendelectronic.co.uk//238,0 +www.bhphotovideo.com//284,www.thenerds.net//827,0 +wallmartphoto.com//596,wallmartphoto.com//344,0 +wallmartphoto.com//596,www.thenerds.net//718,0 +www.bhphotovideo.com//284,wallmartphoto.com//344,0 +www.bhphotovideo.com//284,www.thenerds.net//718,0 +wallmartphoto.com//596,www.thenerds.net//827,0 +wallmartphoto.com//596,wallmartphoto.com//33,0 +www.bhphotovideo.com//284,wallmartphoto.com//33,0 +www.bhphotovideo.com//284,www.thenerds.net//1034,0 +www.bhphotovideo.com//284,www.thenerds.net//1234,0 +www.bhphotovideo.com//284,wallmartphoto.com//209,0 +wallmartphoto.com//596,wallmartphoto.com//209,0 +wallmartphoto.com//596,www.thenerds.net//1034,0 +wallmartphoto.com//596,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//393,www.topendelectronic.co.uk//470,0 +www.thenerds.net//1516,www.topendelectronic.co.uk//470,0 +wallmartphoto.com//309,www.topendelectronic.co.uk//470,0 +www.thenerds.net//956,www.thenerds.net//1290,0 +wallmartphoto.com//309,www.thenerds.net//1290,0 +www.topendelectronic.co.uk//181,www.thenerds.net//1290,0 +www.topendelectronic.co.uk//393,www.thenerds.net//1290,0 +www.topendelectronic.co.uk//181,www.topendelectronic.co.uk//470,0 +www.thenerds.net//1516,www.thenerds.net//1290,0 +www.thenerds.net//956,www.topendelectronic.co.uk//470,0 +www.topendelectronic.co.uk//181,www.pricequebec.com//824,0 +www.topendelectronic.co.uk//393,www.topendelectronic.co.uk//472,0 +www.thenerds.net//956,www.pricequebec.com//824,0 +www.thenerds.net//1516,www.topendelectronic.co.uk//472,0 +wallmartphoto.com//309,www.pricequebec.com//824,0 +www.thenerds.net//1516,www.pricequebec.com//824,0 +wallmartphoto.com//309,www.topendelectronic.co.uk//472,0 +www.topendelectronic.co.uk//393,www.pricequebec.com//824,0 +www.topendelectronic.co.uk//181,www.topendelectronic.co.uk//472,0 +www.thenerds.net//956,www.topendelectronic.co.uk//472,0 +www.topendelectronic.co.uk//393,www.topendelectronic.co.uk//79,0 +www.topendelectronic.co.uk//181,wallmartphoto.com//432,0 +www.thenerds.net//956,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//181,www.topendelectronic.co.uk//79,0 +www.thenerds.net//1516,www.topendelectronic.co.uk//79,0 +wallmartphoto.com//309,www.topendelectronic.co.uk//79,0 +www.thenerds.net//956,www.topendelectronic.co.uk//79,0 +www.thenerds.net//1516,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//393,wallmartphoto.com//432,0 +wallmartphoto.com//309,wallmartphoto.com//432,0 +www.thenerds.net//1516,www.topendelectronic.co.uk//139,0 +www.topendelectronic.co.uk//393,www.topendelectronic.co.uk//238,0 +www.topendelectronic.co.uk//393,www.topendelectronic.co.uk//139,0 +www.topendelectronic.co.uk//181,www.topendelectronic.co.uk//238,0 +wallmartphoto.com//309,www.topendelectronic.co.uk//238,0 +www.thenerds.net//956,www.topendelectronic.co.uk//238,0 +www.thenerds.net//956,www.topendelectronic.co.uk//139,0 +www.topendelectronic.co.uk//181,www.topendelectronic.co.uk//139,0 +www.thenerds.net//1516,www.topendelectronic.co.uk//238,0 +wallmartphoto.com//309,www.topendelectronic.co.uk//139,0 +www.topendelectronic.co.uk//181,www.thenerds.net//718,0 +wallmartphoto.com//309,www.thenerds.net//827,0 +www.thenerds.net//956,www.thenerds.net//827,0 +www.thenerds.net//956,www.thenerds.net//718,0 +www.topendelectronic.co.uk//181,www.thenerds.net//827,0 +wallmartphoto.com//309,www.thenerds.net//718,0 +www.thenerds.net//1516,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//393,www.thenerds.net//718,0 +www.topendelectronic.co.uk//393,wallmartphoto.com//344,0 +www.thenerds.net//1516,www.thenerds.net//718,0 +wallmartphoto.com//309,wallmartphoto.com//344,0 +www.thenerds.net//956,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//181,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//393,www.thenerds.net//827,0 +www.thenerds.net//1516,www.thenerds.net//827,0 +www.thenerds.net//1516,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//393,wallmartphoto.com//33,0 +www.thenerds.net//956,www.thenerds.net//1034,0 +wallmartphoto.com//309,wallmartphoto.com//209,0 +www.thenerds.net//956,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//181,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//181,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//393,wallmartphoto.com//209,0 +wallmartphoto.com//309,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//393,www.thenerds.net//1034,0 +wallmartphoto.com//309,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//181,www.thenerds.net//1234,0 +www.thenerds.net//1516,wallmartphoto.com//209,0 +www.thenerds.net//1516,www.thenerds.net//1234,0 +www.thenerds.net//956,www.thenerds.net//1234,0 +www.thenerds.net//956,wallmartphoto.com//209,0 +www.thenerds.net//1516,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//181,wallmartphoto.com//209,0 +wallmartphoto.com//309,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//393,www.thenerds.net//1234,0 +www.thenerds.net//1290,www.pricequebec.com//824,0 +www.topendelectronic.co.uk//470,www.topendelectronic.co.uk//472,0 +www.thenerds.net//1290,www.topendelectronic.co.uk//472,0 +www.topendelectronic.co.uk//470,www.pricequebec.com//824,0 +www.topendelectronic.co.uk//470,wallmartphoto.com//432,0 +www.thenerds.net//1290,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//470,www.topendelectronic.co.uk//79,0 +www.thenerds.net//1290,www.topendelectronic.co.uk//79,0 +www.topendelectronic.co.uk//470,www.topendelectronic.co.uk//238,0 +www.thenerds.net//1290,www.topendelectronic.co.uk//238,0 +www.thenerds.net//1290,www.topendelectronic.co.uk//139,0 +www.topendelectronic.co.uk//470,www.topendelectronic.co.uk//139,0 +www.thenerds.net//1290,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//470,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//470,www.thenerds.net//827,0 +www.topendelectronic.co.uk//470,www.thenerds.net//718,0 +www.thenerds.net//1290,www.thenerds.net//718,0 +www.thenerds.net//1290,www.thenerds.net//827,0 +www.thenerds.net//1290,www.thenerds.net//1234,0 +www.thenerds.net//1290,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//470,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//470,wallmartphoto.com//209,0 +www.thenerds.net//1290,wallmartphoto.com//209,0 +www.topendelectronic.co.uk//470,www.thenerds.net//1034,0 +www.thenerds.net//1290,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//470,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//472,wallmartphoto.com//432,0 +www.pricequebec.com//824,wallmartphoto.com//432,0 +www.topendelectronic.co.uk//472,www.topendelectronic.co.uk//79,0 +www.pricequebec.com//824,www.topendelectronic.co.uk//79,0 +www.topendelectronic.co.uk//472,www.topendelectronic.co.uk//238,0 +www.pricequebec.com//824,www.topendelectronic.co.uk//139,0 +www.pricequebec.com//824,www.topendelectronic.co.uk//238,0 +www.topendelectronic.co.uk//472,www.topendelectronic.co.uk//139,0 +www.pricequebec.com//824,www.thenerds.net//718,0 +www.topendelectronic.co.uk//472,wallmartphoto.com//344,0 +www.pricequebec.com//824,www.thenerds.net//827,0 +www.topendelectronic.co.uk//472,www.thenerds.net//827,0 +www.topendelectronic.co.uk//472,www.thenerds.net//718,0 +www.pricequebec.com//824,wallmartphoto.com//344,0 +www.pricequebec.com//824,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//472,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//472,wallmartphoto.com//209,0 +www.pricequebec.com//824,www.thenerds.net//1234,0 +www.pricequebec.com//824,www.thenerds.net//1034,0 +www.pricequebec.com//824,wallmartphoto.com//209,0 +www.topendelectronic.co.uk//472,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//472,wallmartphoto.com//33,0 +wallmartphoto.com//432,www.topendelectronic.co.uk//139,0 +www.topendelectronic.co.uk//79,www.topendelectronic.co.uk//238,0 +www.topendelectronic.co.uk//79,www.topendelectronic.co.uk//139,0 +wallmartphoto.com//432,www.topendelectronic.co.uk//238,0 +www.topendelectronic.co.uk//79,www.thenerds.net//827,0 +wallmartphoto.com//432,www.thenerds.net//827,0 +wallmartphoto.com//432,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//79,www.thenerds.net//718,0 +www.topendelectronic.co.uk//79,wallmartphoto.com//344,0 +wallmartphoto.com//432,www.thenerds.net//718,0 +www.topendelectronic.co.uk//79,wallmartphoto.com//209,0 +www.topendelectronic.co.uk//79,www.thenerds.net//1034,0 +wallmartphoto.com//432,wallmartphoto.com//209,0 +wallmartphoto.com//432,www.thenerds.net//1034,0 +wallmartphoto.com//432,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//79,www.thenerds.net//1234,0 +wallmartphoto.com//432,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//79,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//238,www.thenerds.net//827,0 +www.topendelectronic.co.uk//238,www.thenerds.net//718,0 +www.topendelectronic.co.uk//139,www.thenerds.net//718,0 +www.topendelectronic.co.uk//139,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//238,wallmartphoto.com//344,0 +www.topendelectronic.co.uk//139,www.thenerds.net//827,0 +www.topendelectronic.co.uk//139,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//238,wallmartphoto.com//33,0 +www.topendelectronic.co.uk//238,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//139,wallmartphoto.com//209,0 +www.topendelectronic.co.uk//238,www.thenerds.net//1234,0 +www.topendelectronic.co.uk//139,www.thenerds.net//1034,0 +www.topendelectronic.co.uk//238,wallmartphoto.com//209,0 +www.topendelectronic.co.uk//139,www.thenerds.net//1234,0 +www.thenerds.net//827,wallmartphoto.com//33,0 +wallmartphoto.com//344,www.thenerds.net//1034,0 +wallmartphoto.com//344,wallmartphoto.com//209,0 +www.thenerds.net//718,wallmartphoto.com//209,0 +www.thenerds.net//827,wallmartphoto.com//209,0 +wallmartphoto.com//344,wallmartphoto.com//33,0 +www.thenerds.net//827,www.thenerds.net//1234,0 +www.thenerds.net//718,www.thenerds.net//1034,0 +www.thenerds.net//718,www.thenerds.net//1234,0 +wallmartphoto.com//344,www.thenerds.net//1234,0 +www.thenerds.net//827,www.thenerds.net//1034,0 +www.thenerds.net//718,wallmartphoto.com//33,0 diff --git a/wrapper/src/api/database/setup/examples/datasets.ts b/wrapper/src/api/database/setup/examples/datasets.ts index 6a852808..ca3d7054 100644 --- a/wrapper/src/api/database/setup/examples/datasets.ts +++ b/wrapper/src/api/database/setup/examples/datasets.ts @@ -60,4 +60,20 @@ export const exampleDatasets = assertType()({ numberOfRecords: 863, }, }, + computers: { + meta: { + name: 'computers-SIGMOD', + description: 'This is a dataset used for the SIGMOD-contest.', + tags: ['SIGMOD'], + }, + id: 2, + file: { + path: path.join(EXAMPLE_DATASET_DIR, 'computers_sigmod.csv'), + idColumn: 'instance_id', + separator: ',', + quote: '"', + escape: '"', + numberOfRecords: 43, + }, + }, }); diff --git a/wrapper/src/api/database/setup/examples/experiments.ts b/wrapper/src/api/database/setup/examples/experiments.ts index 137f3c2f..f52f0d2e 100644 --- a/wrapper/src/api/database/setup/examples/experiments.ts +++ b/wrapper/src/api/database/setup/examples/experiments.ts @@ -75,4 +75,22 @@ export const exampleExperiments = assertType()({ numberOfPairs: 100, }, }, + computersSigmodGoldstandard1: { + meta: { + name: 'goldstandard', + description: + 'Complete list of all duplicate pairs found in the original datasets.', + algorithmId: exampleAlgorithms.gold.id, + datasetId: exampleDatasets.computers.id, + }, + id: 3, + file: { + format: 'sigmod2021', + path: path.join( + EXAMPLE_EXPERIMENT_DIR, + 'computers_sigmod_goldstandard.csv' + ), + numberOfPairs: 903, + }, + }, }); From 76d0eae46b0aecc763e004b446f8d138c8f1cae8 Mon Sep 17 00:00:00 2001 From: phpfs Date: Thu, 25 Feb 2021 15:18:11 +0100 Subject: [PATCH 24/73] Allow larger content --- app/src/theme/overwrites.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/theme/overwrites.css b/app/src/theme/overwrites.css index e4358061..75f656db 100644 --- a/app/src/theme/overwrites.css +++ b/app/src/theme/overwrites.css @@ -59,5 +59,5 @@ /* Tooltip */ .tooltip-fixed { - max-width: 350px; + max-width: 450px; } From b39eb95ee84e200968a750a084e9a22ada260332 Mon Sep 17 00:00:00 2001 From: phpfs Date: Thu, 25 Feb 2021 15:43:05 +0100 Subject: [PATCH 25/73] Handle generic promise errors more gracefully (Fix #43) --- app/src/utils/requestHandler.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/utils/requestHandler.ts b/app/src/utils/requestHandler.ts index a1df4512..edbef5a3 100644 --- a/app/src/utils/requestHandler.ts +++ b/app/src/utils/requestHandler.ts @@ -26,10 +26,15 @@ const RequestHandler = async ( } ) .catch( - async (response: Response): Promise => { - dispatch( - showToast((await response.text()) ?? UNKNOWN_ERROR, ToastType.Error) - ); + async (result: unknown): Promise => { + // If error occurred with HTTP - show text + if (result instanceof Response) { + dispatch( + showToast((await result.text()) ?? UNKNOWN_ERROR, ToastType.Error) + ); + } + // Else - show generic error + dispatch(showToast(UNKNOWN_ERROR, ToastType.Error)); throw Error; } ) From 5112456f286df49763d7afbfe10ddec72b8071d6 Mon Sep 17 00:00:00 2001 From: phpfs Date: Thu, 25 Feb 2021 15:50:03 +0100 Subject: [PATCH 26/73] Add comment for more clarity --- app/src/store/reducers/AlgorithmDialogReducer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/store/reducers/AlgorithmDialogReducer.ts b/app/src/store/reducers/AlgorithmDialogReducer.ts index 3b4370f6..d4e462e3 100644 --- a/app/src/store/reducers/AlgorithmDialogReducer.ts +++ b/app/src/store/reducers/AlgorithmDialogReducer.ts @@ -34,6 +34,7 @@ export const AlgorithmDialogReducer = ( }; case DialogActions.CLOSE_DIALOG: if (state.dialogType === DialogTypes.ADD_DIALOG) + // Only keep current state for add dialog return { ...state, isOpen: false, From f282f2e4bb6eb3455cde628a4b45b478781ca707 Mon Sep 17 00:00:00 2001 From: phpfs Date: Thu, 25 Feb 2021 15:51:56 +0100 Subject: [PATCH 27/73] Switch to a "bolder" icon --- app/src/components/AlgorithmCard/AlgorithmCard.View.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx b/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx index ceb451b8..3515d531 100644 --- a/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx +++ b/app/src/components/AlgorithmCard/AlgorithmCard.View.tsx @@ -10,7 +10,7 @@ import { IonRow, } from '@ionic/react'; import { AlgorithmCardProps } from 'components/AlgorithmCard/AlgorithmCardProps'; -import { pencil, trash } from 'ionicons/icons'; +import { create, trash } from 'ionicons/icons'; import React from 'react'; const AlgorithmCardView = ({ @@ -33,7 +33,7 @@ const AlgorithmCardView = ({ onClick={editAlgorithm} className="ion-float-left" > - + Edit From 642c37b534223db96942d069ee2cfe47e7c1cc21 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Thu, 25 Feb 2021 16:17:42 +0100 Subject: [PATCH 28/73] Remove TODO --- wrapper/src/api/providers/benchmark/benchmarkProvider.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider.spec.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider.spec.ts index 9195b8c8..c5eee088 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider.spec.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider.spec.ts @@ -15,7 +15,6 @@ interface metaExperiment { name: string; data: { meta: ExperimentValues; file: string[][] }; } -//TODO make this util function function fileToReadable(file: string[][]) { return Readable.from(file.map((row) => row.join(',')).join('\n')); } From 1c9210346089196796efbc2ac6edd6447f344783 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Thu, 25 Feb 2021 16:18:08 +0100 Subject: [PATCH 29/73] Add soft KPIs (WIP) + prepare milestone 2 --- docs/api_specification.yaml | 77 ++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/docs/api_specification.yaml b/docs/api_specification.yaml index 83a93626..4bc2f50a 100644 --- a/docs/api_specification.yaml +++ b/docs/api_specification.yaml @@ -8,7 +8,7 @@ info: Comparing data matching algorithms is still an unsolved topic in both industry and research. With snowman, developers and researchers will be able to compare the performance of different data matching solutions or improve new algorithms. - version: 1.0.0 + version: 2.0.0 title: Snowman API contact: email: snowman@groups.sap.com @@ -18,7 +18,7 @@ info: url: https://github.com servers: - - url: "{protocol}://{server}/api/v1" + - url: "{protocol}://{server}/api/v2" variables: protocol: default: http @@ -74,7 +74,21 @@ paths: $ref: "#/components/schemas/AlgorithmId" 400: description: Bad Request - + /algorithms/soft-kpi-questions: + get: + tags: + - Algorithm + summary: Returns all soft-kpi questions + operationId: getSoftKPIQuestions + responses: + 200: + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/SoftKPIQuestion" /algorithms/{algorithmId}: parameters: - name: algorithmId @@ -551,6 +565,10 @@ paths: schema: type: string example: similarity + - name: mode + in: query + schema: + $ref: "#/components/schemas/ExperimentIntersectionMode" requestBody: $ref: "#/components/requestBodies/ExperimentIntersectionConfiguration" summary: @@ -576,6 +594,11 @@ paths: intersects multiple experiments and returns the counts of the number of records. This can be used to calculate the confusion-matrix operationId: calculateExperimentIntersectionCount + parameters: + - name: mode + in: query + schema: + $ref: "#/components/schemas/ExperimentIntersectionMode" requestBody: $ref: "#/components/requestBodies/ExperimentIntersectionConfiguration" summary: @@ -602,6 +625,31 @@ components: $ref: "#/components/schemas/ExperimentIntersectionConfiguration" required: true schemas: + #### SOFTKPIS #### + SoftKPIQuestion: + type: object + properties: + id: + type: string + example: tco + question: + type: string + example: What is the total cost of ownership of this matching solution? + format: + type: string + enum: [string, number] + SoftKPIAnswer: + type: object + properties: + id: + type: string + example: tco + answer: + type: string + required: + - id + - answer + #### ALGORITHM #### AlgorithmId: type: integer @@ -618,6 +666,10 @@ components: description: type: string example: This is an open source python library + softKPIs: + type: array + items: + $ref: "#/components/schemas/SoftKPIAnswer" Algorithm: allOf: - type: object @@ -780,19 +832,26 @@ components: type: array items: type: array + nullable: true + example: ["1", "test value 1", "test value 2"] items: - type: array - example: ["1", "hallo"] - items: - type: string + type: string required: - header - data ExperimentIntersectionCount: type: object properties: - count: + numberRows: type: integer example: 37 + numberGroups: + type: integer + example: 10 required: - - count + - numberRows + - numberGroups + ExperimentIntersectionMode: + type: string + enum: ["PAIRS", "CLUSTERS", "INVESTIGATIVE"] + default: "PAIRS" From affd52b5a17402cefb076bc43ce9a8e9293c5c10 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Thu, 25 Feb 2021 16:26:03 +0100 Subject: [PATCH 30/73] Dont change the api version as this leads to changes in every generated file --- docs/api_specification.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api_specification.yaml b/docs/api_specification.yaml index 4bc2f50a..e0b6d5ce 100644 --- a/docs/api_specification.yaml +++ b/docs/api_specification.yaml @@ -8,7 +8,7 @@ info: Comparing data matching algorithms is still an unsolved topic in both industry and research. With snowman, developers and researchers will be able to compare the performance of different data matching solutions or improve new algorithms. - version: 2.0.0 + version: 1.0.0 title: Snowman API contact: email: snowman@groups.sap.com @@ -18,7 +18,7 @@ info: url: https://github.com servers: - - url: "{protocol}://{server}/api/v2" + - url: "{protocol}://{server}/api/v1" variables: protocol: default: http From dc4d0d6dfe7888578a06d7591f8ba7759ba4335d Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Thu, 25 Feb 2021 17:54:35 +0100 Subject: [PATCH 31/73] Bugfixes --- .../datasetProvider/datasetProvider.ts | 8 +++++--- wrapper/src/api/tools/executeSynchronized.ts | 20 ++++++++----------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/wrapper/src/api/providers/dataset/datasetProvider/datasetProvider.ts b/wrapper/src/api/providers/dataset/datasetProvider/datasetProvider.ts index b8c28e14..cb76b9be 100644 --- a/wrapper/src/api/providers/dataset/datasetProvider/datasetProvider.ts +++ b/wrapper/src/api/providers/dataset/datasetProvider/datasetProvider.ts @@ -56,18 +56,20 @@ export class DatasetProvider extends BaseDatasetProvider { const priorStoredDataset = this.queries.getDatasetQuery.get(id) as | StoredDataset | undefined; - const storedDataset = this.converter.apiDatasetToStoredDataset({ + const newStoredDataset = this.converter.apiDatasetToStoredDataset({ id, ...dataset, numberOfUploadedRecords: priorStoredDataset?.numberOfUploadedRecords ?? undefined, }); + newStoredDataset.numberOfRecords ??= + priorStoredDataset?.numberOfRecords ?? null; this.checks.throwIfNumberOfRecordsInvalid( id, priorStoredDataset?.numberOfRecords ?? null, - storedDataset.numberOfRecords + newStoredDataset.numberOfRecords ); - this.queries.setDatasetQuery.run(storedDataset); + this.queries.setDatasetQuery.run(newStoredDataset); } deleteDataset(id: DatasetId): void { diff --git a/wrapper/src/api/tools/executeSynchronized.ts b/wrapper/src/api/tools/executeSynchronized.ts index d8b6f02a..1a88e123 100644 --- a/wrapper/src/api/tools/executeSynchronized.ts +++ b/wrapper/src/api/tools/executeSynchronized.ts @@ -23,28 +23,24 @@ export class ExecuteSynchronized { this.lockCounts.set(id, lockCount - 1); } - private setLock(lock: Promise, id?: number) { - this.increaseLockCount(id); - this.locks.set( - id, - lock.then(() => this.decreaseLockCount(id)) - ); - } - call( func: () => Promise | ResultType, lockId?: number ): Promise { + this.increaseLockCount(lockId); return new Promise((resolve, reject) => { - this.setLock( + this.locks.set( + lockId, this.waitForLock(lockId) .then(async () => { - resolve(await func()); + const result = await func(); + this.decreaseLockCount(lockId); + resolve(result); }) .catch((e) => { + this.decreaseLockCount(lockId); reject(e); - }), - lockId + }) ); }); } From 7a22d1fd7c47c4a51a27963e381a132659fd355e Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Mon, 1 Mar 2021 11:09:54 +0100 Subject: [PATCH 32/73] Add script for automatic database migration (still requires config for version upgrade) --- wrapper/src/api/database/schemas/index.ts | 12 +-- .../api/database/schemas/migration.spec.ts | 84 +++++++++++++++++++ .../src/api/database/schemas/schemaVersion.ts | 31 +++++++ .../api/database/schemas/{ => v0}/dataset.ts | 6 +- .../database/schemas/{ => v0}/experiment.ts | 6 +- wrapper/src/api/database/schemas/v0/index.ts | 16 ++++ .../src/api/database/schemas/{ => v0}/meta.ts | 4 +- .../src/api/database/schemas/v0/migration.ts | 12 +++ wrapper/src/api/database/setup/install.ts | 7 +- wrapper/src/api/database/setup/setup.ts | 26 ++++-- wrapper/src/api/database/tools/types.ts | 4 +- .../providers/algorithm/algorithmProvider.ts | 14 ++-- .../helper/evaluator/queries.ts | 4 +- .../helper/idsToRecords/index.ts | 4 +- .../helper/idsToRecords/withTable.ts | 11 +-- .../dataset/datasetProvider/deleter.ts | 6 +- .../dataset/datasetProvider/file/getter.ts | 15 ++-- .../dataset/datasetProvider/file/inserter.ts | 11 +-- .../dataset/datasetProvider/queries.ts | 8 +- .../dataset/datasetProvider/util/checks.ts | 4 +- .../dataset/datasetProvider/util/idMapper.ts | 6 +- .../experiment/experimentProvider.spec.ts | 10 +-- .../experimentProvider/experimentProvider.ts | 4 +- .../file/experimentInserter.ts | 13 ++- .../experimentProvider/file/getter.ts | 18 ++-- .../experiment/experimentProvider/queries.ts | 6 +- wrapper/src/api/tools/cli.ts | 2 +- 27 files changed, 251 insertions(+), 93 deletions(-) create mode 100644 wrapper/src/api/database/schemas/migration.spec.ts create mode 100644 wrapper/src/api/database/schemas/schemaVersion.ts rename wrapper/src/api/database/schemas/{ => v0}/dataset.ts (88%) rename wrapper/src/api/database/schemas/{ => v0}/experiment.ts (86%) create mode 100644 wrapper/src/api/database/schemas/v0/index.ts rename wrapper/src/api/database/schemas/{ => v0}/meta.ts (96%) create mode 100644 wrapper/src/api/database/schemas/v0/migration.ts diff --git a/wrapper/src/api/database/schemas/index.ts b/wrapper/src/api/database/schemas/index.ts index 43ce9e17..698a9feb 100644 --- a/wrapper/src/api/database/schemas/index.ts +++ b/wrapper/src/api/database/schemas/index.ts @@ -1,11 +1 @@ -import { datasetSchemas } from './dataset'; -import { experimentSchemas } from './experiment'; -import { metaSchemas } from './meta'; - -export const tableSchemas = { - ...metaSchemas, - ...datasetSchemas, - ...experimentSchemas, -}; - -export const schemas = ['meta', 'dataset', 'experiment'] as const; +export * as latest from './v0'; diff --git a/wrapper/src/api/database/schemas/migration.spec.ts b/wrapper/src/api/database/schemas/migration.spec.ts new file mode 100644 index 00000000..a7ec6636 --- /dev/null +++ b/wrapper/src/api/database/schemas/migration.spec.ts @@ -0,0 +1,84 @@ +import { databaseBackend, loadOrCreateMainDatabase } from '../setup/backend'; +import { SchemaVersion } from './schemaVersion'; + +abstract class TestSchemaVersion extends SchemaVersion { + abstract successor?: TestSchemaVersion; + migrated = false; + protected migrateFromLastVersion(): void { + this.migrated = true; + } + reset() { + this.migrated = false; + this.successor?.reset(); + } +} + +const TestSchemaV0 = new (class extends TestSchemaVersion { + version = 0; + successor = undefined; +})(); +const TestSchemaV1 = new (class extends TestSchemaVersion { + version = 1; + successor = TestSchemaV0; +})(); +const TestSchemaV2 = new (class extends TestSchemaVersion { + version = 2; + successor = TestSchemaV1; +})(); + +describe('SchemaVersion', () => { + describe('migrates correctly', () => { + beforeEach(() => { + loadOrCreateMainDatabase(true); + TestSchemaV2.reset(); + }); + test('from v0', () => { + TestSchemaV2.migrate(0); + expect(TestSchemaV0.migrated).toBeFalsy(); + expect(TestSchemaV1.migrated).toBeTruthy(); + expect(TestSchemaV2.migrated).toBeTruthy(); + }); + test('from v1', () => { + TestSchemaV2.migrate(1); + expect(TestSchemaV0.migrated).toBeFalsy(); + expect(TestSchemaV1.migrated).toBeFalsy(); + expect(TestSchemaV2.migrated).toBeTruthy(); + }); + test('from v2', () => { + TestSchemaV2.migrate(2); + expect(TestSchemaV0.migrated).toBeFalsy(); + expect(TestSchemaV1.migrated).toBeFalsy(); + expect(TestSchemaV2.migrated).toBeFalsy(); + }); + }); + describe('version stored correctly', () => { + beforeEach(() => { + loadOrCreateMainDatabase(true); + TestSchemaV2.reset(); + }); + test('initial version is 0', () => { + expect(TestSchemaVersion.getInstalledVersion()).toEqual(0); + }); + test('keeps version 0', () => { + TestSchemaV0.migrate(TestSchemaVersion.getInstalledVersion()); + expect(TestSchemaVersion.getInstalledVersion()).toEqual( + TestSchemaV0.version + ); + }); + test('updates to version 1', () => { + TestSchemaV1.migrate(TestSchemaVersion.getInstalledVersion()); + expect(TestSchemaVersion.getInstalledVersion()).toEqual( + TestSchemaV1.version + ); + }); + test('updates to version 2', () => { + TestSchemaV2.migrate(TestSchemaVersion.getInstalledVersion()); + expect(TestSchemaVersion.getInstalledVersion()).toEqual( + TestSchemaV2.version + ); + }); + }); + test('throws error when cannot upgrade from version', () => { + expect(() => TestSchemaV0.migrate(TestSchemaV2.version)).toThrowError(); + }); +}); diff --git a/wrapper/src/api/database/schemas/schemaVersion.ts b/wrapper/src/api/database/schemas/schemaVersion.ts new file mode 100644 index 00000000..70c9e8c3 --- /dev/null +++ b/wrapper/src/api/database/schemas/schemaVersion.ts @@ -0,0 +1,31 @@ +import { databaseBackend } from '../setup/backend'; + +export abstract class SchemaVersion { + abstract readonly version: number; + abstract readonly successor?: SchemaVersion; + protected abstract migrateFromLastVersion(): void; + + migrate(fromVersion: number): void { + if (this.version !== fromVersion) { + databaseBackend().transaction(() => { + if (this.successor) { + this.successor.migrate(fromVersion); + this.migrateFromLastVersion(); + this.setVersion(); + } else { + throw new Error( + `Cannot migrate from database schema version ${fromVersion}: The version has not beend found.` + ); + } + })(); + } + } + + static getInstalledVersion(): number { + return +databaseBackend().pragma('user_version')[0].user_version; + } + + setVersion(): void { + databaseBackend().pragma(`user_version = ${this.version}`); + } +} diff --git a/wrapper/src/api/database/schemas/dataset.ts b/wrapper/src/api/database/schemas/v0/dataset.ts similarity index 88% rename from wrapper/src/api/database/schemas/dataset.ts rename to wrapper/src/api/database/schemas/v0/dataset.ts index ef3209ff..0926ddf6 100644 --- a/wrapper/src/api/database/schemas/dataset.ts +++ b/wrapper/src/api/database/schemas/v0/dataset.ts @@ -1,6 +1,6 @@ -import { assertType } from '../../tools/types'; -import { escapeColumnNames } from '../tools/escapeColumnNames'; -import { Column, Columns, Schemas } from '../tools/types'; +import { assertType } from '../../../tools/types'; +import { escapeColumnNames } from '../../tools/escapeColumnNames'; +import { Column, Columns, Schemas } from '../../tools/types'; export const datasetCustomColumnPrefix = 'dataset_'; export const datasetSchemas = assertType< diff --git a/wrapper/src/api/database/schemas/experiment.ts b/wrapper/src/api/database/schemas/v0/experiment.ts similarity index 86% rename from wrapper/src/api/database/schemas/experiment.ts rename to wrapper/src/api/database/schemas/v0/experiment.ts index 7295aedf..e01e02c4 100644 --- a/wrapper/src/api/database/schemas/experiment.ts +++ b/wrapper/src/api/database/schemas/v0/experiment.ts @@ -1,6 +1,6 @@ -import { assertType } from '../../tools/types'; -import { escapeColumnNames } from '../tools/escapeColumnNames'; -import { Column, Columns, Schemas } from '../tools/types'; +import { assertType } from '../../../tools/types'; +import { escapeColumnNames } from '../../tools/escapeColumnNames'; +import { Column, Columns, Schemas } from '../../tools/types'; export const experimentCustomColumnPrefix = 'experiment_'; export const experimentSchemas = assertType< diff --git a/wrapper/src/api/database/schemas/v0/index.ts b/wrapper/src/api/database/schemas/v0/index.ts new file mode 100644 index 00000000..40a022ba --- /dev/null +++ b/wrapper/src/api/database/schemas/v0/index.ts @@ -0,0 +1,16 @@ +import { datasetSchemas } from './dataset'; +import { experimentSchemas } from './experiment'; +import { metaSchemas } from './meta'; +import { SchemaV0 } from './migration'; + +export { datasetCustomColumnPrefix, datasetSchemas } from './dataset'; +export { experimentCustomColumnPrefix, experimentSchemas } from './experiment'; +export { metaSchemas } from './meta'; + +export const tableSchemas = { + ...metaSchemas, + ...datasetSchemas, + ...experimentSchemas, +}; +export const schemas = ['meta', 'dataset', 'experiment'] as const; +export const version = new SchemaV0(); diff --git a/wrapper/src/api/database/schemas/meta.ts b/wrapper/src/api/database/schemas/v0/meta.ts similarity index 96% rename from wrapper/src/api/database/schemas/meta.ts rename to wrapper/src/api/database/schemas/v0/meta.ts index a7bff5f2..e0007bcf 100644 --- a/wrapper/src/api/database/schemas/meta.ts +++ b/wrapper/src/api/database/schemas/v0/meta.ts @@ -1,5 +1,5 @@ -import { assertType } from '../../tools/types'; -import { ForeignKeys, Schemas } from '../tools/types'; +import { assertType } from '../../../tools/types'; +import { ForeignKeys, Schemas } from '../../tools/types'; export const metaSchemas = assertType< Schemas<'meta', ['algorithm', 'dataset', 'experiment']> diff --git a/wrapper/src/api/database/schemas/v0/migration.ts b/wrapper/src/api/database/schemas/v0/migration.ts new file mode 100644 index 00000000..54cb6336 --- /dev/null +++ b/wrapper/src/api/database/schemas/v0/migration.ts @@ -0,0 +1,12 @@ +import { SchemaVersion } from '../schemaVersion'; + +export class SchemaV0 extends SchemaVersion { + readonly version = 0; + readonly successor?: SchemaVersion | undefined = undefined; + + protected migrateFromLastVersion(): void { + throw new Error( + 'Cannot migrate to database schema version v1 as this is the first schema version.' + ); + } +} diff --git a/wrapper/src/api/database/setup/install.ts b/wrapper/src/api/database/setup/install.ts index 7185d8a5..4520b575 100644 --- a/wrapper/src/api/database/setup/install.ts +++ b/wrapper/src/api/database/setup/install.ts @@ -10,11 +10,14 @@ export function installTables( } class TableInstaller { - constructor(private readonly tables: readonly TableSchema[]) {} + constructor( + private readonly tables: readonly TableSchema[], + protected raiseIfExists = false + ) {} installTables(): void { for (const table of this.topologicallySortedTables) { - new Table(table).create(false); + new Table(table).create(this.raiseIfExists); } } diff --git a/wrapper/src/api/database/setup/setup.ts b/wrapper/src/api/database/setup/setup.ts index 70b0c96e..b2d4640c 100644 --- a/wrapper/src/api/database/setup/setup.ts +++ b/wrapper/src/api/database/setup/setup.ts @@ -1,10 +1,11 @@ import { existsSync } from 'fs'; -import { schemas, tableSchemas } from '../schemas'; +import { latest } from '../schemas'; +import { SchemaVersion } from '../schemas/schemaVersion'; import { mainDatabaseFile } from '../tools/storageStructure'; import { Schema, Schemas, TableSchema } from '../tools/types'; import { attachDatabases } from './attachDatabases'; -import { loadOrCreateMainDatabase } from './backend'; +import { databaseBackend, loadOrCreateMainDatabase } from './backend'; import { loadExamples } from './examples'; import { installTables } from './install'; @@ -17,17 +18,28 @@ export async function setupDatabase({ appPath?: string; loadExampleEntries?: boolean; }): Promise { - const initialSetup = temporary || !existsSync(mainDatabaseFile(appPath)); + const isInitialSetup = temporary || !existsSync(mainDatabaseFile(appPath)); loadOrCreateMainDatabase(temporary, appPath); - attachDatabases(schemas, temporary, appPath); - installTables(getTablesToBeAutoInstalled()); - if (initialSetup && loadExampleEntries) { + attachDatabases(latest.schemas, temporary, appPath); + if (isInitialSetup) { + await initialDatabaseSetup(loadExampleEntries); + } else { + latest.version.migrate(SchemaVersion.getInstalledVersion()); + } +} + +async function initialDatabaseSetup(loadExampleEntries: boolean) { + databaseBackend().transaction(() => { + installTables(getTablesToBeAutoInstalled(), true); + latest.version.setVersion(); + })(); + if (loadExampleEntries) { await loadExamples(); } } function getTablesToBeAutoInstalled(): TableSchema[] { - return Object.values(tableSchemas).flatMap( + return Object.values(latest.tableSchemas).flatMap( (schema) => (Object.values(schema) as Schemas[Schema][string][]).filter( (table) => typeof table !== 'function' && table.autoInstall diff --git a/wrapper/src/api/database/tools/types.ts b/wrapper/src/api/database/tools/types.ts index 603f4f10..2bb4e72e 100644 --- a/wrapper/src/api/database/tools/types.ts +++ b/wrapper/src/api/database/tools/types.ts @@ -1,4 +1,4 @@ -import type { schemas } from '../schemas'; +import type { latest } from '../schemas'; type DataTypes = 'NULL' | 'INTEGER' | 'REAL' | 'TEXT' | 'BLOB'; export declare interface Column { @@ -23,7 +23,7 @@ export declare interface TableSchema< export type ForeignKeys = ReturnType>; export type Columns = TableSchema['columns']; -export type Schema = typeof schemas[number]; +export type Schema = typeof latest.schemas[number]; export type Schemas< SchemaT extends Schema = Schema, TableT extends string[] = string[] diff --git a/wrapper/src/api/providers/algorithm/algorithmProvider.ts b/wrapper/src/api/providers/algorithm/algorithmProvider.ts index 7c638a33..fac7adc5 100644 --- a/wrapper/src/api/providers/algorithm/algorithmProvider.ts +++ b/wrapper/src/api/providers/algorithm/algorithmProvider.ts @@ -1,10 +1,10 @@ import { databaseBackend, Table } from '../../database'; -import { tableSchemas } from '../../database/schemas'; +import { latest } from '../../database/schemas'; import { Algorithm, AlgorithmId, AlgorithmValues } from '../../server/types'; import { BaseAlgorithmProvider } from './baseAlgorithmProvider'; export class AlgorithmProvider extends BaseAlgorithmProvider { - protected readonly schema = tableSchemas.meta.algorithm; + protected readonly schema = latest.tableSchemas.meta.algorithm; protected readonly table = new Table(this.schema); protected readonly listAlgorithmsQuery = databaseBackend().prepare( `SELECT "${this.schema.columns.name.name}" as name, @@ -33,11 +33,13 @@ export class AlgorithmProvider extends BaseAlgorithmProvider { ); protected readonly algorithmUsagesQuery = databaseBackend().prepare( `SELECT "${ - tableSchemas.meta.experiment.columns.name.name + latest.tableSchemas.meta.experiment.columns.name.name }" as experimentName, - "${tableSchemas.meta.experiment.columns.id.name}" as experimentId - FROM ${new Table(tableSchemas.meta.experiment)} - WHERE "${tableSchemas.meta.experiment.columns.algorithm.name}" = ?` + "${ + latest.tableSchemas.meta.experiment.columns.id.name + }" as experimentId + FROM ${new Table(latest.tableSchemas.meta.experiment)} + WHERE "${latest.tableSchemas.meta.experiment.columns.algorithm.name}" = ?` ); listAlgorithms(): Algorithm[] { diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/queries.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/queries.ts index f872825b..3e3f4164 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/queries.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/queries.ts @@ -1,10 +1,10 @@ import { databaseBackend, Table } from '../../../../../database'; -import { tableSchemas } from '../../../../../database/schemas'; +import { latest } from '../../../../../database/schemas'; import { ExperimentId } from '../../../../../server/types'; export class EvaluatorQueries { experimentLinks(experiment: ExperimentId): [number, number][] { - const schema = tableSchemas.experiment.experiment(experiment); + const schema = latest.tableSchemas.experiment.experiment(experiment); const table = new Table(schema); const links = databaseBackend() .prepare( diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/index.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/index.ts index db2b8215..0bb9fcfc 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/index.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/index.ts @@ -1,4 +1,4 @@ -import { tableSchemas } from '../../../../../database/schemas'; +import { latest } from '../../../../../database/schemas'; import { tableExists } from '../../../../../database/table/loader'; import { DatasetId, ExperimentIntersection } from '../../../../../server/types'; import { idClustersToRecordClustersNoTable } from './noTable'; @@ -8,7 +8,7 @@ export function idClustersToRecordClusters( idClusters: number[][], datasetId: DatasetId ): ExperimentIntersection { - const schema = tableSchemas.dataset.dataset(datasetId); + const schema = latest.tableSchemas.dataset.dataset(datasetId); if (tableExists(schema)) { return idClustersToRecordClustersWithTable(idClusters, schema, datasetId); } else { diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/withTable.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/withTable.ts index 4be15096..84d94b56 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/withTable.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/withTable.ts @@ -1,14 +1,15 @@ import { Statement } from 'better-sqlite3'; import { databaseBackend, Table } from '../../../../../database'; -import { tableSchemas } from '../../../../../database/schemas'; -import { datasetCustomColumnPrefix } from '../../../../../database/schemas/dataset'; +import { latest } from '../../../../../database/schemas'; import { loadTableFromDatabase } from '../../../../../database/table/loader'; import { Column } from '../../../../../database/tools/types'; import { DatasetId, ExperimentIntersection } from '../../../../../server/types'; import { DatasetIDMapper } from '../../../../dataset/datasetProvider/util/idMapper'; -type DatasetSchema = ReturnType; +type DatasetSchema = ReturnType< + typeof latest.tableSchemas['dataset']['dataset'] +>; export function idClustersToRecordClustersWithTable( idClusters: number[][], @@ -37,7 +38,7 @@ class IdClustersToRecordClusters { protected readonly idMapper: DatasetIDMapper ) { this.columns = Object.values(this.table.schema.columns).filter((column) => - column.name.startsWith(datasetCustomColumnPrefix) + column.name.startsWith(latest.datasetCustomColumnPrefix) ); this.getRecordByIdQuery = databaseBackend().prepare(` SELECT ${this.getColumnsString()} @@ -55,7 +56,7 @@ class IdClustersToRecordClusters { protected getHeader() { return this.columns.map((column) => - column.name.substring(datasetCustomColumnPrefix.length) + column.name.substring(latest.datasetCustomColumnPrefix.length) ); } diff --git a/wrapper/src/api/providers/dataset/datasetProvider/deleter.ts b/wrapper/src/api/providers/dataset/datasetProvider/deleter.ts index 6ada0fac..4174363e 100644 --- a/wrapper/src/api/providers/dataset/datasetProvider/deleter.ts +++ b/wrapper/src/api/providers/dataset/datasetProvider/deleter.ts @@ -1,5 +1,5 @@ import { databaseBackend, Table } from '../../../database'; -import { tableSchemas } from '../../../database/schemas'; +import { latest } from '../../../database/schemas'; import { ExperimentId } from '../../../server/types'; import { getProviders } from '../..'; import { DatasetProviderQueries } from './queries'; @@ -21,7 +21,9 @@ export class DatasetDeleter { } deleteFile(): void { - new Table(tableSchemas.dataset.dataset(this.datasetId)).delete(false); + new Table(latest.tableSchemas.dataset.dataset(this.datasetId)).delete( + false + ); } private deleteIDMap(): void { diff --git a/wrapper/src/api/providers/dataset/datasetProvider/file/getter.ts b/wrapper/src/api/providers/dataset/datasetProvider/file/getter.ts index ab226f22..9518262e 100644 --- a/wrapper/src/api/providers/dataset/datasetProvider/file/getter.ts +++ b/wrapper/src/api/providers/dataset/datasetProvider/file/getter.ts @@ -1,10 +1,11 @@ import { databaseBackend, Table } from '../../../../database'; -import { tableSchemas } from '../../../../database/schemas'; -import { datasetCustomColumnPrefix } from '../../../../database/schemas/dataset'; +import { latest } from '../../../../database/schemas'; import { loadTableFromDatabase } from '../../../../database/table/loader'; import { DatasetId } from '../../../../server/types'; -type DatasetSchema = ReturnType; +type DatasetSchema = ReturnType< + typeof latest.tableSchemas['dataset']['dataset'] +>; export class DatasetFileGetter { protected table: Table; @@ -17,17 +18,17 @@ export class DatasetFileGetter { sortBy?: string ) { this.table = loadTableFromDatabase( - tableSchemas.dataset.dataset(id) + latest.tableSchemas.dataset.dataset(id) ); this.customColumns = Object.values(this.table.schema.columns) .map((column) => column.name) - .filter((column) => column.startsWith(datasetCustomColumnPrefix)); + .filter((column) => column.startsWith(latest.datasetCustomColumnPrefix)); this.sortedColumn = this.getSortedColumn(sortBy); } *iterate(): IterableIterator { yield this.customColumns.map((column) => - column.substring(datasetCustomColumnPrefix.length) + column.substring(latest.datasetCustomColumnPrefix.length) ); yield* databaseBackend() .prepare( @@ -57,7 +58,7 @@ export class DatasetFileGetter { private getSortedColumn(sortBy?: string): string { let sortedColumn: string; if (sortBy) { - sortedColumn = datasetCustomColumnPrefix + sortBy; + sortedColumn = latest.datasetCustomColumnPrefix + sortBy; if (!this.customColumns.includes(sortedColumn)) { throw new Error( 'Cannot sort by ' + sortBy + ' as this column does not exist.' diff --git a/wrapper/src/api/providers/dataset/datasetProvider/file/inserter.ts b/wrapper/src/api/providers/dataset/datasetProvider/file/inserter.ts index 2c28bf89..99cee241 100644 --- a/wrapper/src/api/providers/dataset/datasetProvider/file/inserter.ts +++ b/wrapper/src/api/providers/dataset/datasetProvider/file/inserter.ts @@ -2,8 +2,7 @@ import { Readable } from 'stream'; import { INSERT_BATCH_SIZE } from '../../../../config'; import { Table } from '../../../../database'; -import { tableSchemas } from '../../../../database/schemas'; -import { datasetCustomColumnPrefix } from '../../../../database/schemas/dataset'; +import { latest } from '../../../../database/schemas'; import { InsertParameters } from '../../../../database/table/inserter'; import { escapeColumnName } from '../../../../database/tools/escapeColumnNames'; import { Column } from '../../../../database/tools/types'; @@ -17,7 +16,9 @@ import { } from '../../../../tools/csvReader'; import { DatasetIDMapper } from '../util/idMapper'; -type DatasetFileSchema = ReturnType; +type DatasetFileSchema = ReturnType< + typeof latest.tableSchemas['dataset']['dataset'] +>; export class DatasetInserter implements CSVReaderStrategy { private table?: Table; @@ -54,7 +55,7 @@ export class DatasetInserter implements CSVReaderStrategy { } private getTableSchema(columns: string[]): DatasetFileSchema { - return tableSchemas.dataset.dataset( + return latest.tableSchemas.dataset.dataset( this.datasetId, columns.map((column) => { return { @@ -84,7 +85,7 @@ export class DatasetInserter implements CSVReaderStrategy { ...Object.entries(row).map(([header, value]) => { return { column: columns[ - escapeColumnName(header, datasetCustomColumnPrefix) + escapeColumnName(header, latest.datasetCustomColumnPrefix) ] as Column<'TEXT'>, value, }; diff --git a/wrapper/src/api/providers/dataset/datasetProvider/queries.ts b/wrapper/src/api/providers/dataset/datasetProvider/queries.ts index f8096c51..b9e3b63b 100644 --- a/wrapper/src/api/providers/dataset/datasetProvider/queries.ts +++ b/wrapper/src/api/providers/dataset/datasetProvider/queries.ts @@ -1,8 +1,8 @@ import { databaseBackend, Table } from '../../../database'; -import { tableSchemas } from '../../../database/schemas'; +import { latest } from '../../../database/schemas'; export class DatasetProviderQueries { - readonly schema = tableSchemas.meta.dataset; + readonly schema = latest.tableSchemas.meta.dataset; readonly table = new Table(this.schema); readonly listDatasetsQuery = databaseBackend().prepare( `SELECT "${this.schema.columns.name.name}" as name, @@ -37,7 +37,9 @@ export class DatasetProviderQueries { WHERE "${this.schema.columns.id.name}" = ?` ); - private readonly experimentsTable = new Table(tableSchemas.meta.experiment); + private readonly experimentsTable = new Table( + latest.tableSchemas.meta.experiment + ); readonly listExperimentsUsingDatasetQuery = databaseBackend().prepare(` SELECT "${this.experimentsTable.schema.columns.id.name}" as id FROM ${this.experimentsTable} diff --git a/wrapper/src/api/providers/dataset/datasetProvider/util/checks.ts b/wrapper/src/api/providers/dataset/datasetProvider/util/checks.ts index 2f1e2dc8..69e0a043 100644 --- a/wrapper/src/api/providers/dataset/datasetProvider/util/checks.ts +++ b/wrapper/src/api/providers/dataset/datasetProvider/util/checks.ts @@ -1,4 +1,4 @@ -import { tableSchemas } from '../../../../database/schemas'; +import { latest } from '../../../../database/schemas'; import { tableExists } from '../../../../database/table/loader'; import { DatasetId } from '../../../../server/types'; import { ExecuteSynchronized } from '../../../../tools/executeSynchronized'; @@ -37,7 +37,7 @@ export class DatasetConsistencyChecks { } datasetFileUploaded(datasetId: DatasetId): boolean { - return tableExists(tableSchemas.dataset.dataset(datasetId)); + return tableExists(latest.tableSchemas.dataset.dataset(datasetId)); } throwIfLocked(datasetId: DatasetId): void { diff --git a/wrapper/src/api/providers/dataset/datasetProvider/util/idMapper.ts b/wrapper/src/api/providers/dataset/datasetProvider/util/idMapper.ts index 11b89755..6abe4d99 100644 --- a/wrapper/src/api/providers/dataset/datasetProvider/util/idMapper.ts +++ b/wrapper/src/api/providers/dataset/datasetProvider/util/idMapper.ts @@ -1,11 +1,11 @@ import { Statement } from 'better-sqlite3'; import { databaseBackend, Table } from '../../../../database'; -import { tableSchemas } from '../../../../database/schemas'; +import { latest } from '../../../../database/schemas'; export class DatasetIDMapper { private readonly schema: ReturnType< - typeof tableSchemas['dataset']['datasetIdMap'] + typeof latest.tableSchemas['dataset']['datasetIdMap'] >; private readonly table: Table; @@ -16,7 +16,7 @@ export class DatasetIDMapper { private firstMappingCreated = false; constructor(datasetId: number) { - this.schema = tableSchemas.dataset.datasetIdMap(datasetId); + this.schema = latest.tableSchemas.dataset.datasetIdMap(datasetId); this.table = new Table(this.schema); this.table.create(false); this.getMappingQuery = databaseBackend().prepare(` diff --git a/wrapper/src/api/providers/experiment/experimentProvider.spec.ts b/wrapper/src/api/providers/experiment/experimentProvider.spec.ts index 252d4484..81b702e4 100644 --- a/wrapper/src/api/providers/experiment/experimentProvider.spec.ts +++ b/wrapper/src/api/providers/experiment/experimentProvider.spec.ts @@ -1,7 +1,7 @@ import { Readable } from 'stream'; import { setupDatabase } from '../../database'; -import { tableSchemas } from '../../database/schemas'; +import { latest } from '../../database/schemas'; import { throwIfTableNotExists } from '../../database/table/loader'; import { AlgorithmValues, @@ -192,7 +192,7 @@ describe('ExperimentProvider', () => { test('set file sets file', async () => { expect(() => throwIfTableNotExists( - tableSchemas.experiment.experiment(addedExperimentIds[0]) + latest.tableSchemas.experiment.experiment(addedExperimentIds[0]) ) ).toThrowError(); const file = [ @@ -206,7 +206,7 @@ describe('ExperimentProvider', () => { ); expect(() => throwIfTableNotExists( - tableSchemas.experiment.experiment(addedExperimentIds[0]) + latest.tableSchemas.experiment.experiment(addedExperimentIds[0]) ) ).not.toThrowError(); }); @@ -214,13 +214,13 @@ describe('ExperimentProvider', () => { test('delete file deletes file', () => { expect(() => throwIfTableNotExists( - tableSchemas.experiment.experiment(addedExperimentIds[1]) + latest.tableSchemas.experiment.experiment(addedExperimentIds[1]) ) ).not.toThrowError(); provider.deleteExperimentFile(addedExperimentIds[1]); expect(() => throwIfTableNotExists( - tableSchemas.experiment.experiment(addedExperimentIds[1]) + latest.tableSchemas.experiment.experiment(addedExperimentIds[1]) ) ).toThrowError(); }); diff --git a/wrapper/src/api/providers/experiment/experimentProvider/experimentProvider.ts b/wrapper/src/api/providers/experiment/experimentProvider/experimentProvider.ts index 8de110ea..4173b214 100644 --- a/wrapper/src/api/providers/experiment/experimentProvider/experimentProvider.ts +++ b/wrapper/src/api/providers/experiment/experimentProvider/experimentProvider.ts @@ -1,7 +1,7 @@ import { Readable } from 'stream'; import { databaseBackend, Table } from '../../../database'; -import { tableSchemas } from '../../../database/schemas'; +import { latest } from '../../../database/schemas'; import { ExperimentValues } from '../../../server/types'; import { Experiment, @@ -159,7 +159,7 @@ export class ExperimentProvider extends BaseExperimentProvider { } private deleteExperimentFileNoChecks(id: ExperimentId): void { - new Table(tableSchemas.experiment.experiment(id)).delete(false); + new Table(latest.tableSchemas.experiment.experiment(id)).delete(false); const storedExperiment = this.queries.getExperimentQuery.get(id) as | StoredExperiment | undefined; diff --git a/wrapper/src/api/providers/experiment/experimentProvider/file/experimentInserter.ts b/wrapper/src/api/providers/experiment/experimentProvider/file/experimentInserter.ts index afc35e33..1424f1dc 100644 --- a/wrapper/src/api/providers/experiment/experimentProvider/file/experimentInserter.ts +++ b/wrapper/src/api/providers/experiment/experimentProvider/file/experimentInserter.ts @@ -2,14 +2,13 @@ import { Readable } from 'stream'; import { INSERT_BATCH_SIZE } from '../../../../config'; import { Table } from '../../../../database'; -import { tableSchemas } from '../../../../database/schemas'; -import { experimentCustomColumnPrefix } from '../../../../database/schemas/experiment'; +import { latest } from '../../../../database/schemas'; import { escapeColumnName } from '../../../../database/tools/escapeColumnNames'; import { Column } from '../../../../database/tools/types'; import { DatasetIDMapper } from '../../../dataset/datasetProvider/util/idMapper'; type ExperimentSchema = ReturnType< - typeof tableSchemas['experiment']['experiment'] + typeof latest.tableSchemas['experiment']['experiment'] >; type ExperimentTable = Table; @@ -70,7 +69,7 @@ export abstract class ExperimentInserter { } private getTableSchema(columns: string[]): ExperimentSchema { - return tableSchemas.experiment.experiment( + return latest.tableSchemas.experiment.experiment( this.experimentId, this.columnsFromNames(columns) ); @@ -87,7 +86,7 @@ export abstract class ExperimentInserter { private rowToInsertParameters( columns: ReturnType< - typeof tableSchemas['experiment']['experiment'] + typeof latest.tableSchemas['experiment']['experiment'] >['columns'], id1: string, id2: string, @@ -96,7 +95,7 @@ export abstract class ExperimentInserter { ) { const inserts: Parameters< Table< - ReturnType + ReturnType >['insert'] > = [ [ @@ -115,7 +114,7 @@ export abstract class ExperimentInserter { ...Object.entries(similarityScores).map(([score, value]) => { return { column: columns[ - escapeColumnName(score, experimentCustomColumnPrefix) + escapeColumnName(score, latest.experimentCustomColumnPrefix) ] as Column<'REAL'> & { autoIncrement: false; }, diff --git a/wrapper/src/api/providers/experiment/experimentProvider/file/getter.ts b/wrapper/src/api/providers/experiment/experimentProvider/file/getter.ts index 284d22a6..a4cc2d54 100644 --- a/wrapper/src/api/providers/experiment/experimentProvider/file/getter.ts +++ b/wrapper/src/api/providers/experiment/experimentProvider/file/getter.ts @@ -1,12 +1,11 @@ import { databaseBackend, Table } from '../../../../database'; -import { tableSchemas } from '../../../../database/schemas'; -import { experimentCustomColumnPrefix } from '../../../../database/schemas/experiment'; +import { latest } from '../../../../database/schemas'; import { loadTableFromDatabase } from '../../../../database/table/loader'; import { Column } from '../../../../database/tools/types'; import { ExperimentId } from '../../../../server/types'; type ExperimentSchema = ReturnType< - typeof tableSchemas['experiment']['experiment'] + typeof latest.tableSchemas['experiment']['experiment'] >; export class ExperimentFileGetter { @@ -14,7 +13,7 @@ export class ExperimentFileGetter { protected columns: Column[]; constructor(private readonly id: ExperimentId) { this.table = loadTableFromDatabase( - tableSchemas.experiment.experiment(id) + latest.tableSchemas.experiment.experiment(id) ); this.columns = Object.values(this.table.schema.columns); } @@ -28,20 +27,23 @@ export class ExperimentFileGetter { limit = limit ?? -1; if (sortBy) { if ( - !(experimentCustomColumnPrefix + sortBy in this.table.schema.columns) + !( + latest.experimentCustomColumnPrefix + sortBy in + this.table.schema.columns + ) ) { throw new Error( `Cannot sort by ${sortBy} as this column does not exist.` ); } else { - sortBy = experimentCustomColumnPrefix + sortBy; + sortBy = latest.experimentCustomColumnPrefix + sortBy; } } else { sortBy = this.table.schema.columns.id1.name; } yield this.columns.map((column) => - column.name.startsWith(experimentCustomColumnPrefix) - ? column.name.substring(experimentCustomColumnPrefix.length) + column.name.startsWith(latest.experimentCustomColumnPrefix) + ? column.name.substring(latest.experimentCustomColumnPrefix.length) : column.name ); yield* databaseBackend() diff --git a/wrapper/src/api/providers/experiment/experimentProvider/queries.ts b/wrapper/src/api/providers/experiment/experimentProvider/queries.ts index 84b568df..1b8df661 100644 --- a/wrapper/src/api/providers/experiment/experimentProvider/queries.ts +++ b/wrapper/src/api/providers/experiment/experimentProvider/queries.ts @@ -1,8 +1,8 @@ import { databaseBackend, Table } from '../../../database'; -import { tableSchemas } from '../../../database/schemas'; +import { latest } from '../../../database/schemas'; export class ExperimentProviderQueries { - readonly schema = tableSchemas.meta.experiment; + readonly schema = latest.tableSchemas.meta.experiment; readonly table = new Table(this.schema); readonly listExperimentsQuery = databaseBackend().prepare( @@ -38,7 +38,7 @@ export class ExperimentProviderQueries { WHERE "${this.schema.columns.id.name}" = ?` ); - readonly datasetSchema = tableSchemas.meta.dataset; + readonly datasetSchema = latest.tableSchemas.meta.dataset; readonly datasetTable = new Table(this.datasetSchema); readonly updateDatasetNumberRecords = databaseBackend().prepare( `UPDATE ${this.datasetTable} diff --git a/wrapper/src/api/tools/cli.ts b/wrapper/src/api/tools/cli.ts index e2bb6080..d52977ad 100644 --- a/wrapper/src/api/tools/cli.ts +++ b/wrapper/src/api/tools/cli.ts @@ -30,7 +30,7 @@ export const cliOptions: dashdash.OptionWithoutAliases[] = [ { name: STORAGE_DIRECTORY_CLI_FLAG, type: 'string', - default: path.join(__dirname, '../../storage'), + default: path.join(__dirname, '../../../storage'), help: 'Where the database and configuration files live.', }, { From 257ed5da117379a2998f11a566f68225f9cc6efa Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Mon, 1 Mar 2021 11:10:14 +0100 Subject: [PATCH 33/73] Remove unused import --- wrapper/src/api/database/schemas/migration.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/src/api/database/schemas/migration.spec.ts b/wrapper/src/api/database/schemas/migration.spec.ts index a7ec6636..5f34383c 100644 --- a/wrapper/src/api/database/schemas/migration.spec.ts +++ b/wrapper/src/api/database/schemas/migration.spec.ts @@ -1,4 +1,4 @@ -import { databaseBackend, loadOrCreateMainDatabase } from '../setup/backend'; +import { loadOrCreateMainDatabase } from '../setup/backend'; import { SchemaVersion } from './schemaVersion'; abstract class TestSchemaVersion extends SchemaVersion { From 7b85e9dbdee03c945c80a5556ef4f82dba722c2a Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Mon, 1 Mar 2021 11:20:36 +0100 Subject: [PATCH 34/73] Test if migration is used in setupDatabase --- .../api/database/schemas/migration.spec.ts | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/wrapper/src/api/database/schemas/migration.spec.ts b/wrapper/src/api/database/schemas/migration.spec.ts index 5f34383c..5334c72c 100644 --- a/wrapper/src/api/database/schemas/migration.spec.ts +++ b/wrapper/src/api/database/schemas/migration.spec.ts @@ -1,4 +1,6 @@ +import { setupDatabase } from '../setup'; import { loadOrCreateMainDatabase } from '../setup/backend'; +import { latest } from '.'; import { SchemaVersion } from './schemaVersion'; abstract class TestSchemaVersion extends SchemaVersion { @@ -27,11 +29,11 @@ const TestSchemaV2 = new (class extends TestSchemaVersion { })(); describe('SchemaVersion', () => { + beforeEach(() => { + loadOrCreateMainDatabase(true); + TestSchemaV2.reset(); + }); describe('migrates correctly', () => { - beforeEach(() => { - loadOrCreateMainDatabase(true); - TestSchemaV2.reset(); - }); test('from v0', () => { TestSchemaV2.migrate(0); expect(TestSchemaV0.migrated).toBeFalsy(); @@ -52,10 +54,6 @@ describe('SchemaVersion', () => { }); }); describe('version stored correctly', () => { - beforeEach(() => { - loadOrCreateMainDatabase(true); - TestSchemaV2.reset(); - }); test('initial version is 0', () => { expect(TestSchemaVersion.getInstalledVersion()).toEqual(0); }); @@ -82,3 +80,15 @@ describe('SchemaVersion', () => { expect(() => TestSchemaV0.migrate(TestSchemaV2.version)).toThrowError(); }); }); + +describe('database setup', () => { + beforeEach(async () => { + await setupDatabase({ + temporary: true, + loadExampleEntries: false, + }); + }); + test('sets latest database version', () => { + expect(SchemaVersion.getInstalledVersion()).toEqual(latest.version.version); + }); +}); From 651b1137abfb452189125b5d20db09e05c4cfc93 Mon Sep 17 00:00:00 2001 From: phpfs Date: Mon, 1 Mar 2021 14:09:25 +0100 Subject: [PATCH 35/73] Augment view component --- app/src/components/OptionCard/OptionCard.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/components/OptionCard/OptionCard.tsx b/app/src/components/OptionCard/OptionCard.tsx index 4f3382f2..07800081 100644 --- a/app/src/components/OptionCard/OptionCard.tsx +++ b/app/src/components/OptionCard/OptionCard.tsx @@ -15,6 +15,7 @@ import { import { OptionCardProps } from 'components/OptionCard/OptionCardProps'; import { checkboxOutline, + create, radioButtonOffOutline, radioButtonOnOutline, squareOutline, @@ -85,7 +86,19 @@ const OptionCard = ({ ) : null} - + + + + Edit + + + Date: Mon, 1 Mar 2021 14:10:44 +0100 Subject: [PATCH 36/73] Introduce card property --- app/src/components/OptionCard/OptionCard.tsx | 5 ++++- app/src/components/OptionCard/OptionCardProps.ts | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/components/OptionCard/OptionCard.tsx b/app/src/components/OptionCard/OptionCard.tsx index 07800081..397ef187 100644 --- a/app/src/components/OptionCard/OptionCard.tsx +++ b/app/src/components/OptionCard/OptionCard.tsx @@ -34,6 +34,9 @@ const OptionCard = ({ deleteCard = () => { console.log('Deletion requested'); }, + editCard = () => { + console.log('Edit requested'); + }, multiple = true, }: OptionCardProps): JSX.Element => { return ( @@ -91,7 +94,7 @@ const OptionCard = ({ size="small" fill="clear" color="primary" - onClick={deleteCard} + onClick={editCard} className="ion-float-left" > diff --git a/app/src/components/OptionCard/OptionCardProps.ts b/app/src/components/OptionCard/OptionCardProps.ts index 17793e49..37f6236e 100644 --- a/app/src/components/OptionCard/OptionCardProps.ts +++ b/app/src/components/OptionCard/OptionCardProps.ts @@ -8,6 +8,7 @@ export interface OptionCardProps { tags?: string[]; clickCard(): void; deleteCard?(): void; + editCard?(): void; isSelected?: boolean; multiple?: boolean; } From 1b367eece22e99b14bff14bb7ca6c6c7c8c255e5 Mon Sep 17 00:00:00 2001 From: phpfs Date: Mon, 1 Mar 2021 14:12:52 +0100 Subject: [PATCH 37/73] Introduce selector property --- app/src/components/OptionSelector/OptionSelector.tsx | 2 ++ app/src/components/OptionSelector/OptionSelectorProps.ts | 1 + app/src/pages/DatasetsPage/DatasetsPage.View.tsx | 1 + app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx | 1 + 4 files changed, 5 insertions(+) diff --git a/app/src/components/OptionSelector/OptionSelector.tsx b/app/src/components/OptionSelector/OptionSelector.tsx index 4887753c..b5342282 100644 --- a/app/src/components/OptionSelector/OptionSelector.tsx +++ b/app/src/components/OptionSelector/OptionSelector.tsx @@ -8,6 +8,7 @@ const OptionSelector = ({ optionsList, clickOnCard, deleteCardHandler, + editCardHandler, selected, title, multiple = true, @@ -30,6 +31,7 @@ const OptionSelector = ({ clickCard={(): void => clickOnCard(anOption.id)} isSelected={selected.includes(anOption.id)} deleteCard={(): void => deleteCardHandler(anOption.id)} + editCard={(): void => editCardHandler(anOption.id)} multiple={multiple} /> diff --git a/app/src/components/OptionSelector/OptionSelectorProps.ts b/app/src/components/OptionSelector/OptionSelectorProps.ts index 72abac10..4d50260b 100644 --- a/app/src/components/OptionSelector/OptionSelectorProps.ts +++ b/app/src/components/OptionSelector/OptionSelectorProps.ts @@ -5,6 +5,7 @@ export interface OptionSelectorProps { optionsList: Option[]; clickOnCard(id: number): void; deleteCardHandler(id: number): void; + editCardHandler(id: number): void; selected: number[]; multiple?: boolean; } diff --git a/app/src/pages/DatasetsPage/DatasetsPage.View.tsx b/app/src/pages/DatasetsPage/DatasetsPage.View.tsx index 9c540da0..762eb18d 100644 --- a/app/src/pages/DatasetsPage/DatasetsPage.View.tsx +++ b/app/src/pages/DatasetsPage/DatasetsPage.View.tsx @@ -39,6 +39,7 @@ const DatasetsPageView = ({ selected={selectedDataset} clickOnCard={clickOnDataset} deleteCardHandler={deleteDataset} + editCardHandler={(id: number) => console.log('Edit with id', id)} multiple={false} /> diff --git a/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx b/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx index 1b046ce2..ee8c41ba 100644 --- a/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx +++ b/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx @@ -39,6 +39,7 @@ const ExperimentsPageView = ({ clickOnCard={clickOnExperiment} selected={selectedExperiments} deleteCardHandler={deleteExperiment} + editCardHandler={(id: number) => console.log('Edit with id', id)} multiple={true} /> From 74306bf602397d5eae588cb04a60e76f7bb49fb3 Mon Sep 17 00:00:00 2001 From: phpfs Date: Mon, 1 Mar 2021 14:15:17 +0100 Subject: [PATCH 38/73] Rename algorithm dialog --- app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx | 4 ++-- app/src/components/AlgorithmDialog/AlgorithmDialog.tsx | 4 ++-- .../{AddAlgorithmDialogProps.ts => AlgorithmDialogProps.ts} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename app/src/components/AlgorithmDialog/{AddAlgorithmDialogProps.ts => AlgorithmDialogProps.ts} (87%) diff --git a/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx b/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx index 23598da9..5b6e8ee3 100644 --- a/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx +++ b/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx @@ -9,7 +9,7 @@ import { IonList, IonTextarea, } from '@ionic/react'; -import { AddAlgorithmDialogProps } from 'components/AlgorithmDialog/AddAlgorithmDialogProps'; +import { AlgorithmDialogProps } from 'components/AlgorithmDialog/AlgorithmDialogProps'; import ModalDialog from 'components/ModalDialog/ModalDialog'; import { addCircleOutline, @@ -28,7 +28,7 @@ const AlgorithmDialogView = ({ changeAlgorithmDescription, changeAlgorithmName, closeDialog, -}: AddAlgorithmDialogProps): JSX.Element => ( +}: AlgorithmDialogProps): JSX.Element => ( Date: Mon, 1 Mar 2021 14:17:35 +0100 Subject: [PATCH 39/73] Rename directory to DatasetDialog --- .../AddDatasetDialog.View.tsx | 4 ++-- .../{AddDatasetDialog => DatasetDialog}/AddDatasetDialog.tsx | 4 ++-- .../AddDatasetDialogProps.ts | 0 .../AddDatasetDialogStyles.css | 0 .../AddDatasetDialogValidator.test.ts | 2 +- app/src/pages/DatasetsPage/DatasetsPage.View.tsx | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename app/src/components/{AddDatasetDialog => DatasetDialog}/AddDatasetDialog.View.tsx (97%) rename app/src/components/{AddDatasetDialog => DatasetDialog}/AddDatasetDialog.tsx (96%) rename app/src/components/{AddDatasetDialog => DatasetDialog}/AddDatasetDialogProps.ts (100%) rename app/src/components/{AddDatasetDialog => DatasetDialog}/AddDatasetDialogStyles.css (100%) rename app/src/components/{AddDatasetDialog => DatasetDialog}/AddDatasetDialogValidator.test.ts (65%) diff --git a/app/src/components/AddDatasetDialog/AddDatasetDialog.View.tsx b/app/src/components/DatasetDialog/AddDatasetDialog.View.tsx similarity index 97% rename from app/src/components/AddDatasetDialog/AddDatasetDialog.View.tsx rename to app/src/components/DatasetDialog/AddDatasetDialog.View.tsx index a420aa7c..09ffefd6 100644 --- a/app/src/components/AddDatasetDialog/AddDatasetDialog.View.tsx +++ b/app/src/components/DatasetDialog/AddDatasetDialog.View.tsx @@ -1,4 +1,4 @@ -import 'components/AddDatasetDialog/AddDatasetDialogStyles.css'; +import 'components/DatasetDialog/AddDatasetDialogStyles.css'; import { IonButton, @@ -17,7 +17,7 @@ import { IonSelectOption, IonTextarea, } from '@ionic/react'; -import { AddDatasetDialogProps } from 'components/AddDatasetDialog/AddDatasetDialogProps'; +import { AddDatasetDialogProps } from 'components/DatasetDialog/AddDatasetDialogProps'; import FileInput from 'components/FileInput/FileInput'; import InputChip from 'components/InputChip/InputChip'; import ModalDialog from 'components/ModalDialog/ModalDialog'; diff --git a/app/src/components/AddDatasetDialog/AddDatasetDialog.tsx b/app/src/components/DatasetDialog/AddDatasetDialog.tsx similarity index 96% rename from app/src/components/AddDatasetDialog/AddDatasetDialog.tsx rename to app/src/components/DatasetDialog/AddDatasetDialog.tsx index 9f3bb0a5..3545a678 100644 --- a/app/src/components/AddDatasetDialog/AddDatasetDialog.tsx +++ b/app/src/components/DatasetDialog/AddDatasetDialog.tsx @@ -1,8 +1,8 @@ -import AddDatasetDialogView from 'components/AddDatasetDialog/AddDatasetDialog.View'; +import AddDatasetDialogView from 'components/DatasetDialog/AddDatasetDialog.View'; import { AddDatasetDialogDispatchProps, AddDatasetDialogStateProps, -} from 'components/AddDatasetDialog/AddDatasetDialogProps'; +} from 'components/DatasetDialog/AddDatasetDialogProps'; import { ChangeEvent } from 'react'; import { connect } from 'react-redux'; import { diff --git a/app/src/components/AddDatasetDialog/AddDatasetDialogProps.ts b/app/src/components/DatasetDialog/AddDatasetDialogProps.ts similarity index 100% rename from app/src/components/AddDatasetDialog/AddDatasetDialogProps.ts rename to app/src/components/DatasetDialog/AddDatasetDialogProps.ts diff --git a/app/src/components/AddDatasetDialog/AddDatasetDialogStyles.css b/app/src/components/DatasetDialog/AddDatasetDialogStyles.css similarity index 100% rename from app/src/components/AddDatasetDialog/AddDatasetDialogStyles.css rename to app/src/components/DatasetDialog/AddDatasetDialogStyles.css diff --git a/app/src/components/AddDatasetDialog/AddDatasetDialogValidator.test.ts b/app/src/components/DatasetDialog/AddDatasetDialogValidator.test.ts similarity index 65% rename from app/src/components/AddDatasetDialog/AddDatasetDialogValidator.test.ts rename to app/src/components/DatasetDialog/AddDatasetDialogValidator.test.ts index deade59a..0f3df6de 100644 --- a/app/src/components/AddDatasetDialog/AddDatasetDialogValidator.test.ts +++ b/app/src/components/DatasetDialog/AddDatasetDialogValidator.test.ts @@ -1,4 +1,4 @@ -describe('Test validation of AddDatasetDialog', (): void => { +describe('Test validation of DatasetDialog', (): void => { test('Should validate correctly answered dialog', (): void => { expect(true).toBeTruthy(); }); diff --git a/app/src/pages/DatasetsPage/DatasetsPage.View.tsx b/app/src/pages/DatasetsPage/DatasetsPage.View.tsx index 762eb18d..c48a7040 100644 --- a/app/src/pages/DatasetsPage/DatasetsPage.View.tsx +++ b/app/src/pages/DatasetsPage/DatasetsPage.View.tsx @@ -1,6 +1,6 @@ import { IonChip, IonLabel } from '@ionic/react'; -import AddDatasetDialog from 'components/AddDatasetDialog/AddDatasetDialog'; import AddDatasetFab from 'components/AddFab/AddDatasetFab'; +import AddDatasetDialog from 'components/DatasetDialog/AddDatasetDialog'; import OptionSelector from 'components/OptionSelector/OptionSelector'; import PageStruct from 'components/PageStruct/PageStruct'; import { DatasetsPageProps } from 'pages/DatasetsPage/DatasetsPageProps'; From 4a4523f1573e78e155f77bbc17583a58005edd48 Mon Sep 17 00:00:00 2001 From: phpfs Date: Mon, 1 Mar 2021 14:20:28 +0100 Subject: [PATCH 40/73] Rename component to DatasetDialog --- ...tDialog.View.tsx => DatasetDialog.View.tsx} | 10 +++++----- ...{AddDatasetDialog.tsx => DatasetDialog.tsx} | 18 +++++++++--------- ...setDialogProps.ts => DatasetDialogProps.ts} | 8 ++++---- ...ialogStyles.css => DatasetDialogStyles.css} | 0 ....test.ts => DatasetDialogValidator.test.ts} | 0 .../pages/DatasetsPage/DatasetsPage.View.tsx | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) rename app/src/components/DatasetDialog/{AddDatasetDialog.View.tsx => DatasetDialog.View.tsx} (95%) rename app/src/components/DatasetDialog/{AddDatasetDialog.tsx => DatasetDialog.tsx} (90%) rename app/src/components/DatasetDialog/{AddDatasetDialogProps.ts => DatasetDialogProps.ts} (84%) rename app/src/components/DatasetDialog/{AddDatasetDialogStyles.css => DatasetDialogStyles.css} (100%) rename app/src/components/DatasetDialog/{AddDatasetDialogValidator.test.ts => DatasetDialogValidator.test.ts} (100%) diff --git a/app/src/components/DatasetDialog/AddDatasetDialog.View.tsx b/app/src/components/DatasetDialog/DatasetDialog.View.tsx similarity index 95% rename from app/src/components/DatasetDialog/AddDatasetDialog.View.tsx rename to app/src/components/DatasetDialog/DatasetDialog.View.tsx index 09ffefd6..5276bea1 100644 --- a/app/src/components/DatasetDialog/AddDatasetDialog.View.tsx +++ b/app/src/components/DatasetDialog/DatasetDialog.View.tsx @@ -1,4 +1,4 @@ -import 'components/DatasetDialog/AddDatasetDialogStyles.css'; +import 'components/DatasetDialog/DatasetDialogStyles.css'; import { IonButton, @@ -17,7 +17,7 @@ import { IonSelectOption, IonTextarea, } from '@ionic/react'; -import { AddDatasetDialogProps } from 'components/DatasetDialog/AddDatasetDialogProps'; +import { DatasetDialogProps } from 'components/DatasetDialog/DatasetDialogProps'; import FileInput from 'components/FileInput/FileInput'; import InputChip from 'components/InputChip/InputChip'; import ModalDialog from 'components/ModalDialog/ModalDialog'; @@ -26,7 +26,7 @@ import React from 'react'; import { $enum } from 'ts-enum-util'; import { DatasetTypes } from 'types/DatasetTypes'; -const AddDatasetDialogView = ({ +const DatasetDialogView = ({ isOpen, isValidForm, closeDialog, @@ -54,7 +54,7 @@ const AddDatasetDialogView = ({ clickOnATag, addNewTagCallback, addDataset, -}: AddDatasetDialogProps): JSX.Element => ( +}: DatasetDialogProps): JSX.Element => ( ); -export default AddDatasetDialogView; +export default DatasetDialogView; diff --git a/app/src/components/DatasetDialog/AddDatasetDialog.tsx b/app/src/components/DatasetDialog/DatasetDialog.tsx similarity index 90% rename from app/src/components/DatasetDialog/AddDatasetDialog.tsx rename to app/src/components/DatasetDialog/DatasetDialog.tsx index 3545a678..fc8c72f6 100644 --- a/app/src/components/DatasetDialog/AddDatasetDialog.tsx +++ b/app/src/components/DatasetDialog/DatasetDialog.tsx @@ -1,8 +1,8 @@ -import AddDatasetDialogView from 'components/DatasetDialog/AddDatasetDialog.View'; +import DatasetDialogView from 'components/DatasetDialog/DatasetDialog.View'; import { - AddDatasetDialogDispatchProps, - AddDatasetDialogStateProps, -} from 'components/DatasetDialog/AddDatasetDialogProps'; + DatasetDialogDispatchProps, + DatasetDialogStateProps, +} from 'components/DatasetDialog/DatasetDialogProps'; import { ChangeEvent } from 'react'; import { connect } from 'react-redux'; import { @@ -44,7 +44,7 @@ const isValidDatasetDialog = (state: Store): boolean => { ); }; -const mapStateToProps = (state: Store): AddDatasetDialogStateProps => ({ +const mapStateToProps = (state: Store): DatasetDialogStateProps => ({ isOpen: state.AddDatasetDialogStore.isOpen, datasetName: state.AddDatasetDialogStore.datasetName, datasetDescription: state.AddDatasetDialogStore.datasetDescription, @@ -62,7 +62,7 @@ const mapStateToProps = (state: Store): AddDatasetDialogStateProps => ({ const mapDispatchToProps = ( dispatch: SnowmanDispatch -): AddDatasetDialogDispatchProps => ({ +): DatasetDialogDispatchProps => ({ closeDialog(): void { (dispatch as SnowmanDispatch)(closeDialog()); }, @@ -126,9 +126,9 @@ const mapDispatchToProps = ( }, }); -const AddDatasetDialog = connect( +const DatasetDialog = connect( mapStateToProps, mapDispatchToProps -)(AddDatasetDialogView); +)(DatasetDialogView); -export default AddDatasetDialog; +export default DatasetDialog; diff --git a/app/src/components/DatasetDialog/AddDatasetDialogProps.ts b/app/src/components/DatasetDialog/DatasetDialogProps.ts similarity index 84% rename from app/src/components/DatasetDialog/AddDatasetDialogProps.ts rename to app/src/components/DatasetDialog/DatasetDialogProps.ts index c14b60c6..a38a0a0a 100644 --- a/app/src/components/DatasetDialog/AddDatasetDialogProps.ts +++ b/app/src/components/DatasetDialog/DatasetDialogProps.ts @@ -2,7 +2,7 @@ import { ChangeEvent } from 'react'; import { DatasetTypes } from 'types/DatasetTypes'; import { IonChangeEvent } from 'types/IonChangeEvent'; -export interface AddDatasetDialogDispatchProps { +export interface DatasetDialogDispatchProps { closeDialog(): void; clickOnCancel(): void; addNewTagCallback(newTagValue: string): void; @@ -19,7 +19,7 @@ export interface AddDatasetDialogDispatchProps { changeSelectedDatasetFiles(event: ChangeEvent): void; } -export interface AddDatasetDialogStateProps { +export interface DatasetDialogStateProps { isOpen: boolean; isValidForm: boolean; datasetName: string; @@ -35,5 +35,5 @@ export interface AddDatasetDialogStateProps { selectedFiles: File[]; } -export type AddDatasetDialogProps = AddDatasetDialogDispatchProps & - AddDatasetDialogStateProps; +export type DatasetDialogProps = DatasetDialogDispatchProps & + DatasetDialogStateProps; diff --git a/app/src/components/DatasetDialog/AddDatasetDialogStyles.css b/app/src/components/DatasetDialog/DatasetDialogStyles.css similarity index 100% rename from app/src/components/DatasetDialog/AddDatasetDialogStyles.css rename to app/src/components/DatasetDialog/DatasetDialogStyles.css diff --git a/app/src/components/DatasetDialog/AddDatasetDialogValidator.test.ts b/app/src/components/DatasetDialog/DatasetDialogValidator.test.ts similarity index 100% rename from app/src/components/DatasetDialog/AddDatasetDialogValidator.test.ts rename to app/src/components/DatasetDialog/DatasetDialogValidator.test.ts diff --git a/app/src/pages/DatasetsPage/DatasetsPage.View.tsx b/app/src/pages/DatasetsPage/DatasetsPage.View.tsx index c48a7040..c50ca8e8 100644 --- a/app/src/pages/DatasetsPage/DatasetsPage.View.tsx +++ b/app/src/pages/DatasetsPage/DatasetsPage.View.tsx @@ -1,6 +1,6 @@ import { IonChip, IonLabel } from '@ionic/react'; import AddDatasetFab from 'components/AddFab/AddDatasetFab'; -import AddDatasetDialog from 'components/DatasetDialog/AddDatasetDialog'; +import DatasetDialog from 'components/DatasetDialog/DatasetDialog'; import OptionSelector from 'components/OptionSelector/OptionSelector'; import PageStruct from 'components/PageStruct/PageStruct'; import { DatasetsPageProps } from 'pages/DatasetsPage/DatasetsPageProps'; @@ -43,7 +43,7 @@ const DatasetsPageView = ({ multiple={false} /> - + ); }; From 61489d09111431c4c28bce2d3d85def67586057a Mon Sep 17 00:00:00 2001 From: phpfs Date: Mon, 1 Mar 2021 14:28:41 +0100 Subject: [PATCH 41/73] Rename store, reducer & actions to DatasetDialog --- app/src/components/AddFab/AddDatasetFab.tsx | 2 +- .../DatasetDialog/DatasetDialog.tsx | 38 +++++++++---------- ...ctions.ts => DatasetDialogStoreActions.ts} | 25 ++++++------ app/src/store/actions/actionTypes.ts | 2 +- app/src/store/models.ts | 4 +- ...alogReducer.ts => DatasetDialogReducer.ts} | 12 +++--- app/src/store/reducers/rootReducer.ts | 4 +- 7 files changed, 42 insertions(+), 45 deletions(-) rename app/src/store/actions/{AddDatasetDialogStoreActions.ts => DatasetDialogStoreActions.ts} (85%) rename app/src/store/reducers/{AddDatasetDialogReducer.ts => DatasetDialogReducer.ts} (88%) diff --git a/app/src/components/AddFab/AddDatasetFab.tsx b/app/src/components/AddFab/AddDatasetFab.tsx index 43f081b3..808d7b1d 100644 --- a/app/src/components/AddFab/AddDatasetFab.tsx +++ b/app/src/components/AddFab/AddDatasetFab.tsx @@ -1,7 +1,7 @@ import AddFabView from 'components/AddFab/AddFab.View'; import { AddFabDispatchProps } from 'components/AddFab/AddFabProps'; import { connect } from 'react-redux'; -import { openDialog } from 'store/actions/AddDatasetDialogStoreActions'; +import { openDialog } from 'store/actions/DatasetDialogStoreActions'; import { SnowmanDispatch } from 'store/messages'; const mapDispatchToProps = ( diff --git a/app/src/components/DatasetDialog/DatasetDialog.tsx b/app/src/components/DatasetDialog/DatasetDialog.tsx index fc8c72f6..149c8f66 100644 --- a/app/src/components/DatasetDialog/DatasetDialog.tsx +++ b/app/src/components/DatasetDialog/DatasetDialog.tsx @@ -18,9 +18,8 @@ import { changeDatasetType, clickOnDatasetTag, closeDialog, - resetDialog, setSelectedFiles, -} from 'store/actions/AddDatasetDialogStoreActions'; +} from 'store/actions/DatasetDialogStoreActions'; import { SnowmanDispatch } from 'store/messages'; import { Store } from 'store/models'; import { DatasetTypes } from 'types/DatasetTypes'; @@ -29,34 +28,34 @@ import { convertFilesListToFilesArray } from 'utils/filesConverter'; const isValidDatasetDialog = (state: Store): boolean => { // Dataset name shall not be empty - if (state.AddDatasetDialogStore.datasetName.length === 0) return false; + if (state.DatasetDialogStore.datasetName.length === 0) return false; // If dataset should be uploaded, select files if ( - state.AddDatasetDialogStore.datasetType === DatasetTypes.full && - state.AddDatasetDialogStore.selectedFiles.length === 0 + state.DatasetDialogStore.datasetType === DatasetTypes.full && + state.DatasetDialogStore.selectedFiles.length === 0 ) { return false; } // If dataset not to be uploaded, specify its size return !( - state.AddDatasetDialogStore.datasetType === DatasetTypes.skeleton && - state.AddDatasetDialogStore.datasetLength === 0 + state.DatasetDialogStore.datasetType === DatasetTypes.skeleton && + state.DatasetDialogStore.datasetLength === 0 ); }; const mapStateToProps = (state: Store): DatasetDialogStateProps => ({ - isOpen: state.AddDatasetDialogStore.isOpen, - datasetName: state.AddDatasetDialogStore.datasetName, - datasetDescription: state.AddDatasetDialogStore.datasetDescription, - datasetType: state.AddDatasetDialogStore.datasetType, - datasetLength: state.AddDatasetDialogStore.datasetLength, - tags: state.AddDatasetDialogStore.availableTags, - selectedTags: state.AddDatasetDialogStore.selectedTags, - selectedFiles: state.AddDatasetDialogStore.selectedFiles, - csvIdColumn: state.AddDatasetDialogStore.csvIdColumn, - csvSeparator: state.AddDatasetDialogStore.csvSeparator, - csvQuote: state.AddDatasetDialogStore.csvQuote, - csvEscape: state.AddDatasetDialogStore.csvEscape, + isOpen: state.DatasetDialogStore.isOpen, + datasetName: state.DatasetDialogStore.datasetName, + datasetDescription: state.DatasetDialogStore.datasetDescription, + datasetType: state.DatasetDialogStore.datasetType, + datasetLength: state.DatasetDialogStore.datasetLength, + tags: state.DatasetDialogStore.availableTags, + selectedTags: state.DatasetDialogStore.selectedTags, + selectedFiles: state.DatasetDialogStore.selectedFiles, + csvIdColumn: state.DatasetDialogStore.csvIdColumn, + csvSeparator: state.DatasetDialogStore.csvSeparator, + csvQuote: state.DatasetDialogStore.csvQuote, + csvEscape: state.DatasetDialogStore.csvEscape, isValidForm: isValidDatasetDialog(state), }); @@ -67,7 +66,6 @@ const mapDispatchToProps = ( (dispatch as SnowmanDispatch)(closeDialog()); }, clickOnCancel(): void { - (dispatch as SnowmanDispatch)(resetDialog()); (dispatch as SnowmanDispatch)(closeDialog()); }, changeDatasetName(event: IonChangeEvent): void { diff --git a/app/src/store/actions/AddDatasetDialogStoreActions.ts b/app/src/store/actions/DatasetDialogStoreActions.ts similarity index 85% rename from app/src/store/actions/AddDatasetDialogStoreActions.ts rename to app/src/store/actions/DatasetDialogStoreActions.ts index 04fcfeca..f8a602da 100644 --- a/app/src/store/actions/AddDatasetDialogStoreActions.ts +++ b/app/src/store/actions/DatasetDialogStoreActions.ts @@ -1,5 +1,5 @@ import { DatasetsApi } from 'api'; -import { AddDatasetDialogStoreActionTypes as DialogActionTypes } from 'store/actions/actionTypes'; +import { DatasetDialogStoreActionTypes as DialogActionTypes } from 'store/actions/actionTypes'; import { getDatasets } from 'store/actions/DatasetsStoreActions'; import { SnowmanDispatch, SnowmanThunkAction } from 'store/messages'; import { store } from 'store/store'; @@ -167,27 +167,26 @@ export const addNewDataset = (): SnowmanThunkAction> => async ( ): Promise => { return dispatch( createNewDataset( - store.getState().AddDatasetDialogStore.datasetName, - store.getState().AddDatasetDialogStore.datasetDescription, - store.getState().AddDatasetDialogStore.datasetType === - DatasetTypes.skeleton - ? store.getState().AddDatasetDialogStore.datasetLength + store.getState().DatasetDialogStore.datasetName, + store.getState().DatasetDialogStore.datasetDescription, + store.getState().DatasetDialogStore.datasetType === DatasetTypes.skeleton + ? store.getState().DatasetDialogStore.datasetLength : undefined, - store.getState().AddDatasetDialogStore.selectedTags + store.getState().DatasetDialogStore.selectedTags ) ) .then((id: number): void => { if ( - store.getState().AddDatasetDialogStore.datasetType === DatasetTypes.full + store.getState().DatasetDialogStore.datasetType === DatasetTypes.full ) { dispatch( uploadDatasetFile( id, - store.getState().AddDatasetDialogStore.csvIdColumn, - store.getState().AddDatasetDialogStore.csvSeparator, - store.getState().AddDatasetDialogStore.csvQuote, - store.getState().AddDatasetDialogStore.csvEscape, - store.getState().AddDatasetDialogStore.selectedFiles[0] + store.getState().DatasetDialogStore.csvIdColumn, + store.getState().DatasetDialogStore.csvSeparator, + store.getState().DatasetDialogStore.csvQuote, + store.getState().DatasetDialogStore.csvEscape, + store.getState().DatasetDialogStore.selectedFiles[0] ) ); } diff --git a/app/src/store/actions/actionTypes.ts b/app/src/store/actions/actionTypes.ts index 53f0c314..0ab2241b 100644 --- a/app/src/store/actions/actionTypes.ts +++ b/app/src/store/actions/actionTypes.ts @@ -5,7 +5,7 @@ export const DatasetStoreActionTypes = { RESET_SELECTED_DATASET: 'DATASET_STORE_ACTION-RESET_SELECTED_DATASET', }; -export const AddDatasetDialogStoreActionTypes = { +export const DatasetDialogStoreActionTypes = { OPEN_DIALOG: 'ADD_DATASET_DIALOG_STORE_ACTION-OPEN_DIALOG', CLOSE_DIALOG: 'ADD_DATASET_DIALOG_STORE_ACTION-CLOSE_DIALOG', RESET_DIALOG: 'ADD_DATASET_DIALOG_STORE_ACTION-RESET_DIALOG', diff --git a/app/src/store/models.ts b/app/src/store/models.ts index d6c0efb6..a1ff3ce2 100644 --- a/app/src/store/models.ts +++ b/app/src/store/models.ts @@ -18,7 +18,7 @@ export interface AlgorithmDialogStore { dialogType: DialogTypes; } -export interface AddDatasetDialogStore { +export interface DatasetDialogStore { isOpen: boolean; datasetName: string; datasetDescription: string; @@ -80,7 +80,7 @@ export interface Store { DatasetsStore: DatasetsStore; ExperimentsStore: ExperimentsStore; AlgorithmsStore: AlgorithmsStore; - AddDatasetDialogStore: AddDatasetDialogStore; + DatasetDialogStore: DatasetDialogStore; AddExperimentDialogStore: AddExperimentDialogStore; AlgorithmDialogStore: AlgorithmDialogStore; GlobalIndicatorStore: GlobalIndicatorStore; diff --git a/app/src/store/reducers/AddDatasetDialogReducer.ts b/app/src/store/reducers/DatasetDialogReducer.ts similarity index 88% rename from app/src/store/reducers/AddDatasetDialogReducer.ts rename to app/src/store/reducers/DatasetDialogReducer.ts index 705bf88e..dbebe629 100644 --- a/app/src/store/reducers/AddDatasetDialogReducer.ts +++ b/app/src/store/reducers/DatasetDialogReducer.ts @@ -1,11 +1,11 @@ import { uniq } from 'lodash'; -import { AddDatasetDialogStoreActionTypes as DialogActionTypes } from 'store/actions/actionTypes'; +import { DatasetDialogStoreActionTypes as DialogActionTypes } from 'store/actions/actionTypes'; import { SnowmanAction } from 'store/messages'; -import { AddDatasetDialogStore } from 'store/models'; +import { DatasetDialogStore } from 'store/models'; import { DatasetTypes } from 'types/DatasetTypes'; import { toggleSelectionArrayMultipleSelect } from 'utils/toggleSelectionArray'; -const initialState: AddDatasetDialogStore = { +const initialState: DatasetDialogStore = { isOpen: false, availableTags: [], datasetName: '', @@ -20,10 +20,10 @@ const initialState: AddDatasetDialogStore = { selectedFiles: [], }; -export const AddDatasetDialogReducer = ( - state: AddDatasetDialogStore = initialState, +export const DatasetDialogReducer = ( + state: DatasetDialogStore = initialState, action: SnowmanAction -): AddDatasetDialogStore => { +): DatasetDialogStore => { switch (action.type) { case DialogActionTypes.OPEN_DIALOG: return { diff --git a/app/src/store/reducers/rootReducer.ts b/app/src/store/reducers/rootReducer.ts index 4ee96844..2461b0b2 100644 --- a/app/src/store/reducers/rootReducer.ts +++ b/app/src/store/reducers/rootReducer.ts @@ -1,8 +1,8 @@ import { combineReducers } from 'redux'; -import { AddDatasetDialogReducer } from 'store/reducers/AddDatasetDialogReducer'; import { AddExperimentDialogReducer } from 'store/reducers/AddExperimentDialogReducer'; import { AlgorithmDialogReducer } from 'store/reducers/AlgorithmDialogReducer'; import { AlgorithmsReducer } from 'store/reducers/AlgorithmsReducer'; +import { DatasetDialogReducer } from 'store/reducers/DatasetDialogReducer'; import { DatasetsReducer } from 'store/reducers/DatasetsReducer'; import { ExperimentsReducer } from 'store/reducers/ExperimentsReducer'; import { GlobalIndicatorReducer } from 'store/reducers/GlobalIndicatorReducer'; @@ -13,7 +13,7 @@ export const rootReducer = combineReducers({ DatasetsStore: DatasetsReducer, ExperimentsStore: ExperimentsReducer, AlgorithmsStore: AlgorithmsReducer, - AddDatasetDialogStore: AddDatasetDialogReducer, + DatasetDialogStore: DatasetDialogReducer, AddExperimentDialogStore: AddExperimentDialogReducer, AlgorithmDialogStore: AlgorithmDialogReducer, GlobalIndicatorStore: GlobalIndicatorReducer, From 24c4d07e056190314c3a61e5b3f3f8261d6a1594 Mon Sep 17 00:00:00 2001 From: phpfs Date: Mon, 1 Mar 2021 14:49:33 +0100 Subject: [PATCH 42/73] Basic implementation setup --- app/src/components/AddFab/AddDatasetFab.tsx | 4 +- .../AlgorithmDialog/AlgorithmDialogProps.ts | 2 +- .../DatasetDialog/DatasetDialog.tsx | 1 + .../DatasetDialog/DatasetDialogProps.ts | 3 +- .../pages/DatasetsPage/DatasetsPage.View.tsx | 3 +- app/src/pages/DatasetsPage/DatasetsPage.tsx | 9 +++- .../pages/DatasetsPage/DatasetsPageProps.tsx | 1 + .../actions/DatasetDialogStoreActions.ts | 54 +++++++++++++------ app/src/store/actions/actionTypes.ts | 3 +- app/src/store/models.ts | 2 + .../store/reducers/DatasetDialogReducer.ts | 45 ++++++++++------ 11 files changed, 87 insertions(+), 40 deletions(-) diff --git a/app/src/components/AddFab/AddDatasetFab.tsx b/app/src/components/AddFab/AddDatasetFab.tsx index 808d7b1d..1c34e277 100644 --- a/app/src/components/AddFab/AddDatasetFab.tsx +++ b/app/src/components/AddFab/AddDatasetFab.tsx @@ -1,13 +1,13 @@ import AddFabView from 'components/AddFab/AddFab.View'; import { AddFabDispatchProps } from 'components/AddFab/AddFabProps'; import { connect } from 'react-redux'; -import { openDialog } from 'store/actions/DatasetDialogStoreActions'; +import { openAddDialog } from 'store/actions/DatasetDialogStoreActions'; import { SnowmanDispatch } from 'store/messages'; const mapDispatchToProps = ( dispatch: SnowmanDispatch ): AddFabDispatchProps => ({ - clickOnFab: (): void => dispatch(openDialog()), + clickOnFab: (): void => dispatch(openAddDialog()), }); const AddDatasetFab = connect(null, mapDispatchToProps)(AddFabView); diff --git a/app/src/components/AlgorithmDialog/AlgorithmDialogProps.ts b/app/src/components/AlgorithmDialog/AlgorithmDialogProps.ts index 65104e4d..d62e746c 100644 --- a/app/src/components/AlgorithmDialog/AlgorithmDialogProps.ts +++ b/app/src/components/AlgorithmDialog/AlgorithmDialogProps.ts @@ -12,7 +12,7 @@ export interface AlgorithmDialogDispatchProps { closeDialog(): void; changeAlgorithmName(event: IonChangeEvent): void; changeAlgorithmDescription(event: IonChangeEvent): void; - clickOnAdd(): void; + clickOnAdd(): void; //Todo: Rename } export type AlgorithmDialogProps = AlgorithmDialogStateProps & diff --git a/app/src/components/DatasetDialog/DatasetDialog.tsx b/app/src/components/DatasetDialog/DatasetDialog.tsx index 149c8f66..5f7fb722 100644 --- a/app/src/components/DatasetDialog/DatasetDialog.tsx +++ b/app/src/components/DatasetDialog/DatasetDialog.tsx @@ -44,6 +44,7 @@ const isValidDatasetDialog = (state: Store): boolean => { }; const mapStateToProps = (state: Store): DatasetDialogStateProps => ({ + isAddDialog: state.DatasetDialogStore.datasetId === null, isOpen: state.DatasetDialogStore.isOpen, datasetName: state.DatasetDialogStore.datasetName, datasetDescription: state.DatasetDialogStore.datasetDescription, diff --git a/app/src/components/DatasetDialog/DatasetDialogProps.ts b/app/src/components/DatasetDialog/DatasetDialogProps.ts index a38a0a0a..da449095 100644 --- a/app/src/components/DatasetDialog/DatasetDialogProps.ts +++ b/app/src/components/DatasetDialog/DatasetDialogProps.ts @@ -21,7 +21,8 @@ export interface DatasetDialogDispatchProps { export interface DatasetDialogStateProps { isOpen: boolean; - isValidForm: boolean; + isAddDialog: boolean; + isValidForm: boolean; //Todo: Rename datasetName: string; datasetDescription: string; datasetType: DatasetTypes; diff --git a/app/src/pages/DatasetsPage/DatasetsPage.View.tsx b/app/src/pages/DatasetsPage/DatasetsPage.View.tsx index c50ca8e8..5c589e6c 100644 --- a/app/src/pages/DatasetsPage/DatasetsPage.View.tsx +++ b/app/src/pages/DatasetsPage/DatasetsPage.View.tsx @@ -14,6 +14,7 @@ const DatasetsPageView = ({ clickOnTag, clickOnDataset, deleteDataset, + editDataset, loadDatasets, }: DatasetsPageProps): JSX.Element => { useEffect((): void => loadDatasets(), [loadDatasets]); @@ -39,7 +40,7 @@ const DatasetsPageView = ({ selected={selectedDataset} clickOnCard={clickOnDataset} deleteCardHandler={deleteDataset} - editCardHandler={(id: number) => console.log('Edit with id', id)} + editCardHandler={editDataset} multiple={false} /> diff --git a/app/src/pages/DatasetsPage/DatasetsPage.tsx b/app/src/pages/DatasetsPage/DatasetsPage.tsx index 7aec0c1f..c19ba792 100644 --- a/app/src/pages/DatasetsPage/DatasetsPage.tsx +++ b/app/src/pages/DatasetsPage/DatasetsPage.tsx @@ -18,6 +18,8 @@ import { Option } from 'types/Option'; import { MagicNotPossibleId } from 'utils/constants'; import { getTagsFromDatasets } from 'utils/tagFactory'; +import { openChangeDialog } from '../../store/actions/DatasetDialogStoreActions'; + const mapStateToProps = (state: Store): DatasetsPageStateProps => ({ selectedTags: state.DatasetsStore.selectedDatasetTags, tags: getTagsFromDatasets(state.DatasetsStore.datasets), @@ -54,8 +56,11 @@ const mapDispatchToProps = ( loadDatasets() { dispatch(getDatasets()).then(); }, - deleteDataset(id: number) { - dispatch(deleteDataset(id)).then(); + deleteDataset(aDatasetId: number) { + dispatch(deleteDataset(aDatasetId)).then(); + }, + editDataset(aDatasetId: number) { + dispatch(openChangeDialog(aDatasetId)).then(); }, }); diff --git a/app/src/pages/DatasetsPage/DatasetsPageProps.tsx b/app/src/pages/DatasetsPage/DatasetsPageProps.tsx index 0be57850..83b8434e 100644 --- a/app/src/pages/DatasetsPage/DatasetsPageProps.tsx +++ b/app/src/pages/DatasetsPage/DatasetsPageProps.tsx @@ -12,6 +12,7 @@ export interface DatasetsPageDispatchProps { clickOnDataset(aDatasetId: number): void; loadDatasets(): void; deleteDataset(aDatasetId: number): void; + editDataset(aDatasetId: number): void; } export type DatasetsPageProps = DatasetsPageDispatchProps & diff --git a/app/src/store/actions/DatasetDialogStoreActions.ts b/app/src/store/actions/DatasetDialogStoreActions.ts index f8a602da..0659295d 100644 --- a/app/src/store/actions/DatasetDialogStoreActions.ts +++ b/app/src/store/actions/DatasetDialogStoreActions.ts @@ -1,5 +1,5 @@ -import { DatasetsApi } from 'api'; -import { DatasetDialogStoreActionTypes as DialogActionTypes } from 'store/actions/actionTypes'; +import { Dataset, DatasetsApi } from 'api'; +import { DatasetDialogStoreActionTypes as DialogActions } from 'store/actions/actionTypes'; import { getDatasets } from 'store/actions/DatasetsStoreActions'; import { SnowmanDispatch, SnowmanThunkAction } from 'store/messages'; import { store } from 'store/store'; @@ -15,15 +15,35 @@ import { } from 'utils/statusMessages'; import { getTagsFromDatasets } from 'utils/tagFactory'; -export const openDialog = (): easyPrimitiveActionReturn => +export const openAddDialog = (): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.OPEN_DIALOG, + type: DialogActions.OPEN_ADD_DIALOG, payload: getTagsFromDatasets(store.getState().DatasetsStore.datasets), }); +export const openChangeDialog = ( + datasetId: number +): SnowmanThunkAction> => async ( + dispatch: SnowmanDispatch +): Promise => { + return RequestHandler( + (): Promise => + new DatasetsApi() + .getDataset({ datasetId: datasetId }) + .then((aDataset: Dataset) => + dispatch({ + type: DialogActions.OPEN_CHANGE_DIALOG, + payload: aDataset, + }) + ) + .then(), + dispatch + ); +}; + export const closeDialog = (): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.CLOSE_DIALOG, + type: DialogActions.CLOSE_DIALOG, // reducer ignores payload payload: false, }); @@ -32,7 +52,7 @@ export const changeDatasetName = ( aDatasetName: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.CHANGE_DATASET_NAME, + type: DialogActions.CHANGE_DATASET_NAME, payload: aDatasetName, }); @@ -40,7 +60,7 @@ export const changeDatasetDescription = ( aDescription: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.CHANGE_DATASET_DESCRIPTION, + type: DialogActions.CHANGE_DATASET_DESCRIPTION, payload: aDescription, }); @@ -48,7 +68,7 @@ export const changeDatasetType = ( aType: DatasetTypes ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.CHANGE_DATASET_TYPE, + type: DialogActions.CHANGE_DATASET_TYPE, payload: aType, }); @@ -56,7 +76,7 @@ export const changeDatasetLength = ( aLength: number ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.CHANGE_DATASET_LENGTH, + type: DialogActions.CHANGE_DATASET_LENGTH, payload: aLength, }); @@ -64,7 +84,7 @@ export const changeDatasetCSVIdColumn = ( anIdColumn: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.CHANGE_CSV_ID_COLUMN, + type: DialogActions.CHANGE_CSV_ID_COLUMN, payload: anIdColumn, }); @@ -72,7 +92,7 @@ export const changeDatasetCSVSeparator = ( aSeparator: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.CHANGE_CSV_SEPARATOR, + type: DialogActions.CHANGE_CSV_SEPARATOR, payload: aSeparator, }); @@ -80,7 +100,7 @@ export const changeDatasetCSVQuote = ( aQuote: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.CHANGE_CSV_QUOTE, + type: DialogActions.CHANGE_CSV_QUOTE, payload: aQuote, }); @@ -88,32 +108,32 @@ export const changeDatasetCSVEscape = ( anEscape: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.CHANGE_CSV_ESCAPE, + type: DialogActions.CHANGE_CSV_ESCAPE, payload: anEscape, }); export const addNewTag = (aTag: string): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.ADD_DATASET_TAG, + type: DialogActions.ADD_DATASET_TAG, payload: aTag, }); export const resetDialog = (): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.RESET_DIALOG, + type: DialogActions.RESET_DIALOG, // payload is not used payload: false, }); export const setSelectedFiles = (files: File[]): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.CHANGE_DATASET_FILES, + type: DialogActions.CHANGE_DATASET_FILES, payload: files, }); export const clickOnDatasetTag = (aTag: string): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActionTypes.CLICK_ON_DATASET_TAG, + type: DialogActions.CLICK_ON_DATASET_TAG, payload: aTag, }); diff --git a/app/src/store/actions/actionTypes.ts b/app/src/store/actions/actionTypes.ts index 0ab2241b..cca93c03 100644 --- a/app/src/store/actions/actionTypes.ts +++ b/app/src/store/actions/actionTypes.ts @@ -6,7 +6,8 @@ export const DatasetStoreActionTypes = { }; export const DatasetDialogStoreActionTypes = { - OPEN_DIALOG: 'ADD_DATASET_DIALOG_STORE_ACTION-OPEN_DIALOG', + OPEN_ADD_DIALOG: 'ADD_DATASET_DIALOG_STORE_ACTION-OPEN_ADD_DIALOG', + OPEN_CHANGE_DIALOG: 'ADD_DATASET_DIALOG_STORE_ACTION-OPEN_CHANGE_DIALOG', CLOSE_DIALOG: 'ADD_DATASET_DIALOG_STORE_ACTION-CLOSE_DIALOG', RESET_DIALOG: 'ADD_DATASET_DIALOG_STORE_ACTION-RESET_DIALOG', CHANGE_DATASET_NAME: 'ADD_DATASET_DIALOG_STORE_ACTION-CHANGE_DATASET_NAME', diff --git a/app/src/store/models.ts b/app/src/store/models.ts index a1ff3ce2..9881fee2 100644 --- a/app/src/store/models.ts +++ b/app/src/store/models.ts @@ -19,6 +19,8 @@ export interface AlgorithmDialogStore { } export interface DatasetDialogStore { + datasetId: number | null; + dialogType: DialogTypes; isOpen: boolean; datasetName: string; datasetDescription: string; diff --git a/app/src/store/reducers/DatasetDialogReducer.ts b/app/src/store/reducers/DatasetDialogReducer.ts index dbebe629..04f19ddc 100644 --- a/app/src/store/reducers/DatasetDialogReducer.ts +++ b/app/src/store/reducers/DatasetDialogReducer.ts @@ -1,12 +1,17 @@ import { uniq } from 'lodash'; -import { DatasetDialogStoreActionTypes as DialogActionTypes } from 'store/actions/actionTypes'; +import { DatasetDialogStoreActionTypes as DialogActions } from 'store/actions/actionTypes'; import { SnowmanAction } from 'store/messages'; import { DatasetDialogStore } from 'store/models'; import { DatasetTypes } from 'types/DatasetTypes'; import { toggleSelectionArrayMultipleSelect } from 'utils/toggleSelectionArray'; +import { Dataset } from '../../api'; +import { DialogTypes } from '../../types/DialogTypes'; + const initialState: DatasetDialogStore = { isOpen: false, + datasetId: null, + dialogType: DialogTypes.ADD_DIALOG, availableTags: [], datasetName: '', datasetDescription: '', @@ -25,65 +30,75 @@ export const DatasetDialogReducer = ( action: SnowmanAction ): DatasetDialogStore => { switch (action.type) { - case DialogActionTypes.OPEN_DIALOG: + case DialogActions.OPEN_ADD_DIALOG: return { ...state, availableTags: action.payload as string[], + dialogType: DialogTypes.ADD_DIALOG, + isOpen: true, + }; + case DialogActions.OPEN_CHANGE_DIALOG: + return { + ...state, isOpen: true, + dialogType: DialogTypes.CHANGE_DIALOG, + datasetId: (action.payload as Dataset).id, + datasetName: (action.payload as Dataset).name, + datasetDescription: (action.payload as Dataset).description ?? '', }; - case DialogActionTypes.CLOSE_DIALOG: + case DialogActions.CLOSE_DIALOG: return { ...state, isOpen: false, }; - case DialogActionTypes.RESET_DIALOG: + case DialogActions.RESET_DIALOG: return initialState; - case DialogActionTypes.CHANGE_DATASET_NAME: + case DialogActions.CHANGE_DATASET_NAME: return { ...state, datasetName: action.payload as string, }; - case DialogActionTypes.CHANGE_DATASET_DESCRIPTION: + case DialogActions.CHANGE_DATASET_DESCRIPTION: return { ...state, datasetDescription: action.payload as string, }; - case DialogActionTypes.CHANGE_DATASET_TYPE: + case DialogActions.CHANGE_DATASET_TYPE: return { ...state, datasetType: action.payload as DatasetTypes, }; - case DialogActionTypes.CHANGE_DATASET_FILES: + case DialogActions.CHANGE_DATASET_FILES: return { ...state, selectedFiles: action.payload as File[], }; - case DialogActionTypes.CHANGE_DATASET_LENGTH: + case DialogActions.CHANGE_DATASET_LENGTH: return { ...state, datasetLength: action.payload as number, }; - case DialogActionTypes.CHANGE_CSV_ID_COLUMN: + case DialogActions.CHANGE_CSV_ID_COLUMN: return { ...state, csvIdColumn: action.payload as string, }; - case DialogActionTypes.CHANGE_CSV_SEPARATOR: + case DialogActions.CHANGE_CSV_SEPARATOR: return { ...state, csvSeparator: action.payload as string, }; - case DialogActionTypes.CHANGE_CSV_QUOTE: + case DialogActions.CHANGE_CSV_QUOTE: return { ...state, csvQuote: action.payload as string, }; - case DialogActionTypes.CHANGE_CSV_ESCAPE: + case DialogActions.CHANGE_CSV_ESCAPE: return { ...state, csvEscape: action.payload as string, }; - case DialogActionTypes.CLICK_ON_DATASET_TAG: + case DialogActions.CLICK_ON_DATASET_TAG: return { ...state, selectedTags: toggleSelectionArrayMultipleSelect( @@ -91,7 +106,7 @@ export const DatasetDialogReducer = ( action.payload as string ), }; - case DialogActionTypes.ADD_DATASET_TAG: + case DialogActions.ADD_DATASET_TAG: return { ...state, availableTags: uniq([...state.availableTags, action.payload as string]), From b53505d5d784fc6c7572a7b0bbcacbedbb12720f Mon Sep 17 00:00:00 2001 From: phpfs Date: Mon, 1 Mar 2021 14:58:39 +0100 Subject: [PATCH 43/73] Basic implementation of tags --- app/src/store/reducers/DatasetDialogReducer.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/src/store/reducers/DatasetDialogReducer.ts b/app/src/store/reducers/DatasetDialogReducer.ts index 04f19ddc..072e22bc 100644 --- a/app/src/store/reducers/DatasetDialogReducer.ts +++ b/app/src/store/reducers/DatasetDialogReducer.ts @@ -33,7 +33,7 @@ export const DatasetDialogReducer = ( case DialogActions.OPEN_ADD_DIALOG: return { ...state, - availableTags: action.payload as string[], + availableTags: action.payload as string[], // Todo: get this from root reducer? dialogType: DialogTypes.ADD_DIALOG, isOpen: true, }; @@ -45,12 +45,19 @@ export const DatasetDialogReducer = ( datasetId: (action.payload as Dataset).id, datasetName: (action.payload as Dataset).name, datasetDescription: (action.payload as Dataset).description ?? '', + selectedTags: (action.payload as Dataset).tags ?? [], + availableTags: [], // Todo: get this from root reducer? }; case DialogActions.CLOSE_DIALOG: - return { - ...state, - isOpen: false, - }; + if (state.dialogType === DialogTypes.ADD_DIALOG) + // Only keep current state for add dialog + return { + ...state, + isOpen: false, + }; + else { + return initialState; + } case DialogActions.RESET_DIALOG: return initialState; case DialogActions.CHANGE_DATASET_NAME: From 2c73d3b43fdc517ac2b6a4215191cec11baecd83 Mon Sep 17 00:00:00 2001 From: phpfs Date: Mon, 1 Mar 2021 15:05:06 +0100 Subject: [PATCH 44/73] Minor improvements --- app/src/store/reducers/DatasetDialogReducer.ts | 1 + app/src/types/DatasetTypes.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/store/reducers/DatasetDialogReducer.ts b/app/src/store/reducers/DatasetDialogReducer.ts index 072e22bc..ba340f22 100644 --- a/app/src/store/reducers/DatasetDialogReducer.ts +++ b/app/src/store/reducers/DatasetDialogReducer.ts @@ -45,6 +45,7 @@ export const DatasetDialogReducer = ( datasetId: (action.payload as Dataset).id, datasetName: (action.payload as Dataset).name, datasetDescription: (action.payload as Dataset).description ?? '', + datasetLength: (action.payload as Dataset).numberOfRecords ?? 0, selectedTags: (action.payload as Dataset).tags ?? [], availableTags: [], // Todo: get this from root reducer? }; diff --git a/app/src/types/DatasetTypes.ts b/app/src/types/DatasetTypes.ts index fbfbdcaa..194ed8b9 100644 --- a/app/src/types/DatasetTypes.ts +++ b/app/src/types/DatasetTypes.ts @@ -1,4 +1,4 @@ export enum DatasetTypes { - full = 'Full records', - skeleton = 'Skeleton only', + full = 'Actual data records', + skeleton = 'Record count only', } From f55bd3a0e771edb9a4f5fb95e019ba102f46c6af Mon Sep 17 00:00:00 2001 From: phpfs Date: Mon, 1 Mar 2021 15:06:38 +0100 Subject: [PATCH 45/73] Rename to submit --- app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx | 4 ++-- app/src/components/AlgorithmDialog/AlgorithmDialog.tsx | 2 +- app/src/components/AlgorithmDialog/AlgorithmDialogProps.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx b/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx index 5b6e8ee3..b9f9eace 100644 --- a/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx +++ b/app/src/components/AlgorithmDialog/AlgorithmDialog.View.tsx @@ -24,7 +24,7 @@ const AlgorithmDialogView = ({ isOpen, isAddDialog, clickOnCancel, - clickOnAdd, + clickOnSubmit, changeAlgorithmDescription, changeAlgorithmName, closeDialog, @@ -55,7 +55,7 @@ const AlgorithmDialogView = ({
- + dispatch(changeAlgorithmDescription(event.detail.value as string)), - clickOnAdd(): void { + clickOnSubmit(): void { dispatch(addOrUpdateAlgorithm()).then(); }, }); diff --git a/app/src/components/AlgorithmDialog/AlgorithmDialogProps.ts b/app/src/components/AlgorithmDialog/AlgorithmDialogProps.ts index d62e746c..40f4f19b 100644 --- a/app/src/components/AlgorithmDialog/AlgorithmDialogProps.ts +++ b/app/src/components/AlgorithmDialog/AlgorithmDialogProps.ts @@ -12,7 +12,7 @@ export interface AlgorithmDialogDispatchProps { closeDialog(): void; changeAlgorithmName(event: IonChangeEvent): void; changeAlgorithmDescription(event: IonChangeEvent): void; - clickOnAdd(): void; //Todo: Rename + clickOnSubmit(): void; } export type AlgorithmDialogProps = AlgorithmDialogStateProps & From 7e64ef603cda10eae4598ddb8e974c8bf98fb5d1 Mon Sep 17 00:00:00 2001 From: phpfs Date: Mon, 1 Mar 2021 15:08:09 +0100 Subject: [PATCH 46/73] Rename to submit (2) --- app/src/components/DatasetDialog/DatasetDialog.View.tsx | 4 ++-- app/src/components/DatasetDialog/DatasetDialog.tsx | 3 ++- app/src/components/DatasetDialog/DatasetDialogProps.ts | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/components/DatasetDialog/DatasetDialog.View.tsx b/app/src/components/DatasetDialog/DatasetDialog.View.tsx index 5276bea1..184b1f95 100644 --- a/app/src/components/DatasetDialog/DatasetDialog.View.tsx +++ b/app/src/components/DatasetDialog/DatasetDialog.View.tsx @@ -53,7 +53,7 @@ const DatasetDialogView = ({ changeDatasetDescription, clickOnATag, addNewTagCallback, - addDataset, + clickOnSubmit, }: DatasetDialogProps): JSX.Element => ( diff --git a/app/src/components/DatasetDialog/DatasetDialog.tsx b/app/src/components/DatasetDialog/DatasetDialog.tsx index 5f7fb722..bf6810fb 100644 --- a/app/src/components/DatasetDialog/DatasetDialog.tsx +++ b/app/src/components/DatasetDialog/DatasetDialog.tsx @@ -120,7 +120,8 @@ const mapDispatchToProps = ( addNewTagCallback(newTagValue: string): void { (dispatch as SnowmanDispatch)(addNewTag(newTagValue)); }, - addDataset(): void { + clickOnSubmit(): void { + // Todo: Rather use addOrUpdateDataset() dispatch(addNewDataset()).then(); }, }); diff --git a/app/src/components/DatasetDialog/DatasetDialogProps.ts b/app/src/components/DatasetDialog/DatasetDialogProps.ts index da449095..226f5536 100644 --- a/app/src/components/DatasetDialog/DatasetDialogProps.ts +++ b/app/src/components/DatasetDialog/DatasetDialogProps.ts @@ -15,7 +15,7 @@ export interface DatasetDialogDispatchProps { changeCsvQuote(event: IonChangeEvent): void; changeCsvEscape(event: IonChangeEvent): void; clickOnATag(aTag: string): void; - addDataset(): void; + clickOnSubmit(): void; changeSelectedDatasetFiles(event: ChangeEvent): void; } From 36fb30774f1b1ef1b4f4c03a1af2d19483821ce9 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Mon, 1 Mar 2021 17:05:07 +0100 Subject: [PATCH 47/73] Add guide on how to update the database schema --- docs/contributing/knowledge.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 docs/contributing/knowledge.md diff --git a/docs/contributing/knowledge.md b/docs/contributing/knowledge.md new file mode 100644 index 00000000..0a386184 --- /dev/null +++ b/docs/contributing/knowledge.md @@ -0,0 +1,13 @@ +# Contributing Knowledge + +This document contains information about how to extend the codebase in specific cases. For general contributing guidelines see [here](https://github.com/HPI-Information-Systems/snowman#Contributing). + +## Updating the database schema + +1. create a folder for the new version in `api/database/schemas` named like `v1` +2. copy all files from the previous version +3. make necessary changes +4. edit the `migration.ts` file and update `version`, `successor` and `migrateFromLastVersion` + - make sure the `version` is unique + - `migrateFromLastVersion` contains the migration logic +5. update `api/database/schemas/index.ts` and export the new version as `latest` From a622d03866b20419726de6ed2370f53cae093fd0 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Mon, 1 Mar 2021 17:11:02 +0100 Subject: [PATCH 48/73] Fix markdown linter issues, move image to center on docs welcome page --- README.md | 6 +++--- docs/basic_usage/datasets.md | 10 ++-------- docs/basic_usage/experiments.md | 19 +++++++------------ docs/basic_usage/introduction.md | 10 +++++----- docs/basic_usage/matching_solutions.md | 2 +- docs/basic_usage/workflow.md | 5 +---- docs/dev_setup/introduction.md | 2 +- docs/index.md | 7 +++---- 8 files changed, 23 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 30d4da2b..c5d714a3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ #### [![General](https://github.com/HPI-Information-Systems/snowman/actions/workflows/general.yml/badge.svg)](https://github.com/HPI-Information-Systems/snowman/actions/workflows/general.yml) [![Documentation](https://github.com/HPI-Information-Systems/snowman/actions/workflows/docs.yml/badge.svg)](https://github.com/HPI-Information-Systems/snowman/actions/workflows/docs.yml) [![Release](https://github.com/HPI-Information-Systems/snowman/actions/workflows/release.yml/badge.svg)](https://github.com/HPI-Information-Systems/snowman/actions/workflows/release.yml) Comparing data matching algorithms is still an unsolved topic in both industry and research. -With snowman, developers and researchers will be able to compare the performance of different data matching solutions or improve new algorithms. +With snowman, developers and researchers will be able to compare the performance of different data matching solutions or improve new algorithms. Besides traditional metrics, the tool will also consider economic aspects like Soft KPIs. _This tool is developed as part of a bachelor's project in collaboration with SAP SE._ @@ -17,7 +17,7 @@ In Q1 and Q2 of 2021, we aim to reach the following milestones: [x] **Milestone 1:** Ability to add/delete datasets, experiments and matching solutions; binary comparison and basic behavior analysis; executable on all major platform [ ] **Milestone 2:** Compare more than two experiments and create new experiments based on results; survey Soft KPIs, allow comparison based on KPIs -[ ] **Milestone 3:** Allow dynamic analysis based on threshold values; complex evaluations like precision/recall diagrams; group/cluster view for result sets +[ ] **Milestone 3:** Allow dynamic analysis based on threshold values; complex evaluations like precision/recall diagrams; group/cluster view for result sets The precise progress is tracked through Github issues and project boards. Please get in touch in case you want a special feature included :) @@ -28,7 +28,7 @@ _After reaching milestone 3, we plan to continue to work on further features whi Contribution guidelines will follow soon. Until then, feel free to open an issue to report a bug or request a feature. In case you want to contribute code, please first open an associated issue and afterwards a pull request containing the proposed solution. -## Documentation +## Documentation Please see our documentation for further information: [Snowman Docs](https://hpi-information-systems.github.io/snowman/) diff --git a/docs/basic_usage/datasets.md b/docs/basic_usage/datasets.md index a0acd4f4..5a6a4c07 100644 --- a/docs/basic_usage/datasets.md +++ b/docs/basic_usage/datasets.md @@ -7,16 +7,10 @@ Each workflow occurs upon a single dataset. As a first step, you'll have to spec ## Add a dataset 1. Open page "Datasets" from the sidebar on the left. - 2. Add a new dataset with the "+" button in the lower left corner of the screen. - 3. Specify a short identifying name and a comprehensive description. - -4. If you do not want to upload the complete dataset, select "Skeleton only" as contents and specify the total amount of tuples by -hand. This is important to be able to still calculate metrics. - +4. If you do not want to upload the complete dataset, select "Skeleton only" as contents and specify the total amount of tuples by hand. This is important to be able to still calculate metrics. 5. If you want to upload the dataset, select "Full upload" as contents, specify the csv parameters and select a file to upload. - 6. Click on "Add dataset" - this process may take several minutes to complete as indexes have to be created! ### Upload failed @@ -25,4 +19,4 @@ Since creating the dataset and uploading its contents are two separate steps, yo that the file upload failed. In this case, the dataset itself was created (most likely) empty with no records. In this case, simply delete the dataset and start over again. -Future versions will allow you to further differentiate and change the records later on. \ No newline at end of file +Future versions will allow you to further differentiate and change the records later on. diff --git a/docs/basic_usage/experiments.md b/docs/basic_usage/experiments.md index eff84275..5703d6b5 100644 --- a/docs/basic_usage/experiments.md +++ b/docs/basic_usage/experiments.md @@ -9,17 +9,12 @@ a list of configuration parameters, or the time spent labeling data. ## Add an experiment -0. Select a dataset. - -1. Open page "Experiments" from the sidebar on the left. - -2. Add a new experiment with the "+" button in the lower left corner of the screen. - -3. Specify a short and concise name, as well as a detailed description. - -4. Select a file containing the result set and choose the correct import format. _(see below)_ - -5. Click on "Add experiment" - this process may take several minutes to complete as indexes have to be created! +1. Select a dataset. +2. Open page "Experiments" from the sidebar on the left. +3. Add a new experiment with the "+" button in the lower left corner of the screen. +4. Specify a short and concise name, as well as a detailed description. +5. Select a file containing the result set and choose the correct import format. _(see below)_ +6. Click on "Add experiment" - this process may take several minutes to complete as indexes have to be created! ### Upload failed @@ -54,7 +49,7 @@ The csv optionally can have a column named "prediction" which contains a `1` in Following this, more columns may be specified with arbitrary content. See the following example: -``` csv +```csv p1,p2,prediction,feat1,feat2,feat3,sum 2,1,1,0.3,0.4,0.4,2 1,2,1,0.3,0.4,0.4,2 diff --git a/docs/basic_usage/introduction.md b/docs/basic_usage/introduction.md index c9c15188..b45e9160 100644 --- a/docs/basic_usage/introduction.md +++ b/docs/basic_usage/introduction.md @@ -14,11 +14,11 @@ Welcome to the Snowman Data Matching Benchmark - this guide aims to help you get If you select local usage, a folder will be created within your home directory which houses our data store. This exact location of this folder is platform specific. -Platform | Folder ----------|------- -macOS | ~/Library/Application Support/snowman-wrapper -Windows | C:\Users\\AppData\Local\snowman-wrapper -Linux | ~/.config/snowman-wrapper +| Platform | Folder | +| -------- | --------------------------------------------------- | +| macOS | ~/Library/Application Support/snowman-wrapper | +| Windows | C:\\Users\\\\\AppData\\Local\\snowman-wrapper | +| Linux | ~/.config/snowman-wrapper | If you changed something about your environment, these paths may be different. Rest assured that Snowman will not touch any other folders or files :) diff --git a/docs/basic_usage/matching_solutions.md b/docs/basic_usage/matching_solutions.md index a7dbd226..5ac2c64e 100644 --- a/docs/basic_usage/matching_solutions.md +++ b/docs/basic_usage/matching_solutions.md @@ -22,4 +22,4 @@ Imagine you are running the Magellan data matching tool (open-source). It allows it operates and thereby a lot of customization is possible. For this use case, you'd only create one matching solution "Magellan" within the tool (use a general description, e.g. ML-based open-source approach). For each configuration, you can -afterwards create a new experiment containing information on the configuration within its description. \ No newline at end of file +afterwards create a new experiment containing information on the configuration within its description. diff --git a/docs/basic_usage/workflow.md b/docs/basic_usage/workflow.md index 6bfb4858..35a9aa47 100644 --- a/docs/basic_usage/workflow.md +++ b/docs/basic_usage/workflow.md @@ -9,16 +9,13 @@ comparison setup. ## Basic setup 1. Select a dataset. - 2. Select multiple experiments. - 3. Decide upon a benchmark to run. _(Currently "Binary Comparison" is the only option and therefore selected by default!)_ ## Assumptions -- Each workflow occurs on exactly one dataset. It is not possible to compare different datasets it later steps of a +- Each workflow occurs on exactly one dataset. It is not possible to compare different datasets it later steps of a workflow. - - Changing the datasets after e.g. experiments where selected will reset the workflow starting from scratch with the new dataset. diff --git a/docs/dev_setup/introduction.md b/docs/dev_setup/introduction.md index d9b514dc..e6bbda63 100644 --- a/docs/dev_setup/introduction.md +++ b/docs/dev_setup/introduction.md @@ -28,4 +28,4 @@ To wrap everything and build a single binary, electron is used. This also includ ## Backend API -The backend can also be reached directly. See our guide "REST API" for details. \ No newline at end of file +The backend can also be reached directly. See our guide "REST API" for details. diff --git a/docs/index.md b/docs/index.md index f0f2bb5f..82bf9d30 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,8 +1,7 @@ -# Welcome to Snowman! - -
+

-

+

+

Welcome to Snowman

Have you ever gotten a newsletter twice? Probably - finding duplicates in data is a pretty difficult problem. Many different algorithms exist with different approaches and tradeoffs. However, not only finding duplicates is difficult From e1aeecc8b5d47cdb324a333c92a2a1a34d31f438 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Mon, 1 Mar 2021 17:26:50 +0100 Subject: [PATCH 49/73] add docs on adding new experiment formats --- docs/contributing/knowledge.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/contributing/knowledge.md b/docs/contributing/knowledge.md index 0a386184..c117a7e2 100644 --- a/docs/contributing/knowledge.md +++ b/docs/contributing/knowledge.md @@ -2,6 +2,16 @@ This document contains information about how to extend the codebase in specific cases. For general contributing guidelines see [here](https://github.com/HPI-Information-Systems/snowman#Contributing). +## Adding new experiment formats + +1. subclass [`ExperimentInserter`](https://github.com/HPI-Information-Systems/snowman/blob/5f9ea889c3e2b273e6da3c584eb7baae438e6683/wrapper/src/api/providers/experiment/experimentProvider/file/experimentInserter.ts) and overwrite the abstract methods + - add duplicates via [`addDuplicate`](https://github.com/HPI-Information-Systems/snowman/blob/5f9ea889c3e2b273e6da3c584eb7baae438e6683/wrapper/src/api/providers/experiment/experimentProvider/file/experimentInserter.ts#L38) + - we also provide a [`CSVInserter`](https://github.com/HPI-Information-Systems/snowman/blob/5f9ea889c3e2b273e6da3c584eb7baae438e6683/wrapper/src/api/providers/experiment/experimentProvider/file/csvInserter.ts) which can be subclassed for csv experiment formats +2. give the format a unique name and register it with the server by adding it to [this](https://github.com/HPI-Information-Systems/snowman/blob/5f9ea889c3e2b273e6da3c584eb7baae438e6683/wrapper/src/api/providers/experiment/experimentProvider/file/index.ts#L16) map. +3. add the format to the [api specification](https://github.com/HPI-Information-Systems/snowman/blob/5f9ea889c3e2b273e6da3c584eb7baae438e6683/docs/api_specification.yaml#L452-L455) (**under docs**) +4. regenerate the client api and generated api specification + - the server types do not need to be updated + ## Updating the database schema 1. create a folder for the new version in `api/database/schemas` named like `v1` From c30ad5de285e0aa468399c6c033673c7bd771667 Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Tue, 2 Mar 2021 10:01:15 +0100 Subject: [PATCH 50/73] Remove soft KPI part --- docs/api_specification.yaml | 45 +------------------------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/docs/api_specification.yaml b/docs/api_specification.yaml index e0b6d5ce..a508e0e1 100644 --- a/docs/api_specification.yaml +++ b/docs/api_specification.yaml @@ -74,21 +74,7 @@ paths: $ref: "#/components/schemas/AlgorithmId" 400: description: Bad Request - /algorithms/soft-kpi-questions: - get: - tags: - - Algorithm - summary: Returns all soft-kpi questions - operationId: getSoftKPIQuestions - responses: - 200: - description: OK - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/SoftKPIQuestion" + /algorithms/{algorithmId}: parameters: - name: algorithmId @@ -625,31 +611,6 @@ components: $ref: "#/components/schemas/ExperimentIntersectionConfiguration" required: true schemas: - #### SOFTKPIS #### - SoftKPIQuestion: - type: object - properties: - id: - type: string - example: tco - question: - type: string - example: What is the total cost of ownership of this matching solution? - format: - type: string - enum: [string, number] - SoftKPIAnswer: - type: object - properties: - id: - type: string - example: tco - answer: - type: string - required: - - id - - answer - #### ALGORITHM #### AlgorithmId: type: integer @@ -666,10 +627,6 @@ components: description: type: string example: This is an open source python library - softKPIs: - type: array - items: - $ref: "#/components/schemas/SoftKPIAnswer" Algorithm: allOf: - type: object From fc07a7e63be5d68a49fd1d51e227ed7176d18d0b Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Tue, 2 Mar 2021 10:14:06 +0100 Subject: [PATCH 51/73] Rename successor -> predecessor --- wrapper/src/api/database/schemas/migration.spec.ts | 10 +++++----- wrapper/src/api/database/schemas/schemaVersion.ts | 6 +++--- wrapper/src/api/database/schemas/v0/migration.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/wrapper/src/api/database/schemas/migration.spec.ts b/wrapper/src/api/database/schemas/migration.spec.ts index 5334c72c..71342e3b 100644 --- a/wrapper/src/api/database/schemas/migration.spec.ts +++ b/wrapper/src/api/database/schemas/migration.spec.ts @@ -4,28 +4,28 @@ import { latest } from '.'; import { SchemaVersion } from './schemaVersion'; abstract class TestSchemaVersion extends SchemaVersion { - abstract successor?: TestSchemaVersion; + abstract predecessor?: TestSchemaVersion; migrated = false; protected migrateFromLastVersion(): void { this.migrated = true; } reset() { this.migrated = false; - this.successor?.reset(); + this.predecessor?.reset(); } } const TestSchemaV0 = new (class extends TestSchemaVersion { version = 0; - successor = undefined; + predecessor = undefined; })(); const TestSchemaV1 = new (class extends TestSchemaVersion { version = 1; - successor = TestSchemaV0; + predecessor = TestSchemaV0; })(); const TestSchemaV2 = new (class extends TestSchemaVersion { version = 2; - successor = TestSchemaV1; + predecessor = TestSchemaV1; })(); describe('SchemaVersion', () => { diff --git a/wrapper/src/api/database/schemas/schemaVersion.ts b/wrapper/src/api/database/schemas/schemaVersion.ts index 70c9e8c3..7faf3025 100644 --- a/wrapper/src/api/database/schemas/schemaVersion.ts +++ b/wrapper/src/api/database/schemas/schemaVersion.ts @@ -2,14 +2,14 @@ import { databaseBackend } from '../setup/backend'; export abstract class SchemaVersion { abstract readonly version: number; - abstract readonly successor?: SchemaVersion; + abstract readonly predecessor?: SchemaVersion; protected abstract migrateFromLastVersion(): void; migrate(fromVersion: number): void { if (this.version !== fromVersion) { databaseBackend().transaction(() => { - if (this.successor) { - this.successor.migrate(fromVersion); + if (this.predecessor) { + this.predecessor.migrate(fromVersion); this.migrateFromLastVersion(); this.setVersion(); } else { diff --git a/wrapper/src/api/database/schemas/v0/migration.ts b/wrapper/src/api/database/schemas/v0/migration.ts index 54cb6336..20f42aed 100644 --- a/wrapper/src/api/database/schemas/v0/migration.ts +++ b/wrapper/src/api/database/schemas/v0/migration.ts @@ -2,7 +2,7 @@ import { SchemaVersion } from '../schemaVersion'; export class SchemaV0 extends SchemaVersion { readonly version = 0; - readonly successor?: SchemaVersion | undefined = undefined; + readonly predecessor?: SchemaVersion | undefined = undefined; protected migrateFromLastVersion(): void { throw new Error( From 3d0c2070339505976adadef05a60ff18a33bfd84 Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Tue, 2 Mar 2021 10:20:37 +0100 Subject: [PATCH 52/73] Handle isAddDialog on View perspective --- app/src/components/DatasetDialog/DatasetDialog.View.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/components/DatasetDialog/DatasetDialog.View.tsx b/app/src/components/DatasetDialog/DatasetDialog.View.tsx index 184b1f95..75f67da6 100644 --- a/app/src/components/DatasetDialog/DatasetDialog.View.tsx +++ b/app/src/components/DatasetDialog/DatasetDialog.View.tsx @@ -28,6 +28,7 @@ import { DatasetTypes } from 'types/DatasetTypes'; const DatasetDialogView = ({ isOpen, + isAddDialog, isValidForm, closeDialog, clickOnCancel, @@ -56,7 +57,7 @@ const DatasetDialogView = ({ clickOnSubmit, }: DatasetDialogProps): JSX.Element => ( @@ -194,7 +195,7 @@ const DatasetDialogView = ({ disabled={!isValidForm} > - Add + {isAddDialog ? 'Add' : 'Update'}
Date: Tue, 2 Mar 2021 10:39:26 +0100 Subject: [PATCH 53/73] Fix a bug with keys in react-generated DOM --- app/src/components/SideMenu/SideMenu.View.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/components/SideMenu/SideMenu.View.tsx b/app/src/components/SideMenu/SideMenu.View.tsx index 8b8fa90d..c65da2f7 100644 --- a/app/src/components/SideMenu/SideMenu.View.tsx +++ b/app/src/components/SideMenu/SideMenu.View.tsx @@ -46,9 +46,7 @@ const SideMenuView = ({ categoryStructure }: SideMenuProps): JSX.Element => (

{aCategoryItem.key}

{aCategoryItem.selectedOptions.map( (anOption: string): JSX.Element => ( - <> -

{anOption}

- +

{anOption}

) )} From c2b7345382712df418df880d6882605937ab58ae Mon Sep 17 00:00:00 2001 From: Martin Graf Date: Tue, 2 Mar 2021 10:48:54 +0100 Subject: [PATCH 54/73] Implement format --- app/src/api/apis/BenchmarkApi.ts | 13 ++++ app/src/api/models/ExperimentIntersection.ts | 6 +- .../api/models/ExperimentIntersectionCount.ts | 14 +++- .../api/models/ExperimentIntersectionMode.ts | 37 ++++++++++ app/src/api/models/index.ts | 1 + .../BinaryMetricsPage/BinaryMetricsPage.tsx | 26 +++---- docs/api_specification.yaml | 8 ++- wrapper/assets/api_specification.yaml | 68 +++++++++++++------ .../benchmark/baseBenchmarkProvider.ts | 8 ++- .../benchmarkProvider/benchmarkProvider.ts | 4 +- .../evaluator/confusionMatrix/modes/index.ts | 17 ++--- .../evaluator/confusionMatrix/modes/modes.ts | 5 -- .../confusionMatrix/test/testCases.ts | 52 +++++++------- .../evaluator/confusionMatrix/tuples.spec.ts | 8 +-- .../evaluator/confusionMatrix/tuples.ts | 5 +- .../helper/evaluator/evaluator.ts | 8 ++- .../helper/idsToRecords/index.ts | 3 +- .../helper/idsToRecords/noTable.ts | 4 +- .../helper/idsToRecords/withTable.ts | 6 +- .../api/server/services/BenchmarkService.ts | 31 +++++++-- .../server/types/ExperimentIntersection.ts | 4 +- .../types/ExperimentIntersectionCount.ts | 8 ++- .../types/ExperimentIntersectionMode.ts | 24 +++++++ wrapper/src/api/server/types/index.ts | 1 + 24 files changed, 251 insertions(+), 110 deletions(-) create mode 100644 app/src/api/models/ExperimentIntersectionMode.ts delete mode 100644 wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/modes/modes.ts create mode 100644 wrapper/src/api/server/types/ExperimentIntersectionMode.ts diff --git a/app/src/api/apis/BenchmarkApi.ts b/app/src/api/apis/BenchmarkApi.ts index 331b05f7..51162b62 100644 --- a/app/src/api/apis/BenchmarkApi.ts +++ b/app/src/api/apis/BenchmarkApi.ts @@ -21,6 +21,9 @@ import { ExperimentIntersectionCount, ExperimentIntersectionCountFromJSON, ExperimentIntersectionCountToJSON, + ExperimentIntersectionMode, + ExperimentIntersectionModeFromJSON, + ExperimentIntersectionModeToJSON, Metric, MetricFromJSON, MetricToJSON, @@ -28,6 +31,7 @@ import { export interface CalculateExperimentIntersectionCountRequest { requestBody: Array; + mode?: ExperimentIntersectionMode; } export interface CalculateExperimentIntersectionRecordsRequest { @@ -35,6 +39,7 @@ export interface CalculateExperimentIntersectionRecordsRequest { startAt?: number; limit?: number; sortBy?: string; + mode?: ExperimentIntersectionMode; } export interface GetBinaryMetricsRequest { @@ -62,6 +67,10 @@ export class BenchmarkApi extends runtime.BaseAPI { const queryParameters: any = {}; + if (requestParameters.mode !== undefined) { + queryParameters['mode'] = requestParameters.mode; + } + const headerParameters: runtime.HTTPHeaders = {}; headerParameters['Content-Type'] = 'application/json'; @@ -109,6 +118,10 @@ export class BenchmarkApi extends runtime.BaseAPI { queryParameters['sortBy'] = requestParameters.sortBy; } + if (requestParameters.mode !== undefined) { + queryParameters['mode'] = requestParameters.mode; + } + const headerParameters: runtime.HTTPHeaders = {}; headerParameters['Content-Type'] = 'application/json'; diff --git a/app/src/api/models/ExperimentIntersection.ts b/app/src/api/models/ExperimentIntersection.ts index 7cc16d5b..d787006b 100644 --- a/app/src/api/models/ExperimentIntersection.ts +++ b/app/src/api/models/ExperimentIntersection.ts @@ -27,10 +27,10 @@ export interface ExperimentIntersection { header: Array; /** * - * @type {Array>>} + * @type {Array>} * @memberof ExperimentIntersection */ - data: Array>>; + data: Array>; } export function ExperimentIntersectionFromJSON(json: any): ExperimentIntersection { @@ -42,7 +42,7 @@ export function ExperimentIntersectionFromJSONTyped(json: any, ignoreDiscriminat return json; } return { - + 'header': json['header'], 'data': json['data'], }; diff --git a/app/src/api/models/ExperimentIntersectionCount.ts b/app/src/api/models/ExperimentIntersectionCount.ts index 659a2604..dfd0b5b9 100644 --- a/app/src/api/models/ExperimentIntersectionCount.ts +++ b/app/src/api/models/ExperimentIntersectionCount.ts @@ -24,7 +24,13 @@ export interface ExperimentIntersectionCount { * @type {number} * @memberof ExperimentIntersectionCount */ - count: number; + numberRows: number; + /** + * + * @type {number} + * @memberof ExperimentIntersectionCount + */ + numberGroups: number; } export function ExperimentIntersectionCountFromJSON(json: any): ExperimentIntersectionCount { @@ -37,7 +43,8 @@ export function ExperimentIntersectionCountFromJSONTyped(json: any, ignoreDiscri } return { - 'count': json['count'], + 'numberRows': json['numberRows'], + 'numberGroups': json['numberGroups'], }; } @@ -50,7 +57,8 @@ export function ExperimentIntersectionCountToJSON(value?: ExperimentIntersection } return { - 'count': value.count, + 'numberRows': value.numberRows, + 'numberGroups': value.numberGroups, }; } diff --git a/app/src/api/models/ExperimentIntersectionMode.ts b/app/src/api/models/ExperimentIntersectionMode.ts new file mode 100644 index 00000000..78237996 --- /dev/null +++ b/app/src/api/models/ExperimentIntersectionMode.ts @@ -0,0 +1,37 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Snowman API + * _This document describes the REST API of the snowman data matching benchmark tool._ Comparing data matching algorithms is still an unsolved topic in both industry and research. With snowman, developers and researchers will be able to compare the performance of different data matching solutions or improve new algorithms. + * + * The version of the OpenAPI document: 1.0.0 + * Contact: snowman@groups.sap.com + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @enum {string} + */ +export enum ExperimentIntersectionMode { + Pairs = 'PAIRS', + Clusters = 'CLUSTERS', + Investigative = 'INVESTIGATIVE' +} + +export function ExperimentIntersectionModeFromJSON(json: any): ExperimentIntersectionMode { + return ExperimentIntersectionModeFromJSONTyped(json, false); +} + +export function ExperimentIntersectionModeFromJSONTyped(json: any, ignoreDiscriminator: boolean): ExperimentIntersectionMode { + return json as ExperimentIntersectionMode; +} + +export function ExperimentIntersectionModeToJSON(value?: ExperimentIntersectionMode | null): any { + return value as any; +} + diff --git a/app/src/api/models/index.ts b/app/src/api/models/index.ts index 16a6bd8f..fec2a8e0 100644 --- a/app/src/api/models/index.ts +++ b/app/src/api/models/index.ts @@ -8,5 +8,6 @@ export * from './Experiment'; export * from './ExperimentAllOf'; export * from './ExperimentIntersection'; export * from './ExperimentIntersectionCount'; +export * from './ExperimentIntersectionMode'; export * from './ExperimentValues'; export * from './Metric'; diff --git a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.tsx b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.tsx index a4dcb10e..e1bccee2 100644 --- a/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.tsx +++ b/app/src/pages/BinaryMetricsPage/BinaryMetricsPage.tsx @@ -1,6 +1,5 @@ import { ExperimentIntersection } from 'api'; import { ColumnDescriptor } from 'components/DataViewer/ColumnDescriptor'; -import { flattenDeep } from 'lodash'; import { BinaryMetricsPageView } from 'pages/BinaryMetricsPage/BinaryMetricsPage.View'; import { BinaryMetricsDispatchProps, @@ -39,24 +38,19 @@ const mapStateToProps = (state: Store): BinaryMetricsDispatchProps => ({ ], selectedMetricsTuplesCategory: state.MetricsStore.selectedDataView, dataViewerTuples: ((): unknown[] => { - const group = getTuplesByTuplesCategory( + const tuples = getTuplesByTuplesCategory( state.MetricsStore, state.MetricsStore.selectedDataView ); - if (group !== undefined) { - return flattenDeep( - group.data.map((aCluster: string[][]): unknown[] => [ - ...aCluster.map((aRow: string[]): unknown => { - const rowObject: Record = {}; - aRow.forEach((value: string, index: number): void => { - rowObject[group.header[index]] = value; - }); - return rowObject; - }), - // we insert an empty line at the end of a cluster - {}, - ]) - ); + if (tuples !== undefined) { + const { header, data: rows } = tuples; + return rows.map((row) => { + const rowObject: Record = {}; + row.forEach((value: string, index: number): void => { + rowObject[header[index]] = value; + }); + return rowObject; + }); } return []; })(), diff --git a/docs/api_specification.yaml b/docs/api_specification.yaml index a508e0e1..23d1997e 100644 --- a/docs/api_specification.yaml +++ b/docs/api_specification.yaml @@ -787,9 +787,15 @@ components: example: ["id", "testColumn1", "testColumn2"] data: type: array + example: + [ + ["1", "test value 1", "test value 2"], + ["2", "test value 1", "test value 2"], + [], + ["3", "other", "group"], + ] items: type: array - nullable: true example: ["1", "test value 1", "test value 2"] items: type: string diff --git a/wrapper/assets/api_specification.yaml b/wrapper/assets/api_specification.yaml index e23bb313..d24a12e8 100644 --- a/wrapper/assets/api_specification.yaml +++ b/wrapper/assets/api_specification.yaml @@ -703,6 +703,13 @@ paths: example: similarity type: string style: form + - explode: true + in: query + name: mode + required: false + schema: + $ref: "#/components/schemas/ExperimentIntersectionMode" + style: form requestBody: $ref: "#/components/requestBodies/ExperimentIntersectionConfiguration" responses: @@ -728,6 +735,14 @@ paths: intersects multiple experiments and returns the counts of the number of records. This can be used to calculate the confusion-matrix operationId: calculateExperimentIntersectionCount + parameters: + - explode: true + in: query + name: mode + required: false + schema: + $ref: "#/components/schemas/ExperimentIntersectionMode" + style: form requestBody: $ref: "#/components/requestBodies/ExperimentIntersectionConfiguration" responses: @@ -858,7 +873,7 @@ components: - 0 - 1 value: 0.75 - info: The meaningfulness of this metric is discussed in the research community. + info: The meaningfulness of the f1 score is debated in the research community. infoLink: https://link.springer.com/article/10.1007/s11222-017-9746-6 properties: name: @@ -868,9 +883,7 @@ components: example: \frac{truePositives}{truePositives + falsePositives} type: string info: - example: - The meaningfulness of this metric is discussed in the research - community. + example: The meaningfulness of the f1 score is debated in the research community. type: string infoLink: example: https://link.springer.com/article/10.1007/s11222-017-9746-6 @@ -918,14 +931,16 @@ components: ExperimentIntersection: example: data: - - - - "1" - - hallo - - - "1" - - hallo - - - - "1" - - hallo - - - "1" - - hallo + - - "1" + - test value 1 + - test value 2 + - - "2" + - test value 1 + - test value 2 + - [] + - - "3" + - "other" + - "group" header: - id - testColumn1 @@ -941,13 +956,12 @@ components: type: array data: items: + example: + - "1" + - test value 1 + - test value 2 items: - example: - - "1" - - hallo - items: - type: string - type: array + type: string type: array type: array required: @@ -956,14 +970,26 @@ components: type: object ExperimentIntersectionCount: example: - count: 37 + numberRows: 37 + numberGroups: 10 properties: - count: + numberRows: example: 37 type: integer + numberGroups: + example: 10 + type: integer required: - - count + - numberGroups + - numberRows type: object + ExperimentIntersectionMode: + default: PAIRS + enum: + - PAIRS + - CLUSTERS + - INVESTIGATIVE + type: string Algorithm_allOf: properties: id: diff --git a/wrapper/src/api/providers/benchmark/baseBenchmarkProvider.ts b/wrapper/src/api/providers/benchmark/baseBenchmarkProvider.ts index 69ce3b15..d1e23cf3 100644 --- a/wrapper/src/api/providers/benchmark/baseBenchmarkProvider.ts +++ b/wrapper/src/api/providers/benchmark/baseBenchmarkProvider.ts @@ -1,4 +1,7 @@ -import { ExperimentIntersection } from '../../server/types'; +import { + ExperimentIntersection, + ExperimentIntersectionMode, +} from '../../server/types'; import { Metric } from '../../server/types'; export abstract class BaseBenchmarkProvider { abstract calculateMetrics( @@ -9,6 +12,7 @@ export abstract class BaseBenchmarkProvider { goldStandardId: number, experimentId: number, goldStandardDuplicates: boolean, - experimentDuplicates: boolean + experimentDuplicates: boolean, + mode: ExperimentIntersectionMode ): ExperimentIntersection; } diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts index 18ed8958..a0d14a03 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/benchmarkProvider.ts @@ -2,12 +2,12 @@ import { Dataset, ExperimentId, ExperimentIntersection, + ExperimentIntersectionMode, } from '../../../server/types'; import { Metric } from '../../../server/types'; import { getProviders } from '../..'; import { BaseBenchmarkProvider } from '../baseBenchmarkProvider'; import { EvaluatorCache } from './helper/evaluator'; -import { ConfusionTupleMode } from './helper/evaluator/confusionMatrix/modes'; import { idClustersToRecordClusters } from './helper/idsToRecords'; import { Accuracy, @@ -37,7 +37,7 @@ export class BenchmarkProvider extends BaseBenchmarkProvider { experimentId: number, goldStandardDuplicates: boolean, experimentDuplicates: boolean, - mode: ConfusionTupleMode = ConfusionTupleMode.PAIRS + mode: ExperimentIntersectionMode ): ExperimentIntersection { const dataset = this.getDatasetByExperimentIds( goldstandardId, diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/modes/index.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/modes/index.ts index cd2eed86..d344d3b2 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/modes/index.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/modes/index.ts @@ -1,21 +1,22 @@ +import { ExperimentIntersectionMode } from '../../../../../../../server/types'; import { NodeID, Subclustering } from '../../../cluster/types'; import { subclustersToClusters } from './clusters'; import { subclustersToInvestigative } from './investigative'; -import { ConfusionTupleMode } from './modes'; import { subclustersToPairs } from './pairs'; import { SubclusterTransformation } from './types'; -export * from './modes'; - -const transformations = new Map([ - [ConfusionTupleMode.PAIRS, subclustersToPairs], - [ConfusionTupleMode.INVESTIGATIVE, subclustersToInvestigative], - [ConfusionTupleMode.CLUSTERS, subclustersToClusters], +const transformations = new Map< + ExperimentIntersectionMode, + SubclusterTransformation +>([ + [ExperimentIntersectionMode.Pairs, subclustersToPairs], + [ExperimentIntersectionMode.Investigative, subclustersToInvestigative], + [ExperimentIntersectionMode.Clusters, subclustersToClusters], ]); export function applySubclusteringTransformation( subclustering: Subclustering, - mode: ConfusionTupleMode + mode: ExperimentIntersectionMode ): NodeID[][] { const transformation = transformations.get(mode); if (!transformation) { diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/modes/modes.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/modes/modes.ts deleted file mode 100644 index 2ac5c1ab..00000000 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/modes/modes.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum ConfusionTupleMode { - PAIRS, - INVESTIGATIVE, - CLUSTERS, -} diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/test/testCases.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/test/testCases.ts index bcd19302..23c8b70d 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/test/testCases.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/test/testCases.ts @@ -1,8 +1,8 @@ +import { ExperimentIntersectionMode } from '../../../../../../../server/types'; import { RelaxedClustering } from '../../../cluster/test/relaxedClusterings'; -import { ConfusionTupleMode } from '../modes'; type Clusters = number[][]; -type ClustersOfModes = { [key in ConfusionTupleMode]: Clusters }; +type ClustersOfModes = { [key in ExperimentIntersectionMode]: Clusters }; export const confusionTuplesTestCases: { goldStandard: RelaxedClustering; @@ -16,14 +16,14 @@ export const confusionTuplesTestCases: { experiment: [], expectedTruePositives: [], expectedFalseNegatives: { - [ConfusionTupleMode.PAIRS]: [], - [ConfusionTupleMode.CLUSTERS]: [], - [ConfusionTupleMode.INVESTIGATIVE]: [], + [ExperimentIntersectionMode.Pairs]: [], + [ExperimentIntersectionMode.Clusters]: [], + [ExperimentIntersectionMode.Investigative]: [], }, expectedFalsePositives: { - [ConfusionTupleMode.PAIRS]: [], - [ConfusionTupleMode.CLUSTERS]: [], - [ConfusionTupleMode.INVESTIGATIVE]: [], + [ExperimentIntersectionMode.Pairs]: [], + [ExperimentIntersectionMode.Clusters]: [], + [ExperimentIntersectionMode.Investigative]: [], }, }, { @@ -39,7 +39,7 @@ export const confusionTuplesTestCases: { [4, 5], ], expectedFalseNegatives: { - [ConfusionTupleMode.PAIRS]: [ + [ExperimentIntersectionMode.Pairs]: [ [0, 2], [0, 3], [0, 4], @@ -53,7 +53,7 @@ export const confusionTuplesTestCases: { [3, 4], [3, 5], ], - [ConfusionTupleMode.CLUSTERS]: [ + [ExperimentIntersectionMode.Clusters]: [ [0, 2, 4], [0, 2, 5], [0, 3, 4], @@ -63,7 +63,7 @@ export const confusionTuplesTestCases: { [1, 3, 4], [1, 3, 5], ], - [ConfusionTupleMode.INVESTIGATIVE]: [ + [ExperimentIntersectionMode.Investigative]: [ [0, 2, 3, 4, 5], [1, 2, 3, 4, 5], [2, 0, 1, 4, 5], @@ -73,9 +73,9 @@ export const confusionTuplesTestCases: { ], }, expectedFalsePositives: { - [ConfusionTupleMode.PAIRS]: [], - [ConfusionTupleMode.CLUSTERS]: [], - [ConfusionTupleMode.INVESTIGATIVE]: [], + [ExperimentIntersectionMode.Pairs]: [], + [ExperimentIntersectionMode.Clusters]: [], + [ExperimentIntersectionMode.Investigative]: [], }, }, { @@ -91,12 +91,12 @@ export const confusionTuplesTestCases: { [4, 5], ], expectedFalseNegatives: { - [ConfusionTupleMode.PAIRS]: [], - [ConfusionTupleMode.CLUSTERS]: [], - [ConfusionTupleMode.INVESTIGATIVE]: [], + [ExperimentIntersectionMode.Pairs]: [], + [ExperimentIntersectionMode.Clusters]: [], + [ExperimentIntersectionMode.Investigative]: [], }, expectedFalsePositives: { - [ConfusionTupleMode.PAIRS]: [ + [ExperimentIntersectionMode.Pairs]: [ [0, 2], [0, 3], [0, 4], @@ -110,7 +110,7 @@ export const confusionTuplesTestCases: { [3, 4], [3, 5], ], - [ConfusionTupleMode.CLUSTERS]: [ + [ExperimentIntersectionMode.Clusters]: [ [0, 2, 4], [0, 2, 5], [0, 3, 4], @@ -120,7 +120,7 @@ export const confusionTuplesTestCases: { [1, 3, 4], [1, 3, 5], ], - [ConfusionTupleMode.INVESTIGATIVE]: [ + [ExperimentIntersectionMode.Investigative]: [ [0, 2, 3, 4, 5], [1, 2, 3, 4, 5], [2, 0, 1, 4, 5], @@ -145,19 +145,19 @@ export const confusionTuplesTestCases: { [4, 5], ], expectedFalseNegatives: { - [ConfusionTupleMode.PAIRS]: [ + [ExperimentIntersectionMode.Pairs]: [ [0, 2], [1, 2], [3, 4], [3, 5], ], - [ConfusionTupleMode.CLUSTERS]: [ + [ExperimentIntersectionMode.Clusters]: [ [0, 2], [1, 2], [3, 4], [3, 5], ], - [ConfusionTupleMode.INVESTIGATIVE]: [ + [ExperimentIntersectionMode.Investigative]: [ [0, 2], [1, 2], [2, 0, 1], @@ -167,9 +167,9 @@ export const confusionTuplesTestCases: { ], }, expectedFalsePositives: { - [ConfusionTupleMode.PAIRS]: [[2, 3]], - [ConfusionTupleMode.CLUSTERS]: [[2, 3]], - [ConfusionTupleMode.INVESTIGATIVE]: [ + [ExperimentIntersectionMode.Pairs]: [[2, 3]], + [ExperimentIntersectionMode.Clusters]: [[2, 3]], + [ExperimentIntersectionMode.Investigative]: [ [2, 3], [3, 2], ], diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/tuples.spec.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/tuples.spec.ts index 1b827ba6..1445e9a6 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/tuples.spec.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/tuples.spec.ts @@ -1,7 +1,7 @@ +import { ExperimentIntersectionMode } from '../../../../../../server/types'; import { Subclustering } from '../../cluster/subclustering'; import { relaxedClusteringToClustering } from '../../cluster/test/relaxedClusterings'; import { expectClusteringsToEqual } from '../../cluster/test/utility'; -import { ConfusionTupleMode } from './modes'; import { confusionTuplesTestCases } from './test/testCases'; import { calculateConfusionMatrixTuples, @@ -32,9 +32,9 @@ describe.each(confusionTuplesTestCases)( ); }); describe.each( - Object.values(ConfusionTupleMode).filter( - (mode) => typeof mode === 'number' - ) as ConfusionTupleMode[] + Object.values(ExperimentIntersectionMode).filter( + (mode) => typeof mode === 'string' + ) as ExperimentIntersectionMode[] )('mode', (mode) => { let confusionMatrixTuples: ConfusionMatrixTuples; beforeAll(() => { diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/tuples.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/tuples.ts index b68e4be4..3ff0c004 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/tuples.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/confusionMatrix/tuples.ts @@ -1,6 +1,7 @@ +import { ExperimentIntersectionMode } from '../../../../../../server/types'; import { relaxedClusteringToArray } from '../../cluster/test/relaxedClusterings'; import { NodeID, Subclustering } from '../../cluster/types'; -import { applySubclusteringTransformation, ConfusionTupleMode } from './modes'; +import { applySubclusteringTransformation } from './modes'; export interface ConfusionMatrixTuples { truePositives: NodeID[][]; @@ -11,7 +12,7 @@ export interface ConfusionMatrixTuples { export function calculateConfusionMatrixTuples( goldSubclustering: Subclustering, experimentSubclustering: Subclustering, - mode: ConfusionTupleMode + mode: ExperimentIntersectionMode ): ConfusionMatrixTuples { return { truePositives: relaxedClusteringToArray(goldSubclustering).filter( diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/evaluator.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/evaluator.ts index 2236c27e..c9c7e216 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/evaluator.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/evaluator/evaluator.ts @@ -1,3 +1,4 @@ +import { ExperimentIntersectionMode } from '../../../../../server/types'; import { LazyProperty } from '../../../../../tools/lazyProperty'; import { Subclustering } from '../cluster/subclustering'; import { @@ -10,7 +11,6 @@ import { ConfusionMatrixCounts, ConfusionMatrixTuples, } from './confusionMatrix'; -import { ConfusionTupleMode } from './confusionMatrix/modes'; export class Evaluator { protected goldSubclustering: LazyProperty; @@ -37,10 +37,12 @@ export class Evaluator { } protected _confusionMatrixTuples = new Map< - ConfusionTupleMode, + ExperimentIntersectionMode, ConfusionMatrixTuples >(); - confusionMatrixTuples(mode: ConfusionTupleMode): ConfusionMatrixTuples { + confusionMatrixTuples( + mode: ExperimentIntersectionMode + ): ConfusionMatrixTuples { let tuples = this._confusionMatrixTuples.get(mode); if (!tuples) { tuples = calculateConfusionMatrixTuples( diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/index.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/index.ts index db2b8215..ccfec803 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/index.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/index.ts @@ -1,11 +1,12 @@ import { tableSchemas } from '../../../../../database/schemas'; import { tableExists } from '../../../../../database/table/loader'; import { DatasetId, ExperimentIntersection } from '../../../../../server/types'; +import { NodeID } from '../cluster/types'; import { idClustersToRecordClustersNoTable } from './noTable'; import { idClustersToRecordClustersWithTable } from './withTable'; export function idClustersToRecordClusters( - idClusters: number[][], + idClusters: NodeID[][], datasetId: DatasetId ): ExperimentIntersection { const schema = tableSchemas.dataset.dataset(datasetId); diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/noTable.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/noTable.ts index 94559f19..b6b8235b 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/noTable.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/noTable.ts @@ -5,6 +5,8 @@ export function idClustersToRecordClustersNoTable( ): ExperimentIntersection { return { header: ['id'], - data: idClusters.map((cluster) => cluster.map((id) => [id.toString()])), + data: idClusters + .flatMap((cluster) => [...cluster.map((id) => [id.toString()]), []]) + .slice(0, -1), }; } diff --git a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/withTable.ts b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/withTable.ts index 4be15096..eab7fec3 100644 --- a/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/withTable.ts +++ b/wrapper/src/api/providers/benchmark/benchmarkProvider/helper/idsToRecords/withTable.ts @@ -60,9 +60,9 @@ class IdClustersToRecordClusters { } protected getRecordClusters(): ExperimentIntersection['data'] { - return this.idClusters.map((cluster) => - cluster.map((id) => this.getRecord(id)) - ); + return this.idClusters + .flatMap((cluster) => [...cluster.map((id) => this.getRecord(id)), []]) + .slice(0, -1); } protected getRecord(id: number): string[] { diff --git a/wrapper/src/api/server/services/BenchmarkService.ts b/wrapper/src/api/server/services/BenchmarkService.ts index 1a342936..3492c924 100644 --- a/wrapper/src/api/server/services/BenchmarkService.ts +++ b/wrapper/src/api/server/services/BenchmarkService.ts @@ -1,5 +1,11 @@ import { getProviders } from '../../providers'; -import { ExperimentId, ExperimentIntersection, Metric } from '../types'; +import { + ExperimentId, + ExperimentIntersection, + ExperimentIntersectionCount, + ExperimentIntersectionMode, + Metric, +} from '../types'; import { Service, SuccessResponse } from './Service'; function provider() { @@ -15,6 +21,7 @@ function provider() { * */ export async function calculateExperimentIntersectionCount({ body, + mode = ExperimentIntersectionMode.Pairs, }: { body: { experimentId: ExperimentId; @@ -22,16 +29,25 @@ export async function calculateExperimentIntersectionCount({ similarityAttribute?: string; similarityScore?: number; }[]; -}): Promise> { + mode?: ExperimentIntersectionMode; +}): Promise> { return Service.response( () => { if (body.length === 2) { - return provider().getConfusionTuples( + const tuples = provider().getConfusionTuples( body[0].experimentId, body[1].experimentId, body[0].predictedCondition, - body[1].predictedCondition - ).data.length; + body[1].predictedCondition, + mode + ).data; + return { + numberGroups: tuples.reduce( + (prev, cur) => prev + (cur.length === 0 ? 1 : 0), + tuples.length === 0 ? 0 : 1 + ), + numberRows: tuples.length, + }; } else { throw new Error( `Intersection for ${body.length} experiments is not supported so far! Please provide exactly two experiments` @@ -58,6 +74,7 @@ export async function calculateExperimentIntersectionRecords({ startAt, limit, sortBy, + mode = ExperimentIntersectionMode.Pairs, }: { body: { experimentId: ExperimentId; @@ -68,6 +85,7 @@ export async function calculateExperimentIntersectionRecords({ startAt?: number; limit?: number; sortBy?: string; + mode?: ExperimentIntersectionMode; }): Promise> { return Service.response( () => { @@ -76,7 +94,8 @@ export async function calculateExperimentIntersectionRecords({ body[0].experimentId, body[1].experimentId, body[0].predictedCondition, - body[1].predictedCondition + body[1].predictedCondition, + mode ); } else { throw new Error( diff --git a/wrapper/src/api/server/types/ExperimentIntersection.ts b/wrapper/src/api/server/types/ExperimentIntersection.ts index 6cc7e66e..2bbd455a 100644 --- a/wrapper/src/api/server/types/ExperimentIntersection.ts +++ b/wrapper/src/api/server/types/ExperimentIntersection.ts @@ -26,8 +26,8 @@ export interface ExperimentIntersection { header: Array; /** * - * @type {Array>>} + * @type {Array>} * @memberof ExperimentIntersection */ - data: Array>>; + data: Array>; } diff --git a/wrapper/src/api/server/types/ExperimentIntersectionCount.ts b/wrapper/src/api/server/types/ExperimentIntersectionCount.ts index f2250378..e5039307 100644 --- a/wrapper/src/api/server/types/ExperimentIntersectionCount.ts +++ b/wrapper/src/api/server/types/ExperimentIntersectionCount.ts @@ -24,5 +24,11 @@ export interface ExperimentIntersectionCount { * @type {number} * @memberof ExperimentIntersectionCount */ - count: number; + numberRows: number; + /** + * + * @type {number} + * @memberof ExperimentIntersectionCount + */ + numberGroups: number; } diff --git a/wrapper/src/api/server/types/ExperimentIntersectionMode.ts b/wrapper/src/api/server/types/ExperimentIntersectionMode.ts new file mode 100644 index 00000000..12a1e070 --- /dev/null +++ b/wrapper/src/api/server/types/ExperimentIntersectionMode.ts @@ -0,0 +1,24 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Snowman API + * _This document describes the REST API of the snowman data matching benchmark tool._ Comparing data matching algorithms is still an unsolved topic in both industry and research. With snowman, developers and researchers will be able to compare the performance of different data matching solutions or improve new algorithms. + * + * The version of the OpenAPI document: 1.0.0 + * Contact: snowman@groups.sap.com + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @enum {string} + */ +export enum ExperimentIntersectionMode { + Pairs = 'PAIRS', + Clusters = 'CLUSTERS', + Investigative = 'INVESTIGATIVE' +} \ No newline at end of file diff --git a/wrapper/src/api/server/types/index.ts b/wrapper/src/api/server/types/index.ts index 7a2530db..ebe2945a 100644 --- a/wrapper/src/api/server/types/index.ts +++ b/wrapper/src/api/server/types/index.ts @@ -12,6 +12,7 @@ export * from './ExperimentFileFormat'; export * from './ExperimentId'; export * from './ExperimentIntersection'; export * from './ExperimentIntersectionCount'; +export * from './ExperimentIntersectionMode'; export * from './ExperimentValues'; export * from './Metric'; export * from './util'; From d0f1d2457b5250e3df7ce89f9a98bd7323a2a846 Mon Sep 17 00:00:00 2001 From: flosld Date: Tue, 2 Mar 2021 10:52:25 +0100 Subject: [PATCH 55/73] Reference contribution guide --- mkdocs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index a982fc0e..0896d945 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -10,8 +10,9 @@ nav: - Datasets: basic_usage/datasets.md - Experiments: basic_usage/experiments.md - Benchmark: basic_usage/benchmark.md - - Development setup: - - Introduction: dev_setup/introduction.md + - Development: + - Setup: dev_setup/introduction.md + - Contributing: contributing/knowledge.md - REST API: ./swagger extra_css: From 2c2be9daaab9ad0efbf1e7d02b11762fd6a9342d Mon Sep 17 00:00:00 2001 From: Lasklu <49564344+Lasklu@users.noreply.github.com> Date: Tue, 2 Mar 2021 11:05:43 +0100 Subject: [PATCH 56/73] rename description and name of computers dataset --- wrapper/src/api/database/setup/examples/datasets.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrapper/src/api/database/setup/examples/datasets.ts b/wrapper/src/api/database/setup/examples/datasets.ts index ca3d7054..ee7c5069 100644 --- a/wrapper/src/api/database/setup/examples/datasets.ts +++ b/wrapper/src/api/database/setup/examples/datasets.ts @@ -62,8 +62,8 @@ export const exampleDatasets = assertType()({ }, computers: { meta: { - name: 'computers-SIGMOD', - description: 'This is a dataset used for the SIGMOD-contest.', + name: 'SIGMOD-computers-X1', + description: 'This is the computers dataset (X1) of the SIGMOD-contest.', tags: ['SIGMOD'], }, id: 2, From 929f26e2db4b0103bafa1a9f10e0e943543c2fe1 Mon Sep 17 00:00:00 2001 From: Lasklu <49564344+Lasklu@users.noreply.github.com> Date: Tue, 2 Mar 2021 11:08:33 +0100 Subject: [PATCH 57/73] renamed description of SIGMOD-experiment --- wrapper/src/api/database/setup/examples/experiments.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrapper/src/api/database/setup/examples/experiments.ts b/wrapper/src/api/database/setup/examples/experiments.ts index f52f0d2e..7c519f5f 100644 --- a/wrapper/src/api/database/setup/examples/experiments.ts +++ b/wrapper/src/api/database/setup/examples/experiments.ts @@ -77,9 +77,9 @@ export const exampleExperiments = assertType()({ }, computersSigmodGoldstandard1: { meta: { - name: 'goldstandard', + name: 'SIGMOD-goldstandard-Y1', description: - 'Complete list of all duplicate pairs found in the original datasets.', + 'Complete list of duplicate pairs in the computers dataset (X1) of the SIGMOD contest.', algorithmId: exampleAlgorithms.gold.id, datasetId: exampleDatasets.computers.id, }, From eeaccd0d6b0917b393c65a2eae83dd6b4dccd27e Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Tue, 2 Mar 2021 11:27:51 +0100 Subject: [PATCH 58/73] Implement edit logic for datasets in redux actions --- .../DatasetDialog/DatasetDialog.tsx | 9 +- .../actions/DatasetDialogStoreActions.ts | 179 +++++++++++------- app/src/store/actions/actionTypes.ts | 1 + .../store/reducers/DatasetDialogReducer.ts | 12 +- app/src/utils/statusMessages.ts | 2 + 5 files changed, 122 insertions(+), 81 deletions(-) diff --git a/app/src/components/DatasetDialog/DatasetDialog.tsx b/app/src/components/DatasetDialog/DatasetDialog.tsx index bf6810fb..95573c92 100644 --- a/app/src/components/DatasetDialog/DatasetDialog.tsx +++ b/app/src/components/DatasetDialog/DatasetDialog.tsx @@ -6,8 +6,8 @@ import { import { ChangeEvent } from 'react'; import { connect } from 'react-redux'; import { - addNewDataset, addNewTag, + addOrUpdateDataset, changeDatasetCSVEscape, changeDatasetCSVIdColumn, changeDatasetCSVQuote, @@ -32,7 +32,9 @@ const isValidDatasetDialog = (state: Store): boolean => { // If dataset should be uploaded, select files if ( state.DatasetDialogStore.datasetType === DatasetTypes.full && - state.DatasetDialogStore.selectedFiles.length === 0 + state.DatasetDialogStore.selectedFiles.length === 0 && + // rule applies only if AddDialog + state.DatasetDialogStore.datasetId === null ) { return false; } @@ -121,8 +123,7 @@ const mapDispatchToProps = ( (dispatch as SnowmanDispatch)(addNewTag(newTagValue)); }, clickOnSubmit(): void { - // Todo: Rather use addOrUpdateDataset() - dispatch(addNewDataset()).then(); + dispatch(addOrUpdateDataset()).then(); }, }); diff --git a/app/src/store/actions/DatasetDialogStoreActions.ts b/app/src/store/actions/DatasetDialogStoreActions.ts index 0659295d..391754d2 100644 --- a/app/src/store/actions/DatasetDialogStoreActions.ts +++ b/app/src/store/actions/DatasetDialogStoreActions.ts @@ -1,9 +1,10 @@ import { Dataset, DatasetsApi } from 'api'; -import { DatasetDialogStoreActionTypes as DialogActions } from 'store/actions/actionTypes'; +import { DatasetDialogStoreActionTypes as DialogActionsTypes } from 'store/actions/actionTypes'; import { getDatasets } from 'store/actions/DatasetsStoreActions'; import { SnowmanDispatch, SnowmanThunkAction } from 'store/messages'; import { store } from 'store/store'; import { DatasetTypes } from 'types/DatasetTypes'; +import { MagicNotPossibleId } from 'utils/constants'; import { easyPrimitiveAction, easyPrimitiveActionReturn, @@ -12,15 +13,27 @@ import RequestHandler from 'utils/requestHandler'; import { SUCCESS_TO_CREATE_NEW_DATASET, SUCCESS_TO_UPLOAD_DATASET_FILE, + SUCCESS_UPDATE_DATASET, } from 'utils/statusMessages'; import { getTagsFromDatasets } from 'utils/tagFactory'; -export const openAddDialog = (): easyPrimitiveActionReturn => +const loadAvailableTags = (): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.OPEN_ADD_DIALOG, + type: DialogActionsTypes.LOAD_AVAILABLE_TAGS, payload: getTagsFromDatasets(store.getState().DatasetsStore.datasets), }); +export const openAddDialog = (): SnowmanThunkAction => ( + dispatch: SnowmanDispatch +): void => { + dispatch({ + type: DialogActionsTypes.OPEN_ADD_DIALOG, + // reducer ignores payload + payload: false, + }); + dispatch(loadAvailableTags()); +}; + export const openChangeDialog = ( datasetId: number ): SnowmanThunkAction> => async ( @@ -32,18 +45,18 @@ export const openChangeDialog = ( .getDataset({ datasetId: datasetId }) .then((aDataset: Dataset) => dispatch({ - type: DialogActions.OPEN_CHANGE_DIALOG, + type: DialogActionsTypes.OPEN_CHANGE_DIALOG, payload: aDataset, }) ) - .then(), + .then((): void => dispatch(loadAvailableTags())), dispatch ); }; export const closeDialog = (): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.CLOSE_DIALOG, + type: DialogActionsTypes.CLOSE_DIALOG, // reducer ignores payload payload: false, }); @@ -52,7 +65,7 @@ export const changeDatasetName = ( aDatasetName: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.CHANGE_DATASET_NAME, + type: DialogActionsTypes.CHANGE_DATASET_NAME, payload: aDatasetName, }); @@ -60,7 +73,7 @@ export const changeDatasetDescription = ( aDescription: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.CHANGE_DATASET_DESCRIPTION, + type: DialogActionsTypes.CHANGE_DATASET_DESCRIPTION, payload: aDescription, }); @@ -68,7 +81,7 @@ export const changeDatasetType = ( aType: DatasetTypes ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.CHANGE_DATASET_TYPE, + type: DialogActionsTypes.CHANGE_DATASET_TYPE, payload: aType, }); @@ -76,7 +89,7 @@ export const changeDatasetLength = ( aLength: number ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.CHANGE_DATASET_LENGTH, + type: DialogActionsTypes.CHANGE_DATASET_LENGTH, payload: aLength, }); @@ -84,7 +97,7 @@ export const changeDatasetCSVIdColumn = ( anIdColumn: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.CHANGE_CSV_ID_COLUMN, + type: DialogActionsTypes.CHANGE_CSV_ID_COLUMN, payload: anIdColumn, }); @@ -92,7 +105,7 @@ export const changeDatasetCSVSeparator = ( aSeparator: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.CHANGE_CSV_SEPARATOR, + type: DialogActionsTypes.CHANGE_CSV_SEPARATOR, payload: aSeparator, }); @@ -100,7 +113,7 @@ export const changeDatasetCSVQuote = ( aQuote: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.CHANGE_CSV_QUOTE, + type: DialogActionsTypes.CHANGE_CSV_QUOTE, payload: aQuote, }); @@ -108,112 +121,134 @@ export const changeDatasetCSVEscape = ( anEscape: string ): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.CHANGE_CSV_ESCAPE, + type: DialogActionsTypes.CHANGE_CSV_ESCAPE, payload: anEscape, }); export const addNewTag = (aTag: string): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.ADD_DATASET_TAG, + type: DialogActionsTypes.ADD_DATASET_TAG, payload: aTag, }); export const resetDialog = (): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.RESET_DIALOG, + type: DialogActionsTypes.RESET_DIALOG, // payload is not used payload: false, }); export const setSelectedFiles = (files: File[]): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.CHANGE_DATASET_FILES, + type: DialogActionsTypes.CHANGE_DATASET_FILES, payload: files, }); export const clickOnDatasetTag = (aTag: string): easyPrimitiveActionReturn => easyPrimitiveAction({ - type: DialogActions.CLICK_ON_DATASET_TAG, + type: DialogActionsTypes.CLICK_ON_DATASET_TAG, payload: aTag, }); -export const createNewDataset = ( - name: string, - description: string, - numberOfRecords = 0, - tags: string[] -): SnowmanThunkAction> => async ( +const createNewDataset = (): SnowmanThunkAction> => async ( dispatch: SnowmanDispatch ): Promise => RequestHandler( (): Promise => new DatasetsApi().addDataset({ datasetValues: { - name: name, - description: description, - numberOfRecords: numberOfRecords, - tags: tags, + name: store.getState().DatasetDialogStore.datasetName, + description: store.getState().DatasetDialogStore.datasetDescription, + numberOfRecords: + store.getState().DatasetDialogStore.datasetType === + DatasetTypes.skeleton + ? store.getState().DatasetDialogStore.datasetLength + : undefined, + tags: store.getState().DatasetDialogStore.selectedTags, }, }), dispatch, SUCCESS_TO_CREATE_NEW_DATASET ); -export const uploadDatasetFile = ( - id: number, - idColumn: string, - separator: string, - quote: string, - escape: string, - file: File -): SnowmanThunkAction> => async (dispatch: SnowmanDispatch) => - RequestHandler( +const setExistingDataset = (): SnowmanThunkAction> => async ( + dispatch: SnowmanDispatch +): Promise => + RequestHandler( (): Promise => - new DatasetsApi().setDatasetFile({ - datasetId: id, - idColumn: idColumn, - quote: quote, - escape: escape, - separator: separator, - body: file as Blob, + new DatasetsApi().setDataset({ + datasetId: + store.getState().DatasetDialogStore.datasetId ?? MagicNotPossibleId, + datasetValues: { + name: store.getState().DatasetDialogStore.datasetName, + description: store.getState().DatasetDialogStore.datasetDescription, + tags: store.getState().DatasetDialogStore.selectedTags, + numberOfRecords: store.getState().DatasetDialogStore.datasetLength, + }, }), dispatch, - SUCCESS_TO_UPLOAD_DATASET_FILE, + SUCCESS_UPDATE_DATASET + ); + +const uploadDatasetFile = ( + id?: number +): SnowmanThunkAction> => async (dispatch: SnowmanDispatch) => { + const willUpload = + store.getState().DatasetDialogStore.selectedFiles.length > 0; + return RequestHandler( + (): Promise => { + if ( + store.getState().DatasetDialogStore.datasetType === DatasetTypes.full && + willUpload + ) + return new DatasetsApi().setDatasetFile({ + datasetId: + id ?? + store.getState().DatasetDialogStore.datasetId ?? + MagicNotPossibleId, + idColumn: store.getState().DatasetDialogStore.csvIdColumn, + quote: store.getState().DatasetDialogStore.csvQuote, + escape: store.getState().DatasetDialogStore.csvEscape, + separator: store.getState().DatasetDialogStore.csvSeparator, + body: store.getState().DatasetDialogStore.selectedFiles[0] as Blob, + }); + return Promise.resolve(); + }, + dispatch, + willUpload ? SUCCESS_TO_UPLOAD_DATASET_FILE : undefined, true ); +}; -export const addNewDataset = (): SnowmanThunkAction> => async ( +const addNewDataset = (): SnowmanThunkAction> => async ( dispatch: SnowmanDispatch ): Promise => { - return dispatch( - createNewDataset( - store.getState().DatasetDialogStore.datasetName, - store.getState().DatasetDialogStore.datasetDescription, - store.getState().DatasetDialogStore.datasetType === DatasetTypes.skeleton - ? store.getState().DatasetDialogStore.datasetLength - : undefined, - store.getState().DatasetDialogStore.selectedTags - ) - ) - .then((id: number): void => { - if ( - store.getState().DatasetDialogStore.datasetType === DatasetTypes.full - ) { - dispatch( - uploadDatasetFile( - id, - store.getState().DatasetDialogStore.csvIdColumn, - store.getState().DatasetDialogStore.csvSeparator, - store.getState().DatasetDialogStore.csvQuote, - store.getState().DatasetDialogStore.csvEscape, - store.getState().DatasetDialogStore.selectedFiles[0] - ) - ); - } - }) + return dispatch(createNewDataset()) + .then((id: number): Promise => dispatch(uploadDatasetFile(id))) .then((): void => dispatch(resetDialog())) .finally((): void => { dispatch(getDatasets()); dispatch(closeDialog()); }); }; + +const updatedExistingDataset = (): SnowmanThunkAction> => async ( + dispatch: SnowmanDispatch +): Promise => { + return dispatch(setExistingDataset()) + .then((): Promise => dispatch(uploadDatasetFile())) + .then((): void => dispatch(resetDialog())) + .finally((): void => { + dispatch(getDatasets()); + dispatch(closeDialog()); + }); +}; + +export const addOrUpdateDataset = (): SnowmanThunkAction< + Promise +> => async (dispatch: SnowmanDispatch): Promise => { + if (store.getState().DatasetDialogStore.datasetId === null) { + return dispatch(addNewDataset()); + } + return dispatch(updatedExistingDataset()); +}; diff --git a/app/src/store/actions/actionTypes.ts b/app/src/store/actions/actionTypes.ts index cca93c03..433e2f17 100644 --- a/app/src/store/actions/actionTypes.ts +++ b/app/src/store/actions/actionTypes.ts @@ -10,6 +10,7 @@ export const DatasetDialogStoreActionTypes = { OPEN_CHANGE_DIALOG: 'ADD_DATASET_DIALOG_STORE_ACTION-OPEN_CHANGE_DIALOG', CLOSE_DIALOG: 'ADD_DATASET_DIALOG_STORE_ACTION-CLOSE_DIALOG', RESET_DIALOG: 'ADD_DATASET_DIALOG_STORE_ACTION-RESET_DIALOG', + LOAD_AVAILABLE_TAGS: 'ADD_DATASET_DIALOG_STORE_ACTION-LOAD_AVAILABLE_TAGS', CHANGE_DATASET_NAME: 'ADD_DATASET_DIALOG_STORE_ACTION-CHANGE_DATASET_NAME', CHANGE_DATASET_DESCRIPTION: 'ADD_DATASET_DIALOG_STORE_ACTION-CHANGE_DATASET_DESCRIPTION', diff --git a/app/src/store/reducers/DatasetDialogReducer.ts b/app/src/store/reducers/DatasetDialogReducer.ts index ba340f22..cdfb176e 100644 --- a/app/src/store/reducers/DatasetDialogReducer.ts +++ b/app/src/store/reducers/DatasetDialogReducer.ts @@ -1,13 +1,12 @@ +import { Dataset } from 'api'; import { uniq } from 'lodash'; import { DatasetDialogStoreActionTypes as DialogActions } from 'store/actions/actionTypes'; import { SnowmanAction } from 'store/messages'; import { DatasetDialogStore } from 'store/models'; import { DatasetTypes } from 'types/DatasetTypes'; +import { DialogTypes } from 'types/DialogTypes'; import { toggleSelectionArrayMultipleSelect } from 'utils/toggleSelectionArray'; -import { Dataset } from '../../api'; -import { DialogTypes } from '../../types/DialogTypes'; - const initialState: DatasetDialogStore = { isOpen: false, datasetId: null, @@ -33,7 +32,6 @@ export const DatasetDialogReducer = ( case DialogActions.OPEN_ADD_DIALOG: return { ...state, - availableTags: action.payload as string[], // Todo: get this from root reducer? dialogType: DialogTypes.ADD_DIALOG, isOpen: true, }; @@ -47,7 +45,6 @@ export const DatasetDialogReducer = ( datasetDescription: (action.payload as Dataset).description ?? '', datasetLength: (action.payload as Dataset).numberOfRecords ?? 0, selectedTags: (action.payload as Dataset).tags ?? [], - availableTags: [], // Todo: get this from root reducer? }; case DialogActions.CLOSE_DIALOG: if (state.dialogType === DialogTypes.ADD_DIALOG) @@ -61,6 +58,11 @@ export const DatasetDialogReducer = ( } case DialogActions.RESET_DIALOG: return initialState; + case DialogActions.LOAD_AVAILABLE_TAGS: + return { + ...state, + availableTags: action.payload as string[], + }; case DialogActions.CHANGE_DATASET_NAME: return { ...state, diff --git a/app/src/utils/statusMessages.ts b/app/src/utils/statusMessages.ts index e0cd3d78..afeac085 100644 --- a/app/src/utils/statusMessages.ts +++ b/app/src/utils/statusMessages.ts @@ -18,3 +18,5 @@ export const SUCCESS_LOAD_BINARY_METRICS = 'Successfully loaded binary metrics!'; export const SUCCESS_UPDATE_ALGORITHM = 'Successfully updated the designated matching solution!'; +export const SUCCESS_UPDATE_DATASET = + 'Successfully updated the designated dataset!'; From fd7fa6a70b223e87fb009b2c0d3ab76ca22636cb Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Tue, 2 Mar 2021 11:35:02 +0100 Subject: [PATCH 59/73] Refactor dispatch typing --- .../DatasetDialog/DatasetDialog.tsx | 40 ++++++------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/app/src/components/DatasetDialog/DatasetDialog.tsx b/app/src/components/DatasetDialog/DatasetDialog.tsx index 95573c92..65362baf 100644 --- a/app/src/components/DatasetDialog/DatasetDialog.tsx +++ b/app/src/components/DatasetDialog/DatasetDialog.tsx @@ -66,61 +66,47 @@ const mapDispatchToProps = ( dispatch: SnowmanDispatch ): DatasetDialogDispatchProps => ({ closeDialog(): void { - (dispatch as SnowmanDispatch)(closeDialog()); + dispatch(closeDialog()); }, clickOnCancel(): void { - (dispatch as SnowmanDispatch)(closeDialog()); + dispatch(closeDialog()); }, changeDatasetName(event: IonChangeEvent): void { - (dispatch as SnowmanDispatch)( - changeDatasetName(event.detail.value as string) - ); + dispatch(changeDatasetName(event.detail.value as string)); }, changeDatasetDescription(event: IonChangeEvent): void { - (dispatch as SnowmanDispatch)( - changeDatasetDescription(event.detail.value as string) - ); + dispatch(changeDatasetDescription(event.detail.value as string)); }, changeDatasetType(event: IonChangeEvent): void { - (dispatch as SnowmanDispatch)( - changeDatasetType(event.detail.value as DatasetTypes) - ); + dispatch(changeDatasetType(event.detail.value as DatasetTypes)); }, changeDatasetLength(event: IonChangeEvent): void { - (dispatch as SnowmanDispatch)( + dispatch( changeDatasetLength(Math.abs(parseInt(event.detail.value as string))) ); }, changeCsvIdColumn(event: IonChangeEvent): void { - (dispatch as SnowmanDispatch)( - changeDatasetCSVIdColumn(event.detail.value as string) - ); + dispatch(changeDatasetCSVIdColumn(event.detail.value as string)); }, changeCsvSeparator(event: IonChangeEvent): void { - (dispatch as SnowmanDispatch)( - changeDatasetCSVSeparator(event.detail.value as string) - ); + dispatch(changeDatasetCSVSeparator(event.detail.value as string)); }, changeCsvQuote(event: IonChangeEvent): void { - (dispatch as SnowmanDispatch)( - changeDatasetCSVQuote(event.detail.value as string) - ); + dispatch(changeDatasetCSVQuote(event.detail.value as string)); }, changeCsvEscape(event: IonChangeEvent): void { - (dispatch as SnowmanDispatch)( - changeDatasetCSVEscape(event.detail.value as string) - ); + dispatch(changeDatasetCSVEscape(event.detail.value as string)); }, changeSelectedDatasetFiles(event: ChangeEvent): void { - (dispatch as SnowmanDispatch)( + dispatch( setSelectedFiles(convertFilesListToFilesArray(event.target.files)) ); }, clickOnATag(aTag: string): void { - (dispatch as SnowmanDispatch)(clickOnDatasetTag(aTag)); + dispatch(clickOnDatasetTag(aTag)); }, addNewTagCallback(newTagValue: string): void { - (dispatch as SnowmanDispatch)(addNewTag(newTagValue)); + dispatch(addNewTag(newTagValue)); }, clickOnSubmit(): void { dispatch(addOrUpdateDataset()).then(); From 8452c434f0502b2eebe3cd1ecc884d920b2c642f Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Tue, 2 Mar 2021 11:47:03 +0100 Subject: [PATCH 60/73] Refactor OptionCard.tsx and OptionSelector.tsx --- app/src/components/OptionCard/OptionCard.tsx | 52 +++++++++---------- .../OptionSelector/OptionSelector.tsx | 12 ++++- .../OptionSelector/OptionSelectorProps.ts | 4 +- .../ExperimentsPage/ExperimentsPage.View.tsx | 1 - 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/app/src/components/OptionCard/OptionCard.tsx b/app/src/components/OptionCard/OptionCard.tsx index 397ef187..64c1b0af 100644 --- a/app/src/components/OptionCard/OptionCard.tsx +++ b/app/src/components/OptionCard/OptionCard.tsx @@ -31,12 +31,8 @@ const OptionCard = ({ tags, clickCard, isSelected, - deleteCard = () => { - console.log('Deletion requested'); - }, - editCard = () => { - console.log('Edit requested'); - }, + deleteCard, + editCard, multiple = true, }: OptionCardProps): JSX.Element => { return ( @@ -90,28 +86,32 @@ const OptionCard = ({ ) : null} - - - Edit - + {editCard !== undefined ? ( + + + Edit + + ) : null} - - - Delete - + {deleteCard !== undefined ? ( + + + Delete + + ) : null} diff --git a/app/src/components/OptionSelector/OptionSelector.tsx b/app/src/components/OptionSelector/OptionSelector.tsx index b5342282..96e181e4 100644 --- a/app/src/components/OptionSelector/OptionSelector.tsx +++ b/app/src/components/OptionSelector/OptionSelector.tsx @@ -30,8 +30,16 @@ const OptionSelector = ({ tags={anOption.tags} clickCard={(): void => clickOnCard(anOption.id)} isSelected={selected.includes(anOption.id)} - deleteCard={(): void => deleteCardHandler(anOption.id)} - editCard={(): void => editCardHandler(anOption.id)} + deleteCard={ + deleteCardHandler !== undefined + ? (): void => deleteCardHandler(anOption.id) + : undefined + } + editCard={ + editCardHandler !== undefined + ? (): void => editCardHandler(anOption.id) + : undefined + } multiple={multiple} /> diff --git a/app/src/components/OptionSelector/OptionSelectorProps.ts b/app/src/components/OptionSelector/OptionSelectorProps.ts index 4d50260b..b996e060 100644 --- a/app/src/components/OptionSelector/OptionSelectorProps.ts +++ b/app/src/components/OptionSelector/OptionSelectorProps.ts @@ -4,8 +4,8 @@ export interface OptionSelectorProps { title: string; optionsList: Option[]; clickOnCard(id: number): void; - deleteCardHandler(id: number): void; - editCardHandler(id: number): void; + deleteCardHandler?(id: number): void; + editCardHandler?(id: number): void; selected: number[]; multiple?: boolean; } diff --git a/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx b/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx index ee8c41ba..1b046ce2 100644 --- a/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx +++ b/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx @@ -39,7 +39,6 @@ const ExperimentsPageView = ({ clickOnCard={clickOnExperiment} selected={selectedExperiments} deleteCardHandler={deleteExperiment} - editCardHandler={(id: number) => console.log('Edit with id', id)} multiple={true} /> From 4f3d8a3e9b660e3d47593e87f4f14ec539b63299 Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Tue, 2 Mar 2021 13:18:23 +0100 Subject: [PATCH 61/73] Do not dispatch both error paths --- app/src/utils/requestHandler.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/utils/requestHandler.ts b/app/src/utils/requestHandler.ts index edbef5a3..59d1bf7c 100644 --- a/app/src/utils/requestHandler.ts +++ b/app/src/utils/requestHandler.ts @@ -32,9 +32,10 @@ const RequestHandler = async ( dispatch( showToast((await result.text()) ?? UNKNOWN_ERROR, ToastType.Error) ); + } else { + // Else - show generic error + dispatch(showToast(UNKNOWN_ERROR, ToastType.Error)); } - // Else - show generic error - dispatch(showToast(UNKNOWN_ERROR, ToastType.Error)); throw Error; } ) From 8334b3212e6fbc2e5cb7962f5c11ef17f9fed04e Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Tue, 2 Mar 2021 13:28:55 +0100 Subject: [PATCH 62/73] Rename AddExperimentDialog to ExperimentDialog --- .../ExperimentDialog.View.tsx} | 8 +++---- .../ExperimentDialog.tsx} | 21 +++++++++---------- .../ExperimentDialogProps.ts} | 8 +++---- .../ExperimentDialogStyles.css} | 0 .../ExperimentsPage/ExperimentsPage.View.tsx | 4 ++-- 5 files changed, 20 insertions(+), 21 deletions(-) rename app/src/components/{AddExperimentDialog/AddExperimentDialog.View.tsx => ExperimentDialog/ExperimentDialog.View.tsx} (93%) rename app/src/components/{AddExperimentDialog/AddExperimentDialog.tsx => ExperimentDialog/ExperimentDialog.tsx} (82%) rename app/src/components/{AddExperimentDialog/AddExperimentDialogProps.ts => ExperimentDialog/ExperimentDialogProps.ts} (78%) rename app/src/components/{AddExperimentDialog/AddExperimentDialogStyles.css => ExperimentDialog/ExperimentDialogStyles.css} (100%) diff --git a/app/src/components/AddExperimentDialog/AddExperimentDialog.View.tsx b/app/src/components/ExperimentDialog/ExperimentDialog.View.tsx similarity index 93% rename from app/src/components/AddExperimentDialog/AddExperimentDialog.View.tsx rename to app/src/components/ExperimentDialog/ExperimentDialog.View.tsx index dbb18fbe..4e4cd674 100644 --- a/app/src/components/AddExperimentDialog/AddExperimentDialog.View.tsx +++ b/app/src/components/ExperimentDialog/ExperimentDialog.View.tsx @@ -11,7 +11,7 @@ import { IonTextarea, } from '@ionic/react'; import { SetExperimentFileFormatEnum } from 'api'; -import { AddExperimentDialogProps } from 'components/AddExperimentDialog/AddExperimentDialogProps'; +import { ExperimentDialogProps } from 'components/ExperimentDialog/ExperimentDialogProps'; import FileInput from 'components/FileInput/FileInput'; import ModalDialog from 'components/ModalDialog/ModalDialog'; import { addCircleOutline, closeCircleOutline } from 'ionicons/icons'; @@ -19,7 +19,7 @@ import React from 'react'; import { $enum } from 'ts-enum-util'; import experimentFileFormatEnum from 'types/ExperimentFileFormats'; -const AddExperimentDialogView = ({ +const ExperimentDialogView = ({ isOpen, closeDialog, clickOnCancel, @@ -36,7 +36,7 @@ const AddExperimentDialogView = ({ isValidForm, selectedFiles, changeSelectedFiles, -}: AddExperimentDialogProps): JSX.Element => ( +}: ExperimentDialogProps): JSX.Element => ( ); -export default AddExperimentDialogView; +export default ExperimentDialogView; diff --git a/app/src/components/AddExperimentDialog/AddExperimentDialog.tsx b/app/src/components/ExperimentDialog/ExperimentDialog.tsx similarity index 82% rename from app/src/components/AddExperimentDialog/AddExperimentDialog.tsx rename to app/src/components/ExperimentDialog/ExperimentDialog.tsx index a3cecdd2..6e71e3e5 100644 --- a/app/src/components/AddExperimentDialog/AddExperimentDialog.tsx +++ b/app/src/components/ExperimentDialog/ExperimentDialog.tsx @@ -1,9 +1,10 @@ -import 'components/AddExperimentDialog/AddExperimentDialogStyles.css'; +import 'components/ExperimentDialog/ExperimentDialogStyles.css'; +import ExperimentDialogView from 'components/ExperimentDialog/ExperimentDialog.View'; import { - AddExperimentDialogDispatchProps, - AddExperimentDialogStateProps, -} from 'components/AddExperimentDialog/AddExperimentDialogProps'; + ExperimentDialogDispatchProps, + ExperimentDialogStateProps, +} from 'components/ExperimentDialog/ExperimentDialogProps'; import { ChangeEvent } from 'react'; import { connect } from 'react-redux'; import { @@ -21,9 +22,7 @@ import experimentFileFormatEnum from 'types/ExperimentFileFormats'; import { IonChangeEvent } from 'types/IonChangeEvent'; import { convertFilesListToFilesArray } from 'utils/filesConverter'; -import AddExperimentDialogView from './AddExperimentDialog.View'; - -const mapStateToProps = (state: Store): AddExperimentDialogStateProps => ({ +const mapStateToProps = (state: Store): ExperimentDialogStateProps => ({ isOpen: state.AddExperimentDialogStore.isOpen, experimentName: state.AddExperimentDialogStore.experimentName, experimentDescription: state.AddExperimentDialogStore.experimentDescription, @@ -41,7 +40,7 @@ const mapStateToProps = (state: Store): AddExperimentDialogStateProps => ({ const mapDispatchToProps = ( dispatch: SnowmanDispatch -): AddExperimentDialogDispatchProps => ({ +): ExperimentDialogDispatchProps => ({ closeDialog: (): void => dispatch(closeDialog()), clickOnCancel: (): void => dispatch(closeDialog()), changeExperimentName: (event: IonChangeEvent): void => @@ -61,9 +60,9 @@ const mapDispatchToProps = ( ), }); -const AddExperimentDialog = connect( +const ExperimentDialog = connect( mapStateToProps, mapDispatchToProps -)(AddExperimentDialogView); +)(ExperimentDialogView); -export default AddExperimentDialog; +export default ExperimentDialog; diff --git a/app/src/components/AddExperimentDialog/AddExperimentDialogProps.ts b/app/src/components/ExperimentDialog/ExperimentDialogProps.ts similarity index 78% rename from app/src/components/AddExperimentDialog/AddExperimentDialogProps.ts rename to app/src/components/ExperimentDialog/ExperimentDialogProps.ts index 007b681e..7201bd57 100644 --- a/app/src/components/AddExperimentDialog/AddExperimentDialogProps.ts +++ b/app/src/components/ExperimentDialog/ExperimentDialogProps.ts @@ -2,7 +2,7 @@ import { ChangeEvent } from 'react'; import experimentFileFormatEnum from 'types/ExperimentFileFormats'; import { IonChangeEvent } from 'types/IonChangeEvent'; -export interface AddExperimentDialogDispatchProps { +export interface ExperimentDialogDispatchProps { closeDialog(): void; clickOnCancel(): void; changeExperimentName(event: IonChangeEvent): void; @@ -13,7 +13,7 @@ export interface AddExperimentDialogDispatchProps { changeSelectedFiles(event: ChangeEvent): void; } -export interface AddExperimentDialogStateProps { +export interface ExperimentDialogStateProps { isOpen: boolean; experimentName: string; experimentDescription: string; @@ -24,5 +24,5 @@ export interface AddExperimentDialogStateProps { selectedFiles: File[]; } -export type AddExperimentDialogProps = AddExperimentDialogStateProps & - AddExperimentDialogDispatchProps; +export type ExperimentDialogProps = ExperimentDialogStateProps & + ExperimentDialogDispatchProps; diff --git a/app/src/components/AddExperimentDialog/AddExperimentDialogStyles.css b/app/src/components/ExperimentDialog/ExperimentDialogStyles.css similarity index 100% rename from app/src/components/AddExperimentDialog/AddExperimentDialogStyles.css rename to app/src/components/ExperimentDialog/ExperimentDialogStyles.css diff --git a/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx b/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx index 1b046ce2..258594d9 100644 --- a/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx +++ b/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx @@ -1,6 +1,6 @@ import { IonChip, IonLabel } from '@ionic/react'; -import AddExperimentDialog from 'components/AddExperimentDialog/AddExperimentDialog'; import AddExperimentFab from 'components/AddFab/AddExperimentFab'; +import ExperimentDialog from 'components/ExperimentDialog/ExperimentDialog'; import OptionSelector from 'components/OptionSelector/OptionSelector'; import PageStruct from 'components/PageStruct/PageStruct'; import { ExperimentsPageProps } from 'pages/ExperimentsPage/ExperimentsPageProps'; @@ -42,7 +42,7 @@ const ExperimentsPageView = ({ multiple={true} /> - + ); }; From cef247a8eb43765a6a766f061dfd98b88165d59a Mon Sep 17 00:00:00 2001 From: flosld Date: Tue, 2 Mar 2021 13:37:27 +0100 Subject: [PATCH 63/73] Feature/highlight active page (#59) * Implement view characteristics * Remove obsolete navigators * Refactor pathHandlers and add isActive indication to side menu Co-authored-by: florian-papsdorf --- app/src/components/SideMenu/SideMenu.View.tsx | 6 +++- app/src/components/SideMenu/SideMenu.tsx | 4 ++- app/src/components/SideMenu/SideMenuProps.ts | 1 + app/src/utils/pathHandlers.ts | 33 +++++-------------- 4 files changed, 18 insertions(+), 26 deletions(-) diff --git a/app/src/components/SideMenu/SideMenu.View.tsx b/app/src/components/SideMenu/SideMenu.View.tsx index c65da2f7..d4bf9e01 100644 --- a/app/src/components/SideMenu/SideMenu.View.tsx +++ b/app/src/components/SideMenu/SideMenu.View.tsx @@ -41,7 +41,11 @@ const SideMenuView = ({ categoryStructure }: SideMenuProps): JSX.Element => ( disabled={!aCategoryItem.couldEnter} onClick={aCategoryItem.enterItem} > - +

{aCategoryItem.key}

{aCategoryItem.selectedOptions.map( diff --git a/app/src/components/SideMenu/SideMenu.tsx b/app/src/components/SideMenu/SideMenu.tsx index 172654d0..8be09021 100644 --- a/app/src/components/SideMenu/SideMenu.tsx +++ b/app/src/components/SideMenu/SideMenu.tsx @@ -5,11 +5,12 @@ import { SideMenuProps, } from 'components/SideMenu/SideMenuProps'; import { skull } from 'ionicons/icons'; -import { difference, sortBy } from 'lodash'; +import { difference, isEqual, sortBy } from 'lodash'; import { connect } from 'react-redux'; import { Store } from 'store/models'; import { getExperimentNameFromId } from 'utils/experimentsHelpers'; import { + getCurrentPathMapper, getPathResolution, IPathMapper, menuCategories, @@ -51,6 +52,7 @@ const getCategoryItemFromPathMapper = ( navigateTo(aPathMapper.path); }, selectedOptions: getSelectedOptionsFromPathMapper(aPathMapper, state), + isActive: isEqual(aPathMapper, getCurrentPathMapper()), }); const getCategories = (): string[] => diff --git a/app/src/components/SideMenu/SideMenuProps.ts b/app/src/components/SideMenu/SideMenuProps.ts index 6533f5f5..261cfafc 100644 --- a/app/src/components/SideMenu/SideMenuProps.ts +++ b/app/src/components/SideMenu/SideMenuProps.ts @@ -5,6 +5,7 @@ export interface CategoryItem { enterItem(): void; selectedOptions: string[]; menuIcon: string; + isActive: boolean; } export interface SideMenuCategory { diff --git a/app/src/utils/pathHandlers.ts b/app/src/utils/pathHandlers.ts index 66a47c16..d7ec318c 100644 --- a/app/src/utils/pathHandlers.ts +++ b/app/src/utils/pathHandlers.ts @@ -138,40 +138,25 @@ export const getPathResolution = (): IPathMapper[] => [ export const navigateTo = (aRoute: string | undefined): void => history.replace(aRoute ?? getPathToRootPage()); -export const navigateToRootPage = (): void => navigateTo(getPathToRootPage()); - -export const navigateToDatasetsSelector = (): void => - navigateTo(getPathToDatasetsSelector()); - -export const navigateToExperimentsSelector = (): void => - navigateTo(getPathToExperimentsSelector()); - -export const navigateToAlgorithmsPage = (): void => - navigateTo(getPathToAlgorithmsPage()); - -export const navigateToMetricsViewer = (): void => - navigateTo(getPathToMetricsViewer()); - const getCurrentPath = (): string => history.location.pathname; -const getCurrentPathMapper = (currentPath: string): IPathMapper => +export const getCurrentPathMapper = (): IPathMapper => getPathResolution().find( - (aPathMapper: IPathMapper): boolean => aPathMapper.path === currentPath + (aPathMapper: IPathMapper): boolean => aPathMapper.path === getCurrentPath() ) ?? getDefaultPathMapper(); -const getNextPathMapper = (currentPath: string): IPathMapper => +const getNextPathMapper = (): IPathMapper => getPathResolution().find( (aPathMapper: IPathMapper): boolean => - aPathMapper.path === getCurrentPathMapper(currentPath).nextPath + aPathMapper.path === getCurrentPathMapper().nextPath ) ?? getDefaultPathMapper(); -const getNextPath = (currentPathName: string): string => - getNextPathMapper(currentPathName).path ?? getPathToRootPage(); +const getNextPath = (): string => + getNextPathMapper().path ?? getPathToRootPage(); -export const navigateToNextPage = (): void => - navigateTo(getNextPath(getCurrentPath())); +export const navigateToNextPage = (): void => navigateTo(getNextPath()); -const existsNextPage = (): boolean => getNextPath(getCurrentPath()) !== null; +const existsNextPage = (): boolean => getNextPath() !== null; export const couldNavigateToNextPage = (aState: Store): boolean => - existsNextPage() && getNextPathMapper(getCurrentPath()).accessGuard(aState); + existsNextPage() && getNextPathMapper().accessGuard(aState); From 8c2670b167139152405125d7d220f4723f0e17fb Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Tue, 2 Mar 2021 14:14:58 +0100 Subject: [PATCH 64/73] Fix typing --- app/src/store/actions/DatasetDialogStoreActions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/store/actions/DatasetDialogStoreActions.ts b/app/src/store/actions/DatasetDialogStoreActions.ts index 391754d2..76e7a35d 100644 --- a/app/src/store/actions/DatasetDialogStoreActions.ts +++ b/app/src/store/actions/DatasetDialogStoreActions.ts @@ -43,12 +43,12 @@ export const openChangeDialog = ( (): Promise => new DatasetsApi() .getDataset({ datasetId: datasetId }) - .then((aDataset: Dataset) => + .then((aDataset: Dataset): void => { dispatch({ type: DialogActionsTypes.OPEN_CHANGE_DIALOG, payload: aDataset, - }) - ) + }); + }) .then((): void => dispatch(loadAvailableTags())), dispatch ); From 2c92e99f752d56549dc6130dd6e77bc41a55da9b Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Tue, 2 Mar 2021 15:31:04 +0100 Subject: [PATCH 65/73] Make experiments editable --- .../components/AddFab/AddExperimentFab.tsx | 4 +- .../ExperimentDialog.View.tsx | 15 +- .../ExperimentDialog/ExperimentDialog.tsx | 39 ++-- .../ExperimentDialog/ExperimentDialogProps.ts | 3 +- .../ExperimentsPage/ExperimentsPage.View.tsx | 2 + .../pages/ExperimentsPage/ExperimentsPage.tsx | 4 + .../ExperimentsPage/ExperimentsPageProps.ts | 1 + .../AddExperimentDialogStoreActions.ts | 126 ----------- .../actions/ExperimentDialogStoreActions.ts | 199 ++++++++++++++++++ app/src/store/actions/actionTypes.ts | 5 +- app/src/store/messages.ts | 32 +-- app/src/store/models.ts | 6 +- ...gReducer.ts => ExperimentDialogReducer.ts} | 44 +++- app/src/store/reducers/rootReducer.ts | 4 +- app/src/utils/statusMessages.ts | 2 + 15 files changed, 310 insertions(+), 176 deletions(-) delete mode 100644 app/src/store/actions/AddExperimentDialogStoreActions.ts create mode 100644 app/src/store/actions/ExperimentDialogStoreActions.ts rename app/src/store/reducers/{AddExperimentDialogReducer.ts => ExperimentDialogReducer.ts} (52%) diff --git a/app/src/components/AddFab/AddExperimentFab.tsx b/app/src/components/AddFab/AddExperimentFab.tsx index d9b79375..1f2292ee 100644 --- a/app/src/components/AddFab/AddExperimentFab.tsx +++ b/app/src/components/AddFab/AddExperimentFab.tsx @@ -1,13 +1,13 @@ import AddFabView from 'components/AddFab/AddFab.View'; import { AddFabDispatchProps } from 'components/AddFab/AddFabProps'; import { connect } from 'react-redux'; -import { openDialog } from 'store/actions/AddExperimentDialogStoreActions'; +import { openAddDialog } from 'store/actions/ExperimentDialogStoreActions'; import { SnowmanDispatch } from 'store/messages'; const mapDispatchToProps = ( dispatch: SnowmanDispatch ): AddFabDispatchProps => ({ - clickOnFab: (): void => dispatch(openDialog()), + clickOnFab: (): void => dispatch(openAddDialog()), }); const AddExperimentFab = connect(null, mapDispatchToProps)(AddFabView); diff --git a/app/src/components/ExperimentDialog/ExperimentDialog.View.tsx b/app/src/components/ExperimentDialog/ExperimentDialog.View.tsx index 4e4cd674..f38ba643 100644 --- a/app/src/components/ExperimentDialog/ExperimentDialog.View.tsx +++ b/app/src/components/ExperimentDialog/ExperimentDialog.View.tsx @@ -21,6 +21,7 @@ import experimentFileFormatEnum from 'types/ExperimentFileFormats'; const ExperimentDialogView = ({ isOpen, + isAddDialog, closeDialog, clickOnCancel, experimentName, @@ -32,13 +33,13 @@ const ExperimentDialogView = ({ changeExperimentName, changeExperimentFileFormat, clickOnMatchingSolutionTag, - addExperiment, + clickOnSubmit, isValidForm, selectedFiles, changeSelectedFiles, }: ExperimentDialogProps): JSX.Element => ( @@ -61,6 +62,12 @@ const ExperimentDialogView = ({ placeholder="e.g. Randomly assigned pairs for testing purposes." /> + {!isAddDialog ? ( + + Note: If you do not give a new experiment file, we do not change the + existing one + + ) : null} File Format: - Add + {isAddDialog ? 'Add' : 'Update'} { + if ( + state.ExperimentDialogStore.experimentName.length < 1 || + state.ExperimentDialogStore.selectedTags.length !== 1 + ) + return false; + if (state.ExperimentDialogStore.dialogType === DialogTypes.ADD_DIALOG) { + if (state.ExperimentDialogStore.selectedFiles.length === 0) return false; + } + return true; +}; + const mapStateToProps = (state: Store): ExperimentDialogStateProps => ({ - isOpen: state.AddExperimentDialogStore.isOpen, - experimentName: state.AddExperimentDialogStore.experimentName, - experimentDescription: state.AddExperimentDialogStore.experimentDescription, + isOpen: state.ExperimentDialogStore.isOpen, + isAddDialog: state.ExperimentDialogStore.experimentId === null, + experimentName: state.ExperimentDialogStore.experimentName, + experimentDescription: state.ExperimentDialogStore.experimentDescription, tags: state.AlgorithmsStore.algorithms.map( (anAlgorithm: Algorithm): string => anAlgorithm.name ), - selectedTags: state.AddExperimentDialogStore.selectedTags, - isValidForm: - state.AddExperimentDialogStore.experimentName.length > 0 && - state.AddExperimentDialogStore.selectedTags.length === 1 && - state.AddExperimentDialogStore.selectedFiles.length > 0, - selectedFiles: state.AddExperimentDialogStore.selectedFiles, - experimentFileFormat: state.AddExperimentDialogStore.experimentFileFormat, + selectedTags: state.ExperimentDialogStore.selectedTags, + isValidForm: isValidExperimentDialog(state), + selectedFiles: state.ExperimentDialogStore.selectedFiles, + experimentFileFormat: state.ExperimentDialogStore.experimentFileFormat, }); const mapDispatchToProps = ( @@ -51,8 +62,8 @@ const mapDispatchToProps = ( dispatch(changeFileFormat(event.detail.value as experimentFileFormatEnum)), clickOnMatchingSolutionTag: (aTag: string): void => dispatch(clickOnMatchingSolutionTag(aTag)), - addExperiment: (): void => { - dispatch(addNewExperiment()).then(); + clickOnSubmit: (): void => { + dispatch(addOrUpdateExperiment()).then(); }, changeSelectedFiles: (event: ChangeEvent): void => dispatch( diff --git a/app/src/components/ExperimentDialog/ExperimentDialogProps.ts b/app/src/components/ExperimentDialog/ExperimentDialogProps.ts index 7201bd57..119e8232 100644 --- a/app/src/components/ExperimentDialog/ExperimentDialogProps.ts +++ b/app/src/components/ExperimentDialog/ExperimentDialogProps.ts @@ -9,12 +9,13 @@ export interface ExperimentDialogDispatchProps { changeExperimentDescription(event: IonChangeEvent): void; changeExperimentFileFormat(event: IonChangeEvent): void; clickOnMatchingSolutionTag(aTag: string): void; - addExperiment(): void; + clickOnSubmit(): void; changeSelectedFiles(event: ChangeEvent): void; } export interface ExperimentDialogStateProps { isOpen: boolean; + isAddDialog: boolean; experimentName: string; experimentDescription: string; experimentFileFormat: experimentFileFormatEnum; diff --git a/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx b/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx index 258594d9..028eaa9d 100644 --- a/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx +++ b/app/src/pages/ExperimentsPage/ExperimentsPage.View.tsx @@ -14,6 +14,7 @@ const ExperimentsPageView = ({ clickOnTag, clickOnExperiment, deleteExperiment, + editExperiment, loadExperiments, }: ExperimentsPageProps): JSX.Element => { useEffect((): void => loadExperiments(), [loadExperiments]); @@ -39,6 +40,7 @@ const ExperimentsPageView = ({ clickOnCard={clickOnExperiment} selected={selectedExperiments} deleteCardHandler={deleteExperiment} + editCardHandler={editExperiment} multiple={true} /> diff --git a/app/src/pages/ExperimentsPage/ExperimentsPage.tsx b/app/src/pages/ExperimentsPage/ExperimentsPage.tsx index 23aaf96e..4fd263f6 100644 --- a/app/src/pages/ExperimentsPage/ExperimentsPage.tsx +++ b/app/src/pages/ExperimentsPage/ExperimentsPage.tsx @@ -6,6 +6,7 @@ import { ExperimentsPageStateProps, } from 'pages/ExperimentsPage/ExperimentsPageProps'; import { connect } from 'react-redux'; +import { openChangeDialog } from 'store/actions/ExperimentDialogStoreActions'; import { clickOnExperiment, clickOnExperimentTag, @@ -71,6 +72,9 @@ const mapDispatchToProps = ( deleteExperiment(anExperimentId: number): void { dispatch(deleteExperiment(anExperimentId)).then(); }, + editExperiment(anExperimentId: number) { + dispatch(openChangeDialog(anExperimentId)).then(); + }, }); const ExperimentsPage = connect( diff --git a/app/src/pages/ExperimentsPage/ExperimentsPageProps.ts b/app/src/pages/ExperimentsPage/ExperimentsPageProps.ts index 087bae11..92b6501c 100644 --- a/app/src/pages/ExperimentsPage/ExperimentsPageProps.ts +++ b/app/src/pages/ExperimentsPage/ExperimentsPageProps.ts @@ -12,6 +12,7 @@ export interface ExperimentsPageDispatchProps { clickOnExperiment(anExperimentId: number): void; loadExperiments(): void; deleteExperiment(anExperimentId: number): void; + editExperiment(anExperimentId: number): void; } export type ExperimentsPageProps = ExperimentsPageDispatchProps & diff --git a/app/src/store/actions/AddExperimentDialogStoreActions.ts b/app/src/store/actions/AddExperimentDialogStoreActions.ts deleted file mode 100644 index f11b71dd..00000000 --- a/app/src/store/actions/AddExperimentDialogStoreActions.ts +++ /dev/null @@ -1,126 +0,0 @@ -import 'types/ExperimentFileFormats'; - -import { ExperimentsApi } from 'api'; -import { AddExperimentDialogStoreActionTypes as actionTypes } from 'store/actions/actionTypes'; -import { getExperiments } from 'store/actions/ExperimentsStoreActions'; -import { SnowmanDispatch, SnowmanThunkAction } from 'store/messages'; -import { store } from 'store/store'; -import experimentFileFormatEnum from 'types/ExperimentFileFormats'; -import { getAlgorithmIdFromTag } from 'utils/algorithmHelpers'; -import { - easyPrimitiveAction, - easyPrimitiveActionReturn, -} from 'utils/easyActionsFactory'; -import RequestHandler from 'utils/requestHandler'; -import { - SUCCESS_TO_ADD_NEW_EXPERIMENT, - SUCCESS_TO_UPLOAD_EXPERIMENT_FILE, -} from 'utils/statusMessages'; - -export const openDialog = (): easyPrimitiveActionReturn => - easyPrimitiveAction({ - type: actionTypes.OPEN_DIALOG, - // reducer ignores payload - payload: false, - }); - -export const closeDialog = (): easyPrimitiveActionReturn => - easyPrimitiveAction({ - type: actionTypes.CLOSE_DIALOG, - // reducer ignores payload - payload: false, - }); - -export const changeExperimentName = ( - aName: string -): easyPrimitiveActionReturn => - easyPrimitiveAction({ - type: actionTypes.CHANGE_EXPERIMENT_NAME, - payload: aName, - }); - -export const changeExperimentDescription = ( - aDescription: string -): easyPrimitiveActionReturn => - easyPrimitiveAction({ - type: actionTypes.CHANGE_EXPERIMENT_DESCRIPTION, - payload: aDescription, - }); - -export const changeFileFormat = ( - aFormat: experimentFileFormatEnum -): easyPrimitiveActionReturn => - easyPrimitiveAction({ - type: actionTypes.CHANGE_EXPERIMENT_FORMAT, - payload: aFormat, - }); - -export const resetDialog = (): easyPrimitiveActionReturn => - easyPrimitiveAction({ - type: actionTypes.RESET_DIALOG, - // payload is not used - payload: true, - }); - -export const clickOnMatchingSolutionTag = ( - aTag: string -): easyPrimitiveActionReturn => - easyPrimitiveAction({ - type: actionTypes.CLICK_ON_MATCHING_SOLUTION_TAG, - payload: aTag, - }); - -export const changeSelectedFiles = (files: File[]): easyPrimitiveActionReturn => - easyPrimitiveAction({ - type: actionTypes.CHANGE_SELECTED_FILES, - payload: files, - }); - -const createNewExperiment = (): SnowmanThunkAction> => async ( - dispatch: SnowmanDispatch -): Promise => - RequestHandler( - () => - new ExperimentsApi().addExperiment({ - experimentValues: { - name: store.getState().AddExperimentDialogStore.experimentName, - description: store.getState().AddExperimentDialogStore - .experimentDescription, - datasetId: store.getState().DatasetsStore.selectedDataset?.id ?? -1, - algorithmId: getAlgorithmIdFromTag( - store.getState().AddExperimentDialogStore.selectedTags[0] ?? [], - store.getState().AlgorithmsStore.algorithms - ), - }, - }), - dispatch, - SUCCESS_TO_ADD_NEW_EXPERIMENT - ); - -export const uploadExperimentFile = ( - id: number -): SnowmanThunkAction> => async ( - dispatch: SnowmanDispatch -): Promise => - RequestHandler( - () => - new ExperimentsApi().setExperimentFile({ - experimentId: id, - format: store.getState().AddExperimentDialogStore.experimentFileFormat, - body: store.getState().AddExperimentDialogStore - .selectedFiles[0] as Blob, - }), - dispatch, - SUCCESS_TO_UPLOAD_EXPERIMENT_FILE, - true - ); - -export const addNewExperiment = (): SnowmanThunkAction> => async ( - dispatch: SnowmanDispatch -): Promise => { - dispatch(createNewExperiment()) - .then((id: number): Promise => dispatch(uploadExperimentFile(id))) - .then((): Promise => dispatch(getExperiments())) - .then((): void => dispatch(resetDialog())) - .finally((): void => dispatch(closeDialog())); -}; diff --git a/app/src/store/actions/ExperimentDialogStoreActions.ts b/app/src/store/actions/ExperimentDialogStoreActions.ts new file mode 100644 index 00000000..b22a88f0 --- /dev/null +++ b/app/src/store/actions/ExperimentDialogStoreActions.ts @@ -0,0 +1,199 @@ +import 'types/ExperimentFileFormats'; + +import { Experiment, ExperimentsApi } from 'api'; +import { ExperimentDialogStoreActionTypes as actionTypes } from 'store/actions/actionTypes'; +import { getExperiments } from 'store/actions/ExperimentsStoreActions'; +import { SnowmanDispatch, SnowmanThunkAction } from 'store/messages'; +import { store } from 'store/store'; +import experimentFileFormatEnum from 'types/ExperimentFileFormats'; +import { getAlgorithmIdFromTag } from 'utils/algorithmHelpers'; +import { MagicNotPossibleId } from 'utils/constants'; +import { + easyPrimitiveAction, + easyPrimitiveActionReturn, +} from 'utils/easyActionsFactory'; +import RequestHandler from 'utils/requestHandler'; +import { + SUCCESS_TO_ADD_NEW_EXPERIMENT, + SUCCESS_TO_UPDATE_EXPERIMENT, + SUCCESS_TO_UPLOAD_EXPERIMENT_FILE, +} from 'utils/statusMessages'; + +export const openAddDialog = (): easyPrimitiveActionReturn => + easyPrimitiveAction({ + type: actionTypes.OPEN_ADD_DIALOG, + // reducer ignores payload + payload: false, + }); + +export const openChangeDialog = ( + experimentId: number +): SnowmanThunkAction> => async ( + dispatch: SnowmanDispatch +): Promise => { + return RequestHandler( + (): Promise => + new ExperimentsApi() + .getExperiment({ experimentId: experimentId }) + .then((anExperiment: Experiment): void => { + dispatch({ + type: actionTypes.OPEN_CHANGE_DIALOG, + payload: anExperiment, + optionalPayload: store.getState().AlgorithmsStore.algorithms, + }); + }), + dispatch + ); +}; + +export const closeDialog = (): easyPrimitiveActionReturn => + easyPrimitiveAction({ + type: actionTypes.CLOSE_DIALOG, + // reducer ignores payload + payload: false, + }); + +export const changeExperimentName = ( + aName: string +): easyPrimitiveActionReturn => + easyPrimitiveAction({ + type: actionTypes.CHANGE_EXPERIMENT_NAME, + payload: aName, + }); + +export const changeExperimentDescription = ( + aDescription: string +): easyPrimitiveActionReturn => + easyPrimitiveAction({ + type: actionTypes.CHANGE_EXPERIMENT_DESCRIPTION, + payload: aDescription, + }); + +export const changeFileFormat = ( + aFormat: experimentFileFormatEnum +): easyPrimitiveActionReturn => + easyPrimitiveAction({ + type: actionTypes.CHANGE_EXPERIMENT_FORMAT, + payload: aFormat, + }); + +export const resetDialog = (): easyPrimitiveActionReturn => + easyPrimitiveAction({ + type: actionTypes.RESET_DIALOG, + // payload is not used + payload: true, + }); + +export const clickOnMatchingSolutionTag = ( + aTag: string +): easyPrimitiveActionReturn => + easyPrimitiveAction({ + type: actionTypes.CLICK_ON_MATCHING_SOLUTION_TAG, + payload: aTag, + }); + +export const changeSelectedFiles = (files: File[]): easyPrimitiveActionReturn => + easyPrimitiveAction({ + type: actionTypes.CHANGE_SELECTED_FILES, + payload: files, + }); + +const createNewExperiment = (): SnowmanThunkAction> => async ( + dispatch: SnowmanDispatch +): Promise => + RequestHandler( + () => + new ExperimentsApi().addExperiment({ + experimentValues: { + name: store.getState().ExperimentDialogStore.experimentName, + description: store.getState().ExperimentDialogStore + .experimentDescription, + datasetId: store.getState().DatasetsStore.selectedDataset?.id ?? -1, + algorithmId: getAlgorithmIdFromTag( + store.getState().ExperimentDialogStore.selectedTags[0] ?? [], + store.getState().AlgorithmsStore.algorithms + ), + }, + }), + dispatch, + SUCCESS_TO_ADD_NEW_EXPERIMENT + ); + +const uploadExperimentFile = ( + id?: number +): SnowmanThunkAction> => async ( + dispatch: SnowmanDispatch +): Promise => { + const willUpload = + store.getState().ExperimentDialogStore.selectedFiles.length > 0; + return RequestHandler( + () => + new ExperimentsApi().setExperimentFile({ + experimentId: + id ?? + store.getState().ExperimentDialogStore.experimentId ?? + MagicNotPossibleId, + format: store.getState().ExperimentDialogStore.experimentFileFormat, + body: store.getState().ExperimentDialogStore.selectedFiles[0] as Blob, + }), + dispatch, + willUpload ? SUCCESS_TO_UPLOAD_EXPERIMENT_FILE : undefined, + true + ); +}; + +const editExistingExperiment = (): SnowmanThunkAction> => async ( + dispatch: SnowmanDispatch +): Promise => { + return RequestHandler( + (): Promise => + new ExperimentsApi().setExperiment({ + experimentId: + store.getState().ExperimentDialogStore.experimentId ?? + MagicNotPossibleId, + experimentValues: { + name: store.getState().ExperimentDialogStore.experimentName, + description: store.getState().ExperimentDialogStore + .experimentDescription, + datasetId: + store.getState().DatasetsStore.selectedDataset?.id ?? + MagicNotPossibleId, + algorithmId: getAlgorithmIdFromTag( + store.getState().ExperimentDialogStore.selectedTags[0] ?? 'Unknown', + store.getState().AlgorithmsStore.algorithms + ), + }, + }), + dispatch, + SUCCESS_TO_UPDATE_EXPERIMENT + ); +}; + +const addNewExperiment = (): SnowmanThunkAction> => async ( + dispatch: SnowmanDispatch +): Promise => { + dispatch(createNewExperiment()) + .then((id: number): Promise => dispatch(uploadExperimentFile(id))) + .then((): Promise => dispatch(getExperiments())) + .then((): void => dispatch(resetDialog())) + .finally((): void => dispatch(closeDialog())); +}; + +const updateExistingExperiment = (): SnowmanThunkAction< + Promise +> => async (dispatch: SnowmanDispatch): Promise => { + dispatch(editExistingExperiment()) + .then((): Promise => dispatch(uploadExperimentFile())) + .then((): Promise => dispatch(getExperiments())) + .then((): void => dispatch(resetDialog())) + .finally((): void => dispatch(closeDialog())); +}; + +export const addOrUpdateExperiment = (): SnowmanThunkAction< + Promise +> => async (dispatch: SnowmanDispatch): Promise => { + if (store.getState().ExperimentDialogStore.experimentId === null) { + return dispatch(addNewExperiment()); + } + return dispatch(updateExistingExperiment()); +}; diff --git a/app/src/store/actions/actionTypes.ts b/app/src/store/actions/actionTypes.ts index 433e2f17..3f40951d 100644 --- a/app/src/store/actions/actionTypes.ts +++ b/app/src/store/actions/actionTypes.ts @@ -30,8 +30,9 @@ export const AlgorithmsStoreActionTypes = { SET_ALL_ALGORITHMS: 'ALGORITHMS_STORE_ACTION-SET_ALL_ALGORITHMS', }; -export const AddExperimentDialogStoreActionTypes = { - OPEN_DIALOG: 'ADD_EXPERIMENT_DIALOG_STORE_ACTION-OPEN_DIALOG', +export const ExperimentDialogStoreActionTypes = { + OPEN_ADD_DIALOG: 'ADD_EXPERIMENT_DIALOG_STORE_ACTION-OPEN_ADD_DIALOG', + OPEN_CHANGE_DIALOG: '', CLOSE_DIALOG: 'ADD_EXPERIMENT_DIALOG_STORE_ACTION-CLOSE_DIALOG', CHANGE_EXPERIMENT_NAME: 'ADD_EXPERIMENT_DIALOG_STORE_ACTION-CHANGE_EXPERIMENT_NAME', diff --git a/app/src/store/messages.ts b/app/src/store/messages.ts index f8fdf632..262e026b 100644 --- a/app/src/store/messages.ts +++ b/app/src/store/messages.ts @@ -11,21 +11,25 @@ import { Store } from 'store/models'; import { DatasetTypes } from 'types/DatasetTypes'; import experimentFileFormatEnum from 'types/ExperimentFileFormats'; +type ActionPayload = + | Metric[] + | ExperimentIntersection + | string + | string[] + | boolean + | File[] + | DatasetTypes + | experimentFileFormatEnum + | Algorithm[] + | Dataset + | Dataset[] + | number + | Experiment[] + | Experiment; + export interface SnowmanAction extends Action { - payload: - | Metric[] - | ExperimentIntersection - | string - | string[] - | boolean - | File[] - | DatasetTypes - | experimentFileFormatEnum - | Algorithm[] - | Dataset - | Dataset[] - | number - | Experiment[]; + payload: ActionPayload; + optionalPayload?: ActionPayload; } export type SnowmanThunkAction = ThunkAction; diff --git a/app/src/store/models.ts b/app/src/store/models.ts index 9881fee2..08a60bb4 100644 --- a/app/src/store/models.ts +++ b/app/src/store/models.ts @@ -35,8 +35,10 @@ export interface DatasetDialogStore { selectedFiles: File[]; } -export interface AddExperimentDialogStore { +export interface ExperimentDialogStore { + dialogType: DialogTypes; isOpen: boolean; + experimentId: number | null; experimentName: string; experimentDescription: string; experimentFileFormat: experimentFileFormatEnum; @@ -83,7 +85,7 @@ export interface Store { ExperimentsStore: ExperimentsStore; AlgorithmsStore: AlgorithmsStore; DatasetDialogStore: DatasetDialogStore; - AddExperimentDialogStore: AddExperimentDialogStore; + ExperimentDialogStore: ExperimentDialogStore; AlgorithmDialogStore: AlgorithmDialogStore; GlobalIndicatorStore: GlobalIndicatorStore; MetricsStore: MetricsStore; diff --git a/app/src/store/reducers/AddExperimentDialogReducer.ts b/app/src/store/reducers/ExperimentDialogReducer.ts similarity index 52% rename from app/src/store/reducers/AddExperimentDialogReducer.ts rename to app/src/store/reducers/ExperimentDialogReducer.ts index a5b06f89..0c443766 100644 --- a/app/src/store/reducers/AddExperimentDialogReducer.ts +++ b/app/src/store/reducers/ExperimentDialogReducer.ts @@ -1,11 +1,16 @@ -import { AddExperimentDialogStoreActionTypes as actionTypes } from 'store/actions/actionTypes'; +import { Algorithm, Experiment } from 'api'; +import { ExperimentDialogStoreActionTypes as actionTypes } from 'store/actions/actionTypes'; import { SnowmanAction } from 'store/messages'; -import { AddExperimentDialogStore } from 'store/models'; +import { ExperimentDialogStore } from 'store/models'; +import { DialogTypes } from 'types/DialogTypes'; import experimentFileFormatEnum from 'types/ExperimentFileFormats'; +import { getAlgorithmTagFromId } from 'utils/algorithmHelpers'; import { toggleSelectionArraySingleSelect } from 'utils/toggleSelectionArray'; -const initialState: AddExperimentDialogStore = { +const initialState: ExperimentDialogStore = { + dialogType: DialogTypes.ADD_DIALOG, isOpen: false, + experimentId: null, experimentName: '', experimentDescription: '', experimentFileFormat: experimentFileFormatEnum.Pilot, @@ -13,21 +18,42 @@ const initialState: AddExperimentDialogStore = { selectedFiles: [], }; -export const AddExperimentDialogReducer = ( - state: AddExperimentDialogStore = initialState, +export const ExperimentDialogReducer = ( + state: ExperimentDialogStore = initialState, action: SnowmanAction -): AddExperimentDialogStore => { +): ExperimentDialogStore => { switch (action.type) { - case actionTypes.OPEN_DIALOG: + case actionTypes.OPEN_ADD_DIALOG: return { ...state, + dialogType: DialogTypes.ADD_DIALOG, isOpen: true, }; - case actionTypes.CLOSE_DIALOG: + case actionTypes.OPEN_CHANGE_DIALOG: return { ...state, - isOpen: false, + dialogType: DialogTypes.CHANGE_DIALOG, + isOpen: true, + experimentId: (action.payload as Experiment).id, + experimentName: (action.payload as Experiment).name, + experimentDescription: (action.payload as Experiment).description ?? '', + selectedTags: [ + getAlgorithmTagFromId( + (action.payload as Experiment).algorithmId, + action.optionalPayload as Algorithm[] + ), + ], }; + case actionTypes.CLOSE_DIALOG: + if (state.dialogType === DialogTypes.ADD_DIALOG) + // Only keep current state for add dialog + return { + ...state, + isOpen: false, + }; + else { + return initialState; + } case actionTypes.CHANGE_EXPERIMENT_NAME: return { ...state, diff --git a/app/src/store/reducers/rootReducer.ts b/app/src/store/reducers/rootReducer.ts index 2461b0b2..fab5f6dd 100644 --- a/app/src/store/reducers/rootReducer.ts +++ b/app/src/store/reducers/rootReducer.ts @@ -1,9 +1,9 @@ import { combineReducers } from 'redux'; -import { AddExperimentDialogReducer } from 'store/reducers/AddExperimentDialogReducer'; import { AlgorithmDialogReducer } from 'store/reducers/AlgorithmDialogReducer'; import { AlgorithmsReducer } from 'store/reducers/AlgorithmsReducer'; import { DatasetDialogReducer } from 'store/reducers/DatasetDialogReducer'; import { DatasetsReducer } from 'store/reducers/DatasetsReducer'; +import { ExperimentDialogReducer } from 'store/reducers/ExperimentDialogReducer'; import { ExperimentsReducer } from 'store/reducers/ExperimentsReducer'; import { GlobalIndicatorReducer } from 'store/reducers/GlobalIndicatorReducer'; import { InputChipReducer } from 'store/reducers/InputChipReducer'; @@ -14,7 +14,7 @@ export const rootReducer = combineReducers({ ExperimentsStore: ExperimentsReducer, AlgorithmsStore: AlgorithmsReducer, DatasetDialogStore: DatasetDialogReducer, - AddExperimentDialogStore: AddExperimentDialogReducer, + ExperimentDialogStore: ExperimentDialogReducer, AlgorithmDialogStore: AlgorithmDialogReducer, GlobalIndicatorStore: GlobalIndicatorReducer, MetricsStore: MetricsReducer, diff --git a/app/src/utils/statusMessages.ts b/app/src/utils/statusMessages.ts index afeac085..5c0aa91e 100644 --- a/app/src/utils/statusMessages.ts +++ b/app/src/utils/statusMessages.ts @@ -20,3 +20,5 @@ export const SUCCESS_UPDATE_ALGORITHM = 'Successfully updated the designated matching solution!'; export const SUCCESS_UPDATE_DATASET = 'Successfully updated the designated dataset!'; +export const SUCCESS_TO_UPDATE_EXPERIMENT = + 'Successfully updated the designated experiment!'; From 0071da27c7afb0bb397fb10e27f191c162eab02a Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Tue, 2 Mar 2021 15:32:34 +0100 Subject: [PATCH 66/73] Make experiments editable --- app/src/store/actions/AlgorithmDialogStoreActions.ts | 4 ++-- app/src/store/actions/DatasetDialogStoreActions.ts | 4 ++-- app/src/utils/statusMessages.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/store/actions/AlgorithmDialogStoreActions.ts b/app/src/store/actions/AlgorithmDialogStoreActions.ts index c9a80c65..7914964e 100644 --- a/app/src/store/actions/AlgorithmDialogStoreActions.ts +++ b/app/src/store/actions/AlgorithmDialogStoreActions.ts @@ -11,7 +11,7 @@ import { import RequestHandler from 'utils/requestHandler'; import { SUCCESS_TO_ADD_NEW_ALGORITHM, - SUCCESS_UPDATE_ALGORITHM, + SUCCESS_TO_UPDATE_ALGORITHM, } from 'utils/statusMessages'; export const openAddDialog = (): easyPrimitiveActionReturn => @@ -115,7 +115,7 @@ const updateAlgorithm = (): SnowmanThunkAction> => async ( dispatch(getAlgorithms()); }), dispatch, - SUCCESS_UPDATE_ALGORITHM + SUCCESS_TO_UPDATE_ALGORITHM ); export const addOrUpdateAlgorithm = (): SnowmanThunkAction< diff --git a/app/src/store/actions/DatasetDialogStoreActions.ts b/app/src/store/actions/DatasetDialogStoreActions.ts index 76e7a35d..13753eae 100644 --- a/app/src/store/actions/DatasetDialogStoreActions.ts +++ b/app/src/store/actions/DatasetDialogStoreActions.ts @@ -12,8 +12,8 @@ import { import RequestHandler from 'utils/requestHandler'; import { SUCCESS_TO_CREATE_NEW_DATASET, + SUCCESS_TO_UPDATE_DATASET, SUCCESS_TO_UPLOAD_DATASET_FILE, - SUCCESS_UPDATE_DATASET, } from 'utils/statusMessages'; import { getTagsFromDatasets } from 'utils/tagFactory'; @@ -187,7 +187,7 @@ const setExistingDataset = (): SnowmanThunkAction> => async ( }, }), dispatch, - SUCCESS_UPDATE_DATASET + SUCCESS_TO_UPDATE_DATASET ); const uploadDatasetFile = ( diff --git a/app/src/utils/statusMessages.ts b/app/src/utils/statusMessages.ts index 5c0aa91e..2446c9ed 100644 --- a/app/src/utils/statusMessages.ts +++ b/app/src/utils/statusMessages.ts @@ -16,9 +16,9 @@ export const SUCCESS_LOAD_METRICS_TUPLES = 'Successfully loaded metrics tuples!'; export const SUCCESS_LOAD_BINARY_METRICS = 'Successfully loaded binary metrics!'; -export const SUCCESS_UPDATE_ALGORITHM = +export const SUCCESS_TO_UPDATE_ALGORITHM = 'Successfully updated the designated matching solution!'; -export const SUCCESS_UPDATE_DATASET = +export const SUCCESS_TO_UPDATE_DATASET = 'Successfully updated the designated dataset!'; export const SUCCESS_TO_UPDATE_EXPERIMENT = 'Successfully updated the designated experiment!'; From d5fc6f89f9f62a502fd7ffe5494dfb7c14ad2a5d Mon Sep 17 00:00:00 2001 From: florian-papsdorf Date: Tue, 2 Mar 2021 15:50:46 +0100 Subject: [PATCH 67/73] Fix bug that only allows update experiment with new file --- .../actions/ExperimentDialogStoreActions.ts | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/app/src/store/actions/ExperimentDialogStoreActions.ts b/app/src/store/actions/ExperimentDialogStoreActions.ts index b22a88f0..4b4346e1 100644 --- a/app/src/store/actions/ExperimentDialogStoreActions.ts +++ b/app/src/store/actions/ExperimentDialogStoreActions.ts @@ -126,20 +126,23 @@ const uploadExperimentFile = ( ): Promise => { const willUpload = store.getState().ExperimentDialogStore.selectedFiles.length > 0; - return RequestHandler( - () => - new ExperimentsApi().setExperimentFile({ - experimentId: - id ?? - store.getState().ExperimentDialogStore.experimentId ?? - MagicNotPossibleId, - format: store.getState().ExperimentDialogStore.experimentFileFormat, - body: store.getState().ExperimentDialogStore.selectedFiles[0] as Blob, - }), - dispatch, - willUpload ? SUCCESS_TO_UPLOAD_EXPERIMENT_FILE : undefined, - true - ); + if (willUpload) { + return RequestHandler( + () => + new ExperimentsApi().setExperimentFile({ + experimentId: + id ?? + store.getState().ExperimentDialogStore.experimentId ?? + MagicNotPossibleId, + format: store.getState().ExperimentDialogStore.experimentFileFormat, + body: store.getState().ExperimentDialogStore.selectedFiles[0] as Blob, + }), + dispatch, + willUpload ? SUCCESS_TO_UPLOAD_EXPERIMENT_FILE : undefined, + true + ); + } + return Promise.resolve(); }; const editExistingExperiment = (): SnowmanThunkAction> => async ( From a221cbc57b279ce7314432f4c900b115b1125c58 Mon Sep 17 00:00:00 2001 From: phpfs Date: Tue, 2 Mar 2021 16:11:45 +0100 Subject: [PATCH 68/73] Include notice about uploads --- .../DatasetDialog/DatasetDialog.View.tsx | 10 ++++++++++ .../DatasetDialog/DatasetDialogStyles.css | 7 ++++++- .../ExperimentDialog/ExperimentDialog.View.tsx | 16 ++++++++++------ .../ExperimentDialog/ExperimentDialogStyles.css | 7 ++++++- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/app/src/components/DatasetDialog/DatasetDialog.View.tsx b/app/src/components/DatasetDialog/DatasetDialog.View.tsx index 75f67da6..d6df4b68 100644 --- a/app/src/components/DatasetDialog/DatasetDialog.View.tsx +++ b/app/src/components/DatasetDialog/DatasetDialog.View.tsx @@ -12,6 +12,7 @@ import { IonItemGroup, IonLabel, IonList, + IonNote, IonRow, IonSelect, IonSelectOption, @@ -188,6 +189,15 @@ const DatasetDialogView = ({ addNewTag={addNewTagCallback} /> + {!isAddDialog ? ( +
+ + Note: Uploading a file is optional here! +
+ If no file is selected, the stored records will remain unchanged. +
+
+ ) : null}
- {!isAddDialog ? ( - - Note: If you do not give a new experiment file, we do not change the - existing one - - ) : null} File Format: + {!isAddDialog ? ( +
+ + Note: Uploading a file is optional here! +
+ If no file is selected, the stored records will remain unchanged. +
+
+ ) : null}
Date: Tue, 2 Mar 2021 16:55:37 +0100 Subject: [PATCH 69/73] Improve upload notice --- .../DatasetDialog/DatasetDialog.View.tsx | 20 +++++++++---------- .../DatasetDialog/DatasetDialogStyles.css | 2 +- .../ExperimentDialog.View.tsx | 20 +++++++++---------- .../ExperimentDialogStyles.css | 2 +- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/app/src/components/DatasetDialog/DatasetDialog.View.tsx b/app/src/components/DatasetDialog/DatasetDialog.View.tsx index d6df4b68..ca1931fc 100644 --- a/app/src/components/DatasetDialog/DatasetDialog.View.tsx +++ b/app/src/components/DatasetDialog/DatasetDialog.View.tsx @@ -161,7 +161,7 @@ const DatasetDialogView = ({ - Source File*: + Source File: ) : null} + {!isAddDialog ? ( +
+ + Note: Uploading a file is optional here! +
+ If no file is selected, the stored records will remain unchanged. +
+
+ ) : null}
{tags.map( (aTag: string): JSX.Element => ( @@ -189,15 +198,6 @@ const DatasetDialogView = ({ addNewTag={addNewTagCallback} />
- {!isAddDialog ? ( -
- - Note: Uploading a file is optional here! -
- If no file is selected, the stored records will remain unchanged. -
-
- ) : null}
- Name/ID: + Name/ID*: + {!isAddDialog ? ( +
+ + Note: Uploading a file is optional here! +
+ If no file is selected, the stored records will remain unchanged. +
+
+ ) : null}
{tags.map( (aTag: string): JSX.Element => ( @@ -101,15 +110,6 @@ const ExperimentDialogView = ({ ) )}
- {!isAddDialog ? ( -
- - Note: Uploading a file is optional here! -
- If no file is selected, the stored records will remain unchanged. -
-
- ) : null}
Date: Tue, 2 Mar 2021 16:56:00 +0100 Subject: [PATCH 70/73] Improve file input --- app/src/components/FileInput/FileInput.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/components/FileInput/FileInput.tsx b/app/src/components/FileInput/FileInput.tsx index 490f34c7..55d113ab 100644 --- a/app/src/components/FileInput/FileInput.tsx +++ b/app/src/components/FileInput/FileInput.tsx @@ -20,9 +20,14 @@ const FileInput = ({ }; /* React.Fragment is required here to ensure the elements have correct parent! */ return ( - - - {selectedFiles.map((f: File): string => f.name).join(', ')} + <> + 0 ? 'primary' : 'medium'} + style={{ marginLeft: '8px' }} + > + {selectedFiles.length > 0 + ? selectedFiles.map((f: File): string => f.name).join(', ') + : '(none selected)'} @@ -36,7 +41,7 @@ const FileInput = ({ multiple={allowMultiple} ref={fileInput} /> - + ); }; From 5dc793a68d762fbc0e1a281057165b6313f1914c Mon Sep 17 00:00:00 2001 From: phpfs Date: Tue, 2 Mar 2021 17:03:20 +0100 Subject: [PATCH 71/73] Simply if-statement --- app/src/components/ExperimentDialog/ExperimentDialog.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/components/ExperimentDialog/ExperimentDialog.tsx b/app/src/components/ExperimentDialog/ExperimentDialog.tsx index bc526a78..a4847c02 100644 --- a/app/src/components/ExperimentDialog/ExperimentDialog.tsx +++ b/app/src/components/ExperimentDialog/ExperimentDialog.tsx @@ -29,10 +29,10 @@ const isValidExperimentDialog = (state: Store): boolean => { state.ExperimentDialogStore.selectedTags.length !== 1 ) return false; - if (state.ExperimentDialogStore.dialogType === DialogTypes.ADD_DIALOG) { - if (state.ExperimentDialogStore.selectedFiles.length === 0) return false; - } - return true; + return !( + state.ExperimentDialogStore.dialogType === DialogTypes.ADD_DIALOG && + state.ExperimentDialogStore.selectedFiles.length === 0 + ); }; const mapStateToProps = (state: Store): ExperimentDialogStateProps => ({ From c4011d87c6948c7d87063a1ec4613d82e13a496a Mon Sep 17 00:00:00 2001 From: phpfs Date: Tue, 2 Mar 2021 17:04:17 +0100 Subject: [PATCH 72/73] Remove comment about React.Fragment --- app/src/components/FileInput/FileInput.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/components/FileInput/FileInput.tsx b/app/src/components/FileInput/FileInput.tsx index 55d113ab..f5e37b14 100644 --- a/app/src/components/FileInput/FileInput.tsx +++ b/app/src/components/FileInput/FileInput.tsx @@ -18,7 +18,6 @@ const FileInput = ({ currentFileInput.click(); } }; - /* React.Fragment is required here to ensure the elements have correct parent! */ return ( <> Date: Tue, 2 Mar 2021 17:14:33 +0100 Subject: [PATCH 73/73] Increment version to 1.1.0 --- app/package.json | 3 +-- package.json | 4 ++-- wrapper/package.json | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/package.json b/app/package.json index cf216e0c..162a7eb3 100644 --- a/app/package.json +++ b/app/package.json @@ -2,10 +2,9 @@ "name": "snowman-app", "author": "Snowman Team", "description": "Compare data matching algorithms with each other", - "version": "1.0.0", + "version": "1.1.0", "license": "MIT", "homepage": "./", - "private": true, "dependencies": { "@ionic/react": "^5.0.7", "@ionic/react-router": "^5.0.7", diff --git a/package.json b/package.json index 7bfb2d6f..f17013cb 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "name": "snowman", "author": "Snowman Team", "description": "Compare data matching algorithms with each other", - "version": "1.0.0", + "version": "1.1.0", "license": "MIT", - "private": true, + "repository": "https://github.com/HPI-Information-Systems/snowman", "scripts": { "eslint": "eslint --ext .ts,.tsx .", "csslint": "stylelint \"./app/src/**/*.css\"", diff --git a/wrapper/package.json b/wrapper/package.json index 46ea537a..4359a737 100644 --- a/wrapper/package.json +++ b/wrapper/package.json @@ -2,9 +2,8 @@ "name": "snowman-wrapper", "author": "Snowman Team", "description": "Compare data matching algorithms with each other", - "version": "1.0.0", + "version": "1.1.0", "license": "MIT", - "private": true, "main": "dist/main.js", "scripts": { "erebuild": "run-script-os",