This application was created as my bachelor's thesis.
It serves as a digital database of receipts. The receipts are captured using a phone camera. An OCR service extracts the receipt information and pre-fills the form. The form can then be further edited. The receipts can be searched by the merchant name, merchant address and item names of the receipt. It is written in React Native. It uses Microsoft Form Recognizer for information extraction from the image, Magnitude to categorize receipt items and OpenCV for image processing. It is available on Android and Windows. However, it is not in the stores yet.
Not all functionality (e.g. running unit tests) is described in this README. See
package.json
for all available npm scripts.
If you want to try the application:
- go to GitHub CI action
- click on the latest run that passed
- scroll to the end of the page and download the
app-release.apk
artifact - unzip it and move the
app-release.apk
file to your phone and install it - now run the newly installed Receipts Scanner
If you want to run the application on the emulator or develop the application, follow the environment setup. (Choose React Native CLI Quickstart, your Development OS and Android as the Target OS.)
Then:
# clone the repository
git clone https://github.com/petr7555/bachelors_thesis_accounting_ocr
cd bachelors_thesis_accounting_ocr
# install dependencies
npm ci
# run the application on an emulator or connected device (this command starts Metro bundler (and emulator) automatically)
npm run android
There is currently no executable you could install to run the application. You need to setup the environment. If you do not have Windows PC, you can use virtual machine.
Then:
# clone the repository
git clone https://github.com/petr7555/bachelors_thesis_accounting_ocr
cd bachelors_thesis_accounting_ocr
# install dependencies
npm ci
# run the application on PC (this command starts Metro bundler automatically)
npm run windows
Storybook has been used as a component library. It can be run on the emulator/device, optionally with a browser tab open to switch the stories. Thanks to React Native for Web, also regular Storybook for Web can be used.
-
npm run android
-
npm run storybook:rn
-
Refresh the application in the metro bundler. The components should now be visible in the browser in the sidebar.
-
The Storybook running in the browser is used to navigate between components. The components themselves are shown on a mobile device/emulator.
-
When adding a new story, you must import it in
~/storybook-rn/storybook-registry.ts
. -
This video shows how Storybook running on an emulator looks:
-
Web version of Storybook is deployed to https://bachelors-thesis-accounting-ocr.vercel.app/ with each push to GitHub. This is not part of GitHub Actions but Vercel's own repository hook. This deployment is partially redundant because Storybook is also deployed during Chromatic build.
-
A directory
.vercel
containing aproject.json
with orgId and projectId needs to be present in the root of the repository to deploy the Storybook to Vercel locally, which is done by runningnpm run deploy-vercel
(currently does not work because the file number limit is reached). This directory is ignored in version control as it contains sensitive information. -
Keep in mind, that in Storybook for Web
Platform.OS === 'web'
, so bothisAndroid
andisWindows
arefalse
. -
Storybook for Web finds all stories that match this pattern
../src/**/*.stories.tsx
. -
This image shows how Storybook running on the web looks:
Because Storybook for Web is in version 6, it can use the modern Storybook syntax. However, the latest Storybook for React Native is only in version 5 and requires the old syntax.
storiesOf
creates the story for React Nativeexport const WithEmoji
together with
export default {
title: 'Buttons/PrimaryButton',
};
creates the story for Web. We can reuse some code by writing the story the following way:
export const WithEmoji = () => (
<PrimaryButton onPress={action('clicked-emoji')} title="😀 😎 👍 💯 🚀"/>
);
storiesOf('PrimaryButton', module).add('With emoji', WithEmoji);
export default {
title: 'Buttons/PrimaryButton',
};
npm run start
to start the Metro bundler. We need it because debug version of the app is run.npm run e2e:build
npm run e2e:test
- troubleshooting:
npm run start --reset-cache
- make sure the App is visible and not the Storybook
- Release version of the app is built, therefore the Metro bundler is not needed.
- Artifacts from the tests (both passing and failing) are saved. These contain:
- logs
- screenshots
- videos
- timeline
- Configuration of artifacts
⚠️ At the current stage, the visual regression Loki tests for React Native are unreliable and should not be used.
npm run android
npm run storybook:rn
- Refresh the application in the metro bundler. The components should now be visible in the browser in the sidebar.
npm run loki:update:rn
to create reference imagesnpm run loki:test:rn
to create new images and compare them with the reference images⚠️ setconst VISUAL_TESTS = true;
inindex.js
, this reduces flakiness⚠️ there is still a lot of flakiness, many elements interfere with the snapshots
npm run storybook:web
npm run loki:update:web
to create reference imagesnpm run loki:test:web
to create new images and compare them with the reference images
⚠️ Storycap / Reg Suit tests do not work correctly (Reg Suit does not recognize properly the id of the previous snapshots and reports all stories as new), therefore Chromatic should be used instead.
-
npm run storycap:web
takes the screenshots and saves them to__screenshots__
-
npm run reg-suit:web:dev
dev, as opposed to ci, exports path to Google Application Credentials file. A fileg_app_cred.json
containing the credentials needs to be present in the root of the repository. These credentials are used to connect to Google's bucket, where snapshot images are stored. This file is ignored in version control as it contains sensitive information. In the CI pipeline, repository secrets stored on GitHub are used instead. -
visual-test:web:dev
is a shortcut for the two commands above- First, note that doing this makes sense when there are no uncommitted changes.
- Reg-suit expects that this is run after you have committed all changes.
- You take screenshots of the current state of the app by running
npm run storycap:web
. They are saved into__screenshots__
. reg-suit sync-expected
uses the keygen plugin to detect the previous commit when snapshots have been published. It downloads screenshots from Google Cloud for this commit. The screenshots are stored in Google Cloud Storage insidereg-publish-bucket-...
in a folder whose name is the detected commit. From there, they are downloaded and stored inside.reg/expected
locally.reg-suit compare
copies new images from__screenshots__
to.reg/actual
, compares them with the images in.reg/expected
and stores the difference in.reg/diff
.reg-suit publish
uses the last commit hash to create a new folder in Google Cloud and upload new images there.- in CI,
reg-suit publish -n
is run with the-n
parameter to create a PR comment on GitHub.
- Chromatic is used for visual testing and Storybook deployment.
- During each push, the stories are compared for visual changes, and the Storybook is deployed.
- Apart from the ordinal Storybook UI, Chromatic also creates its own component library.
- The visual changes can be reviewed and, if they are desired, they should be accepted in the Chromatic UI.
- The link to the newest changes is provided in the build output.
- To run it locally, run
npm run chromatic
.
- build:
npm run build-storybook:web
(this command is also used by Chromatic to build the static version of Storybook) - serve:
npm run serve-storybook:web
- TSLint is deprecated in favour of ESLint with TypeScript plugins.
- ESLint performs automated scans for common syntax and style errors.
- Prettier scans files for style issues and automatically reformats code to ensure consistent rules are being followed for indentation, spacing, semicolons, single quotes vs double quotes, etc.
- ESLint and Prettier might clash. This solves
eslint-config-prettier
configuration, which turns off formatting-related rules that might conflict with Prettier. eslint-plugin-prettier
is a plugin that integrates Prettier to ESLint, so that ESLint reports Prettier errors.- ESLint can also catch type errors. However, using the TypeScript compiler itself is probably better -
tsc --noEmit
.
- Run
RN Debug
configuration in debug mode. - Press
⌘M
to toggle In-App Developer Menu. Click Debug (second option from the top). - Reload the application (in the browser on
http://localhost:8081/debugger-ui/
or in the In-App Developer Menu (⌘M
)) .
- Reactotron
- if the device is not connected, run
adb reverse tcp:9090 tcp:9090
and refresh (r
) in the metro bundler. This applies both to an emulator and a real device.
- if the device is not connected, run
- patch-package can be used to fix third-party dependencies
in
node_modules
.postinstall
script is run automatically both afternpm install
andnpm ci
.
- Use constants in
~/src/global/styles/colors.ts
for colors that do not change based on light/dark mode. - Use
useTheme
hook fromreact-native-elements
to obtain a theme that changes according to whether a dark mode is enabled or not (for black and white colors). - This
Component
will have a black color in light mode and white color in dark mode.
import {useTheme} from 'react-native-elements';
const {theme} = useTheme();
return <Component color={theme.colors?.black}/>;
- Always use
Text
fromreact-native-elements
to reflect the dark mode settings. - Use
useTheme
hook from@react-navigation/native
to obtain a theme that changes according to whether a dark mode is enabled or not for any colors. These colors have to be defined inApp.tsx
forMyDefaultTheme
andMyDarkTheme
.
import {useTheme} from '@react-navigation/native';
import {MixedTheme} from '../../../App';
const {colors} = useTheme() as MixedTheme;
return <Component style={{color: colors.secondary}}/>;
- Do not use
console.log()
etc. - Use
LOG.info()
etc. from~/src/services/Logger/logger.ts
instead. See react-native-logs for usage instructions. - In development mode, all logs will appear in the console. WARN and higher will be sent to the Sentry too (both in development and release).
emulator -list-avds
- the emulator to be cleaned must not be running
emulator -avd Pixel_3a_API_29 -wipe-data
Follow https://reactnative.dev/docs/signed-apk-android or run:
cd android
./gradlew bundleRelease
The build uses a password stored in a Keychain, as described
in this tutorial. It also
requires my-upload-key.keystore
file that should be in ~/android/app/
folder. This file is ignored in version
control as it contains sensitive information. If the file is not present (e.g. in a CI pipeline), the debug
signingConfig will be used, and a warning will be shown in the console.
Upload the generated ~/android/app/build/outputs/bundle/release/app-release.aab
to Google Play Console.
This project uses Google Cloud Platform and Google Firebase for:
- Authentication
- Firestore
- Storage
- Google Cloud Build, Google Container Registry and Google Cloud Run for the deployment of Python API container
Microsoft Azure's services are used for:
- OCR - Form Recognizer from Cognitive Services
This application uses its own Python API for image processing and item categorization. See the python-api/README.md .
Auto-linking happens automatically at the start of npm run android
/npm run windows
command. It can be triggered
manually by running npx react-native link
.
- Invariant Violation: Module AppRegistry is not a registered callable module (calling runApplication)
- solution:
npm start -- --reset-cache
- Check if there are other errors above,
e.g.
TypeError: Cannot read property '<some property of native module>' of undefined
. This happens on Windows. It means that the native module has not been linked automatically. Try linking it manually. However, most likely, the Windows support is old, vnext support is needed. Mock the library and wait for the vnext support to be implemented.
- solution:
- If you have multiple connections in Reactotron, switch to the correct one to be able to control Storybook.
- DO NOT DELETE
package-lock.json
! Dependencies will resolve badly, and something like this will show up:error: Error: Unable to resolve module ./prebuilt.rn-99b47b70.js from /Users/petr.janik/Documents/Projects/bachelors_thesis_accounting_ocr/node_modules/@firebase/firestore/dist/rn/index.js: ./prebuilt.rn-99b47b70.js could not be found within the project.
new Date()
behaves differently in RN and in the browser. The browser implementation is used when Debug is enabled. Whereasnew Date("2007/13:29:17")
fails without the debug mode, with debug mode and in the browser console, it returns the correct date.
Failed to restore the NuGet packages: Error: Errors in packages.config projects
The process cannot access the file 'C:\Users\User\Desktop\bachelors_thesis_accounting_ocr\windows\packages\Microsoft.SourceLink.GitHub.1.0.0\tools\net461\Microsoft.SourceLink.GitHub.dll' because it is being used by another process.
Solution: Kill MSBuild.exe
in Task Manager.
Invariant Violation: Module AppRegistry is not a registered callable module (calling runApplication)
Solution: Kill Node.js
in Task Manager.
-
When adding GitHub branch as a dependency, add it as
"dependency-name": "user/repository#branch"
intopackage.json
and make sure thatpackage-lock.json
contains"resolved": "git+https://git@github.com/...
, not"resolved": "git+ssh://git@github.com/...
. -
INSTALL_FAILED_INSUFFICIENT_STORAGE
Uninstall previously installed app.
- Error: Unable to resolve module ./debugger-ui/debuggerWorker.aca173c4 from .../bachelors_thesis_accounting_ocr/.: ./debugger-ui/debuggerWorker.aca173c4 could not be found within the project.
This is not a problem, ignore it.
Privacy Policy and Terms & Conditions have been generated using https://app-privacy-policy-generator.firebaseapp.com/ and slightly modified in the paragraph Privacy Policy -> Information Collection and Use.
They are available at Privacy Policy and Terms & Conditions.
They can be edited at https://app.flycricket.com/.
The source for the thesis text is available in a separate repository. It describes the application architecture and functionality.