diff --git a/app/autocorrection.ts b/app/autocorrection.ts
index b12fd0d..3e0e173 100644
--- a/app/autocorrection.ts
+++ b/app/autocorrection.ts
@@ -64,7 +64,7 @@ export default class AutoCorrection {
);
});
- ipcMain.on(AUTOCORRECTION_CANCEL, (event: IpcMainEvent, arg) => {
+ ipcMain.on(AUTOCORRECTION_CANCEL, (event: IpcMainEvent) => {
const { sender } = event;
this.cancelRequest = true;
sender.send(AUTOCORRECTION_CANCEL_PENDING);
diff --git a/app/components/ConditionalCommentSettings.tsx b/app/components/ConditionalCommentSettings.tsx
index 26ccf24..0dbef38 100644
--- a/app/components/ConditionalCommentSettings.tsx
+++ b/app/components/ConditionalCommentSettings.tsx
@@ -2,16 +2,14 @@
/* eslint-disable @typescript-eslint/no-empty-interface */
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react';
-import { Theme, withStyles } from '@material-ui/core/styles';
import AddCircleIcon from '@material-ui/icons/AddCircle';
import Typography from '@material-ui/core/Typography';
import {
- Fade,
Grid,
IconButton,
Paper,
Slider,
- Tooltip,
+ Theme,
useTheme,
} from '@material-ui/core';
import { useDispatch, useSelector } from 'react-redux';
@@ -22,7 +20,12 @@ import {
settingsUpdateConditionalCommentValue,
} from '../model/SettingsSlice';
-function ValueLabelComponent(props: any) {
+function ValueLabelComponent(props: {
+ children;
+ value: number;
+ comments: string[];
+ theme: Theme;
+}) {
const { children, value, comments, theme } = props;
return (
<>
diff --git a/app/components/ConditionalCommentTextInput.tsx b/app/components/ConditionalCommentTextInput.tsx
index 2d68787..34c8f5c 100644
--- a/app/components/ConditionalCommentTextInput.tsx
+++ b/app/components/ConditionalCommentTextInput.tsx
@@ -2,7 +2,6 @@
/* eslint-disable react/prop-types */
import { IconButton, TextField } from '@material-ui/core';
import CancelIcon from '@material-ui/icons/Cancel';
-import { relative } from 'path';
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import ConditionalComment from '../model/ConditionalComment';
diff --git a/app/components/LoadingItem.tsx b/app/components/LoadingItem.tsx
index 4035c14..9464bca 100644
--- a/app/components/LoadingItem.tsx
+++ b/app/components/LoadingItem.tsx
@@ -2,7 +2,12 @@ import { Grid, CircularProgress, Typography } from '@material-ui/core';
import React from 'react';
import DoneIcon from '@material-ui/icons/Done';
-export default function LoadingItem(props: any): JSX.Element {
+export type LoadingItemProps = {
+ complete: boolean;
+ message: string;
+};
+
+export default function LoadingItem(props: LoadingItemProps): JSX.Element {
const { complete, message } = props;
const loadingIcon = complete ? (
diff --git a/app/components/LoadingItemList.tsx b/app/components/LoadingItemList.tsx
index 4d2d4ac..30b2943 100644
--- a/app/components/LoadingItemList.tsx
+++ b/app/components/LoadingItemList.tsx
@@ -1,16 +1,20 @@
import { List, ListItem } from '@material-ui/core';
import React from 'react';
-import LoadingItem from './LoadingItem';
+import LoadingItem, { LoadingItemProps } from './LoadingItem';
-export default function LoadingItemList(props: any): JSX.Element {
+type LoadingItemListProps = {
+ progress: (LoadingItemProps & { active: boolean })[];
+};
+
+export default function LoadingItemList(
+ props: LoadingItemListProps
+): JSX.Element {
const { progress } = props;
- const activeItems = progress.filter(
- (item: { active: boolean }) => item.active
- );
+ const activeItems = progress.filter((item) => item.active);
return (
- {activeItems.map((item: { message: string; complete: boolean }) => {
+ {activeItems.map((item) => {
return (
diff --git a/app/components/OutputFormatSelect.tsx b/app/components/OutputFormatSelect.tsx
index c4cd96d..4e7c572 100644
--- a/app/components/OutputFormatSelect.tsx
+++ b/app/components/OutputFormatSelect.tsx
@@ -6,7 +6,7 @@ import {
selectSettingsExport,
settingsSetExport,
} from '../model/SettingsSlice';
-import { ParserType } from '../parser/Parser';
+import ParserType from '../parser/ParserType';
const OutputFormatSelect = () => {
const dispatch = useDispatch();
diff --git a/app/containers/CorrectionViewPage.tsx b/app/containers/CorrectionViewPage.tsx
index bb68275..6f6c729 100644
--- a/app/containers/CorrectionViewPage.tsx
+++ b/app/containers/CorrectionViewPage.tsx
@@ -27,7 +27,6 @@ import {
} from '../model/Selectors';
import Sheet from '../model/Sheet';
-import { sheetsUpsertOne } from '../model/SheetSlice';
import { serializeTerm } from '../utils/Formatter';
import './SplitPane.css';
@@ -64,6 +63,7 @@ export default function CorrectionViewPage() {
);
}
};
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [index]);
function handleCloseDialog() {
diff --git a/app/containers/OverviewPage.tsx b/app/containers/OverviewPage.tsx
index 4506f24..bb907a5 100644
--- a/app/containers/OverviewPage.tsx
+++ b/app/containers/OverviewPage.tsx
@@ -2,6 +2,6 @@
import React from 'react';
import Overview from '../features/overview/Overview';
-export default function OverviewPage(props: any) {
+export default function OverviewPage(props) {
return ;
}
diff --git a/app/containers/Root.tsx b/app/containers/Root.tsx
index 86b17d2..226bb13 100644
--- a/app/containers/Root.tsx
+++ b/app/containers/Root.tsx
@@ -6,6 +6,7 @@ import { AnyAction, EnhancedStore } from '@reduxjs/toolkit';
import Providers from './Providers';
type Props = {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
store: EnhancedStore;
history: History;
};
diff --git a/app/containers/TitleBar.tsx b/app/containers/TitleBar.tsx
index fbefc3f..d8ade3d 100644
--- a/app/containers/TitleBar.tsx
+++ b/app/containers/TitleBar.tsx
@@ -8,6 +8,7 @@ import { useSelector } from 'react-redux';
import { useTheme, Snackbar } from '@material-ui/core';
import { remote } from 'electron';
import { Alert } from '@material-ui/lab';
+import { MenuItem } from 'frameless-titlebar/dist/title-bar/typings';
import {
selectRecentPaths,
selectWorkspacePath,
@@ -109,7 +110,7 @@ export default function TitleBar(props: TitleBarProps) {
recentPaths,
setOpenFileError,
setReload
- ) as any
+ ) as MenuItem[] | undefined
}
theme={{
bar: {
diff --git a/app/exporter.ts b/app/exporter.ts
index 77177fe..78dbf9d 100644
--- a/app/exporter.ts
+++ b/app/exporter.ts
@@ -11,10 +11,11 @@ import {
} from './constants/ExportIPC';
import ConditionalComment from './model/ConditionalComment';
import Correction from './model/Correction';
-import Parser, { ParserType } from './parser/Parser';
+import Parser from './parser/Parser';
import instanciateParser from './parser/ParserUtil';
import { serializeCorrection } from './utils/Formatter';
import { loadFilesFromWorkspaceMainProcess } from './utils/FileAccess';
+import ParserType from './parser/ParserType';
export interface ExportProgress {
steps: {
diff --git a/app/features/correction/CorrectionOverview.tsx b/app/features/correction/CorrectionOverview.tsx
index 7fb9263..ba4ff5f 100644
--- a/app/features/correction/CorrectionOverview.tsx
+++ b/app/features/correction/CorrectionOverview.tsx
@@ -49,6 +49,7 @@ export default function CorrectionOverview(props: CorrectionOverviewProps) {
correction.status === Status.Marked
)
);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [index]);
const handleClick = () => {
diff --git a/app/features/media-viewer/PDFViewer.tsx b/app/features/media-viewer/PDFViewer.tsx
index 54f3e24..219ea02 100644
--- a/app/features/media-viewer/PDFViewer.tsx
+++ b/app/features/media-viewer/PDFViewer.tsx
@@ -28,8 +28,10 @@ export default function PDFViewer(props: ViewerProps) {
const textLayers = document.querySelectorAll(
'.react-pdf__Page__textContent'
);
- textLayers.forEach((layer: any) => {
- const { style } = layer;
+ textLayers.forEach((layer) => {
+ const { style } = (layer as unknown) as {
+ style: { top: string; left: string; transform: string };
+ };
style.top = '0';
style.left = '0';
style.transform = '';
diff --git a/app/features/media-viewer/ViewerToolbar.tsx b/app/features/media-viewer/ViewerToolbar.tsx
index 8d0ed97..72861fb 100644
--- a/app/features/media-viewer/ViewerToolbar.tsx
+++ b/app/features/media-viewer/ViewerToolbar.tsx
@@ -113,6 +113,7 @@ export default function ViewerToolbar(props: ViewerToolbarProps) {
return () => {
window.removeEventListener('wheel', handleScrollEvent);
};
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
diff --git a/app/importer.ts b/app/importer.ts
index dbc4bd3..b3d8416 100644
--- a/app/importer.ts
+++ b/app/importer.ts
@@ -10,7 +10,7 @@ import {
} from 'electron';
import { normalize } from 'normalizr';
import Correction from './model/Correction';
-import Parser, { ParserType } from './parser/Parser';
+import Parser from './parser/Parser';
import instanciateParser from './parser/ParserUtil';
import {
IMPORT_CONFLICTS,
@@ -27,6 +27,7 @@ import {
getAllFilesInDirectory,
} from './utils/FileAccess';
import { CorrectionSchema } from './model/NormalizationSchema';
+import ParserType from './parser/ParserType';
export interface ImportProgress {
name: string;
@@ -82,6 +83,7 @@ export default class Importer {
// Create new workspace file if no workspace is allocated yet
if (workspacePath.length === 0) {
const p: string | undefined = dialog.showSaveDialogSync(this.mainWindow, {
+ defaultPath: `${Path.parse(path).name}.cor`,
filters: [{ name: 'Correctinator', extensions: ['cor'] }],
});
if (!p) {
@@ -162,7 +164,8 @@ export default class Importer {
const content = fs.readFileSync(importPath, Importer.ENCODING);
const correction: Correction = parser.deserialize(
content,
- Path.basename(Path.dirname(importPath))
+ Path.basename(Path.dirname(importPath)),
+ Path.basename(importPath)
);
// Were the submissions already imported?
@@ -224,7 +227,8 @@ export default class Importer {
const content = zipEntry.getData().toString(Importer.ENCODING);
const correction: Correction = parser.deserialize(
content,
- Path.basename(Path.dirname(zipEntry.entryName))
+ Path.basename(Path.dirname(zipEntry.entryName)),
+ Path.basename(zipEntry.entryName)
);
// Were the submissions already imported?
@@ -381,7 +385,8 @@ export default class Importer {
const parser: Parser = instanciateParser(c.parser);
const correction: Correction = parser.deserialize(
content,
- Path.basename(Path.dirname(c.path))
+ Path.basename(Path.dirname(c.path)),
+ Path.basename(c.path)
);
corrections.push(
this.ingestCorrectionFromZip(
@@ -407,7 +412,8 @@ export default class Importer {
const parser: Parser = instanciateParser(c.parser);
const correction: Correction = parser.deserialize(
content,
- Path.basename(Path.dirname(c.path))
+ Path.basename(Path.dirname(c.path)),
+ Path.basename(c.path)
);
corrections.push(
this.ingestCorrectionFromFolder(
diff --git a/app/menu.ts b/app/menu.ts
index fda31a5..9716e53 100644
--- a/app/menu.ts
+++ b/app/menu.ts
@@ -26,11 +26,6 @@ export default class MenuBuilder {
this.setupDevelopmentEnvironment();
}
- const template =
- process.platform === 'darwin'
- ? this.buildDarwinTemplate()
- : this.buildDefaultTemplate();
-
const menu = Menu.buildFromTemplate([]);
Menu.setApplicationMenu(menu);
diff --git a/app/menu/FileMenu.ts b/app/menu/FileMenu.ts
index 1dae56a..8a4877e 100644
--- a/app/menu/FileMenu.ts
+++ b/app/menu/FileMenu.ts
@@ -192,6 +192,7 @@ const buildFileMenu = (
},
{
label: 'Close file',
+ disabled: !workspace,
click: async () => {
unsavedChangesDialog('');
},
diff --git a/app/modals/AutoCorrectionModal.tsx b/app/modals/AutoCorrectionModal.tsx
index 0a54d3c..819dfb0 100644
--- a/app/modals/AutoCorrectionModal.tsx
+++ b/app/modals/AutoCorrectionModal.tsx
@@ -138,7 +138,7 @@ const AutoCorrectionModal: FC = ({ ...props }) => {
) => {
setAutoCorrectionProgress(progress);
};
- const handleAutoCorrectionCancelPending = (_event: IpcRendererEvent) => {
+ const handleAutoCorrectionCancelPending = () => {
setAutoCorrectionState(AutoCorrectionState.AUTOCORRECTION_CANCEL_PENDING);
};
@@ -236,9 +236,10 @@ const AutoCorrectionModal: FC = ({ ...props }) => {
handleProgress
);
};
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
- let content;
+ let content: JSX.Element | null;
switch (autoCorrectionState) {
case AutoCorrectionState.AUTOCORRECTION_STARTED:
diff --git a/app/modals/ExportModal.tsx b/app/modals/ExportModal.tsx
index 58b3297..61c3394 100644
--- a/app/modals/ExportModal.tsx
+++ b/app/modals/ExportModal.tsx
@@ -31,7 +31,6 @@ import DialogTitleWithCloseIcon from './DialogTitleWithCloseIcon';
import { ModalProps } from './ModalProvider';
import { selectCorrectionsBySheetId } from '../model/Selectors';
import * as ExportIPC from '../constants/ExportIPC';
-import { ParserType } from '../parser/Parser';
import { ExportProgress } from '../exporter';
import CircularProgressWithLabel from '../components/CircularProgressWithLabel';
import Status from '../model/Status';
@@ -41,8 +40,7 @@ import {
} from '../model/SettingsSlice';
import OutputFormatSelect from '../components/OutputFormatSelect';
import ConditionalCommentSettings from '../components/ConditionalCommentSettings';
-import Correction from '../model/Correction';
-import { selectSheetById } from '../model/SheetSlice';
+import ParserType from '../parser/ParserType';
type ExportModalProps = ModalProps & {
sheetId: string;
diff --git a/app/modals/ImportModal.tsx b/app/modals/ImportModal.tsx
index 8b90090..fcd1b48 100644
--- a/app/modals/ImportModal.tsx
+++ b/app/modals/ImportModal.tsx
@@ -26,12 +26,12 @@ import {
import { CorrectionsSchema } from '../model/NormalizationSchema';
import { loadCorrections } from '../model/CorrectionsSlice';
import CircularProgressWithLabel from '../components/CircularProgressWithLabel';
-import { ParserType } from '../parser/Parser';
// eslint-disable-next-line import/no-cycle
import OverwriteDuplicateSubmissionsDialog from '../dialogs/OverwriteDuplicateSubmissionsDialog';
import ConfirmationDialog from '../dialogs/ConfirmationDialog';
import { save } from '../utils/FileAccess';
import { selectSettingsGeneral } from '../model/SettingsSlice';
+import ParserType from '../parser/ParserType';
type ImportModalProps = ModalProps & {
path?: string;
@@ -127,6 +127,7 @@ const ImportModal: FC = ({ ...props }) => {
ipcRenderer.removeListener(ImportIPC.IMPORT_FAILED, handleImportFailed);
ipcRenderer.removeListener(ImportIPC.IMPORT_PROGRESS, handleProgress);
};
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
let content;
diff --git a/app/modals/ModalProvider.tsx b/app/modals/ModalProvider.tsx
index 4e6395d..ea8dd37 100644
--- a/app/modals/ModalProvider.tsx
+++ b/app/modals/ModalProvider.tsx
@@ -16,8 +16,9 @@ const ModalContext = createContext<
export default function ModalProvider(props: ModalProviderProps) {
const { children } = props;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
const [Modal, setModal] = useState(null);
- const [modalOptions, setModalOptions] = useState(null);
+ const [modalOptions, setModalOptions] = useState(null);
const openModal = (
component: FC,
diff --git a/app/modals/ReleaseNotesModal.tsx b/app/modals/ReleaseNotesModal.tsx
index 6abcd8f..c1640b1 100644
--- a/app/modals/ReleaseNotesModal.tsx
+++ b/app/modals/ReleaseNotesModal.tsx
@@ -52,6 +52,7 @@ const ReleaseNotesModal: FC = ({ ...props }) => {
.catch(() => {
setLoading(false);
});
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
let content: JSX.Element;
diff --git a/app/model/AnnotationSlice.tsx b/app/model/AnnotationSlice.tsx
index 2b0542c..5cbf6c5 100644
--- a/app/model/AnnotationSlice.tsx
+++ b/app/model/AnnotationSlice.tsx
@@ -29,7 +29,7 @@ const slice = createSlice({
adapter.upsertMany(state, action.payload.annotations);
}
},
- [deleteEntities.type]: (state, action) => {
+ [deleteEntities.type]: (state) => {
adapter.removeAll(state);
},
},
@@ -53,6 +53,8 @@ export const {
selectEntities: selectAnnotationEntities,
selectAll: selectAllAnnotations,
selectTotal: selectTotalAnnotations,
-} = adapter.getSelectors((state: any) => state.annotations);
+} = adapter.getSelectors(
+ (state: { annotations: EntityState }) => state.annotations
+);
export default slice.reducer;
diff --git a/app/model/CommentSlice.tsx b/app/model/CommentSlice.tsx
index e5bad56..232776a 100644
--- a/app/model/CommentSlice.tsx
+++ b/app/model/CommentSlice.tsx
@@ -1,5 +1,9 @@
/* eslint-disable import/no-cycle */
-import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
+import {
+ createEntityAdapter,
+ createSlice,
+ EntityState,
+} from '@reduxjs/toolkit';
import CommentEntity from './CommentEntity';
import { loadCorrections, deleteEntities } from './CorrectionsSlice';
@@ -49,6 +53,8 @@ export const {
selectEntities: selectCommentsEntities,
selectAll: selectAllComments,
selectTotal: selectTotalComments,
-} = adapter.getSelectors((state: any) => state.comments);
+} = adapter.getSelectors(
+ (state: { comments: EntityState }) => state.comments
+);
export default slice.reducer;
diff --git a/app/model/Correction.tsx b/app/model/Correction.tsx
index db080df..5ba3c29 100644
--- a/app/model/Correction.tsx
+++ b/app/model/Correction.tsx
@@ -6,6 +6,7 @@ import Note from './Note';
import Annotation from './Annotation';
import Submission from './Submission';
import Rating from './Rating';
+import ParserOptions from '../parser/ParserOptions';
type Correction = {
id: string;
@@ -18,6 +19,7 @@ type Correction = {
annotation?: Annotation;
timeElapsed?: number;
autoCorrectionAttempted?: boolean;
+ parserOptions?: ParserOptions;
};
export default Correction;
diff --git a/app/model/CorrectionsSlice.tsx b/app/model/CorrectionsSlice.tsx
index 6a558ff..3834678 100644
--- a/app/model/CorrectionsSlice.tsx
+++ b/app/model/CorrectionsSlice.tsx
@@ -3,6 +3,7 @@ import {
createAction,
createEntityAdapter,
createSlice,
+ EntityState,
} from '@reduxjs/toolkit';
import { normalize } from 'normalizr';
import Correction from './Correction';
@@ -56,7 +57,9 @@ export const {
selectEntities: selectCorrectionEntities,
selectAll: selectAllCorrections,
selectTotal: selectTotalCorrections,
-} = adapter.getSelectors((state: any) => state.corrections);
+} = adapter.getSelectors(
+ (state: { corrections: EntityState }) => state.corrections
+);
export function upsertCorrection(correction: Correction) {
return (dispatch) => {
diff --git a/app/model/CorrectorSlice.tsx b/app/model/CorrectorSlice.tsx
index 3ee56f7..09f7900 100644
--- a/app/model/CorrectorSlice.tsx
+++ b/app/model/CorrectorSlice.tsx
@@ -29,7 +29,7 @@ const slice = createSlice({
[loadCorrections.type]: (state, action) => {
adapter.upsertMany(state, action.payload.correctors);
},
- [deleteEntities.type]: (state, action) => {
+ [deleteEntities.type]: (state) => {
adapter.removeAll(state);
},
},
@@ -41,7 +41,9 @@ export const {
selectEntities: selectCorrectorEntities,
selectAll: selectAllCorrectors,
selectTotal: selectTotalCorrectors,
-} = adapter.getSelectors((state: any) => state.correctors);
+} = adapter.getSelectors(
+ (state: { correctors: EntityState }) => state.correctors
+);
export const {
correctorsAddOne,
diff --git a/app/model/CourseSlice.tsx b/app/model/CourseSlice.tsx
index 1c72ffd..4b01afb 100644
--- a/app/model/CourseSlice.tsx
+++ b/app/model/CourseSlice.tsx
@@ -29,7 +29,7 @@ const slice = createSlice({
[loadCorrections.type]: (state, action) => {
adapter.upsertMany(state, action.payload.courses);
},
- [deleteEntities.type]: (state, action) => {
+ [deleteEntities.type]: (state) => {
adapter.removeAll(state);
},
},
@@ -41,7 +41,9 @@ export const {
selectEntities: selectCourseEntities,
selectAll: selectAllCourses,
selectTotal: selectTotalCourses,
-} = adapter.getSelectors((state: any) => state.courses);
+} = adapter.getSelectors(
+ (state: { courses: EntityState }) => state.courses
+);
export const {
coursesAddOne,
diff --git a/app/model/LocationSlice.tsx b/app/model/LocationSlice.tsx
index bdce402..821d657 100644
--- a/app/model/LocationSlice.tsx
+++ b/app/model/LocationSlice.tsx
@@ -31,7 +31,7 @@ const slice = createSlice({
adapter.upsertMany(state, action.payload.locations);
}
},
- [deleteEntities.type]: (state, action) => {
+ [deleteEntities.type]: (state) => {
adapter.removeAll(state);
},
},
@@ -43,7 +43,9 @@ export const {
selectEntities: selectLocationEntities,
selectAll: selectAllLocations,
selectTotal: selectTotalLocations,
-} = adapter.getSelectors((state: any) => state.locations);
+} = adapter.getSelectors(
+ (state: { locations: EntityState }) => state.locations
+);
export const {
locationsAddOne,
diff --git a/app/model/NoteSlice.tsx b/app/model/NoteSlice.tsx
index 5588fea..e49dca5 100644
--- a/app/model/NoteSlice.tsx
+++ b/app/model/NoteSlice.tsx
@@ -29,7 +29,7 @@ const slice = createSlice({
adapter.upsertMany(state, action.payload.notes);
}
},
- [deleteEntities.type]: (state, action) => {
+ [deleteEntities.type]: (state) => {
adapter.removeAll(state);
},
},
@@ -53,6 +53,6 @@ export const {
selectEntities: selectNoteEntities,
selectAll: selectAllNotes,
selectTotal: selectTotalNotes,
-} = adapter.getSelectors((state: any) => state.notes);
+} = adapter.getSelectors((state: { notes: EntityState }) => state.notes);
export default slice.reducer;
diff --git a/app/model/RatingSlice.tsx b/app/model/RatingSlice.tsx
index 5f1c748..db27b55 100644
--- a/app/model/RatingSlice.tsx
+++ b/app/model/RatingSlice.tsx
@@ -3,7 +3,6 @@ import {
createEntityAdapter,
createSlice,
EntityState,
- PayloadAction,
} from '@reduxjs/toolkit';
import { loadCorrections, deleteEntities } from './CorrectionsSlice';
import RatingEntity from './RatingEntity';
@@ -32,7 +31,7 @@ const slice = createSlice({
adapter.upsertMany(state, action.payload.ratings);
}
},
- [deleteEntities.type]: (state, action) => {
+ [deleteEntities.type]: (state) => {
adapter.removeAll(state);
},
},
@@ -44,7 +43,9 @@ export const {
selectEntities: selectRatingEntities,
selectAll: selectAllRatings,
selectTotal: selectTotalRatings,
-} = adapter.getSelectors((state: any) => state.ratings);
+} = adapter.getSelectors(
+ (state: { ratings: EntityState }) => state.ratings
+);
export const {
ratingsAddOne,
diff --git a/app/model/SchoolSlice.tsx b/app/model/SchoolSlice.tsx
index 546434f..3587029 100644
--- a/app/model/SchoolSlice.tsx
+++ b/app/model/SchoolSlice.tsx
@@ -29,7 +29,7 @@ const slice = createSlice({
[loadCorrections.type]: (state, action) => {
adapter.upsertMany(state, action.payload.schools);
},
- [deleteEntities.type]: (state, action) => {
+ [deleteEntities.type]: (state) => {
adapter.removeAll(state);
},
},
@@ -41,7 +41,9 @@ export const {
selectEntities: selectSchoolEntities,
selectAll: selectAllSchools,
selectTotal: selectTotalSchools,
-} = adapter.getSelectors((state: any) => state.schools);
+} = adapter.getSelectors(
+ (state: { schools: EntityState }) => state.schools
+);
export const {
schoolsAddOne,
diff --git a/app/model/Selectors.ts b/app/model/Selectors.ts
index 0072911..e2a92c6 100644
--- a/app/model/Selectors.ts
+++ b/app/model/Selectors.ts
@@ -13,8 +13,7 @@ import {
SheetsSchema,
} from './NormalizationSchema';
import Sheet from './Sheet';
-import SheetEntity from './SheetEntity';
-import { selectAllSheets, selectSheetIds } from './SheetSlice';
+import { selectSheetIds } from './SheetSlice';
export const selectAllEntities = createSelector(
[
diff --git a/app/model/SettingsSlice.ts b/app/model/SettingsSlice.ts
index 5cdd696..f83a890 100644
--- a/app/model/SettingsSlice.ts
+++ b/app/model/SettingsSlice.ts
@@ -1,5 +1,5 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
-import { ParserType } from '../parser/Parser';
+import ParserType from '../parser/ParserType';
import ConditionalComment from './ConditionalComment';
import { Theme } from './Theme';
diff --git a/app/model/SheetSlice.tsx b/app/model/SheetSlice.tsx
index 2882b20..55d31e2 100644
--- a/app/model/SheetSlice.tsx
+++ b/app/model/SheetSlice.tsx
@@ -1,5 +1,9 @@
/* eslint-disable import/no-cycle */
-import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
+import {
+ createEntityAdapter,
+ createSlice,
+ EntityState,
+} from '@reduxjs/toolkit';
import { loadCorrections, deleteEntities } from './CorrectionsSlice';
import SheetEntity from './SheetEntity';
@@ -50,4 +54,6 @@ export const {
selectEntities: selectSheetEntities,
selectAll: selectAllSheets,
selectTotal: selectTotalSheets,
-} = adapter.getSelectors((state: any) => state.sheets);
+} = adapter.getSelectors(
+ (state: { sheets: EntityState }) => state.sheets
+);
diff --git a/app/model/SubmissionSlice.tsx b/app/model/SubmissionSlice.tsx
index 5135de8..c7478fd 100644
--- a/app/model/SubmissionSlice.tsx
+++ b/app/model/SubmissionSlice.tsx
@@ -1,5 +1,9 @@
/* eslint-disable import/no-cycle */
-import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
+import {
+ createEntityAdapter,
+ createSlice,
+ EntityState,
+} from '@reduxjs/toolkit';
import { loadCorrections, deleteEntities } from './CorrectionsSlice';
import SubmissionEntity from './SubmissionEntity';
@@ -48,4 +52,6 @@ export const {
selectEntities: selectSubmissionEntities,
selectAll: selectAllSubmissions,
selectTotal: selectTotalSubmissions,
-} = adapter.getSelectors((state: any) => state.submissions);
+} = adapter.getSelectors(
+ (state: { submissions: EntityState }) => state.submissions
+);
diff --git a/app/model/TaskSlice.tsx b/app/model/TaskSlice.tsx
index 815c6c1..84e1101 100644
--- a/app/model/TaskSlice.tsx
+++ b/app/model/TaskSlice.tsx
@@ -2,6 +2,7 @@
import {
createEntityAdapter,
createSlice,
+ EntityState,
PayloadAction,
} from '@reduxjs/toolkit';
import { loadCorrections, deleteEntities } from './CorrectionsSlice';
@@ -46,7 +47,7 @@ const slice = createSlice({
}, */
},
extraReducers: {
- [loadCorrections.type]: (state: any, action) => {
+ [loadCorrections.type]: (state: EntityState, action) => {
if (action.payload.tasks !== undefined) {
adapter.upsertMany(state, action.payload.tasks);
}
@@ -105,4 +106,6 @@ export const {
selectEntities: selectTaskEntities,
selectAll: selectAllTasks,
selectTotal: selectTotalTasks,
-} = adapter.getSelectors((state: any) => state.tasks);
+} = adapter.getSelectors(
+ (state: { tasks: EntityState }) => state.tasks
+);
diff --git a/app/model/TermSlice.tsx b/app/model/TermSlice.tsx
index f264632..929949e 100644
--- a/app/model/TermSlice.tsx
+++ b/app/model/TermSlice.tsx
@@ -29,7 +29,7 @@ const slice = createSlice({
[loadCorrections.type]: (state, action) => {
adapter.upsertMany(state, action.payload.terms);
},
- [deleteEntities.type]: (state, action) => {
+ [deleteEntities.type]: (state) => {
adapter.removeAll(state);
},
},
@@ -41,7 +41,7 @@ export const {
selectEntities: selectTermEntities,
selectAll: selectAllTerms,
selectTotal: selectTotalTerms,
-} = adapter.getSelectors((state: any) => state.terms);
+} = adapter.getSelectors((state: { terms: EntityState }) => state.terms);
export const {
termsAddOne,
diff --git a/app/package.json b/app/package.json
index 99f98f2..d501685 100644
--- a/app/package.json
+++ b/app/package.json
@@ -1,7 +1,7 @@
{
"name": "correctinator",
"productName": "correctinator",
- "version": "1.4.4",
+ "version": "1.4.5",
"description": "A correction program with media viewer for Uni2Work rating files",
"main": "./main.prod.js",
"author": {
diff --git a/app/parser/Parser.ts b/app/parser/Parser.ts
index 4f1ef5f..dc34e5c 100644
--- a/app/parser/Parser.ts
+++ b/app/parser/Parser.ts
@@ -1,12 +1,9 @@
import Correction from '../model/Correction';
-
-export enum ParserType {
- Uni2Work = 'UNI2WORK',
-}
+import ParserType from './ParserType';
export default interface Parser {
configFilePattern: RegExp;
- deserialize(text: string, dirName: string): Correction;
+ deserialize(text: string, dirName: string, fileName: string): Correction;
serialize(correction: Correction, tasksAndComments?: string): string;
getConfigFileName(correction: Correction): string;
getType(): ParserType;
diff --git a/app/parser/ParserOptions.ts b/app/parser/ParserOptions.ts
new file mode 100644
index 0000000..9a571fc
--- /dev/null
+++ b/app/parser/ParserOptions.ts
@@ -0,0 +1,9 @@
+import ParserType from './ParserType';
+
+type ParserOptions = {
+ type: ParserType;
+ language: string;
+ fileName: string;
+};
+
+export default ParserOptions;
diff --git a/app/parser/ParserType.ts b/app/parser/ParserType.ts
new file mode 100644
index 0000000..49eb682
--- /dev/null
+++ b/app/parser/ParserType.ts
@@ -0,0 +1,4 @@
+enum ParserType {
+ Uni2Work = 'UNI2WORK',
+}
+export default ParserType;
diff --git a/app/parser/ParserUtil.ts b/app/parser/ParserUtil.ts
index 4e065f2..78d2216 100644
--- a/app/parser/ParserUtil.ts
+++ b/app/parser/ParserUtil.ts
@@ -1,4 +1,5 @@
-import Parser, { ParserType } from './Parser';
+import Parser from './Parser';
+import ParserType from './ParserType';
import Uni2WorkParser from './Uni2WorkParser';
const instanciateParser = (type: ParserType): Parser => {
diff --git a/app/parser/Uni2WorkParser.test.ts b/app/parser/Uni2WorkParser.test.ts
index 1c9d173..38c5ded 100644
--- a/app/parser/Uni2WorkParser.test.ts
+++ b/app/parser/Uni2WorkParser.test.ts
@@ -1,6 +1,7 @@
import Correction from '../model/Correction';
import Status from '../model/Status';
import Parser from './Parser';
+import ParserType from './ParserType';
import Uni2WorkParser, { Uni2WorkDataStructure } from './Uni2WorkParser';
const parser: Parser = new Uni2WorkParser();
@@ -31,6 +32,32 @@ rating_done: false # TODO: Von false auf true setzen, sobald Bewertung abgeschlo
# TODO: Korrektur-Kommentar für die Studierenden unterhalb der Abtrennung (...) eintragen
...`;
+const u2wTestString4 = `
+%YAML 1.2
+---
+# Meta-Informationen zur Korrektur (werden beim Hochladen ignoriert)
+term: Winter 2021/22
+school: Institut für Informatik
+course: Rechnerarchitektur
+sheet:
+ name: Online-Hausarbeit 5
+ type: normal
+ grading:
+ max: 10
+ type: points
+rated_by: John Doe
+rated_at: null
+
+# Abgabenummer; wird beim Hochladen mit dem Dateinamen abgeglichen
+submission: uwazxvya2akrnnc2
+
+# Bewertung
+points: null # TODO: Hier die Punktezahl statt null eintragen (bis zu zwei Nachkommastellen, Punkt als Dezimalseparator; z.B. 17.03)
+rating_done: false # TODO: Von false auf true setzen, sobald Bewertung abgeschlossen; sonst Korrektur für die Studierenden nicht sichtbar und keine Anrechnung auf Prüfungsbonus
+
+# TODO: Korrektur-Kommentar für die Studierenden unterhalb der Abtrennung (...) eintragen
+...`;
+
const u2wTestData1: Uni2WorkDataStructure = {
term: 'SoSe 2020',
school: 'Institut für Informatik',
@@ -84,9 +111,59 @@ const correctionTestData1: Correction = {
},
// note: { text: '' },
// annotation: { text: '' },
+ parserOptions: {
+ fileName: 'bewertung_uwazxvya2akrnnc2.txt',
+ language: 'de',
+ type: ParserType.Uni2Work,
+ },
};
correctionTestData1.submission.correction = correctionTestData1;
+const correctionTestData4: Correction = {
+ id: '5475c708-3ced-5ff5-ad51-a1c12f8a2757',
+ submission: {
+ id: 'd519b90c-6b6e-5d67-9e0d-318f05693b01',
+ name: 'uwazxvya2akrnnc2',
+ sheet: {
+ id: 'cbce52d6-5bba-599d-ba60-273d23ef3d2e',
+ name: 'Online-Hausarbeit 5',
+ type: 'normal',
+ maxValue: 10,
+ valueType: 'points',
+ school: {
+ id: '344bd582-e110-53f6-a10c-6a14d6a7e291',
+ name: 'Institut für Informatik',
+ },
+ term: {
+ id: '2bb8000f-5fed-5a2e-a43d-a0435b3476d8',
+ year: 2021,
+ summerterm: false,
+ },
+ course: {
+ id: '73ae54b7-afdd-5912-a5d0-a1dd488fe912',
+ name: 'Rechnerarchitektur',
+ },
+ },
+ },
+ corrector: {
+ id: 'b3aa67d2-4ae3-5441-860a-88ab5391673d',
+ name: 'John Doe',
+ },
+ status: Status.Todo,
+ location: {
+ id: '148bdfa9-7596-5319-a197-ead64880df40',
+ name: null,
+ },
+ // note: { text: '' },
+ // annotation: { text: '' },
+ parserOptions: {
+ fileName: 'rating_uwazxvya2akrnnc2.txt',
+ language: 'en',
+ type: ParserType.Uni2Work,
+ },
+};
+correctionTestData4.submission.correction = correctionTestData4;
+
const u2wTestString2 = `term: SoSe 2020
school: Institut für Informatik
course: Rechnerarchitektur
@@ -339,6 +416,40 @@ test('serializeTerm WiSe 2020/21', () => {
).toBe(u2wTestData2.term);
});
+test('serializeTerm SoSe 2021 eng', () => {
+ expect(
+ Uni2WorkParser.serializeTerm(
+ {
+ id: 'b91bb5ff-1141-53f7-96b5-2cafc53ce6ea',
+ year: 2021,
+ summerterm: true,
+ },
+ {
+ fileName: 'rating_uwazxvya2akrnnc2.txt',
+ language: 'en',
+ type: ParserType.Uni2Work,
+ }
+ )
+ ).toBe('Summer 2021');
+});
+
+test('serializeTerm WiSe 2020/21 eng', () => {
+ expect(
+ Uni2WorkParser.serializeTerm(
+ {
+ id: 'cc47b29b-a507-533f-b4db-1fee84ed81fb',
+ year: 2021,
+ summerterm: false,
+ },
+ {
+ fileName: 'rating_uwazxvya2akrnnc2.txt',
+ language: 'en',
+ type: ParserType.Uni2Work,
+ }
+ )
+ ).toBe('Winter 2021/22');
+});
+
test('deserializeTerm SoSe 2020', () => {
expect(Uni2WorkParser.deserializeTerm(u2wTestData1.term)).toStrictEqual({
id: 'b91bb5ff-1141-53f7-96b5-2cafc53ce6ea',
@@ -355,6 +466,22 @@ test('deserializeTerm WiSe 2020/21', () => {
});
});
+test('deserializeTerm SoSe 2021 eng', () => {
+ expect(Uni2WorkParser.deserializeTerm('Summer 2021')).toStrictEqual({
+ id: '230fe0c4-e9cc-5515-9101-2449f3b96dec',
+ year: 2021,
+ summerterm: true,
+ });
+});
+
+test('deserializeTerm WiSe 2021/22 eng', () => {
+ expect(Uni2WorkParser.deserializeTerm('Winter 2021/22')).toStrictEqual({
+ id: '2bb8000f-5fed-5a2e-a43d-a0435b3476d8',
+ year: 2021,
+ summerterm: false,
+ });
+});
+
// Status
test('deserializeStatus with rating_done false', () => {
@@ -417,9 +544,23 @@ test('deserializeSchool Institut für Informatik', () => {
// Correction
test('deserialize Correction u2wTestData1', () => {
- expect(parser.deserialize(u2wTestString1, 'uwazxvya2akrnnc2')).toMatchObject(
- correctionTestData1
- );
+ expect(
+ parser.deserialize(
+ u2wTestString1,
+ 'uwazxvya2akrnnc2',
+ 'bewertung_uwazxvya2akrnnc2.txt'
+ )
+ ).toMatchObject(correctionTestData1);
+});
+
+test('deserialize Correction u2wTestData4', () => {
+ expect(
+ parser.deserialize(
+ u2wTestString4,
+ 'uwazxvya2akrnnc2',
+ 'rating_uwazxvya2akrnnc2.txt'
+ )
+ ).toMatchObject(correctionTestData4);
});
test('serialize correctionTestData2', () => {
diff --git a/app/parser/Uni2WorkParser.ts b/app/parser/Uni2WorkParser.ts
index 0cc8f1b..dc5ed2e 100644
--- a/app/parser/Uni2WorkParser.ts
+++ b/app/parser/Uni2WorkParser.ts
@@ -1,6 +1,6 @@
/* eslint-disable class-methods-use-this */
import * as YAML from 'yaml';
-import Parser, { ParserType } from './Parser';
+import Parser from './Parser';
import UUID from '../utils/UUID';
import Term from '../model/Term';
import Correction from '../model/Correction';
@@ -10,6 +10,8 @@ import Location from '../model/Location';
import Course from '../model/Course';
import School from '../model/School';
import Rating from '../model/Rating';
+import ParserOptions from './ParserOptions';
+import ParserType from './ParserType';
export type Uni2WorkDataStructure = {
term: string;
@@ -39,10 +41,14 @@ export type Uni2WorkDataStructure = {
};
export default class Uni2WorkParser implements Parser {
- public configFilePattern = /bewertung_([a-z0-9]{16})\.txt/g;
-
- public deserialize(text: string, dirName: string): Correction {
- const [configText, ...rest] = text.split('...');
+ public configFilePattern = /(bewertung|rating)_([a-z0-9]{16})\.txt/g;
+
+ public deserialize(
+ text: string,
+ dirName: string,
+ fileName: string
+ ): Correction {
+ const configText = text.split('...')[0];
const u2wDoc = YAML.parseDocument(configText);
if (u2wDoc.errors.length > 0) {
@@ -86,6 +92,11 @@ export default class Uni2WorkParser implements Parser {
corrector: Uni2WorkParser.deserializeCorrector(u2wData.rated_by),
status: Uni2WorkParser.deserializeStatus(u2wData.rating_done),
location: Uni2WorkParser.deserializeLocation(u2wData.rated_at),
+ parserOptions: {
+ type: ParserType.Uni2Work,
+ language: fileName.includes('rating') ? 'en' : 'de',
+ fileName,
+ },
};
// Exam Sheet
@@ -105,8 +116,8 @@ export default class Uni2WorkParser implements Parser {
public static deserializeTerm(term: string): Term {
// More detailed: 1. WiSe|SoSe 2. Century e.g. 20 3. Year start 4. Year end (only WiSe)
// const termPattern = /(wise|sose)\s*(\d{2})(\d{2})(?:\/(\d{2}))?/gi;
- const termPattern = /(wise|sose)\s*(\d{4})/i;
- const summertermPattern = /sose/gi;
+ const termPattern = /(wise|sose|Winter|Summer)\s*(\d{4})/i;
+ const summertermPattern = /sose|Summer/gi;
const termGroups = term.match(termPattern);
if (termGroups === null) {
throw new Error(`Could not parse term!`);
@@ -121,8 +132,16 @@ export default class Uni2WorkParser implements Parser {
};
}
- public static serializeTerm(term: Term): string {
- return `${term.summerterm ? 'SoSe' : 'WiSe'} ${term.year}${
+ public static serializeTerm(term: Term, options?: ParserOptions): string {
+ let summerterm = 'SoSe';
+ let winterterm = 'WiSe';
+
+ if (options?.language === 'en') {
+ summerterm = 'Summer';
+ winterterm = 'Winter';
+ }
+
+ return `${term.summerterm ? summerterm : winterterm} ${term.year}${
term.summerterm ? '' : `/${String(term.year + 1).slice(-2)}`
}`;
}
@@ -165,7 +184,10 @@ export default class Uni2WorkParser implements Parser {
public serialize(correction: Correction, tasksAndComments = ''): string {
const u2wData: Uni2WorkDataStructure = {
- term: Uni2WorkParser.serializeTerm(correction.submission.sheet.term),
+ term: Uni2WorkParser.serializeTerm(
+ correction.submission.sheet.term,
+ correction.parserOptions
+ ),
school: correction.submission.sheet.school.name,
course: correction.submission.sheet.course.name,
sheet: {
@@ -199,7 +221,10 @@ export default class Uni2WorkParser implements Parser {
}
getConfigFileName(correction: Correction): string {
- return `bewertung_${correction.submission.name}.txt`;
+ return (
+ correction.parserOptions?.fileName ||
+ `bewertung_${correction.submission.name}.txt`
+ );
}
getType(): ParserType {
diff --git a/app/store.ts b/app/store.ts
index 15d3598..656b33a 100644
--- a/app/store.ts
+++ b/app/store.ts
@@ -33,6 +33,7 @@ const persistConfig = {
export const history = createHashHistory();
const combinedReducer = createRootReducer(history);
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
const rootReducer: any = persistReducer(persistConfig, combinedReducer);
export type RootState = ReturnType;
@@ -100,4 +101,4 @@ export const configuredStore = (initialState?: RootState) => {
export type Store = ReturnType;
export type AppThunk = ThunkAction>;
// eslint-disable-next-line import/prefer-default-export
-export const useAppDispatch = () => useDispatch();
+export const useAppDispatch = () => useDispatch();
diff --git a/app/utils/TaskUtil.ts b/app/utils/TaskUtil.ts
index bbfe561..de77780 100644
--- a/app/utils/TaskUtil.ts
+++ b/app/utils/TaskUtil.ts
@@ -35,19 +35,21 @@ export function getTaskType(task: Task | TaskEntity): TaskType {
return TaskType.Parent;
}
-export function hasTasksWithZeroMax(ts: Task[] | TaskEntity[]): boolean {
- const test: Task | TaskEntity = (ts as any).find((t: Task | TaskEntity) => {
- if (isRateableTask(t)) {
- if (t.max === 0) {
- return true;
- }
- } else if (isSingleChoiceTask(t)) {
- if (t.answer.value === 0) {
- return true;
+export function hasTasksWithZeroMax(ts: (Task | TaskEntity)[]): boolean {
+ const test: Task | TaskEntity | undefined = ts.find(
+ (t: Task | TaskEntity) => {
+ if (isRateableTask(t)) {
+ if (t.max === 0) {
+ return true;
+ }
+ } else if (isSingleChoiceTask(t)) {
+ if (t.answer.value === 0) {
+ return true;
+ }
}
+ return false;
}
- return false;
- });
+ );
return test !== undefined;
}
@@ -72,22 +74,6 @@ export function getTopLevelTasks(tasks: Task[]): Task[] {
return top;
}
-export function removeTaskIds(tasks: Task[], res: any = []) {
- tasks.forEach((t) => {
- if (isParentTask(t)) {
- const { id, ...rest } = t;
- const temp: any = { ...rest };
- temp.tasks = removeTaskIds(t.tasks);
- res.push(temp);
- } else {
- const { id, ...rest } = t;
- const temp = { ...rest };
- res.push(temp);
- }
- });
- return res;
-}
-
export function flatMapTask(pt: ParentTask, list: Task[] = []) {
list.push(pt);
pt.tasks.forEach((t) => {
diff --git a/package.json b/package.json
index fea2c5e..d944bb7 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "correctinator",
"productName": "correctinator",
- "version": "1.4.4",
+ "version": "1.4.5",
"description": "A correction program with media viewer for Uni2Work rating files",
"scripts": {
"build": "concurrently \"yarn build-main\" \"yarn build-renderer\"",